当前位置:   article > 正文

微信公众号 智能客服

微信公众号智能客服开发

前言

微信公众号的开发,园子里有很多资料,这里简述。
虽说是智能,现在是仿佛智障,很多是hard code逻辑,日后将逐步加入LUIS,现在一些常用的打招呼(你好,您好,hi,hey,hello,how are you等识别比较好)。

业务性的处理,更多是逻辑上,比如常用的回复1,2,3,4然后返回对应的消息。这里涉及多轮会话,要求对上文的记忆,不然容易回答串题了。

还有就是一些分词技术,这个目前对空格敏感,还有就是南京市长江大桥,是南京市长,还是长江大桥?这个是日后话题了。

.NET Core APIi的方式,创建一个WeChat API Controller

验证微信服务器地址的方法:

  1. [HttpGet]
  2. [Route("api/wechat")]
  3. public IActionResult Get(string signature, string timestamp, string nonce, string echostr)
  4. {
  5. var token = ConfigReader.TokenStr;//微信公众平台后台设置的Token
  6. if (string.IsNullOrEmpty(token))
  7. {
  8. return NotFound("请先设置Token!");
  9. //return new HttpResponseMessage() { Content = new StringContent("请先设置Token!", Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded") };
  10. }
  11. var ent = "";
  12. if (!BasicAPI.CheckSignature(signature, timestamp, nonce, token, out ent))
  13. {
  14. return NotFound("验证微信签名失败!");
  15. //return new HttpResponseMessage() { Content = new StringContent("参数错误!", Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded") };
  16. }
  17. //返回随机字符串则表示验证通过
  18. return Ok(echostr);
  19. //return new HttpResponseMessage() { Content = new StringContent(echostr, Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded") };
  20. }

验证成功之后,所有消息,微信会转到这个方法:

  1. 用户发送消息后,微信平台自动Post一个请求到这里,并等待响应XML。
  2. [HttpPost]
  3. [Route("api/wechat")]
  4. public async Task<IActionResult> Post()
  5. {
  6. WeChatMessage message = null;
  7. string signature = HttpContext.Request.Query["signature"].ToString();
  8. string timestamp = HttpContext.Request.Query["timestamp"].ToString();
  9. string nonce = HttpContext.Request.Query["nonce"].ToString();
  10. string echostr = HttpContext.Request.Query["echostr"].ToString();
  11. var safeMode = HttpContext.Request.Query["encrypt_type"].ToString() == "aes";
  12. using (var streamReader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
  13. {
  14. var decryptMsg = string.Empty;
  15. var msg = streamReader.ReadToEnd();
  16. #region 解密
  17. if (safeMode)
  18. {
  19. var msg_signature = HttpContext.Request.Query["msg_signature"];
  20. var wechatBizMsgCrypt = new WeChatMsgCrypt(ConfigReader.TokenUrl, ConfigReader.EncodingAESKey, ConfigReader.AppId);
  21. var ret = wechatBizMsgCrypt.DecryptMsg(msg_signature, timestamp, nonce, msg, ref decryptMsg);
  22. if (ret != 0)//解密失败
  23. {
  24. //TODO:失败的业务处理逻辑
  25. }
  26. }
  27. else
  28. {
  29. decryptMsg = msg;
  30. }
  31. #endregion
  32. message = ParseMessageType.Parse(decryptMsg);
  33. }
  34. var response = await new WeChatExecutor(repo).Execute(message);
  35. var encryptMsg = string.Empty;
  36. #region 加密
  37. if (safeMode)
  38. {
  39. var msg_signature = HttpContext.Request.Query["msg_signature"];
  40. var wxBizMsgCrypt = new WeChatMsgCrypt(ConfigReader.TokenUrl, ConfigReader.EncodingAESKey, ConfigReader.AppId);
  41. var ret = wxBizMsgCrypt.EncryptMsg(response, timestamp, nonce, ref encryptMsg);
  42. if (ret != 0)//加密失败
  43. {
  44. //TODO:开发者加密失败的业务处理逻辑
  45. }
  46. }
  47. else
  48. {
  49. encryptMsg = response;
  50. }
  51. #endregion
  52. return Ok(encryptMsg);
  53. //return new HttpResponseMessage()
  54. //{
  55. // Content = new StringContent(encryptMsg, Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded")
  56. // //ContentType = "text/xml",
  57. //};
  58. }

将接收到的消息,转发到Bot(Microsoft BotFramework)服务,将Bot服务的SecretKey配置上:

  1. public async static Task<string> PostMessage(string message, string openId)
  2. {
  3. HttpClient client;
  4. HttpResponseMessage response;
  5. bool IsReplyReceived = false;
  6. string ReceivedString = null;
  7. client = new HttpClient();
  8. client.BaseAddress = new Uri("https://directline.botframework.com/api/conversations/");
  9. client.DefaultRequestHeaders.Accept.Clear();
  10. client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  11. //bot-int WeChat channel
  12. client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("BotConnector", ConfigReader.BotSecretKey);
  13. response = await client.GetAsync("/api/tokens/");
  14. if (response.IsSuccessStatusCode)
  15. {
  16. var conversation = new Conversation();
  17. response = await client.PostAsJsonAsync("/api/conversations/", conversation);
  18. if (response.IsSuccessStatusCode)
  19. {
  20. Conversation ConversationInfo = response.Content.ReadAsAsync(typeof(Conversation)).Result as Conversation;
  21. var contentString = response.Content.ReadAsStringAsync();
  22. string conversationUrl = ConversationInfo.conversationId + "/messages/";
  23. Message msg = new Message() { text = message, from = openId, channelData = "WeChat" };
  24. response = await client.PostAsJsonAsync(conversationUrl, msg);
  25. if (response.IsSuccessStatusCode)
  26. {
  27. response = await client.GetAsync(conversationUrl);
  28. if (response.IsSuccessStatusCode)
  29. {
  30. MessageSet BotMessage = response.Content.ReadAsAsync(typeof(MessageSet)).Result as MessageSet;
  31. ReceivedString = BotMessage.messages[1].text;
  32. IsReplyReceived = true;
  33. }
  34. }
  35. }
  36. }
  37. return ReceivedString;
  38. }

这里要定义几个model,用于序列化Bot返回的数据类型:

  1. public class Conversation
  2. {
  3. public string conversationId { get; set; }
  4. public string token { get; set; }
  5. public int expires_in { get; set; }
  6. }
  7. public class MessageSet
  8. {
  9. public Message[] messages { get; set; }
  10. public string watermark { get; set; }
  11. public string eTag { get; set; }
  12. }
  13. public class Message
  14. {
  15. public string id { get; set; }
  16. public string conversationId { get; set; }
  17. public DateTime created { get; set; }
  18. public string from { get; set; }
  19. public string text { get; set; }
  20. public string channelData { get; set; }
  21. public string[] images { get; set; }
  22. public Attachment[] attachments { get; set; }
  23. public string eTag { get; set; }
  24. }
  25. public class Attachment
  26. {
  27. public string url { get; set; }
  28. public string contentType { get; set; }
  29. }

Bot server中的消息路由处理方法

  1. public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
  2. {
  3. if (activity != null && activity.GetActivityType() == ActivityTypes.Message)
  4. {
  5. logger.Information(new ConversationEvent
  6. {
  7. EventType = LogEventType.ChatBotActivity,
  8. Message = $"ChatBot message controller receives a message, content: {activity.Text}",
  9. Activity = activity
  10. });
  11. var isAgent = await ServiceManager.AgentManager.IsAgent(activity.From.Id);
  12. var context = await ServiceManager.ContextManager.GetUserContext(activity, isAgent);
  13. var activityLogger = new Logger.CosmosDbActivityLogger();
  14. if (await ServiceManager.CommandMessageHandler.HandleCommandAsync(activity, isAgent) == false)
  15. {
  16. if (!string.IsNullOrEmpty(activity.Text)
  17. && activity.Text.ToLower().Contains(CommandRequestConnection))
  18. {
  19. var contextManager = ServiceManager.ContextManager;
  20. var messageRouterResultHandler = ServiceManager.MessageRouterResultHandler;
  21. var result = await contextManager.RequestConnection(context, activity);
  22. await activityLogger.LogAsync(activity);
  23. // Handle the result, if required
  24. await messageRouterResultHandler.HandleResultAsync(result);
  25. }
  26. else
  27. {
  28. if (isAgent)
  29. {
  30. IAgentActor agentActor = SFHelper.GetActor<IAgentActor>(activity);
  31. await activityLogger.LogAsync(activity);
  32. await agentActor.MessagePost(context, new ActivityDataContract(activity));
  33. }
  34. else
  35. {
  36. ICustomerActor customerActor = SFHelper.GetActor<ICustomerActor>(activity);
  37. await customerActor.MessagePost(context, new ActivityDataContract(activity));
  38. if (activity.Text.Equals("没有解决") || activity.Text.Equals("解决了"))
  39. {
  40. await ServiceManager.InquiryManager.CloseInquiryAsync(context);
  41. }
  42. }
  43. }
  44. }
  45. }
  46. else
  47. {
  48. HandleSystemMessage(activity);
  49. }
  50. return new HttpResponseMessage(HttpStatusCode.Accepted);
  51. }

定义回复消息内容,markdown格式,微信支持不友好。

  1. public static Activity BuildProactiveGreeting(Activity context)
  2. {
  3. var responseMessage = context.CreateReply();
  4. // TODO: Move to MenuManager via DataReposotry to persist to CosmosDB
  5. var topLevelMenuItems = new List<CardActionItem>
  6. {
  7. new CardActionItem() { Title = "技术支持", ActionType = ActionTypes.ImBack },
  8. new CardActionItem() { Title = "账单/订阅/配额支持", ActionType = ActionTypes.ImBack },
  9. new CardActionItem() { Title = "产品咨询", ActionType = ActionTypes.ImBack },
  10. new CardActionItem() { Title = "注册问题", ActionType = ActionTypes.ImBack }
  11. };
  12. // TODO: make sure we get correct name from wechat channel
  13. string cardTitleText = "Hi,我是您的智能客服,等您很久啦~猜您可能对以下内容感兴趣:";
  14. var heroCard = BuildHeroCardWithActionButtons(cardTitleText, topLevelMenuItems);
  15. responseMessage.AttachmentLayout = AttachmentLayoutTypes.List;
  16. responseMessage.Attachments = new List<Attachment>() { heroCard.ToAttachment() };
  17. return responseMessage;
  18. }

Bot回复消息:

  1. var client = new ConnectorClient(new Uri(message.ServiceUrl), new MicrosoftAppCredentials());
  2. var reply = GreetingHelper.BuildProactiveGreeting(message);
  3. client.Conversations.ReplyToActivityAsync(reply);

效果图

600371-20180330115900623-609902364.png

就是打招呼的时候,会回复一个简单得产品介绍,暂时没有多轮会话,因为上下文理解出现一些问题,就不献丑了(滑稽)。

后记

日后还有个切换到人工客服,当输入人工客服时,调用微信公众号的客服消息接口,每两小时(过期时间7200s)获取一次token,
然后根据用户的OPENID,将微信支持的消息格式,发送到粉丝端,获取token的IP必须添加到白名单中。
这个要求公众号是认证的,个人的没有授权。

转载于:https://www.cnblogs.com/shy-huang/p/8675446.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/845190?site
推荐阅读
相关标签
  

闽ICP备14008679号