diff --git a/BotSharp.Channel.FacebookMessenger/BotSharp.Channel.FacebookMessenger.csproj b/BotSharp.Channel.FacebookMessenger/BotSharp.Channel.FacebookMessenger.csproj new file mode 100644 index 000000000..1add55568 --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/BotSharp.Channel.FacebookMessenger.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.1 + + + + + + + + + + + + + + + diff --git a/BotSharp.Channel.FacebookMessenger/Controllers/FacebookMessengerController.cs b/BotSharp.Channel.FacebookMessenger/Controllers/FacebookMessengerController.cs new file mode 100644 index 000000000..458a84bac --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Controllers/FacebookMessengerController.cs @@ -0,0 +1,114 @@ +using BotSharp.Channel.FacebookMessenger.Models; +using BotSharp.Platform.Models; +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.Channel.FacebookMessenger.Controllers +{ + [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 ai = new ApiAi(); + var agent = ai.LoadAgent(agentId); + ai.AiConfig = new AIConfiguration(agent.ClientAccessToken, SupportedLanguage.English) { AgentId = agentId }; + ai.AiConfig.SessionId = message.Sender.Id; + var aiResponse = ai.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 = String.IsNullOrEmpty(aiResponse.Result.Fulfillment.Speech) ? aiResponse.Result.Action : 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.Channel.FacebookMessenger/IWebhookMessageBody.cs b/BotSharp.Channel.FacebookMessenger/IWebhookMessageBody.cs new file mode 100644 index 000000000..19d72ace6 --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/IWebhookMessageBody.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger +{ + public interface IWebhookMessageBody + { + } +} diff --git a/BotSharp.Channel.FacebookMessenger/Models/WebhookEvent.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookEvent.cs new file mode 100644 index 000000000..d4ba1d72a --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookEvent.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + public class WebhookEvent + { + public string Object { get; set; } + public List Entry { get; set; } + } + + +} diff --git a/BotSharp.Channel.FacebookMessenger/Models/WebhookEventEntry.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookEventEntry.cs new file mode 100644 index 000000000..4889fb972 --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookEventEntry.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + public class WebhookEventEntry + { + public string Id { get; set; } + public long Time { get; set; } + public List Messaging { get; set; } + } +} diff --git a/BotSharp.Channel.FacebookMessenger/Models/WebhookMessage.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessage.cs new file mode 100644 index 000000000..e06642f7f --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessage.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + 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.Channel.FacebookMessenger/Models/WebhookMessageQuickReply.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageQuickReply.cs new file mode 100644 index 000000000..b67f9a238 --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageQuickReply.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + public class WebhookMessageQuickReply + { + public String Payload { get; set; } + } +} diff --git a/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageRecipient.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageRecipient.cs new file mode 100644 index 000000000..cba0f51ae --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageRecipient.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + public class WebhookMessageRecipient + { + /// + /// PAGE_ID + /// + public String Id { get; set; } + } +} diff --git a/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageSender.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageSender.cs new file mode 100644 index 000000000..888aadfa1 --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookMessageSender.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + public class WebhookMessageSender + { + /// + /// PSID + /// + public String Id { get; set; } + } +} diff --git a/BotSharp.Channel.FacebookMessenger/Models/WebhookTextMessage.cs b/BotSharp.Channel.FacebookMessenger/Models/WebhookTextMessage.cs new file mode 100644 index 000000000..16d0fa73f --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/Models/WebhookTextMessage.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Channel.FacebookMessenger.Models +{ + 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.Channel.FacebookMessenger/ModuleInjector.cs b/BotSharp.Channel.FacebookMessenger/ModuleInjector.cs new file mode 100644 index 000000000..b0d237236 --- /dev/null +++ b/BotSharp.Channel.FacebookMessenger/ModuleInjector.cs @@ -0,0 +1,22 @@ +using BotSharp.Core.Modules; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace BotSharp.Channel.FacebookMessenger +{ + public class ModuleInjector : IModule + { + public void ConfigureServices(IServiceCollection services, IConfiguration config) + { + + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env/*, IOptions senparcSetting, IOptions senparcWeixinSetting*/) + { + + } + } +} diff --git a/BotSharp.Channel.Weixin/BotSharp.Channel.Weixin.csproj b/BotSharp.Channel.Weixin/BotSharp.Channel.Weixin.csproj new file mode 100644 index 000000000..0b31f4dda --- /dev/null +++ b/BotSharp.Channel.Weixin/BotSharp.Channel.Weixin.csproj @@ -0,0 +1,30 @@ + + + + netcoreapp2.1 + true + Haiping Chen + https://github.com/Oceania2018/botsharp-channel-weixin + git + https://github.com/Oceania2018/botsharp-channel-weixin + Apache 2.0 + botsharp, wechat, wexin, chatbot + 0.1.1.0 + 0.1.1.0 + 0.1.1 + + + + DEBUG;TRACE + + + + + + + + + + + + diff --git a/BotSharp.Channel.Weixin/Controllers/WeixinAsyncController.cs b/BotSharp.Channel.Weixin/Controllers/WeixinAsyncController.cs new file mode 100644 index 000000000..2139efecf --- /dev/null +++ b/BotSharp.Channel.Weixin/Controllers/WeixinAsyncController.cs @@ -0,0 +1,142 @@ +using BotSharp.Channel.Weixin.Models; +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Dialogflow; +using BotSharp.Platform.Dialogflow.Models; +using BotSharp.Platform.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Senparc.CO2NET; +using Senparc.CO2NET.HttpUtility; +using Senparc.Weixin.MP; +using Senparc.Weixin.MP.Entities.Request; +using Senparc.Weixin.MP.MvcExtension; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Channel.Weixin.Controllers +{ + /// + /// 此Controller为异步Controller(Action),使用异步线程处理并发请求。 + /// 为了方便演示,此Controller中没有加入多余的日志记录等示例,保持了最简单的Controller写法。日志等其他操作可以参考WeixinController.cs。 + /// 提示:异步Controller并不是在任何情况下都能提升效率(响应时间),当请求量非常小的时候反而会增加一定的开销。 + /// + [Route("weixin")] + public class WeixinAsyncController : ControllerBase + { + readonly Func _getRandomFileName = () => DateTime.Now.ToString("yyyyMMdd-HHmmss") + "_Async_" + Guid.NewGuid().ToString("n").Substring(0, 6); + + readonly IConfiguration config; + private IPlatformBuilder builder; + + public WeixinAsyncController(IPlatformBuilder platform, IConfiguration configuration) + { + config = configuration; + builder = platform; + } + + [HttpGet] + public Task Get(string signature, string timestamp, string nonce, string echostr) + { + var token = config.GetValue("weixinChannel:token"); + return Task.Factory.StartNew(() => + { + if (CheckSignature.Check(signature, timestamp, nonce, token)) + { + return echostr; //返回随机字符串则表示验证通过 + } + else + { + return "failed:" + signature + "," + Senparc.Weixin.MP.CheckSignature.GetSignature(timestamp, nonce, token) + "。" + + "如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。"; + } + }).ContinueWith(task => Content(task.Result)); + } + + public CustomMessageHandler MessageHandler = null;//开放出MessageHandler是为了做单元测试,实际使用过程中不需要 + + /// + /// 最简化的处理流程 + /// + [HttpPost] + public async Task Post(PostModel postModel) + { + var token = config.GetValue("weixinChannel:token"); + + if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, token)) + { + return new WeixinResult("参数错误!"); + } + + postModel.Token = token; + postModel.EncodingAESKey = config.GetValue("weixinChannel:encodingAESKey"); + postModel.AppId = config.GetValue("weixinChannel:appId"); + + var messageHandler = new CustomMessageHandler(builder, config, Request.GetRequestMemoryStream(), postModel, 10); + + messageHandler.DefaultMessageHandlerAsyncEvent = Senparc.NeuChar.MessageHandlers.DefaultMessageHandlerAsyncEvent.SelfSynicMethod;//没有重写的异步方法将默认尝试调用同步方法中的代码(为了偷懒) + + #region 设置消息去重 + + /* 如果需要添加消息去重功能,只需打开OmitRepeatedMessage功能,SDK会自动处理。 + * 收到重复消息通常是因为微信服务器没有及时收到响应,会持续发送2-5条不等的相同内容的RequestMessage*/ + messageHandler.OmitRepeatedMessage = true;//默认已经开启,此处仅作为演示,也可以设置为false在本次请求中停用此功能 + + #endregion + + #region 记录 Request 日志 + + var logPath = Server.GetMapPath(string.Format("~/App_Data/MP/{0}/", DateTime.Now.ToString("yyyy-MM-dd"))); + if (!Directory.Exists(logPath)) + { + Directory.CreateDirectory(logPath); + } + + //测试时可开启此记录,帮助跟踪数据,使用前请确保App_Data文件夹存在,且有读写权限。 + messageHandler.RequestDocument.Save(Path.Combine(logPath, string.Format("{0}_Request_{1}_{2}.txt", _getRandomFileName(), + messageHandler.RequestMessage.FromUserName, + messageHandler.RequestMessage.MsgType))); + if (messageHandler.UsingEcryptMessage) + { + messageHandler.EcryptRequestDocument.Save(Path.Combine(logPath, string.Format("{0}_Request_Ecrypt_{1}_{2}.txt", _getRandomFileName(), + messageHandler.RequestMessage.FromUserName, + messageHandler.RequestMessage.MsgType))); + } + + #endregion + + await messageHandler.ExecuteAsync(); //执行微信处理过程 + + #region 记录 Response 日志 + + //测试时可开启,帮助跟踪数据 + + //if (messageHandler.ResponseDocument == null) + //{ + // throw new Exception(messageHandler.RequestDocument.ToString()); + //} + if (messageHandler.ResponseDocument != null) + { + messageHandler.ResponseDocument.Save(Path.Combine(logPath, string.Format("{0}_Response_{1}_{2}.txt", _getRandomFileName(), + messageHandler.ResponseMessage.ToUserName, + messageHandler.ResponseMessage.MsgType))); + } + + if (messageHandler.UsingEcryptMessage && messageHandler.FinalResponseDocument != null) + { + //记录加密后的响应信息 + messageHandler.FinalResponseDocument.Save(Path.Combine(logPath, string.Format("{0}_Response_Final_{1}_{2}.txt", _getRandomFileName(), + messageHandler.ResponseMessage.ToUserName, + messageHandler.ResponseMessage.MsgType))); + } + + #endregion + + MessageHandler = messageHandler;//开放出MessageHandler是为了做单元测试,实际使用过程中不需要 + + return new FixWeixinBugWeixinResult(messageHandler); + } + } +} diff --git a/BotSharp.Channel.Weixin/LocationService.cs b/BotSharp.Channel.Weixin/LocationService.cs new file mode 100644 index 000000000..ea21ab261 --- /dev/null +++ b/BotSharp.Channel.Weixin/LocationService.cs @@ -0,0 +1,94 @@ +/*---------------------------------------------------------------- + Copyright (C) 2018 Senparc + + 文件名:LocationService.cs + 文件功能描述:地理位置信息处理 + + + 创建标识:Senparc - 20150312 +----------------------------------------------------------------*/ + +using System.Collections.Generic; +using Senparc.Weixin.MP.Entities; +using Senparc.CO2NET.Helpers.BaiduMap; +using Senparc.CO2NET.Helpers.GoogleMap; +using Senparc.Weixin.MP.Helpers; +using Senparc.CO2NET.Helpers; +using Senparc.NeuChar.Entities; + +namespace BotSharp.Channel.Weixin +{ + public class LocationService + { + public ResponseMessageNews GetResponseMessage(RequestMessageLocation requestMessage) + { + var responseMessage = ResponseMessageBase.CreateFromRequestMessage(requestMessage); + + #region 百度地图 + + { + var markersList = new List(); + markersList.Add(new BaiduMarkers() + { + Longitude = requestMessage.Location_X, + Latitude = requestMessage.Location_Y, + Color = "red", + Label = "S", + Size = BaiduMarkerSize.m + }); + + var mapUrl = BaiduMapHelper.GetBaiduStaticMap(requestMessage.Location_X, requestMessage.Location_Y, 1, 6, markersList); + responseMessage.Articles.Add(new Article() + { + Description = string.Format("【来自百度地图】您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}", + requestMessage.Location_X, requestMessage.Location_Y, + requestMessage.Scale, requestMessage.Label), + PicUrl = mapUrl, + Title = "定位地点周边地图", + Url = mapUrl + }); + } + + #endregion + + #region GoogleMap + + { + var markersList = new List(); + markersList.Add(new GoogleMapMarkers() + { + X = requestMessage.Location_X, + Y = requestMessage.Location_Y, + Color = "red", + Label = "S", + Size = GoogleMapMarkerSize.Default, + }); + var mapSize = "480x600"; + var mapUrl = GoogleMapHelper.GetGoogleStaticMap(19 /*requestMessage.Scale*//*微信和GoogleMap的Scale不一致,这里建议使用固定值*/, + markersList, mapSize); + responseMessage.Articles.Add(new Article() + { + Description = string.Format("【来自GoogleMap】您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}", + requestMessage.Location_X, requestMessage.Location_Y, + requestMessage.Scale, requestMessage.Label), + PicUrl = mapUrl, + Title = "定位地点周边地图", + Url = mapUrl + }); + } + + #endregion + + + responseMessage.Articles.Add(new Article() + { + Title = "微信公众平台SDK 官网链接", + Description = "Senparc.Weixin.MK SDK地址", + PicUrl = "http://sdk.weixin.senparc.com/images/logo.jpg", + Url = "http://sdk.weixin.senparc.com" + }); + + return responseMessage; + } + } +} \ No newline at end of file diff --git a/BotSharp.Channel.Weixin/Models/CustomMessageContext.cs b/BotSharp.Channel.Weixin/Models/CustomMessageContext.cs new file mode 100644 index 000000000..0dd6a67c3 --- /dev/null +++ b/BotSharp.Channel.Weixin/Models/CustomMessageContext.cs @@ -0,0 +1,51 @@ +/*---------------------------------------------------------------- + Copyright (C) 2018 Senparc + + 文件名:CustomMessageContext.cs + 文件功能描述:微信消息上下文 + + + 创建标识:Senparc - 20150312 +----------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Senparc.NeuChar.Context; +using Senparc.NeuChar.Entities; + +namespace BotSharp.Channel.Weixin.Models +{ + public class CustomMessageContext : MessageContext + { + public CustomMessageContext() + { + base.MessageContextRemoved += CustomMessageContext_MessageContextRemoved; + } + + + /// + /// 当上下文过期,被移除时触发的时间 + /// + /// + /// + void CustomMessageContext_MessageContextRemoved(object sender, Senparc.NeuChar.Context.WeixinContextRemovedEventArgs e) + { + /* 注意,这个事件不是实时触发的(当然你也可以专门写一个线程监控) + * 为了提高效率,根据WeixinContext中的算法,这里的过期消息会在过期后下一条请求执行之前被清除 + */ + + var messageContext = e.MessageContext as CustomMessageContext; + if (messageContext == null) + { + return;//如果是正常的调用,messageContext不会为null + } + + //TODO:这里根据需要执行消息过期时候的逻辑,下面的代码仅供参考 + + //Log.InfoFormat("{0}的消息上下文已过期",e.OpenId); + //api.SendMessage(e.OpenId, "由于长时间未搭理客服,您的客服状态已退出!"); + } + } +} diff --git a/BotSharp.Channel.Weixin/Models/CustomMessageHandler.cs b/BotSharp.Channel.Weixin/Models/CustomMessageHandler.cs new file mode 100644 index 000000000..b31dce6dc --- /dev/null +++ b/BotSharp.Channel.Weixin/Models/CustomMessageHandler.cs @@ -0,0 +1,380 @@ +/*---------------------------------------------------------------- + Copyright (C) 2018 Senparc + + 文件名:CustomMessageHandler.cs + 文件功能描述:微信公众号自定义MessageHandler + + + 创建标识:Senparc - 20150312 + + 修改标识:Senparc - 20171027 + 修改描述:v14.8.3 添加OnUnknownTypeRequest()方法Demo + +----------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; + +using Senparc.Weixin.MP.Agent; +using Senparc.NeuChar.Context; +using Senparc.Weixin.Exceptions; +using Senparc.Weixin.Helpers; +using Senparc.Weixin.MP.Entities; +using Senparc.Weixin.MP.Entities.Request; +using Senparc.Weixin.MP.MessageHandlers; +using Senparc.Weixin.MP.Helpers; +using System.Xml.Linq; +using Senparc.Weixin.MP.AdvancedAPIs; +using System.Threading.Tasks; +using Senparc.NeuChar.Entities.Request; +using Senparc.CO2NET.Helpers; +using Senparc.NeuChar.Helpers; +using Senparc.NeuChar.Entities; +using Senparc.Weixin; +using Senparc.Weixin.MP; +using BotSharp.Platform.Models.AiRequest; +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Dialogflow.Models; +using Microsoft.Extensions.Configuration; + +#if NET45 +using System.Web; +using System.Configuration; +using System.Web.Configuration; +using Senparc.Weixin.MP.Sample.CommonService.Utilities; +#else +using Microsoft.AspNetCore.Http; +#endif + +namespace BotSharp.Channel.Weixin.Models +{ + /// + /// 自定义MessageHandler + /// 把MessageHandler作为基类,重写对应请求的处理方法 + /// + public partial class CustomMessageHandler : MessageHandler + { + /* + * 重要提示:v1.5起,MessageHandler提供了一个DefaultResponseMessage的抽象方法, + * DefaultResponseMessage必须在子类中重写,用于返回没有处理过的消息类型(也可以用于默认消息,如帮助信息等); + * 其中所有原OnXX的抽象方法已经都改为虚方法,可以不必每个都重写。若不重写,默认返回DefaultResponseMessage方法中的结果。 + */ + + +#if !DEBUG || NETSTANDARD1_6 || NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 + string agentUrl = "http://localhost:12222/App/Weixin/4"; + string agentToken = "27C455F496044A87"; + string wiweihiKey = "CNadjJuWzyX5bz5Gn+/XoyqiqMa5DjXQ"; +#else + //下面的Url和Token可以用其他平台的消息,或者到www.weiweihi.com注册微信用户,将自动在“微信营销工具”下得到 + private string agentUrl = Config.SenparcWeixinSetting.AgentUrl;//这里使用了www.weiweihi.com微信自动托管平台 + private string agentToken = Config.SenparcWeixinSetting.AgentToken;//Token + private string wiweihiKey = Config.SenparcWeixinSetting.SenparcWechatAgentKey;//WeiweihiKey专门用于对接www.Weiweihi.com平台,获取方式见:http://www.weiweihi.com/ApiDocuments/Item/25#51 +#endif + +#if NET45 + private string appId = Config.SenparcWeixinSetting.WeixinAppId; + private string appSecret = Config.SenparcWeixinSetting.WeixinAppSecret; +#else + private string appId = "appId"; + private string appSecret = "appSecret"; +#endif + + /// + /// 模板消息集合(Key:checkCode,Value:OpenId) + /// + public static Dictionary TemplateMessageCollection = new Dictionary(); + + private IPlatformBuilder nluPlatform = null; + private readonly IConfiguration config; + + public CustomMessageHandler(IPlatformBuilder nluPlatform, IConfiguration configuration, Stream inputStream, PostModel postModel, int maxRecordCount = 0) + : base(inputStream, postModel, maxRecordCount) + { + this.nluPlatform = nluPlatform; + this.config = configuration; + + //这里设置仅用于测试,实际开发可以在外部更全局的地方设置, + //比如MessageHandler.GlobalGlobalMessageContext.ExpireMinutes = 3。 + GlobalMessageContext.ExpireMinutes = 3; + + if (!string.IsNullOrEmpty(postModel.AppId)) + { + appId = postModel.AppId;//通过第三方开放平台发送过来的请求 + } + + //在指定条件下,不使用消息去重 + base.OmitRepeatedMessageFunc = requestMessage => + { + var textRequestMessage = requestMessage as RequestMessageText; + if (textRequestMessage != null && textRequestMessage.Content == "容错") + { + return false; + } + return true; + }; + } + + public override void OnExecuting() + { + //测试MessageContext.StorageData + if (CurrentMessageContext.StorageData == null) + { + CurrentMessageContext.StorageData = 0; + } + base.OnExecuting(); + } + + public override void OnExecuted() + { + base.OnExecuted(); + CurrentMessageContext.StorageData = ((int)CurrentMessageContext.StorageData) + 1; + } + + /// + /// 处理文字请求 + /// + /// + public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage) + { + var defaultResponseMessage = base.CreateResponseMessage(); + + var requestHandler = + requestMessage.StartHandler() + .Keyword("BotSharp",() => + { + var result = new StringBuilder(); + result.AppendFormat("{0}", "你好!欢迎使用BotSharp NLU服务,BotSharp通过强大的自然语言理解技术让你更轻松的处理智能回复。"); + defaultResponseMessage.Content = result.ToString(); + return defaultResponseMessage; + }) + .Keyword("再见", () => + { + var result = new StringBuilder(); + result.AppendFormat("{0}", "谢谢使用,再见!"); + defaultResponseMessage.Content = result.ToString(); + return defaultResponseMessage; + }) + .Default(() => + { + var result = new StringBuilder(); + + // send text to BotSharp platform emulator + var aIResponse = nluPlatform.TextRequest(new AiRequest + { + Text = requestMessage.Content, + AgentId = config.GetValue("weixinChannel:agentId"), + SessionId = requestMessage.FromUserName + }); + + result.AppendFormat("{0}", aIResponse.Result.Fulfillment.Speech); + + defaultResponseMessage.Content = result.ToString(); + return defaultResponseMessage; + }); + + return requestHandler.GetResponseMessage() as IResponseMessageBase; + } + + /// + /// 处理位置请求 + /// + /// + /// + public override IResponseMessageBase OnLocationRequest(RequestMessageLocation requestMessage) + { + var locationService = new LocationService(); + var responseMessage = locationService.GetResponseMessage(requestMessage as RequestMessageLocation); + return responseMessage; + } + + public override IResponseMessageBase OnShortVideoRequest(RequestMessageShortVideo requestMessage) + { + var responseMessage = this.CreateResponseMessage(); + responseMessage.Content = "您刚才发送的是小视频"; + return responseMessage; + } + + /// + /// 处理图片请求 + /// + /// + /// + public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage) + { + //一隔一返回News或Image格式 + if (base.GlobalMessageContext.GetMessageContext(requestMessage).RequestMessages.Count() % 2 == 0) + { + var responseMessage = CreateResponseMessage(); + + responseMessage.Articles.Add(new Article() + { + Title = "您刚才发送了图片信息", + Description = "您发送的图片将会显示在边上", + PicUrl = requestMessage.PicUrl, + Url = "http://sdk.weixin.senparc.com" + }); + responseMessage.Articles.Add(new Article() + { + Title = "第二条", + Description = "第二条带连接的内容", + PicUrl = requestMessage.PicUrl, + Url = "http://sdk.weixin.senparc.com" + }); + + return responseMessage; + } + else + { + var responseMessage = CreateResponseMessage(); + responseMessage.Image.MediaId = requestMessage.MediaId; + return responseMessage; + } + } + + /// + /// 处理语音请求 + /// + /// + /// + public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage) + { + var responseMessage = CreateResponseMessage(); + //上传缩略图 + //var accessToken = Containers.AccessTokenContainer.TryGetAccessToken(appId, appSecret); + var uploadResult = Senparc.Weixin.MP.AdvancedAPIs.MediaApi.UploadTemporaryMedia(appId, UploadMediaFileType.image, + Server.GetMapPath("~/Images/Logo.jpg")); + + //设置音乐信息 + responseMessage.Music.Title = "天籁之音"; + responseMessage.Music.Description = "播放您上传的语音"; + responseMessage.Music.MusicUrl = "http://sdk.weixin.senparc.com/Media/GetVoice?mediaId=" + requestMessage.MediaId; + responseMessage.Music.HQMusicUrl = "http://sdk.weixin.senparc.com/Media/GetVoice?mediaId=" + requestMessage.MediaId; + responseMessage.Music.ThumbMediaId = uploadResult.media_id; + + //推送一条客服消息 + try + { + CustomApi.SendText(appId, WeixinOpenId, "本次上传的音频MediaId:" + requestMessage.MediaId); + + } + catch + { + } + + return responseMessage; + } + /// + /// 处理视频请求 + /// + /// + /// + public override IResponseMessageBase OnVideoRequest(RequestMessageVideo requestMessage) + { + var responseMessage = CreateResponseMessage(); + responseMessage.Content = "您发送了一条视频信息,ID:" + requestMessage.MediaId; + + #region 上传素材并推送到客户端 + + Task.Factory.StartNew(async () => + { + //上传素材 + var dir = Server.GetMapPath("~/App_Data/TempVideo/"); + var file = await MediaApi.GetAsync(appId, requestMessage.MediaId, dir); + var uploadResult = await MediaApi.UploadTemporaryMediaAsync(appId, UploadMediaFileType.video, file, 50000); + await CustomApi.SendVideoAsync(appId, base.WeixinOpenId, uploadResult.media_id, "这是您刚才发送的视频", "这是一条视频消息"); + }).ContinueWith(async task => + { + if (task.Exception != null) + { + WeixinTrace.Log("OnVideoRequest()储存Video过程发生错误:", task.Exception.Message); + + var msg = string.Format("上传素材出错:{0}\r\n{1}", + task.Exception.Message, + task.Exception.InnerException != null + ? task.Exception.InnerException.Message + : null); + await CustomApi.SendTextAsync(appId, base.WeixinOpenId, msg); + } + }); + + #endregion + + return responseMessage; + } + + + /// + /// 处理链接消息请求 + /// + /// + /// + public override IResponseMessageBase OnLinkRequest(RequestMessageLink requestMessage) + { + var responseMessage = ResponseMessageBase.CreateFromRequestMessage(requestMessage); + responseMessage.Content = string.Format(@"您发送了一条连接信息: +Title:{0} +Description:{1} +Url:{2}", requestMessage.Title, requestMessage.Description, requestMessage.Url); + return responseMessage; + } + + public override IResponseMessageBase OnFileRequest(RequestMessageFile requestMessage) + { + var responseMessage = requestMessage.CreateResponseMessage(); + responseMessage.Content = string.Format(@"您发送了一个文件: +文件名:{0} +说明:{1} +大小:{2} +MD5:{3}", requestMessage.Title, requestMessage.Description, requestMessage.FileTotalLen, requestMessage.FileMd5); + return responseMessage; + } + + /// + /// 处理事件请求(这个方法一般不用重写,这里仅作为示例出现。除非需要在判断具体Event类型以外对Event信息进行统一操作 + /// + /// + /// + public override IResponseMessageBase OnEventRequest(IRequestMessageEventBase requestMessage) + { + var eventResponseMessage = base.OnEventRequest(requestMessage);//对于Event下属分类的重写方法,见:CustomerMessageHandler_Events.cs + //TODO: 对Event信息进行统一操作 + return eventResponseMessage; + } + + public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage) + { + /* 所有没有被处理的消息会默认返回这里的结果, + * 因此,如果想把整个微信请求委托出去(例如需要使用分布式或从其他服务器获取请求), + * 只需要在这里统一发出委托请求,如: + * var responseMessage = MessageAgent.RequestResponseMessage(agentUrl, agentToken, RequestDocument.ToString()); + * return responseMessage; + */ + + var responseMessage = this.CreateResponseMessage(); + responseMessage.Content = "这条消息来自DefaultResponseMessage。"; + return responseMessage; + } + + + public override IResponseMessageBase OnUnknownTypeRequest(RequestMessageUnknownType requestMessage) + { + /* + * 此方法用于应急处理SDK没有提供的消息类型, + * 原始XML可以通过requestMessage.RequestDocument(或this.RequestDocument)获取到。 + * 如果不重写此方法,遇到未知的请求类型将会抛出异常(v14.8.3 之前的版本就是这么做的) + */ + var msgType = Senparc.NeuChar.Helpers.MsgTypeHelper.GetRequestMsgTypeString(requestMessage.RequestDocument); + var responseMessage = this.CreateResponseMessage(); + responseMessage.Content = "未知消息类型:" + msgType; + + WeixinTrace.SendCustomLog("未知请求消息类型", requestMessage.RequestDocument.ToString());//记录到日志中 + + return responseMessage; + } + } +} diff --git a/BotSharp.Channel.Weixin/ModuleInjector.cs b/BotSharp.Channel.Weixin/ModuleInjector.cs new file mode 100644 index 000000000..e987a7842 --- /dev/null +++ b/BotSharp.Channel.Weixin/ModuleInjector.cs @@ -0,0 +1,32 @@ +using BotSharp.Core.Modules; +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET; +using Senparc.CO2NET.RegisterServices; +using Senparc.Weixin.RegisterServices; +using System; + +namespace BotSharp.Channel.Weixin +{ + public class ModuleInjector : IModule + { + public void ConfigureServices(IServiceCollection services, IConfiguration config) + { + // Senparc.CO2NET, Senparc.Weixin + services.AddSenparcGlobalServices(config) + .AddSenparcWeixinServices(config); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env/*, IOptions senparcSetting, IOptions senparcWeixinSetting*/) + { + + //https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore/Startup.cs + /* IRegisterService register = RegisterService.Start(env, senparcSetting.Value) + .UseSenparcGlobal();*/ + } + } +} diff --git a/BotSharp.Channel.Weixin/README.md b/BotSharp.Channel.Weixin/README.md new file mode 100644 index 000000000..dcc516840 --- /dev/null +++ b/BotSharp.Channel.Weixin/README.md @@ -0,0 +1,47 @@ +# botsharp-channel-weixin +A channel module of BotSharp for Tencent Weixin + +### How to install through NuGet + +``` +PM> Install-Package BotSharp.Channel.Weixin +``` + +### How to run locally +``` +git clone https://github.com/dotnetcore/BotSharp +``` + +Check app.json to use DialogflowAi +``` +{ + "version": "0.1.0", + "assemblies": "BotSharp.Core", + + "platformModuleName": "DialogflowAi" +} +``` + +Update channels.weixin.json to set the corresponding KEY +``` +{ + "weixinChannel": { + "token": "botsharp", + "encodingAESKey": "", + "appId": "", + "agentId": "60bee6f9-ba58-4fe8-8b95-94af69d6fd41" + } +} +``` + +F5 run BotSharp.WebHost + +Access http://localhost:3112 + +Import demo (Spotify.zip) agent located at App_Data + +Train agent (id: 60bee6f9-ba58-4fe8-8b95-94af69d6fd41) + +Or refer [BotSharp docs](https://botsharp.readthedocs.io) to design your new chatbot. + +Setup Wechat webhood from https://mp.weixin.qq.com/. diff --git a/BotSharp.Channel.Weixin/Server.cs b/BotSharp.Channel.Weixin/Server.cs new file mode 100644 index 000000000..df286e4a0 --- /dev/null +++ b/BotSharp.Channel.Weixin/Server.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +#if NET45 +using System.Web; +#else +using Microsoft.AspNetCore.Http; +#endif + +namespace BotSharp.Channel.Weixin +{ + public static class Server + { + private static string _appDomainAppPath; + public static string AppDomainAppPath + { + get + { + if (_appDomainAppPath == null) + { +#if NET45 + _appDomainAppPath = HttpRuntime.AppDomainAppPath; +#else + _appDomainAppPath = AppContext.BaseDirectory; //dll所在目录:; +#endif + } + return _appDomainAppPath; + } + set + { + _appDomainAppPath = value; +#if NETSTANDARD1_6 || NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 + if (!_appDomainAppPath.EndsWith("/")) + { + _appDomainAppPath += "/"; + } +#endif + } + } + + private static string _webRootPath; + /// + /// wwwroot文件夹目录(专供ASP.NET Core MVC使用) + /// + public static string WebRootPath + { + get + { + if (_webRootPath == null) + { +#if NET45 + _webRootPath = AppDomainAppPath; +#else + _webRootPath = AppDomainAppPath + "wwwroot/";//asp.net core的wwwroot文件目录结构不一样 +#endif + } + return _webRootPath; + } + set { _webRootPath = value; } + } + + public static string GetMapPath(string virtualPath) + { + if (virtualPath == null) + { + return ""; + } + else if (virtualPath.StartsWith("~/")) + { + return virtualPath.Replace("~/", AppDomainAppPath); + } + else + { + return Path.Combine(AppDomainAppPath, virtualPath); + } + } + + public static HttpContext HttpContext + { + get + { +#if NET45 + HttpContext context = HttpContext.Current; + if (context == null) + { + HttpRequest request = new HttpRequest("Default.aspx", "http://sdk.weixin.senparc.com/default.aspx", null); + StringWriter sw = new StringWriter(); + HttpResponse response = new HttpResponse(sw); + context = new HttpContext(request, response); + } +#else + HttpContext context = new DefaultHttpContext(); +#endif + return context; + } + } + } +} diff --git a/BotSharp.Platform.Dialogflow/README.md b/BotSharp.Platform.Dialogflow/README.md index 0ad3b31f1..a127c2219 100644 --- a/BotSharp.Platform.Dialogflow/README.md +++ b/BotSharp.Platform.Dialogflow/README.md @@ -23,3 +23,7 @@ var aIResponse = nluPlatform.TextRequest(new AiRequest }); ``` +### How to run locally +``` +git clone https://github.com/dotnetcore/BotSharp +``` \ No newline at end of file diff --git a/BotSharp.WebHost/BotSharp.WebHost.csproj b/BotSharp.WebHost/BotSharp.WebHost.csproj index 41e6ab50e..9cb8cb19c 100644 --- a/BotSharp.WebHost/BotSharp.WebHost.csproj +++ b/BotSharp.WebHost/BotSharp.WebHost.csproj @@ -84,6 +84,8 @@ + + diff --git a/BotSharp.WebHost/Settings/app.json b/BotSharp.WebHost/Settings/app.json index ba623afac..486cfadcf 100644 --- a/BotSharp.WebHost/Settings/app.json +++ b/BotSharp.WebHost/Settings/app.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.2.0", "assemblies": "BotSharp.Core", "platformModuleName": "DialogflowAi", @@ -9,6 +9,14 @@ { "Name": "DialogflowAi", "Type": "BotSharp.Platform.Dialogflow" + }, + { + "Name": "WeixinChannel", + "Type": "BotSharp.Channel.Weixin" + }, + { + "Name": "FacebookMessengerChannel", + "Type": "BotSharp.Channel.FacebookMessenger" } ] } diff --git a/BotSharp.WebHost/Settings/channels.weixin.json b/BotSharp.WebHost/Settings/channels.weixin.json index 38bf69db4..072cc323e 100644 --- a/BotSharp.WebHost/Settings/channels.weixin.json +++ b/BotSharp.WebHost/Settings/channels.weixin.json @@ -2,6 +2,7 @@ "weixinChannel": { "token": "botsharp", "encodingAESKey": "", - "appId": "" + "appId": "", + "agentId": "" } } diff --git a/BotSharp.sln b/BotSharp.sln index 4f140ffea..c803e2386 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -19,6 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Platform.Models", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Platform.Dialogflow", "BotSharp.Platform.Dialogflow\BotSharp.Platform.Dialogflow.csproj", "{8C82E35D-DC94-45C9-90FD-1FF62E75CD9B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Channel.Weixin", "BotSharp.Channel.Weixin\BotSharp.Channel.Weixin.csproj", "{D93BD270-10A6-4EFB-938D-508FD2EAC1E0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Channel.FacebookMessenger", "BotSharp.Channel.FacebookMessenger\BotSharp.Channel.FacebookMessenger.csproj", "{22C52A04-581B-4186-8C04-0CD359FA568A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution ARTICULATE|Any CPU = ARTICULATE|Any CPU @@ -179,6 +183,54 @@ Global {8C82E35D-DC94-45C9-90FD-1FF62E75CD9B}.Test|Any CPU.Build.0 = Debug|Any CPU {8C82E35D-DC94-45C9-90FD-1FF62E75CD9B}.Test|x64.ActiveCfg = Debug|Any CPU {8C82E35D-DC94-45C9-90FD-1FF62E75CD9B}.Test|x64.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.ARTICULATE|Any CPU.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.ARTICULATE|Any CPU.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.ARTICULATE|x64.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.ARTICULATE|x64.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Debug|x64.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.DIALOGFLOW|Any CPU.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.DIALOGFLOW|Any CPU.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.DIALOGFLOW|x64.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.DIALOGFLOW|x64.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.RASA|Any CPU.ActiveCfg = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.RASA|Any CPU.Build.0 = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.RASA|x64.ActiveCfg = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.RASA|x64.Build.0 = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Release|Any CPU.Build.0 = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Release|x64.ActiveCfg = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Release|x64.Build.0 = Release|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Test|Any CPU.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Test|Any CPU.Build.0 = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Test|x64.ActiveCfg = Debug|Any CPU + {D93BD270-10A6-4EFB-938D-508FD2EAC1E0}.Test|x64.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.ARTICULATE|Any CPU.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.ARTICULATE|Any CPU.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.ARTICULATE|x64.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.ARTICULATE|x64.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Debug|x64.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Debug|x64.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.DIALOGFLOW|Any CPU.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.DIALOGFLOW|Any CPU.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.DIALOGFLOW|x64.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.DIALOGFLOW|x64.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.RASA|Any CPU.ActiveCfg = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.RASA|Any CPU.Build.0 = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.RASA|x64.ActiveCfg = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.RASA|x64.Build.0 = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Release|Any CPU.Build.0 = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Release|x64.ActiveCfg = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Release|x64.Build.0 = Release|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Test|Any CPU.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Test|Any CPU.Build.0 = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Test|x64.ActiveCfg = Debug|Any CPU + {22C52A04-581B-4186-8C04-0CD359FA568A}.Test|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.rst b/README.rst index 2d794b5dd..721e3cde0 100644 --- a/README.rst +++ b/README.rst @@ -59,23 +59,23 @@ Quick Started You can use docker compose to run BotSharp quickly, make sure you've got `Docker`_ installed. :: - PS D:\> git clone https://github.com/Oceania2018/BotSharp + PS D:\> git clone https://github.com/dotnetcore/BotSharp PS D:\> cd BotSharp - PS D:\BotSharp\> docker-compose -f dockerfiles/docker-compose-articulateui.yml up + PS D:\BotSharp\> docker-compose -f dockerfiles/docker-compose-core.yml up -Point your web browser at http://localhost:**** and enjoy BotSharp with Articulate-UI. +Point your web browser at http://localhost:3112 and enjoy BotSharp Core. Extension Libraries ----------------- 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 platform emulator extension which is compatible with RASA NLU. `botsharp-rasa`_ -* BotSharp platform emulator extension which is compatible with Google Dialogflow. `botsharp-dialogflow`_ +* BotSharp platform emulator extension which is compatible with Google Dialogflow. * BotSharp platform emulator extension which is compatible with Articulate AI. `botsharp-articulate`_ * BotSharp platform emulator extension which is compatible with RasaTalk. `botsharp-rasatalk`_ -* A channel module of BotSharp for Facebook Messenger. `botsharp-channel-fbmessenger`_ -* A channel module of BotSharp for Tencent Wechat. `botsharp-channel-weixin`_ -* A channel module of BotSharp for Telegram. `botsharp-channel-telegram`_ +* A channel module of BotSharp for Facebook Messenger. +* A channel module of BotSharp for Tencent Wechat. +* A channel module of BotSharp for Telegram. * Articulate UI customized for BotSharp NLU. `articulate-ui`_ Documents @@ -102,10 +102,6 @@ Scan to join group in Wechat .. _license: https://raw.githubusercontent.com/Oceania2018/BotSharp/master/LICENSE .. _botsharpnuget: https://www.nuget.org/packages/BotSharp.Core .. _botsharp-rasa: https://github.com/Oceania2018/botsharp-rasa -.. _botsharp-dialogflow: https://github.com/Oceania2018/botsharp-dialogflow .. _botsharp-articulate: https://github.com/Oceania2018/botsharp-articulate .. _botsharp-rasatalk: https://github.com/Obrain2016/botsharp-rasatalk -.. _botsharp-channel-fbmessenger: https://github.com/Oceania2018/botsharp-channel-fbmessenger -.. _botsharp-channel-weixin: https://github.com/Oceania2018/botsharp-channel-weixin -.. _botsharp-channel-telegram: https://github.com/Oceania2018/botsharp-channel-telegram .. _articulate-ui: https://github.com/Oceania2018/articulate-ui diff --git a/dockerfiles/docker-compose-core.yml b/dockerfiles/docker-compose-core.yml new file mode 100644 index 000000000..351554569 --- /dev/null +++ b/dockerfiles/docker-compose-core.yml @@ -0,0 +1,9 @@ +version: '3.0' + +services: + botsharp: + image: botsharpdocker/botsharp-core:0.1.0 + ports: ['0.0.0.0:3112:3112'] + networks: ['botsharp-network'] + +networks: {botsharp-network: {}}