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,