From 2e271e1de75b8fe27dfdaa459f5b04f66b9be5c0 Mon Sep 17 00:00:00 2001 From: xbotter Date: Sat, 24 Jun 2023 17:25:55 +0800 Subject: [PATCH] feat: add WeChat Plugin --- .../BotSharp.Plugin.WeChat.csproj | 7 +- .../BotSharpMessageHandler.cs | 50 +++++++++++ .../BotSharp.Plugin.WeChat/IMessageQueue.cs | 12 +++ .../WeChatBackgroundService.cs | 90 +++++++++++++++++++ .../BotSharp.Plugin.WeChat/WeChatMessage.cs | 13 +++ .../BotSharp.Plugin.WeChat/WeChatPlugin.cs | 34 +++++++ src/WebStarter/appsettings.json | 2 +- 7 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.WeChat/BotSharpMessageHandler.cs create mode 100644 src/Plugins/BotSharp.Plugin.WeChat/IMessageQueue.cs create mode 100644 src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs create mode 100644 src/Plugins/BotSharp.Plugin.WeChat/WeChatMessage.cs create mode 100644 src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs diff --git a/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj b/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj index d1ee4dc0..4dc14bdf 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj +++ b/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj @@ -20,7 +20,12 @@ - + + + + + + diff --git a/src/Plugins/BotSharp.Plugin.WeChat/BotSharpMessageHandler.cs b/src/Plugins/BotSharp.Plugin.WeChat/BotSharpMessageHandler.cs new file mode 100644 index 00000000..f25c882d --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/BotSharpMessageHandler.cs @@ -0,0 +1,50 @@ +using BotSharp.Abstraction.Conversations; +using BotSharp.Abstraction.Infrastructures.ContentTransmitters; +using BotSharp.Abstraction.Models; +using Microsoft.Extensions.DependencyInjection; +using Senparc.NeuChar.App.AppStore; +using Senparc.NeuChar.Entities; +using Senparc.Weixin.MP.Entities; +using Senparc.Weixin.MP.Entities.Request; +using Senparc.Weixin.MP.MessageContexts; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace BotSharp.Plugin.WeChat +{ + public class BotSharpMessageHandler : Senparc.Weixin.MP.MessageHandlers.MessageHandler + { + public BotSharpMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0, bool onlyAllowEncryptMessage = false, DeveloperInfo developerInfo = null, IServiceProvider serviceProvider = null) : base(inputStream, postModel, maxRecordCount, onlyAllowEncryptMessage, developerInfo, serviceProvider) + { + } + + public BotSharpMessageHandler(XDocument requestDocument, PostModel postModel, int maxRecordCount = 0, bool onlyAllowEncryptMessage = false, DeveloperInfo developerInfo = null, IServiceProvider serviceProvider = null) : base(requestDocument, postModel, maxRecordCount, onlyAllowEncryptMessage, developerInfo, serviceProvider) + { + } + + public BotSharpMessageHandler(RequestMessageBase requestMessageBase, PostModel postModel, int maxRecordCount = 0, bool onlyAllowEncryptMessage = false, DeveloperInfo developerInfo = null, IServiceProvider serviceProvider = null) : base(requestMessageBase, postModel, maxRecordCount, onlyAllowEncryptMessage, developerInfo, serviceProvider) + { + } + + public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage) + { + return null; + } + + public async override Task OnTextRequestAsync(RequestMessageText requestMessage) + { + var messageQueue = ServiceProvider.GetRequiredService(); + await messageQueue.EnqueueAsync(new WeChatMessage() + { + OpenId = OpenId, + Message = requestMessage.Content, + Type = "text" + }); + return await base.OnTextRequestAsync(requestMessage); + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.WeChat/IMessageQueue.cs b/src/Plugins/BotSharp.Plugin.WeChat/IMessageQueue.cs new file mode 100644 index 00000000..b7d31cf6 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/IMessageQueue.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.WeChat +{ + public interface IMessageQueue + { + Task EnqueueAsync(WeChatMessage message); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs b/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs new file mode 100644 index 00000000..83a7092f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs @@ -0,0 +1,90 @@ +using BotSharp.Abstraction.Conversations; +using BotSharp.Abstraction.Infrastructures.ContentTransmitters; +using BotSharp.Abstraction.Models; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.WeChat +{ + public class WeChatBackgroundService : IHostedService, IMessageQueue + { + private readonly Channel _queue; + private readonly IConversationService _conversationService; + private readonly IContentTransfer _contentTransfer; + private readonly ILogger _logger; + + public WeChatBackgroundService(IConversationService conversationService, + IContentTransfer contentTransfer, + ILogger logger) + { + this._conversationService = conversationService; + this._contentTransfer = contentTransfer; + this._logger = logger; + this._queue = Channel.CreateUnbounded(); + } + + private async Task HandleTextMessageAsync(string openid, string message) + { + var conversations = _conversationService.GetDialogHistory(openid); + conversations.Add(new RoleDialogModel + { + Role = "User", + Text = message, + }); + + var container = new ContentContainer + { + Conversations = conversations + }; + + var result = await _contentTransfer.Transport(container); + + if (result.IsSuccess) + { + var output = container.Output.Text.Trim(); + await ReplyTextMessageAsync(openid, output); + _conversationService.AddDialog(new RoleDialogModel() + { + Role = "Assistant", + Text = output, + }); + } + } + + 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); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + var message = await _queue.Reader.ReadAsync(cancellationToken); + await HandleTextMessageAsync(message.OpenId, message.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occurred Handle Message"); + } + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public async Task EnqueueAsync(WeChatMessage message) + { + await _queue.Writer.WriteAsync(message); + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.WeChat/WeChatMessage.cs b/src/Plugins/BotSharp.Plugin.WeChat/WeChatMessage.cs new file mode 100644 index 00000000..944c2fce --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/WeChatMessage.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Plugin.WeChat +{ + public class WeChatMessage + { + public string OpenId { get; set; } + public string Type { get; set; } + public string Message { get; set; } + } +} diff --git a/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs b/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs new file mode 100644 index 00000000..f5b24a1c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WeChat/WeChatPlugin.cs @@ -0,0 +1,34 @@ +using BotSharp.Abstraction.Plugins; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Senparc.Weixin.AspNet; +using Senparc.Weixin.RegisterServices; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Plugin.WeChat +{ + + public class WeChatPlugin : IBotSharpPlugin + { + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + services.AddMemoryCache(); + + services.AddSenparcWeixinServices(config); + + services.AddHostedService(); + + services.TryAddSingleton(s => s.GetRequiredService()); + } + + public void ConfigurateApplication(IApplicationBuilder app) + { + // TODO: app.UseSenparcWeixin + } + } +} diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 90f666a2..d3bd76f2 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -48,7 +48,7 @@ "Master": "mongodb://localhost:27017/chat-ui" }, "Agent": { - "Master": "Data Source=(localdb)\\ProjectModels;Initial Catalog=Agent;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False", + "Master": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=Agent;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False", "Slavers": [] }, "UseCamelCase": true,