diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index e50426d7..cdbdf95b 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -18,7 +18,7 @@ public partial class AgentService { var db = _services.GetRequiredService(); var query = from agent in db.Agent - where agent.OwnerId == _user.Id && agent.Id == id + where agent.Id == id select agent.ToAgent(); var profile = query.FirstOrDefault(); diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs index 436a60c8..7482d5d9 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs @@ -14,12 +14,12 @@ public class UserIdentity : IUserIdentity _contextAccessor = contextAccessor; } - public string Id => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value; + public string Id => _claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value; - public string Email => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value; + public string Email => _claims.First(x => x.Type == ClaimTypes.Email).Value; - public string FirstName => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname").Value; + public string FirstName => _claims.First(x => x.Type == ClaimTypes.GivenName).Value; - public string LastName => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname").Value; + public string LastName => _claims.First(x => x.Type == ClaimTypes.Surname).Value; } diff --git a/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj b/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj index 611146dd..f8ab250e 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj +++ b/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Plugins/BotSharp.Plugin.WeChat/README.md b/src/Plugins/BotSharp.Plugin.WeChat/README.md index dcc51684..35344421 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/README.md +++ b/src/Plugins/BotSharp.Plugin.WeChat/README.md @@ -1,47 +1,45 @@ -# botsharp-channel-weixin -A channel module of BotSharp for Tencent Weixin +# botsharp-plugin-wechat -### How to install through NuGet - -``` -PM> Install-Package BotSharp.Channel.Weixin -``` +A channel plugin of BotSharp for Tencent WeChat ### How to run locally ``` -git clone https://github.com/dotnetcore/BotSharp +git clone https://github.com/SciSharp/BotSharp ``` -Check app.json to use DialogflowAi +Check appsettings.json to import module ``` { - "version": "0.1.0", - "assemblies": "BotSharp.Core", - - "platformModuleName": "DialogflowAi" -} -``` - -Update channels.weixin.json to set the corresponding KEY -``` -{ - "weixinChannel": { - "token": "botsharp", - "encodingAESKey": "", - "appId": "", - "agentId": "60bee6f9-ba58-4fe8-8b95-94af69d6fd41" + "PluginLoader": { + "Assemblies": [ + ... + "BotSharp.Plugin.WeChat" + ], + "Plugins": [ + ... + "WeChatPlugin" + ] } } ``` -F5 run BotSharp.WebHost +Update appsettings.json to set the corresponding KEY +``` +{ + "WeChat": { + "AgentId": "437bed34-1169-4833-95ce-c24b8b56154a", + "Token": "#{Token}#", + "EncodingAESKey": "#{EncodingAESKey}#", + "WeixinAppId": "#{WeixinAppId}#", + "WeixinAppSecret": "#{WeixinAppSecret}#" + } +} +``` -Access http://localhost:3112 +Update WeChat WebHook form https://mp.weixin.qq.com/ ,set as `https://{HOST}/WeChatAsync` -Import demo (Spotify.zip) agent located at App_Data +F5 run WebStarter -Train agent (id: 60bee6f9-ba58-4fe8-8b95-94af69d6fd41) +Access http://localhost:5500 -Or refer [BotSharp docs](https://botsharp.readthedocs.io) to design your new chatbot. - -Setup Wechat webhood from https://mp.weixin.qq.com/. +If you are debugging and developing locally, you can use an intranet penetration tool to receive WeChat message push. It is recommended to use the Dev Tunnels of VS. diff --git a/src/Plugins/BotSharp.Plugin.WeChat/Users/IWeChatAccountUserService.cs b/src/Plugins/BotSharp.Plugin.WeChat/Users/IWeChatAccountUserService.cs new file mode 100644 index 00000000..68f21019 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/Users/IWeChatAccountUserService.cs @@ -0,0 +1,11 @@ +using BotSharp.Abstraction.Users; +using BotSharp.Abstraction.Users.Models; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.WeChat.Users +{ + public interface IWeChatAccountUserService + { + Task GetOrCreateWeChatAccountUserAsync(string appId, string openId); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WeChat/Users/WeChatAccountUserService.cs b/src/Plugins/BotSharp.Plugin.WeChat/Users/WeChatAccountUserService.cs new file mode 100644 index 00000000..bf48c433 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/Users/WeChatAccountUserService.cs @@ -0,0 +1,30 @@ +using BotSharp.Abstraction.Users; +using BotSharp.Abstraction.Users.Models; +using BotSharp.Core.Repository; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.WeChat.Users +{ + public class WeChatAccountUserService : IWeChatAccountUserService + { + private readonly IUserService userService; + + public WeChatAccountUserService(IUserService userService) + { + this.userService = userService; + } + public async Task GetOrCreateWeChatAccountUserAsync(string appId, string openId) + { + var user = await userService.CreateUser(new User() + { + Email = openId + "@" + appId + ".wechat", + Password = openId + }); + return user; + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs b/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs index d1ed3109..d8946603 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs +++ b/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs @@ -1,10 +1,20 @@ +using BotSharp.Abstraction.Agents; using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Models; +using BotSharp.Abstraction.Users; +using BotSharp.Abstraction.Users.Models; +using BotSharp.Plugin.WeChat.Users; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Senparc.Weixin.Entities; using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -16,6 +26,8 @@ namespace BotSharp.Plugin.WeChat private readonly Channel _queue; private readonly IServiceProvider _service; private readonly ILogger _logger; + private string WeChatAppId => Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId; + public static string AgentId { get; set; } public WeChatBackgroundService( IServiceProvider service, @@ -30,9 +42,21 @@ namespace BotSharp.Plugin.WeChat private async Task HandleTextMessageAsync(string openid, string message) { var scoped = _service.CreateScope().ServiceProvider; + + var user = await GetWeChatAccountUserAsync(openid, scoped); + BindWeChatAccountUser(user, scoped); + var conversationService = scoped.GetRequiredService(); - var result = await conversationService.SendMessage(openid, Guid.Empty.ToString(), new RoleDialogModel + var latestConversationId = (await conversationService.GetConversations()).OrderByDescending(_ => _.CreatedTime).FirstOrDefault()?.Id; + + latestConversationId ??= (await conversationService.NewConversation(new Conversation() + { + UserId = openid, + AgentId = AgentId + }))?.Id; + + var result = await conversationService.SendMessage(AgentId, latestConversationId, new RoleDialogModel { Role = "user", Text = message, @@ -41,10 +65,34 @@ namespace BotSharp.Plugin.WeChat await ReplyTextMessageAsync(openid, result); } + private async Task GetWeChatAccountUserAsync(string openId, IServiceProvider service) + { + var userService = service.GetRequiredService(); + + return await userService.GetOrCreateWeChatAccountUserAsync(WeChatAppId, openId); + } + private void BindWeChatAccountUser(User user,IServiceProvider service) + { + var context = service.GetService(); + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, user.Id) + }; + + if (!string.IsNullOrWhiteSpace(user.Email)) + { + claims.Add(new Claim(ClaimTypes.Email, user.Email)); + } + + context.HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(claims)) + }; + } + private async Task ReplyTextMessageAsync(string openid, string content) { - var appId = Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId; - await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(appId, openid, content); + await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(WeChatAppId, openid, content); } public async Task EnqueueAsync(WeChatMessage message) diff --git a/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs b/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs index 38c6592f..b81d8ea4 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs @@ -17,6 +17,8 @@ using Senparc.Weixin.Entities; using Senparc.CO2NET.RegisterServices; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; +using BotSharp.Abstraction.Users; +using BotSharp.Plugin.WeChat.Users; namespace BotSharp.Plugin.WeChat { @@ -25,6 +27,8 @@ namespace BotSharp.Plugin.WeChat { public void RegisterDI(IServiceCollection services, IConfiguration config) { + services.AddScoped (); + services.AddMemoryCache(); services.Configure(config.GetSection("WeChat")); @@ -33,9 +37,10 @@ namespace BotSharp.Plugin.WeChat { services = services.AddSenparcGlobalServices(config); } + WeChatBackgroundService.AgentId = config["WeChat:AgentId"]; services.AddSingleton(); - + services.AddHostedService(s => s.GetRequiredService()); services.TryAddSingleton(s => s.GetRequiredService()); diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index ed480529..e891f61d 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -58,6 +58,7 @@ }, "WeChat": { + "AgentId": "437bed34-1169-4833-95ce-c24b8b56154a", "Token": "#{Token}#", "EncodingAESKey": "#{EncodingAESKey}#", "WeixinAppId": "#{WeixinAppId}#",