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: {}}