http://www.cnblogs.com/sonic1abc/p/5941442.html 这边文章是我去年10月看到的,在2017年3月参见微软PowerBI培训中有幸见到博主了。“如何将 Microsoft Bot Framework 链接至微信公共号”该文章详细描述了如何将 微信公众号、Bot、Luis完美结合在一起。整个交互过程如下:
接下来就介绍如何把微信和Luis做结合,省去Bot Server这一环节。基本原理图如下:
接下来看具体实现:
微信开发接入
- 公众号申请及配置
- 微信公众号申请步骤此处省略可以到https://mp.weixin.qq.com/申请
- 需要对微信管理后台启用服务器配置
- 微信服务器校验
- 根据微信公众号开发文档提示如下:
-
- 检验signature的C#示例代码(采用C# MVC、Senparc.Weixin SDK)
1 using Senparc.Weixin.MP; 2 using Senparc.Weixin.MP.Entities.Request; 3 using System; 4 using System.Collections.Generic; 5 using System.IO; 6 using System.Linq; 7 using System.Web; 8 using System.Web.Configuration; 9 using System.Web.Mvc; 10 using WeChat.Conduits; 11 12 13 14 namespace WeChat.Paltform.Controllers 15 { 16 using Senparc.Weixin.MP.MvcExtension; 17 using WeChat.Conduits.MessageHandlers.CustomMessageHandler; 18 public class WeixinController : Controller 19 { 20 public static readonly string Token = WebConfigurationManager.AppSettings["WeixinToken"];//与微信公众账号后台的Token设置保持一致,区分大小写。 21 public static readonly string EncodingAESKey = WebConfigurationManager.AppSettings["WeixinEncodingAESKey"];//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。 22 public static readonly string AppId = WebConfigurationManager.AppSettings["WeixinAppId"];//与微信公众账号后台的AppId设置保持一致,区分大小写。 23 readonly Func<string> _getRandomFileName = () => DateTime.Now.ToString("yyyyMMdd-HHmmss") + Guid.NewGuid().ToString("n").Substring(0, 6); 24 public WeixinController() 25 { 26 27 } 28 /// <summary> 29 /// / 微信后台验证地址(使用Get),微信后台的“接口配置信息”的Url填写如 30 /// </summary> 31 /// <param name="postModel"></param> 32 /// <param name="echostr"></param> 33 /// <returns></returns> 34 [HttpGet] 35 [ActionName("Index")] 36 public ActionResult Get(PostModel postModel, string echostr) 37 { 38 if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token)) 39 { 40 return Content(echostr); //返回随机字符串则表示验证通过 41 } 42 else 43 { 44 return Content("failed:" + postModel.Signature + "," + Senparc.Weixin.MP.CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) + "。" + 45 "如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。"); 46 } 47 } 48 /// <summary> 49 /// 用户发送消息后,微信平台自动Post一个请求到这里,并等待响应XML。 50 /// PS:此方法为简化方法,效果与OldPost一致。 51 /// v0.8之后的版本可以结合Senparc.Weixin.MP.MvcExtension扩展包,使用WeixinResult,见MiniPost方法。 52 /// </summary> 53 [HttpPost] 54 [ActionName("Index")] 55 public ActionResult Post(PostModel postModel) 56 { 57 if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token)) 58 { 59 return Content("参数错误!"); 60 } 61 62 postModel.Token = Token; 63 postModel.EncodingAESKey = EncodingAESKey; 64 postModel.AppId = AppId; 65 66 67 var maxRecordCount = 10; 68 69 var logPath = Server.MapPath(string.Format("~/App_Data/MP/{0}/", DateTime.Now.ToString("yyyy-MM-dd"))); 70 if (!Directory.Exists(logPath)) 71 { 72 Directory.CreateDirectory(logPath); 73 } 74 75 var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, maxRecordCount); 76 77 78 try 79 { 80 //测试时可开启此记录,帮助跟踪数据,使用前请确保App_Data文件夹存在,且有读写权限。 81 messageHandler.RequestDocument.Save(Path.Combine(logPath, string.Format("{0}_Request_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.FromUserName))); 82 if (messageHandler.UsingEcryptMessage) 83 { 84 messageHandler.EcryptRequestDocument.Save(Path.Combine(logPath, string.Format("{0}_Request_Ecrypt_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.FromUserName))); 85 } 86 messageHandler.OmitRepeatedMessage = true; 87 88 //执行微信处理过程 89 messageHandler.Execute(); 90 91 92 if (messageHandler.ResponseDocument != null) 93 { 94 messageHandler.ResponseDocument.Save(Path.Combine(logPath, string.Format("{0}_Response_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.FromUserName))); 95 } 96 97 if (messageHandler.UsingEcryptMessage) 98 { 99 //记录加密后的响应信息 100 messageHandler.FinalResponseDocument.Save(Path.Combine(logPath, string.Format("{0}_Response_Final_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.FromUserName))); 101 } 102 103 return Content(messageHandler.ResponseDocument.ToString());//v0.7- 104 } 105 catch (Exception ex) 106 { 107 using (TextWriter tw = new StreamWriter(Server.MapPath("~/App_Data/Error_" + _getRandomFileName() + ".txt"))) 108 { 109 tw.WriteLine("ExecptionMessage:" + ex.Message); 110 tw.WriteLine(ex.Source); 111 tw.WriteLine(ex.StackTrace); 112 //tw.WriteLine("InnerExecptionMessage:" + ex.InnerException.Message); 113 114 if (messageHandler.ResponseDocument != null) 115 { 116 tw.WriteLine(messageHandler.ResponseDocument.ToString()); 117 } 118 119 if (ex.InnerException != null) 120 { 121 tw.WriteLine("========= InnerException ========="); 122 tw.WriteLine(ex.InnerException.Message); 123 tw.WriteLine(ex.InnerException.Source); 124 tw.WriteLine(ex.InnerException.StackTrace); 125 } 126 127 tw.Flush(); 128 tw.Close(); 129 } 130 return Content(""); 131 } 132 } 133 } 134 }
-
- 微信消息处理代码(微信消息类型有很多种,以下是只贴出文本、语音消息处理)
1 /// <summary> 2 /// 处理文字请求 3 /// </summary> 4 /// <returns></returns> 5 public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage) 6 { 7 8 9 var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); 10 if (requestMessage.Content == "您好" || requestMessage.Content == "你好") 11 { 12 responseMessage.Content = requestMessage.Content; 13 } 14 else { 15 responseMessage.Content = LuisService.Instance.PostMessage(requestMessage.Content); 16 } 17 18 return responseMessage; 19 } 20 21 /// <summary> 22 /// 处理语音请求 23 /// </summary> 24 /// <param name="requestMessage"></param> 25 /// <returns></returns> 26 public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage) 27 { 28 29 var responseMessage = CreateResponseMessage<ResponseMessageText>(); 30 31 responseMessage.Content = LuisService.Instance.PostMessage(requestMessage.Recognition.ToString()); 32 33 return responseMessage; 34 }
Luis简介和应用构建
- Luis介绍
牛津计划(Project Oxford)”的最新成果“语义理解智能服务(LUIS)”,作为微软智能云Azure服务,“语义理解智能服务”目前已正式面向公众开放,并且支持汉语应用,开发者无需机器学习或人工智能方面的专业技能,即可在其移动应用中加入自然语言识别能力。开发者可以访问https://www.luis.ai/ 获取更多信息。
“牛津计划”旨在帮助开发者制作更智能的移动应用,令其方便快捷地在跨平台应用中加入面部、图像、语言、文本等识别功能。一度在国内社交媒体上非常流行的How-Old.net和TwinsOrNot应用,就是以此为基础在几小时内开发出来的。
- Luis App创建
- 用自己的微软账号登录后,首先创建App,点击New App进行创建
-
- 创建完成应用后需要确定应用意图
意图是指想通过语句表达出来目的或期望的操作。它们是你应用程序主要构建的基础。你现在在需要确定你希望应用程序检测的意图(如:天气查询、股票查询),在网站意图菜单连接中创建意图
-
- 为意图添加句式
现在你需要开始为每个意图创建示例,以便及其学习模型了解不同的模式(例如:明天上海会下雨吗?、帮我查一下300170股价)。选择你刚刚添加的意图,然后开始句式添加并保存到你意图
-
- 添加实体
既然我们已经有意图,我们将继续添加实体。实体是对应用执行任务以及为语义解析提取起着至关重要的作用。下一步就是在意图的句式中标记实体,在你添加的句式中就会突显相关的实体标记
-
- 训练和测试
我们基本要完成一用的创建了。现在应该和测试我们所创建的模型和性能了,通过侧菜单转到“训练和测试”页面。并开始测试你的模型。最后发布应用我们可以得到Endpoint Url值我就可以在后端代码中对器进行调用
Luis App调用
- 结合微信输入(语音、文本) + luis 语义理解,先效果图。
- LuisAPI定义
1 using Newtonsoft.Json; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Net.Http; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace WeChat.Luis 10 { 11 public class LuisAPI 12 { 13 /// <summary> 14 /// Luis Public app Id 15 /// </summary> 16 private static readonly string URI = "发布后生成的Endpoint url"; 17 18 /// <summary> 19 ///实例化 20 /// </summary> 21 public static LuisAPI Instance 22 { 23 get { return new LuisAPI(); } 24 } 25 /// <summary> 26 /// Get Luis 接口 27 /// </summary> 28 /// <param name="Query"></param> 29 /// <returns></returns> 30 public LuisResponseMessage GetLuis(string Query) 31 { 32 Query = Uri.EscapeDataString(Query); 33 LuisResponseMessage Data = new LuisResponseMessage(); 34 using (HttpClient client = new HttpClient()) 35 { 36 string RequestURI = string.Format(URI, Query); 37 var task = client.GetAsync(RequestURI); 38 HttpResponseMessage msg = task.Result; 39 40 if (msg.IsSuccessStatusCode) 41 { 42 var task1 = msg.Content.ReadAsStringAsync(); 43 var JsonDataResponse = task1.Result; 44 Data = JsonConvert.DeserializeObject<LuisResponseMessage>(JsonDataResponse); 45 } 46 } 47 return Data; 48 } 49 50 } 51 }
- LuisService定义
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace WeChat.Luis 8 { 9 public class LuisService 10 { 11 /// <summary> 12 ///实例化 13 /// </summary> 14 public static LuisService Instance 15 { 16 get { return new LuisService(); } 17 } 18 19 public string PostMessage(string query) 20 { 21 LuisResponseMessage luisMsg = LuisAPI.Instance.GetLuis(query); 22 if (luisMsg == null ) 23 return LuisInfos.DEFAULT_LUIS; 24 25 string reponse = ""; 26 switch (luisMsg.topScoringIntent.intent) 27 { 28 case "天气查询": 29 if (luisMsg.entities == null || luisMsg.entities.Count <= 0) 30 reponse = "亲你要查询哪个地方的天气信息呢,快把城市的名字发给我吧"; 31 else 32 reponse = LuisWeather.Instance.GetWeather(luisMsg.entities[0].entity); 33 34 break; 35 case "股票查询": 36 if (luisMsg.entities == null || luisMsg.entities.Count <= 0) 37 reponse = "亲你需要要查询哪只股票信息呢,快把股票代码发给我吧"; 38 else 39 reponse = LuisStock.Instance.QueryStock(luisMsg.entities[0].entity); 40 41 42 break; 43 //case "查询客户信息": 44 // reponse = LuisPartner.Default.QueryInfo(luisMsg.entities[0].entity); 45 // break; 46 //case "查覆盖率": 47 // reponse = LuisStaConver.Default.QueryInfo(luisMsg.entities[0].entity); 48 // break; 49 default: 50 reponse = LuisInfos.DEFAULT_LUIS; 51 break; 52 } 53 54 return reponse; 55 } 56 } 57 }
- 微信消息处理接口调用
/// <summary> /// 处理文字请求 /// </summary> /// <returns></returns> public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage) { var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); if (requestMessage.Content == "您好" || requestMessage.Content == "你好") { responseMessage.Content = requestMessage.Content; } else { responseMessage.Content = LuisService.Instance.PostMessage(requestMessage.Content); } return responseMessage; } /// <summary> /// 处理语音请求 /// </summary> /// <param name="requestMessage"></param> /// <returns></returns> public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage) { var responseMessage = CreateResponseMessage<ResponseMessageText>(); responseMessage.Content = LuisService.Instance.PostMessage(requestMessage.Recognition.ToString()); return responseMessage; }