diff --git a/BotSharp.sln b/BotSharp.sln
index d0a61d4d2..29e7cb8e2 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.MongoStorag
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.GoogleAI", "src\Plugins\BotSharp.Plugin.GoogleAI\BotSharp.Plugin.GoogleAI.csproj", "{8BC29F8A-78D6-422C-B522-10687ADC38ED}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.Twilio", "src\Plugins\BotSharp.Plugin.Twilio\BotSharp.Plugin.Twilio.csproj", "{E627F1E3-BE03-443A-83A2-86A855A278EB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -223,6 +225,14 @@ Global
{8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|Any CPU.Build.0 = Release|Any CPU
{8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|x64.ActiveCfg = Release|Any CPU
{8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|x64.Build.0 = Release|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|x64.Build.0 = Debug|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|x64.ActiveCfg = Release|Any CPU
+ {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -254,6 +264,7 @@ Global
{5CD330E1-9E5A-4112-8346-6E31CA98EF78} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{DB3DE37B-1208-4ED3-9615-A52AD0AAD69C} = {5CD330E1-9E5A-4112-8346-6E31CA98EF78}
{8BC29F8A-78D6-422C-B522-10687ADC38ED} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
+ {E627F1E3-BE03-443A-83A2-86A855A278EB} = {64264688-0F5C-4AB0-8F2B-B59B717CCE00}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/README.md b/README.md
index 7c0006212..af398158f 100644
--- a/README.md
+++ b/README.md
@@ -39,14 +39,39 @@ It's written in C# running on .Net Core that is full cross-platform framework, t
```
2. Run UI project, reference to [Chatbot UI](src/Plugins/BotSharp.Plugin.ChatbotUI/Chatbot-UI.md).
-### Extension Libraries
+### Plugins
-BotSharp uses component design, the kernel is kept to a minimum, and business functions are implemented by external components. The modular design also allows contributors to better participate.
+BotSharp uses component design, the kernel is kept to a minimum, and business functions are implemented by external components. The modular design also allows contributors to better participate. Below are the bulit-in plugins:
-* Chatbot UI connector.
-* A channel module of BotSharp for Facebook Messenger.
-* A channel module of BotSharp for Tencent Wechat.
-* A channel module of BotSharp for Telegram.
+#### Data Storages
+- BotSharp.Plugin.MongoStorage
+
+#### LLMs
+- BotSharp.Plugin.AzureOpenAI
+- BotSharp.Plugin.GoogleAI
+- BotSharp.Plugin.MetaAI
+- BotSharp.Plugin.HuggingFace
+- BotSharp.Plugin.LLamaSharp
+
+#### Messaging / Channel
+- BotSharp.OpenAPI
+- BotSharp.Plugin.MetaMessenger
+- BotSharp.Plugin.Twilio
+- BotSharp.Plugin.WeChat
+
+#### RAGS
+- BotSharp.Plugin.KnowledgeBase
+- BotSharp.Plugin.Qdrant
+
+#### Visions
+- BotSharp.Plugin.PaddleSharp
+
+#### Tools
+- BotSharp.Plugin.RoutingSpeeder
+- BotSharp.Plugin.PizzaBot
+
+#### UIs
+- BotSharp.Plugin.ChatbotUI
### Documents
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj b/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj
new file mode 100644
index 000000000..99c35ca75
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net6.0
+ $(LangVersion)
+ $(BotSharpVersion)
+ $(GeneratePackageOnBuild)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs
new file mode 100644
index 000000000..7913c0567
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs
@@ -0,0 +1,115 @@
+using BotSharp.Abstraction.Conversations;
+using BotSharp.Plugin.Twilio.Settings;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
+using System;
+using Twilio.AspNet.Common;
+using Twilio.AspNet.Core;
+using Twilio.TwiML;
+using Twilio.TwiML.Voice;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using BotSharp.Abstraction.Routing.Settings;
+using Twilio.Http;
+using Twilio.TwiML.Messaging;
+using BotSharp.Abstraction.Agents.Enums;
+using BotSharp.Abstraction.Conversations.Models;
+
+namespace BotSharp.Plugin.Twilio.Controllers;
+
+[AllowAnonymous]
+public class TwilioVoiceController : TwilioController
+{
+ private readonly TwilioSetting _settings;
+ private readonly IServiceProvider _services;
+
+ public TwilioVoiceController(TwilioSetting settings, IServiceProvider services)
+ {
+ _settings = settings;
+ _services = services;
+ }
+
+ [HttpPost("/twilio/voice/welcome")]
+ public async Task StartConversation(VoiceRequest request)
+ {
+ string sessionId = $"TwilioVoice_{request.CallSid}";
+ var response = ReturnInstructions("Hello, how may I help you?");
+ return TwiML(response);
+ }
+
+ [HttpPost("/twilio/voice/{agentId}")]
+ public async Task ReceivedVoiceMessage([FromRoute] string agentId, VoiceRequest input)
+ {
+ string sessionId = $"TwilioVoice_{input.CallSid}";
+
+ var conv = _services.GetRequiredService();
+ conv.SetConversationId(sessionId, new List
+ {
+ "channel=phone",
+ $"calling_phone={input.DialCallSid}"
+ });
+
+ VoiceResponse response = default;
+
+ var result = await conv.SendMessage(agentId, new RoleDialogModel(AgentRole.User, input.SpeechResult), async msg =>
+ {
+ response = HangUp(msg.Content);
+ }, async functionExecuting =>
+ {
+ }, async functionExecuted =>
+ {
+ });
+
+ return TwiML(response);
+ }
+
+ private VoiceResponse ReturnInstructions(string message)
+ {
+ var routingSetting = _services.GetRequiredService();
+
+ var response = new VoiceResponse();
+ var gather = new Gather()
+ {
+ Input = new List()
+ {
+ Gather.InputEnum.Speech
+ },
+ Action = new Uri($"{_settings.CallbackHost}/twilio/voice/{routingSetting.RouterId}")
+ };
+ gather.Say(message);
+ response.Append(gather);
+ return response;
+ }
+
+ private VoiceResponse HangUp(string message)
+ {
+ var response = new VoiceResponse();
+ if (!string.IsNullOrEmpty(message))
+ {
+ response.Say(message);
+ }
+ response.Hangup();
+ return response;
+ }
+
+ private VoiceResponse HoldOn(int interval, string message = null)
+ {
+ var routingSetting = _services.GetRequiredService();
+
+ var response = new VoiceResponse();
+ var gather = new Gather()
+ {
+ Input = new List() { Gather.InputEnum.Speech },
+ Action = new Uri($"{_settings.CallbackHost}/twilio/voice/{routingSetting.RouterId}"),
+ ActionOnEmptyResult = true
+ };
+ if (!string.IsNullOrEmpty(message))
+ {
+ gather.Say(message);
+ }
+ gather.Pause(interval);
+ response.Append(gather);
+ return response;
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs b/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs
new file mode 100644
index 000000000..a1048b8cd
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs
@@ -0,0 +1,9 @@
+namespace BotSharp.Plugin.Twilio.Settings;
+
+public class TwilioSetting
+{
+ public string PhoneNumber { get; set; }
+ public string AccountSID { get; set; }
+ public string AuthToken { get; set; }
+ public string CallbackHost { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs b/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs
new file mode 100644
index 000000000..ff8884b22
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs
@@ -0,0 +1,16 @@
+using BotSharp.Abstraction.Plugins;
+using BotSharp.Plugin.Twilio.Settings;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BotSharp.Plugin.Twilio;
+
+public class TwilioPlugin : IBotSharpPlugin
+{
+ public void RegisterDI(IServiceCollection services, IConfiguration config)
+ {
+ var setting = new TwilioSetting();
+ config.Bind("Twilio", setting);
+ services.AddSingleton(setting);
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Using.cs b/src/Plugins/BotSharp.Plugin.Twilio/Using.cs
new file mode 100644
index 000000000..5026d4913
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.Twilio/Using.cs
@@ -0,0 +1,17 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Text;
+global using System.Threading.Tasks;
+global using System.Linq;
+global using System.Text.Json;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Configuration;
+global using Microsoft.Extensions.Logging;
+global using BotSharp.Abstraction.Functions;
+global using BotSharp.Abstraction.Functions.Models;
+global using BotSharp.Abstraction.Agents.Enums;
+global using BotSharp.Abstraction.Models;
+global using BotSharp.Abstraction.Agents;
+global using BotSharp.Abstraction.Conversations;
+global using BotSharp.Abstraction.Plugins;
+global using BotSharp.Abstraction.Conversations.Models;
\ No newline at end of file
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index 3cb6b16bc..8c14beff8 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -63,6 +63,7 @@
+
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index ed51f673b..157755eaa 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -89,6 +89,13 @@
"PageAccessToken": ""
},
+ "Twilio": {
+ "PhoneNumber": "+1",
+ "AccountSID": "",
+ "AuthToken": "",
+ "CallbackHost": "https://"
+ },
+
"Database": {
"Default": "FileRepository",
"TablePrefix": "BotSharp",
@@ -132,6 +139,7 @@
"BotSharp.Plugin.AzureOpenAI",
"BotSharp.Plugin.GoogleAI",
"BotSharp.Plugin.MetaAI",
+ // "BotSharp.Plugin.Twilio",
"BotSharp.Plugin.HuggingFace",
"BotSharp.Plugin.LLamaSharp",
"BotSharp.Plugin.KnowledgeBase",