diff --git a/BotSharp.Core/Agents/Agent.cs b/BotSharp.Core/Agents/Agent.cs index e24ed2fb..dd67022a 100644 --- a/BotSharp.Core/Agents/Agent.cs +++ b/BotSharp.Core/Agents/Agent.cs @@ -72,5 +72,8 @@ namespace BotSharp.Core.Agents [ForeignKey("AgentId")] public AgentMlConfig MlConfig { get; set; } + + [ForeignKey("AgentId")] + public List Integrations { get; set; } } } diff --git a/BotSharp.Core/Agents/AgentIntegration.cs b/BotSharp.Core/Agents/AgentIntegration.cs new file mode 100644 index 00000000..aa344186 --- /dev/null +++ b/BotSharp.Core/Agents/AgentIntegration.cs @@ -0,0 +1,26 @@ +using EntityFrameworkCore.BootKit; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace BotSharp.Core.Agents +{ + [Table("Bot_AgentIntegration")] + public class AgentIntegration : DbRecord, IDbRecord + { + [Required] + [StringLength(36)] + public String AgentId { get; set; } + + [MaxLength(64)] + public String Platform { get; set; } + + [MaxLength(64)] + public String VerifyToken { get; set; } + + [MaxLength(256)] + public String AccessToken { get; set; } + } +} diff --git a/BotSharp.Core/Engines/AgentImportHeader.cs b/BotSharp.Core/Engines/AgentImportHeader.cs index b73c7d4c..42d625da 100644 --- a/BotSharp.Core/Engines/AgentImportHeader.cs +++ b/BotSharp.Core/Engines/AgentImportHeader.cs @@ -1,4 +1,5 @@ -using System; +using BotSharp.Core.Agents; +using System; using System.Collections.Generic; using System.Text; @@ -11,5 +12,10 @@ namespace BotSharp.Core.Engines public String UserId { get; set; } public String ClientAccessToken { get; set; } public String DeveloperAccessToken { get; set; } + + /// + /// Integration with other social media platform + /// + public List Integrations { get; set; } } } diff --git a/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs b/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs index 41581cb0..3a6a1016 100644 --- a/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs +++ b/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs @@ -44,6 +44,11 @@ namespace BotSharp.Core.Engines result.MlConfig = agent.ToObject(); result.MlConfig.MinConfidence = agent.MlMinConfidence; result.MlConfig.AgentId = agent.Id; + if(agentHeader.Integrations != null) + { + agentHeader.Integrations.ForEach(x => x.AgentId = agent.Id); + result.Integrations = agentHeader.Integrations; + } return result; } diff --git a/BotSharp.MachineLearning/BotSharp.MachineLearning.csproj b/BotSharp.MachineLearning/BotSharp.MachineLearning.csproj index 86ea3bbe..78982f41 100644 --- a/BotSharp.MachineLearning/BotSharp.MachineLearning.csproj +++ b/BotSharp.MachineLearning/BotSharp.MachineLearning.csproj @@ -4,4 +4,8 @@ netcoreapp2.1 + + + + diff --git a/BotSharp.Core/Engines/SpaCy/server.py b/BotSharp.MachineLearning/SpaCy/server.py similarity index 100% rename from BotSharp.Core/Engines/SpaCy/server.py rename to BotSharp.MachineLearning/SpaCy/server.py diff --git a/BotSharp.RestApi/Integrations/AmazonAlexaController.cs b/BotSharp.RestApi/Integrations/AmazonAlexaController.cs new file mode 100644 index 00000000..6aa28b45 --- /dev/null +++ b/BotSharp.RestApi/Integrations/AmazonAlexaController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations +{ + [Route("v1/[controller]/[action]")] + public class AmazonAlexaController : ControllerBase + { + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/FacebookMessengerController.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/FacebookMessengerController.cs new file mode 100644 index 00000000..89618d0f --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/FacebookMessengerController.cs @@ -0,0 +1,117 @@ +using BotSharp.Core.Agents; +using BotSharp.Core.Engines; +using BotSharp.Core.Models; +using BotSharp.RestApi.Integrations.FacebookMessenger; +using DotNetToolkit; +using EntityFrameworkCore.BootKit; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using RestSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.RestApi.Integrations +{ + [Route("v1/[controller]")] + public class FacebookMessengerController : ControllerBase + { + [HttpGet("{agentId}")] + public ActionResult Verify([FromRoute] string agentId) + { + var mode = Request.Query.ContainsKey("hub.mode") ? Request.Query["hub.mode"].ToString() : String.Empty; + var token = Request.Query.ContainsKey("hub.verify_token") ? Request.Query["hub.verify_token"].ToString() : String.Empty; + var challenge = Request.Query.ContainsKey("hub.challenge") ? Request.Query["hub.challenge"].ToString() : String.Empty; + + if (mode == "subscribe") + { + var dc = new DefaultDataContextLoader().GetDefaultDc(); + var config = dc.Table().FirstOrDefault(x => x.AgentId == agentId && x.Platform == "Facebook Messenger"); + + return config.VerifyToken == token ? Ok(challenge) : Ok(agentId); + } + + return BadRequest(); + } + + [HttpPost("{agentId}")] + public async Task CallbackAsync([FromRoute] string agentId) + { + WebhookEvent body; + IWebhookMessageBody response = null; + + using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) + { + var json = await reader.ReadToEndAsync(); + body = JsonConvert.DeserializeObject(json); + } + + body.Entry.ForEach(entry => + { + entry.Messaging.ForEach(msg => + { + // received text message + if (msg.Message.ContainsKey("text")) + { + OnTextMessaged(agentId, new WebhookMessage + { + Sender = msg.Sender, + Recipient = msg.Recipient, + Timestamp = msg.Timestamp, + Message = msg.Message.ToObject() + }); + } + }); + + }); + + return Ok(); + } + + private void OnTextMessaged(string agentId, WebhookMessage message) + { + Console.WriteLine($"OnTextMessaged: {message.Message.Text}"); + + var rasa = new RasaAi(); + var agent = rasa.LoadAgent(agentId); + rasa.AiConfig = new AIConfiguration(agent.ClientAccessToken, SupportedLanguage.English) { AgentId = agentId }; + rasa.AiConfig.SessionId = message.Sender.Id; + var aiResponse = rasa.TextRequest(new AIRequest { Query = new String[] { message.Message.Text } }); + + var dc = new DefaultDataContextLoader().GetDefaultDc(); + var config = dc.Table().FirstOrDefault(x => x.AgentId == agentId && x.Platform == "Facebook Messenger"); + + SendTextMessage(config.AccessToken, new WebhookMessage + { + Recipient = message.Sender.ToObject(), + Message = new WebhookTextMessage + { + Text = aiResponse.Result.Fulfillment.Speech + } + }); + } + + private void SendTextMessage(string accessToken, WebhookMessage body) + { + var client = new RestClient("https://graph.facebook.com"); + + var rest = new RestRequest("v2.6/me/messages", Method.POST); + string json = JsonConvert.SerializeObject(body, + new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore + }); + + rest.AddParameter("application/json", json, ParameterType.RequestBody); + rest.AddQueryParameter("access_token", accessToken); + + var response = client.Execute(rest); + } + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/IWebhookMessageBody.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/IWebhookMessageBody.cs new file mode 100644 index 00000000..20e97b01 --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/IWebhookMessageBody.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public interface IWebhookMessageBody + { + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookEvent.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookEvent.cs new file mode 100644 index 00000000..86dd445a --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookEvent.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookEvent + { + public string Object { get; set; } + public List Entry { get; set; } + } + + +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookEventEntry.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookEventEntry.cs new file mode 100644 index 00000000..ab1cadd1 --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookEventEntry.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookEventEntry + { + public string Id { get; set; } + public long Time { get; set; } + public List Messaging { get; set; } + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessage.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessage.cs new file mode 100644 index 00000000..7dfd387b --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessage.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookMessage + { + public WebhookMessageSender Sender { get; set; } + + public WebhookMessageRecipient Recipient { get; set; } + + public long Timestamp { get; set; } + + public JObject Message { get; set; } + } + + public class WebhookMessage where TWebhookMessage : IWebhookMessageBody + { + public WebhookMessageSender Sender { get; set; } + + public WebhookMessageRecipient Recipient { get; set; } + + public long Timestamp { get; set; } + + public TWebhookMessage Message { get; set; } + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageQuickReply.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageQuickReply.cs new file mode 100644 index 00000000..9abccb34 --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageQuickReply.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookMessageQuickReply + { + public String Payload { get; set; } + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageRecipient.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageRecipient.cs new file mode 100644 index 00000000..ecea618e --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageRecipient.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookMessageRecipient + { + /// + /// PAGE_ID + /// + public String Id { get; set; } + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageSender.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageSender.cs new file mode 100644 index 00000000..ea1b37ad --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookMessageSender.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookMessageSender + { + /// + /// PSID + /// + public String Id { get; set; } + } +} diff --git a/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookTextMessage.cs b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookTextMessage.cs new file mode 100644 index 00000000..b7e8b1d3 --- /dev/null +++ b/BotSharp.RestApi/Integrations/FacebookMessenger/WebhookTextMessage.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations.FacebookMessenger +{ + public class WebhookTextMessage : IWebhookMessageBody + { + public string Mid { get; set; } + public String Text { get; set; } + [JsonProperty("quick_reply")] + public WebhookMessageQuickReply QuickReply { get; set; } + } +} diff --git a/BotSharp.RestApi/Integrations/SkypeController.cs b/BotSharp.RestApi/Integrations/SkypeController.cs new file mode 100644 index 00000000..13480b65 --- /dev/null +++ b/BotSharp.RestApi/Integrations/SkypeController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations +{ + [Route("v1/[controller]/[action]")] + public class SkypeController : ControllerBase + { + } +} diff --git a/BotSharp.RestApi/Integrations/SlackController.cs b/BotSharp.RestApi/Integrations/SlackController.cs new file mode 100644 index 00000000..ebfca15d --- /dev/null +++ b/BotSharp.RestApi/Integrations/SlackController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations +{ + [Route("v1/[controller]/[action]")] + public class SlackController : ControllerBase + { + } +} diff --git a/BotSharp.RestApi/FacebookMessengerController.cs b/BotSharp.RestApi/Integrations/TelegramController.cs similarity index 50% rename from BotSharp.RestApi/FacebookMessengerController.cs rename to BotSharp.RestApi/Integrations/TelegramController.cs index c01d3d4c..76539255 100644 --- a/BotSharp.RestApi/FacebookMessengerController.cs +++ b/BotSharp.RestApi/Integrations/TelegramController.cs @@ -3,15 +3,11 @@ using System; using System.Collections.Generic; using System.Text; -namespace BotSharp.RestApi +namespace BotSharp.RestApi.Integrations { [Route("v1/[controller]/[action]")] - public class FacebookMessengerController : ControllerBase + public class TelegramController : ControllerBase { - [HttpGet] - public void Facebook() - { - } } } diff --git a/BotSharp.RestApi/Integrations/TwitterController.cs b/BotSharp.RestApi/Integrations/TwitterController.cs new file mode 100644 index 00000000..9e72dce7 --- /dev/null +++ b/BotSharp.RestApi/Integrations/TwitterController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations +{ + [Route("v1/[controller]/[action]")] + public class TwitterController : ControllerBase + { + } +} diff --git a/BotSharp.RestApi/Integrations/ViberController.cs b/BotSharp.RestApi/Integrations/ViberController.cs new file mode 100644 index 00000000..6d424f82 --- /dev/null +++ b/BotSharp.RestApi/Integrations/ViberController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.RestApi.Integrations +{ + [Route("v1/[controller]/[action]")] + public class ViberController : ControllerBase + { + } +} diff --git a/BotSharp.UnitTest/ConversationTest.cs b/BotSharp.UnitTest/ConversationTest.cs index c412c005..2d70c4fb 100644 --- a/BotSharp.UnitTest/ConversationTest.cs +++ b/BotSharp.UnitTest/ConversationTest.cs @@ -16,9 +16,8 @@ namespace BotSharp.UnitTest { var rasa = new RasaAi(); var agent = rasa.LoadAgent(BOT_ID); - - var config = new AIConfiguration(agent.ClientAccessToken, SupportedLanguage.English) { AgentId = BOT_ID }; - config.SessionId = Guid.NewGuid().ToString(); + rasa.AiConfig = new AIConfiguration(agent.ClientAccessToken, SupportedLanguage.English) { AgentId = BOT_ID }; + rasa.AiConfig.SessionId = Guid.NewGuid().ToString(); // Round 1 var response = rasa.TextRequest(new AIRequest { Query = new String[] { "Can you play country music?" } }); diff --git a/BotSharp.WebHost/App_Data/DbInitializer/Agents/agents.json b/BotSharp.WebHost/App_Data/DbInitializer/Agents/agents.json index 4793960a..2f455665 100644 --- a/BotSharp.WebHost/App_Data/DbInitializer/Agents/agents.json +++ b/BotSharp.WebHost/App_Data/DbInitializer/Agents/agents.json @@ -8,8 +8,9 @@ "Integrations": [ { - "Platform": "FacebookMessenger", - "PageAccessToken": "EAAJym8gGQFMBAPnsxTw6rZBE2WVfYtCJeGkCQ2NZC3VbQd45SyUlXjjgUf1gAkCOWq7v0blxTb1xLZAeMAXYhQj3btcMlMO9YbG7StMyx8ussz5TnD1tjjIZCye5u66PZAuFxSyIPXtTCin8GPiJ19cYB8ZCyH3rr5sro7OxUSyAZDZD" + "Platform": "Facebook Messenger", + "VerifyToken": "spotify_music_bot", + "AccessToken": "EAAJym8gGQFMBAPnsxTw6rZBE2WVfYtCJeGkCQ2NZC3VbQd45SyUlXjjgUf1gAkCOWq7v0blxTb1xLZAeMAXYhQj3btcMlMO9YbG7StMyx8ussz5TnD1tjjIZCye5u66PZAuFxSyIPXtTCin8GPiJ19cYB8ZCyH3rr5sro7OxUSyAZDZD" } ] } diff --git a/BotSharp.WebHost/wwwroot/images/spotify-clip.png b/BotSharp.WebHost/wwwroot/images/spotify-clip.png new file mode 100644 index 00000000..6f52b402 Binary files /dev/null and b/BotSharp.WebHost/wwwroot/images/spotify-clip.png differ diff --git a/BotSharp.WebHost/wwwroot/images/spotify.png b/BotSharp.WebHost/wwwroot/images/spotify.png new file mode 100644 index 00000000..0b84b87f Binary files /dev/null and b/BotSharp.WebHost/wwwroot/images/spotify.png differ