Merge pull request #80 from xbotter/wechat-plugin

Wechat plugin
This commit is contained in:
Haiping 2023-06-26 22:01:02 -05:00 committed by GitHub
commit 884314dc2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 288 additions and 4 deletions

View file

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Builder;
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Abstraction.Plugins
{
public interface IBotSharpAppPlugin: IBotSharpPlugin
{
void Configure(IApplicationBuilder app);
}
}

View file

@ -37,6 +37,8 @@ public static class BotSharpServiceCollectionExtensions
throw new ArgumentNullException(nameof(app));
}
app.ApplicationServices.GetRequiredService<PluginLoader>().Configure(app);
return app;
}
@ -74,5 +76,7 @@ public static class BotSharpServiceCollectionExtensions
var loader = new PluginLoader(services, config, pluginSettings);
loader.Load();
services.AddSingleton(loader);
}
}

View file

@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using System.Drawing;
using System.IO;
@ -13,7 +14,7 @@ public class PluginLoader
private readonly PluginLoaderSettings _settings;
private static List<IBotSharpPlugin> _modules = new List<IBotSharpPlugin>();
public PluginLoader(IServiceCollection services,
public PluginLoader(IServiceCollection services,
IConfiguration config,
PluginLoaderSettings settings)
{
@ -59,4 +60,23 @@ public class PluginLoader
}
});
}
public void Configure(IApplicationBuilder app)
{
if(_modules.Count == 0)
{
Console.WriteLine($"No plugin loaded. Please check whether the Load() method is called.", Color.Yellow);
}
_modules.ForEach(module =>
{
if (module.GetType().GetInterface(nameof(IBotSharpAppPlugin)) != null)
{
if (_settings.Plugins.Contains(module.GetType().Name))
{
(module as IBotSharpAppPlugin).Configure(app);
}
}
});
}
}

View file

@ -20,7 +20,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Senparc.Weixin.MP.MVC" Version="7.12.5.7" />
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Senparc.Weixin.MP.Middleware" Version="0.8.6" />
<PackageReference Include="System.Threading.Channels" Version="7.0.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,53 @@
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<DefaultMpMessageContext>
{
public static Func<Stream, PostModel, int, IServiceProvider, BotSharpMessageHandler> GenerateMessageHandler = (stream, postModel, maxRecordCount, serviceProvider)
=> new BotSharpMessageHandler(stream, postModel, maxRecordCount, false /* 是否只允许处理加密消息,以提高安全性 */, serviceProvider: serviceProvider);
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<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)
{
var messageQueue = ServiceProvider.GetRequiredService<IMessageQueue>();
await messageQueue.EnqueueAsync(new WeChatMessage()
{
OpenId = OpenId,
Message = requestMessage.Content,
Type = "text"
});
return await base.OnTextRequestAsync(requestMessage);
}
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,89 @@
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Infrastructures.ContentTransmitters;
using BotSharp.Abstraction.Models;
using Microsoft.Extensions.DependencyInjection;
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 : BackgroundService, IMessageQueue
{
private readonly Channel<WeChatMessage> _queue;
private readonly IServiceProvider _service;
private readonly ILogger<WeChatBackgroundService> _logger;
public WeChatBackgroundService(
IServiceProvider service,
ILogger<WeChatBackgroundService> logger)
{
this._service = service;
this._logger = logger;
this._queue = Channel.CreateUnbounded<WeChatMessage>();
}
private async Task HandleTextMessageAsync(string openid, string message)
{
var scoped = _service.CreateScope().ServiceProvider;
var conversationService = scoped.GetRequiredService<IConversationService>();
var contentTransfer = scoped.GetRequiredService<IContentTransfer>();
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 EnqueueAsync(WeChatMessage message)
{
await _queue.Writer.WriteAsync(message);
}
protected override async Task ExecuteAsync(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");
}
}
}
}
}

View file

@ -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; }
}
}

View file

@ -0,0 +1,66 @@
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.CO2NET.AspNet;
using Senparc.CO2NET;
using Senparc.Weixin.RegisterServices;
using System;
using System.Collections.Generic;
using System.Text;
using Senparc.Weixin;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.MessageHandlers.Middleware;
using Senparc.Weixin.Entities;
using Senparc.CO2NET.RegisterServices;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
namespace BotSharp.Plugin.WeChat
{
public class WeChatPlugin : IBotSharpAppPlugin
{
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddMemoryCache();
services.Configure<SenparcWeixinSetting>(config.GetSection("WeChat"));
if (!Senparc.CO2NET.RegisterServices.RegisterServiceExtension.SenparcGlobalServicesRegistered)
{
services = services.AddSenparcGlobalServices(config);
}
services.AddSingleton<WeChatBackgroundService>();
services.AddHostedService(s => s.GetRequiredService<WeChatBackgroundService>());
services.TryAddSingleton<IMessageQueue>(s => s.GetRequiredService<WeChatBackgroundService>());
}
public void Configure(IApplicationBuilder app)
{
var env = app.ApplicationServices.GetRequiredService<IHostEnvironment>();
var logger = app.ApplicationServices.GetRequiredService<ILogger<WeChatPlugin>>();
var register = app.UseSenparcGlobal(env);
register.UseSenparcWeixin(null, (svc, settings) =>
{
svc.RegisterMpAccount(settings, "WeChat");
}, app.ApplicationServices);
app.UseMessageHandlerForMp("/WeChatAsync", BotSharpMessageHandler.GenerateMessageHandler, options =>
{
options.AccountSettingFunc = context => Senparc.Weixin.Config.SenparcWeixinSetting;
options.EnbleResponseLog = false;
options.EnableRequestLog = false;
});
logger.LogInformation("WeChat Message Handler is running on /WeChatAsync.");
}
}
}

View file

@ -42,6 +42,7 @@
<ProjectReference Include="..\Plugins\BotSharp.Plugin.MetaAI\BotSharp.Plugin.MetaAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.PaddleSharp\BotSharp.Plugin.PaddleSharp.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Qdrant\BotSharp.Plugin.Qdrant.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.WeChat\BotSharp.Plugin.WeChat.csproj" />
</ItemGroup>
</Project>

View file

@ -60,6 +60,13 @@
"ApiKey": ""
},
"WeChat": {
"Token": "#{Token}#",
"EncodingAESKey": "#{EncodingAESKey}#",
"WeixinAppId": "#{WeixinAppId}#",
"WeixinAppSecret": "#{WeixinAppSecret}#"
},
"KnowledgeBase": {
"VectorDb": "MemVecDbProvider"
},
@ -70,7 +77,8 @@
"BotSharp.Plugin.AzureOpenAI",
"BotSharp.Plugin.MetaAI",
"BotSharp.Plugin.Qdrant",
"BotSharp.Plugin.PaddleSharp"
"BotSharp.Plugin.PaddleSharp",
"BotSharp.Plugin.WeChat"
],
"Plugins": [
"KnowledgeBasePlugin",
@ -79,7 +87,8 @@
"AzureOpenAiPlugin",
"MetaAiPlugin",
"QdrantPlugin",
"PaddleSharpPlugin"
"PaddleSharpPlugin",
"WeChatPlugin"
]
}
}