You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

598 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*----------------------------------------------------------------
Copyright (C) 2023 Senparc
文件名CustomMessageHandler_Events.cs
文件功能描述自定义MessageHandler
创建标识Senparc - 20150312
----------------------------------------------------------------*/
//DPBMARK_FILE MP
using Senparc.CO2NET;
using Senparc.CO2NET.Extensions;
using Senparc.CO2NET.Utilities;
using Senparc.NeuChar.Agents;
using Senparc.NeuChar.Entities;
using Senparc.Weixin.Exceptions;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.MP.Entities;
using Senparc.Weixin.Sample.CommonService.Download;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
//#if NET462
//using System.Web;
//#endif
namespace Senparc.Weixin.Sample.CommonService.CustomMessageHandler
{
/// <summary>
/// 自定义MessageHandler
/// </summary>
public partial class CustomMessageHandler
{
private string GetWelcomeInfo()
{
//获取Senparc.Weixin.MP.dll版本信息
#if NET462
var filePath = ServerUtility.ContentRootMapPath("~/bin/Senparc.Weixin.MP.dll");//发布路径
#else
//var filePath = ServerUtility.ContentRootMapPath("~/bin/Release/netcoreapp2.2/Senparc.Weixin.MP.dll");//本地测试路径
var filePath = ServerUtility.ContentRootMapPath("~/Senparc.Weixin.MP.dll");//发布路径
#endif
var fileVersionInfo = FileVersionInfo.GetVersionInfo(filePath);
string version = fileVersionInfo == null
? "-"
: string.Format("{0}.{1}.{2}", fileVersionInfo.FileMajorPart, fileVersionInfo.FileMinorPart, fileVersionInfo.FileBuildPart);
return string.Format(
@"欢迎关注【Senparc.Weixin 微信公众平台SDK】当前运行版本v{0}。
您可以发送【文字】【位置】【图片】【语音】【文件】等不同类型的信息,查看不同格式的回复。
您也可以直接点击菜单查看各种类型的回复。
还可以点击菜单体验微信支付。
SDK官方地址https://weixin.senparc.com
SDK Demohttps://sdk.weixin.senparc.com
源代码及Demo下载地址https://github.com/JeffreySu/WeiXinMPSDK
Nuget地址https://www.nuget.org/packages/Senparc.Weixin.MP
QQ群377815480
===============
更多:
1、JSSDK测试https://sdk.weixin.senparc.com/WeixinJSSDK
2、开放平台测试建议PC上打开https://sdk.weixin.senparc.com/OpenOAuth/JumpToMpOAuth
3、回复关键字
【open】 进入第三方开放平台Senparc.Weixin.Open测试
【tm】 测试异步模板消息
【openid】 获取OpenId等用户信息
【约束】 测试微信浏览器约束
【AsyncTest】 异步并发测试
【错误】 体验发生错误无法返回正确信息
【容错】 体验去重容错
【ex】 体验错误日志推送提醒
【mute】 不返回任何消息,也无出错信息
【jssdk】 测试JSSDK图文转发接口
格式:【数字#数字】如2010#0102调用正则表达式匹配
【订阅】 测试“一次性订阅消息”接口
【SP】 测试 ServiceProvider
",
version);
}
public string GetDownloadInfo(CodeRecord codeRecord)
{
return string.Format(@"您已通过二维码验证,浏览器即将开始下载 Senparc.Weixin SDK 帮助文档。
当前选择的版本v{0}{1}
我们期待您的意见和建议客服热线400-031-8816。
感谢您对盛派网络的支持!
© {2} Senparc", codeRecord.Version, codeRecord.IsWebVersion ? "网页版" : ".chm文档版", SystemTime.Now.Year);
}
public override IResponseMessageBase OnTextOrEventRequest(RequestMessageText requestMessage)
{
// 预处理文字或事件类型请求。
// 这个请求是一个比较特殊的请求,通常用于统一处理来自文字或菜单按钮的同一个执行逻辑,
// 会在执行OnTextRequest或OnEventRequest之前触发具有以下一些特征
// 1、如果返回null则继续执行OnTextRequest或OnEventRequest
// 2、如果返回不为null则终止执行OnTextRequest或OnEventRequest返回最终ResponseMessage
// 3、如果是事件则会将RequestMessageEvent自动转为RequestMessageText类型其中RequestMessageText.Content就是RequestMessageEvent.EventKey
if (requestMessage.Content == "OneClick")
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
strongResponseMessage.Content = "您点击了底部按钮。\r\n为了测试微信软件换行bug的应对措施这里做了一个——\r\n换行";
return strongResponseMessage;
}
return null;//返回null则继续执行OnTextRequest或OnEventRequest
}
/// <summary>
/// 点击事件
/// </summary>
/// <param name="requestMessage">请求消息</param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ClickRequest(RequestMessageEvent_Click requestMessage)
{
IResponseMessageBase reponseMessage = null;
//菜单点击需要跟创建菜单时的Key匹配
switch (requestMessage.EventKey)
{
case "OneClick":
{
//这个过程实际已经在OnTextOrEventRequest中命中“OneClick”关键字并完成回复这里不会执行到。
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Content = "您点击了底部按钮。\r\n为了测试微信软件换行bug的应对措施这里做了一个——\r\n换行";
}
break;
case "SubClickRoot_Text":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Content = "您点击了子菜单按钮。";
}
break;
case "SubClickRoot_News":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageNews>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Articles.Add(new Article()
{
Title = "您点击了子菜单图文按钮",
Description = "您点击了子菜单图文按钮这是一条图文信息。这个区域是Description内容\r\n可以使用\\r\\n进行换行。",
PicUrl = "https://sdk.weixin.senparc.com/Images/qrcode.jpg",
Url = "https://sdk.weixin.senparc.com"
});
//随机添加一条图文,或只输出一条图文信息
if (SystemTime.Now.Second % 2 == 0)
{
strongResponseMessage.Articles.Add(new Article()
{
Title = "这是随机产生的第二条图文信息,用于测试多条图文的样式",
Description = "这是随机产生的第二条图文信息,用于测试多条图文的样式",
PicUrl = "https://sdk.weixin.senparc.com/Images/qrcode.jpg",
Url = "https://sdk.weixin.senparc.com"
});
}
}
break;
case "SubClickRoot_Music":
{
//上传缩略图
#if NET462
var filePath = "~/Images/Logo.thumb.jpg";
#else
var filePath = "~/wwwroot/Images/Logo.thumb.jpg";
#endif
var uploadResult = MP.AdvancedAPIs.MediaApi.UploadTemporaryMedia(appId, UploadMediaFileType.thumb,
ServerUtility.ContentRootMapPath(filePath));
//PS缩略图官方没有特别提示文件大小限制实际测试哪怕114K也会返回文件过大的错误因此尽量控制在小一点当前图片39K
//设置音乐信息
var strongResponseMessage = CreateResponseMessage<ResponseMessageMusic>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Music.Title = "天籁之音";
strongResponseMessage.Music.Description = "真的是天籁之音";
strongResponseMessage.Music.MusicUrl = "https://sdk.weixin.senparc.com/Content/music1.mp3";
strongResponseMessage.Music.HQMusicUrl = "https://sdk.weixin.senparc.com/Content/music1.mp3";
strongResponseMessage.Music.ThumbMediaId = uploadResult.thumb_media_id;
}
break;
case "SubClickRoot_Image":
{
//上传图片
#if NET462
var filePath = "~/Images/Logo.jpg";
#else
var filePath = "~/wwwroot/Images/Logo.jpg";
#endif
var uploadResult = MP.AdvancedAPIs.MediaApi.UploadTemporaryMedia(appId, UploadMediaFileType.image,
ServerUtility.ContentRootMapPath(filePath));
//设置图片信息
var strongResponseMessage = CreateResponseMessage<ResponseMessageImage>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Image.MediaId = uploadResult.media_id;
}
break;
case "SendMenu"://菜单消息
{
//注意:
//1、此接口可以在任意地方调用包括后台线程此处演示为通过
//2、一下"s:"前缀只是 Senparc.Weixin 的内部约定,可以使用 OnTextRequest事件中的 requestHandler.SelectMenuKeyword() 方法自动匹配到后缀如101
var menuContentList = new List<SendMenuContent>(){
new SendMenuContent("101","满意"),
new SendMenuContent("102","一般"),
new SendMenuContent("103","不满意")
};
//使用异步接口
CustomApi.SendMenuAsync(appId, OpenId, "请对 Senparc.Weixin SDK 给出您的评价", menuContentList, "感谢您的参与!");
reponseMessage = new ResponseMessageNoResponse();//不返回任何消息
}
break;
case "SubClickRoot_Agent"://代理消息
{
//获取返回的XML
var dt1 = SystemTime.Now;
reponseMessage = MessageAgent.RequestResponseMessage(this,
#if NET462
null,
#else
Senparc.CO2NET.SenparcDI.GetServiceProvider(),
# endif
agentUrl, agentToken, RequestDocument.ToString());
//上面的方法也可以使用扩展方法this.RequestResponseMessage(this,agentUrl, agentToken, RequestDocument.ToString());
var dt2 = SystemTime.Now;
if (reponseMessage is ResponseMessageNews)
{
(reponseMessage as ResponseMessageNews)
.Articles[0]
.Description += string.Format("\r\n\r\n代理过程总耗时{0}毫秒", (dt2 - dt1).Milliseconds);
}
}
break;
case "Member"://托管代理会员信息
{
//原始方法为MessageAgent.RequestXml(this,agentUrl, agentToken, RequestDocument.ToString());//获取返回的XML
reponseMessage = this.RequestResponseMessage(
#if NET462
null,
#else
Senparc.CO2NET.SenparcDI.GetServiceProvider(),
#endif
agentUrl, agentToken, RequestDocument.ToString());
}
break;
case "OAuth"://OAuth授权测试
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageNews>();
strongResponseMessage.Articles.Add(new Article()
{
Title = "OAuth2.0测试",
Description = "选择下面两种不同的方式进行测试,区别在于授权成功后,最后停留的页面。",
//Url = "https://sdk.weixin.senparc.com/oauth2",
//PicUrl = "https://sdk.weixin.senparc.com/Images/qrcode.jpg"
});
strongResponseMessage.Articles.Add(new Article()
{
Title = "OAuth2.0测试不带returnUrl测试环境可使用",
Description = "OAuth2.0测试不带returnUrl",
Url = "https://sdk.weixin.senparc.com/oauth2",
PicUrl = "https://sdk.weixin.senparc.com/Images/qrcode.jpg"
});
var returnUrl = "/OAuth2/TestReturnUrl";
strongResponseMessage.Articles.Add(new Article()
{
Title = "OAuth2.0测试带returnUrl生产环境强烈推荐使用",
Description = "OAuth2.0测试带returnUrl",
Url = "https://sdk.weixin.senparc.com/oauth2?returnUrl=" + returnUrl.UrlEncode(),
PicUrl = "https://sdk.weixin.senparc.com/Images/qrcode.jpg"
});
reponseMessage = strongResponseMessage;
}
break;
case "Description":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
strongResponseMessage.Content = GetWelcomeInfo();
reponseMessage = strongResponseMessage;
}
break;
case "SubClickRoot_PicPhotoOrAlbum":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Content = "您点击了【微信拍照】按钮。系统将会弹出拍照或者相册发图。";
}
break;
case "SubClickRoot_ScancodePush":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Content = "您点击了【微信扫码】按钮。";
}
break;
case "ConditionalMenu_Male":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Content = "您点击了个性化菜单按钮,您的微信性别设置为:男。";
}
break;
case "ConditionalMenu_Femle":
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
reponseMessage = strongResponseMessage;
strongResponseMessage.Content = "您点击了个性化菜单按钮,您的微信性别设置为:女。";
}
break;
case "GetNewMediaId"://获取新的MediaId
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
try
{
var result = MP.AdvancedAPIs.MediaApi.UploadForeverMedia(appId, ServerUtility.ContentRootMapPath("~/Images/logo.jpg"), UploadForeverMediaType.image);
strongResponseMessage.Content = result.media_id;
}
catch (Exception e)
{
strongResponseMessage.Content = "发生错误:" + e.Message;
WeixinTrace.SendCustomLog("调用UploadForeverMedia()接口发生异常", e.Message);
}
}
break;
default:
{
var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
strongResponseMessage.Content = "您点击了按钮EventKey" + requestMessage.EventKey;
reponseMessage = strongResponseMessage;
}
break;
}
return reponseMessage;
}
/// <summary>
/// 进入事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_EnterRequest(RequestMessageEvent_Enter requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = "您刚才发送了ENTER事件请求。";
return responseMessage;
}
/// <summary>
/// 位置事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_LocationRequest(RequestMessageEvent_Location requestMessage)
{
//这里是微信客户端(通过微信服务器)自动发送过来的位置信息
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "这里写什么都无所谓,比如:上帝爱你!";
return responseMessage;//这里也可以返回null需要注意写日志时候null的问题
}
/// <summary>
/// 通过二维码扫描关注扫描事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScanRequest(RequestMessageEvent_Scan requestMessage)
{
//通过扫描关注
var responseMessage = CreateResponseMessage<ResponseMessageText>();
//下载文档
if (!string.IsNullOrEmpty(requestMessage.EventKey))
{
var sceneId = long.Parse(requestMessage.EventKey.Replace("qrscene_", ""));
//var configHelper = new ConfigHelper(new HttpContextWrapper(HttpContext.Current));
var codeRecord =
ConfigHelper.CodeCollection.Values.FirstOrDefault(z => z.QrCodeTicket != null && z.QrCodeId == sceneId);
if (codeRecord != null)
{
//确认可以下载
codeRecord.AllowDownload = true;
responseMessage.Content = GetDownloadInfo(codeRecord);
}
}
responseMessage.Content = responseMessage.Content ?? string.Format("通过扫描二维码进入,场景值:{0}", requestMessage.EventKey);
return responseMessage;
}
/// <summary>
/// 打开网页事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ViewRequest(RequestMessageEvent_View requestMessage)
{
//说明这条消息只作为接收下面的responseMessage到达不了客户端类似OnEvent_UnsubscribeRequest
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "您点击了view按钮将打开网页" + requestMessage.EventKey;
return responseMessage;
}
/// <summary>
/// 群发完成事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_MassSendJobFinishRequest(RequestMessageEvent_MassSendJobFinish requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "接收到了群发完成的信息。";
return responseMessage;
}
/// <summary>
/// 订阅(关注)事件
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = GetWelcomeInfo();
if (!string.IsNullOrEmpty(requestMessage.EventKey))
{
responseMessage.Content += "\r\n============\r\n场景值" + requestMessage.EventKey;
}
//推送消息
//下载文档
if (requestMessage.EventKey.StartsWith("qrscene_"))
{
var sceneId = long.Parse(requestMessage.EventKey.Replace("qrscene_", ""));
//var configHelper = new ConfigHelper(new HttpContextWrapper(HttpContext.Current));
var codeRecord =
ConfigHelper.CodeCollection.Values.FirstOrDefault(z => z.QrCodeTicket != null && z.QrCodeId == sceneId);
if (codeRecord != null)
{
if (codeRecord.AllowDownload)
{
Task.Factory.StartNew(() => CustomApi.SendTextAsync(null, OpenId, "下载已经开始,如需下载其他版本,请刷新页面后重新扫一扫。"));
}
else
{
//确认可以下载
codeRecord.AllowDownload = true;
Task.Factory.StartNew(() => CustomApi.SendTextAsync(null, OpenId, GetDownloadInfo(codeRecord)));
}
}
}
return responseMessage;
}
/// <summary>
/// 退订
/// 实际上用户无法收到非订阅账号的消息,所以这里可以随便写。
/// unsubscribe事件的意义在于及时删除网站应用中已经记录的OpenID绑定消除冗余数据。并且关注用户流失的情况。
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "有空再来";
return responseMessage;
}
/// <summary>
/// 事件之扫码推事件(scancode_push)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScancodePushRequest(RequestMessageEvent_Scancode_Push requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "事件之扫码推事件";
return responseMessage;
}
/// <summary>
/// 事件之扫码推事件且弹出“消息接收中”提示框(scancode_waitmsg)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScancodeWaitmsgRequest(RequestMessageEvent_Scancode_Waitmsg requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "事件之扫码推事件且弹出“消息接收中”提示框";
return responseMessage;
}
/// <summary>
/// 事件之弹出拍照或者相册发图pic_photo_or_album
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicPhotoOrAlbumRequest(RequestMessageEvent_Pic_Photo_Or_Album requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "事件之弹出拍照或者相册发图";
return responseMessage;
}
/// <summary>
/// 事件之弹出系统拍照发图(pic_sysphoto)
/// 实际测试时发现微信并没有推送RequestMessageEvent_Pic_Sysphoto消息只能接收到用户在微信中发送的图片消息。
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicSysphotoRequest(RequestMessageEvent_Pic_Sysphoto requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "事件之弹出系统拍照发图";
return responseMessage;
}
/// <summary>
/// 事件之弹出微信相册发图器(pic_weixin)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicWeixinRequest(RequestMessageEvent_Pic_Weixin requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "事件之弹出微信相册发图器";
return responseMessage;
}
/// <summary>
/// 事件之弹出地理位置选择器location_select
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_LocationSelectRequest(RequestMessageEvent_Location_Select requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "事件之弹出地理位置选择器";
return responseMessage;
}
#region 微信认证事件推送
public override IResponseMessageBase OnEvent_QualificationVerifySuccessRequest(RequestMessageEvent_QualificationVerifySuccess requestMessage)
{
//以下方法可以强制定义返回的字符串值
//TextResponseMessage = "your content";
//return null;
return new SuccessResponseMessage();//返回"success"字符串
}
#endregion
}
}