381 lines
16 KiB
C#
381 lines
16 KiB
C#
|
|
/*----------------------------------------------------------------
|
|||
|
|
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
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 自定义MessageHandler
|
|||
|
|
/// 把MessageHandler作为基类,重写对应请求的处理方法
|
|||
|
|
/// </summary>
|
|||
|
|
public partial class CustomMessageHandler : MessageHandler<CustomMessageContext>
|
|||
|
|
{
|
|||
|
|
/*
|
|||
|
|
* 重要提示: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
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 模板消息集合(Key:checkCode,Value:OpenId)
|
|||
|
|
/// </summary>
|
|||
|
|
public static Dictionary<string, string> TemplateMessageCollection = new Dictionary<string, string>();
|
|||
|
|
|
|||
|
|
private IPlatformBuilder<AgentModel> nluPlatform = null;
|
|||
|
|
private readonly IConfiguration config;
|
|||
|
|
|
|||
|
|
public CustomMessageHandler(IPlatformBuilder<AgentModel> nluPlatform, IConfiguration configuration, Stream inputStream, PostModel postModel, int maxRecordCount = 0)
|
|||
|
|
: base(inputStream, postModel, maxRecordCount)
|
|||
|
|
{
|
|||
|
|
this.nluPlatform = nluPlatform;
|
|||
|
|
this.config = configuration;
|
|||
|
|
|
|||
|
|
//这里设置仅用于测试,实际开发可以在外部更全局的地方设置,
|
|||
|
|
//比如MessageHandler<MessageContext>.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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理文字请求
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)
|
|||
|
|
{
|
|||
|
|
var defaultResponseMessage = base.CreateResponseMessage<ResponseMessageText>();
|
|||
|
|
|
|||
|
|
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<AIResponseResult>(new AiRequest
|
|||
|
|
{
|
|||
|
|
Text = requestMessage.Content,
|
|||
|
|
AgentId = config.GetValue<string>("weixinChannel:agentId"),
|
|||
|
|
SessionId = requestMessage.FromUserName
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
result.AppendFormat("{0}", aIResponse.Result.Fulfillment.Speech);
|
|||
|
|
|
|||
|
|
defaultResponseMessage.Content = result.ToString();
|
|||
|
|
return defaultResponseMessage;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return requestHandler.GetResponseMessage() as IResponseMessageBase;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理位置请求
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requestMessage"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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<ResponseMessageText>();
|
|||
|
|
responseMessage.Content = "您刚才发送的是小视频";
|
|||
|
|
return responseMessage;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理图片请求
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requestMessage"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)
|
|||
|
|
{
|
|||
|
|
//一隔一返回News或Image格式
|
|||
|
|
if (base.GlobalMessageContext.GetMessageContext(requestMessage).RequestMessages.Count() % 2 == 0)
|
|||
|
|
{
|
|||
|
|
var responseMessage = CreateResponseMessage<ResponseMessageNews>();
|
|||
|
|
|
|||
|
|
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<ResponseMessageImage>();
|
|||
|
|
responseMessage.Image.MediaId = requestMessage.MediaId;
|
|||
|
|
return responseMessage;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理语音请求
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requestMessage"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage)
|
|||
|
|
{
|
|||
|
|
var responseMessage = CreateResponseMessage<ResponseMessageMusic>();
|
|||
|
|
//上传缩略图
|
|||
|
|
//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;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理视频请求
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requestMessage"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override IResponseMessageBase OnVideoRequest(RequestMessageVideo requestMessage)
|
|||
|
|
{
|
|||
|
|
var responseMessage = CreateResponseMessage<ResponseMessageText>();
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理链接消息请求
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requestMessage"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override IResponseMessageBase OnLinkRequest(RequestMessageLink requestMessage)
|
|||
|
|
{
|
|||
|
|
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(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<ResponseMessageText>();
|
|||
|
|
responseMessage.Content = string.Format(@"您发送了一个文件:
|
|||
|
|
文件名:{0}
|
|||
|
|
说明:{1}
|
|||
|
|
大小:{2}
|
|||
|
|
MD5:{3}", requestMessage.Title, requestMessage.Description, requestMessage.FileTotalLen, requestMessage.FileMd5);
|
|||
|
|
return responseMessage;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理事件请求(这个方法一般不用重写,这里仅作为示例出现。除非需要在判断具体Event类型以外对Event信息进行统一操作
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requestMessage"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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<ResponseMessageText>();
|
|||
|
|
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<ResponseMessageText>();
|
|||
|
|
responseMessage.Content = "未知消息类型:" + msgType;
|
|||
|
|
|
|||
|
|
WeixinTrace.SendCustomLog("未知请求消息类型", requestMessage.RequestDocument.ToString());//记录到日志中
|
|||
|
|
|
|||
|
|
return responseMessage;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|