首页 > 代码库 > 公众号消息会话开发
公众号消息会话开发
目录
1 概要 2
1.1 关于Magicodes.WeChat.SDK 3
2 群发消息 3
2.1 概要图 3
2.2 发送流程 4
2.2.1 图文消息群发流程 4
2.2.2 文本群发流程 4
2.2.3 其他类型群发流程 5
2.3 注意事项 5
2.4 开发实践 5
2.4.1 开发思路 6
2.4.2 框架设计 6
2.4.3 Demo 7
3 被动回复消息 9
3.1 概要图 10
3.2 被动回复流程 11
3.2.1 普通消息回复流程 11
3.2.2 事件推送消息流程 12
3.3 配置 12
3.4 开发实践 14
3.4.1 开发思路 14
3.4.2 框架设计 14
3.4.3 Demo 14
4 客服消息 28
4.1 概要图 29
4.2 客服消息流程 30
4.3 开发实践 31
4.3.1 开发思路 31
4.3.2 框架设计 31
4.3.3 Demo 32
5 模板消息 33
5.1 概要图 34
5.2 效果 35
5.3 使用规则 36
5.4 开发实践 36
5.4.1 开发思路 36
5.4.2 框架设计 36
5.4.3 Demo 37
概要
博客使用Word发博,发布后,排版会出现很多问题,敬请谅解。可加群获取原始文档。
公众号是以微信用户的一个联系人形式存在的,消息会话是公众号与用户交互的基础。
本篇主要围绕消息会话进行讲述。
关于Magicodes.WeChat.SDK
MAGICODES.WECHAT.SDK为心莱团队封装的轻量级微信SDK,现已全部开源,开源库地址为:https://github.com/xin-lai/Magicodes.WeChat.SDK
更多介绍,请关注后续博客。
群发消息
公众号可以以一定频次(订阅号为每天1次,服务号为每月4次),向用户群发消息,包括文字消息、图文消息、图片、视频、语音等。
概要图
发送流程
图文消息群发流程
文本群发流程
其他类型群发流程
注意事项
- 对于认证订阅号,群发接口每天可成功调用1次,此次群发可选择发送给全部用户或某个分组;
- 对于认证服务号虽然开发者使用高级群发接口的每日调用限制为100次,但是用户每月只能接收4条,无论在公众平台网站上,还是使用接口群发,用户每月只能接收4条群发消息,多于4条的群发将对该用户发送失败;
- 具备微信支付权限的公众号,在使用群发接口上传、群发图文消息类型时,可使用<a>标签加入外链;
- 4、开发者可以使用预览接口校对消息样式和排版,通过预览接口可发送编辑好的消息给指定用户校验效果。
开发实践
开发思路
框架设计
为了简化接口调用,Magicodes.WeChat.SDK对此进行了封装。目前框架仅封装了多图文的接口调用,其他封装类似:
多图文接口
注意:多图文群发时,图片地址必须为上传图片素材时获取到的微信服务器地址。
Demo
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Magicodes.WeChat.SDK.Apis.Material;
using System.Linq;
namespace Magicodes.WeChat.SDK.Test.ApiTests
{
[TestClass]
public class NewsApiTest : ApiTestBase
{
NewsApi api = new NewsApi();
public NewsApiTest()
{
api.SetKey(1);
}
[TestMethod]
public void NewsApiTest_GetById()
{
var testNews = db.Site_News.FirstOrDefault();
if (testNews == null)
{
Assert.Fail("没有数据!");
}
var result = api.Get(testNews.MediaId);
if (!result.IsSuccess())
{
Assert.Fail("获取多图文信息失败,返回结果如下:" + result.DetailResult);
}
}
[TestMethod]
public void NewsApiTest_Get()
{
var result = api.Get();
if (!result.IsSuccess())
{
Assert.Fail("获取多图文信息失败,返回结果如下:" + result.DetailResult);
}
}
[TestMethod]
public void NewsApiTest_Post()
{
//不能使用第三方的图片
var model = new NewsPostModel() {
Articles=new System.Collections.Generic.List<NewsPostModel.ArticleInfo>()
{
new NewsPostModel.ArticleInfo()
{
Author="liwq",
Content="<p><img data-s=\"300,640\" data-type=\"png\" data-src=http://www.mamicode.com/\"http://mmbiz.qpic.cn/mmbiz/SLeRFiaVmNAS3kOq3icjbfpz1GicHibTN4P9jicick8xyiaia8TMEzafuB4dSBfba5IdshdYX2qXJqBP689NMhPHuo3PsQ/0?wx_fmt=png\" data-ratio=\"0.7733812949640287\" data-w=\"\" /><br /></p><p><span style=\"line-height: 25.6px;\">"Magicodes.WeiChat,是由Magicode.WeiChat团队打造的一个基于ASP.NET MVC5微信业务快速开发与定制的开发框架。目的让微信业务开发与定制更快速、简单。"</span></p>",
ContentSourceUrl ="http://www.cnblogs.com/codelove/p/5306395.html",
Digest="每周一小更,每月一大更。我们要做最好的微信快速定制开发框架。",
ShowCoverPic=0,
ThumbMediaId="HXIy1CJD5Qt12D9XBuSx0pXEqWaCbkwdYwCQ50spLlE",
Title="版本历史"
},
new NewsPostModel.ArticleInfo()
{
Author="liwq",
Content="<p><img data-s=\"300,640\" data-type=\"png\" data-src=http://www.mamicode.com/\"http://mmbiz.qpic.cn/mmbiz/SLeRFiaVmNAS3kOq3icjbfpz1GicHibTN4P9jicick8xyiaia8TMEzafuB4dSBfba5IdshdYX2qXJqBP689NMhPHuo3PsQ/0?wx_fmt=png\" data-ratio=\"0.7733812949640287\" data-w=\"\" /><br /></p><p><span style=\"line-height: 25.6px;\">"Magicodes.WeiChat,是由Magicode.WeiChat团队打造的一个基于ASP.NET MVC5微信业务快速开发与定制的开发框架。目的让微信业务开发与定制更快速、简单。"</span></p>",
ContentSourceUrl ="http://www.cnblogs.com/codelove/p/5306395.html",
Digest="每周一小更,每月一大更。我们要做最好的微信快速定制开发框架。",
ShowCoverPic=0,
ThumbMediaId="HXIy1CJD5Qt12D9XBuSx0pXEqWaCbkwdYwCQ50spLlE",
Title="版本历史"
},
new NewsPostModel.ArticleInfo()
{
Author="liwq",
Content="<div><img src=http://www.mamicode.com/\"http://mmbiz.qpic.cn/mmbiz/SLeRFiaVmNAS3kOq3icjbfpz1GicHibTN4P9jicick8xyiaia8TMEzafuB4dSBfba5IdshdYX2qXJqBP689NMhPHuo3PsQ/0?wx_fmt=png\" /></div>",
ContentSourceUrl="http://www.cnblogs.com/codelove/p/5306395.html",
Digest="每周一小更,每月一大更。我们要做最好的微信快速定制开发框架。",
ShowCoverPic=0,
ThumbMediaId="HXIy1CJD5Qt12D9XBuSx0pXEqWaCbkwdYwCQ50spLlE",
Title="版本历史"
},
new NewsPostModel.ArticleInfo()
{
Author="liwq",
Content="<div><img src=http://www.mamicode.com/\"http://mmbiz.qpic.cn/mmbiz/SLeRFiaVmNAS3kOq3icjbfpz1GicHibTN4P9jicick8xyiaia8TMEzafuB4dSBfba5IdshdYX2qXJqBP689NMhPHuo3PsQ/0?wx_fmt=png\" /></div>",
ContentSourceUrl="http://www.cnblogs.com/codelove/p/5306395.html",
Digest="每周一小更,每月一大更。我们要做最好的微信快速定制开发框架。",
ShowCoverPic=0,
ThumbMediaId="HXIy1CJD5Qt12D9XBuSx0pXEqWaCbkwdYwCQ50spLlE",
Title="版本历史"
}
}
};
var result = api.Post(model);
if (!result.IsSuccess())
{
Assert.Fail("添加多图文信息失败,返回结果如下:" + result.DetailResult);
}
}
}
}
注意:在大多数情况下,建议使用WeChatApisContext来调用API,比如WeChatApisContext.Current.MenuApi.Get()。使用new Api(API实现类)的情况仅限于微信服务器事件代码以及某些无法通过当前用户请求获取到TenantId的情形。
被动回复消息
在用户给公众号发消息后,微信服务器会将消息发到开发者预先在开发者中心设置的服务器地址(开发者需要进行消息真实性验证),公众号应该在5秒内做出回复,可以回复一个消息,也可以回复命令告诉微信服务器这条消息暂不回复。被动回复消息可以设置加密(在公众平台官网的开发者中心处设置,设置后,按照消息加解密文档来进行处理。
概要图
被动回复流程
普通消息回复流程
注意:对于普通消息,我们还可以通过编程将某些消息指向多客服来接收。
事件推送消息流程
配置
在开发之前,请务必按此步骤配置好认证服务号。
进入公众号后台,进入【开发-基本配置】页面。请按要求配置如图所示的内容:
注意:如果使用的是Magicodes.WeiChat,请进入公众号管理的公众号设置页面,获得Url以及Token。
开发实践
开发思路
框架设计
Demo
这里使用了Senparc.Weixin:
using Magicodes.WeiChat.Data;
using Magicodes.WeiChat.Data.Models.WeiChat;
using Magicodes.WeChat.SDK;
using NLog;
using Senparc.Weixin.Context;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.MP.AdvancedAPIs.Media;
using Senparc.Weixin.MP.Agent;
using Senparc.Weixin.MP.Entities;
using Senparc.Weixin.MP.MessageHandlers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Data.Entity;
using Magicodes.WeiChat.Helpers;
using Magicodes.WeiChat.Data.Models;
using Magicodes.WeChat.SDK.Apis.User;
using EntityFramework.DynamicFilters;
using Magicodes.WeiChat.Infrastructure.Tenant;
namespace Magicodes.WeiChat.App_Start
{
/// <summary>
/// 微信消息与事件处理
/// </summary>
public class MessageHandler : MessageHandler<MessageContext<IRequestMessageBase, IResponseMessageBase>>
{
private Lazy<AppDbContext> _db;
protected AppDbContext db
{
get
{
return _db.Value;
}
}
/// <summary>
/// 租户Id
/// </summary>
public int TenantId { get; set; }
/// <summary>
/// 日志记录
/// </summary>
ILogger logger = LogManager.GetLogger("WeiChat.MessageHandler");
#region 基础内容
/// <summary>
/// 访问凭据
/// </summary>
private string AccessToken
{
get
{
return WeiChatConfigManager.Current.AccessToken;
}
}
public MessageHandler(Stream inputStream, int tenantId, int maxRecordCount = 0)
: base(inputStream, null, maxRecordCount)
{
WeixinContext.ExpireMinutes = 3;
TenantId = tenantId;
_db = new Lazy<AppDbContext>(() =>
{
var _tmpDb = new AppDbContext();
//启用租户筛选器
TenantManager.Current.EnableTenantFilter(_tmpDb, TenantId);
return _tmpDb;
});
}
public override void OnExecuting()
{
//测试MessageContext.StorageData
if (CurrentMessageContext.StorageData =http://www.mamicode.com/= null)
{
CurrentMessageContext.StorageData = http://www.mamicode.com/0;
}
base.OnExecuting();
}
public override void OnExecuted()
{
base.OnExecuted();
CurrentMessageContext.StorageData = http://www.mamicode.com/(int)CurrentMessageContext.StorageData + 1;
}
#endregion
/// <summary>
/// 所有没有被处理的消息会默认返回这里的结果
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
{
//所有没有被处理的消息会默认返回这里的结果
var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "您好,客服人员在忙,请稍后。";
return responseMessage;
}
/// <summary>
/// 订阅(关注)事件
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
IResponseMessageBase responseMessage = null;
{
#region 获取并更新新关注用户的信息
try
{
//由于这里是基于微信服务器事件,故不能使用WeChatApisContext.Current.UserApi方式获取到UserApi接口,请new一个然后通过SetKey进行赋值
var userApi = new Framework.Apis.User.UserApi();
userApi.SetKey(TenantId);
//获取新关注用户的用户信息
var userInfoResult = userApi.Get(requestMessage.FromUserName);
if (userInfoResult.IsSuccess())
{
var user = db.WeiChat_Users.FirstOrDefault(p => p.OpenId == userInfoResult.OpenId);
if (user == null)
{
user = new WeiChat_User()
{
City = userInfoResult.City,
Country = userInfoResult.Country,
GroupId = userInfoResult.GroupId,
HeadImgUrl = userInfoResult.Headimgurl,
Language = userInfoResult.Language,
NickName = userInfoResult.NickName,
OpenId = userInfoResult.OpenId,
Province = userInfoResult.Province,
Remark = userInfoResult.Remark,
Sex = userInfoResult.Sex,
Subscribe = true,
SubscribeTime = userInfoResult.SubscribeTime,
UnionId = userInfoResult.Unionid,
TenantId = TenantId
};
db.WeiChat_Users.Add(user);
}
else
{
user.City = userInfoResult.City;
user.Country = userInfoResult.Country;
user.GroupId = userInfoResult.GroupId;
user.HeadImgUrl = userInfoResult.Headimgurl;
user.Language = userInfoResult.Language;
user.NickName = userInfoResult.NickName;
//user.OpenId=userInfoResult.OpenId
user.Province = userInfoResult.Province;
user.Remark = userInfoResult.Remark;
user.Sex = userInfoResult.Sex;
user.Subscribe = true;
user.SubscribeTime = userInfoResult.SubscribeTime;
user.TenantId = TenantId;
user.UnionId = userInfoResult.Unionid;
}
db.SaveChanges();
}
else
{
logger.Error(userInfoResult.GetFriendlyMessage() + "\n\r详细错误:" + userInfoResult.DetailResult);
}
}
catch (Exception ex)
{
logger.Error("MessageId:" + requestMessage.MsgId + Environment.NewLine + "具体错误信息:" + ex.ToString());
}
#endregion
var keyword = string.Format("{0}[系统关注事件]", requestMessage.EventKey);
//回复日志
var replylog = new WeiChat_KeyWordReplyLog()
{
ReceiveWords = keyword,
CreateTime = DateTime.Now,
TenantId = TenantId,
From = requestMessage.FromUserName,
To = requestMessage.ToUserName,
EventKey = requestMessage.EventKey,
MsgId = requestMessage.MsgId
};
try
{
var subscribeReplies = db.WeiChat_SubscribeReplies.FirstOrDefault();
if (subscribeReplies != null)
{
replylog.ContentId = subscribeReplies.ContentId;
replylog.KeyWord = "";
replylog.WeiChat_KeyWordAutoReplyId = subscribeReplies.Id;
responseMessage = this.ReplyResponseMessage(db, keyword, subscribeReplies.ContentId, subscribeReplies.KeyWordContentType, (key, contentId, type) =>
{
replylog.IsSuccess = true;
});
}
}
catch (Exception ex)
{
replylog.Error = ex.ToString();
responseMessage = CreateResponseMessage<ResponseMessageText>();
((ResponseMessageText)responseMessage).Content = HttpContext.Current.IsDebuggingEnabled ? ex.ToString() : "出现错误,无法处理请求,具体信息请查看回复日志!";
}
//记录日志
db.WeiChat_KeyWordReplyLogs.Add(replylog);
db.SaveChanges();
}
if (responseMessage == null)
{
responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
((ResponseMessageText)responseMessage).Content = "欢迎您关注Magicodes.WeiChat!";
return responseMessage;
}
return responseMessage;
}
/// <summary>
/// 退订
/// 实际上用户无法收到非订阅账号的消息,所以这里可以随便写。
/// unsubscribe事件的意义在于及时删除网站应用中已经记录的OpenID绑定,消除冗余数据。并且关注用户流失的情况。
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage)
{
//更新粉丝信息
using (AppDbContext db = new AppDbContext())
{
var user = db.WeiChat_Users.FirstOrDefault(p => p.OpenId == requestMessage.FromUserName);
if (user != null)
{
user.Subscribe = false;
db.SaveChanges();
}
//TODO:增加取消关注的日志记录
}
return null;
}
/// <summary>
/// 扫描带参数二维码事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScanRequest(RequestMessageEvent_Scan requestMessage)
{
//通过扫描关注
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "Key未处理:" + requestMessage.EventKey;
if (!string.IsNullOrEmpty(requestMessage.EventKey))
{
var qrCode = db.WeiChat_QRCodes.FirstOrDefault(p => p.ParamsValue =http://www.mamicode.com/= requestMessage.EventKey);
if (qrCode != null)
{
switch (qrCode.UserFor)
{
case QRCodeUseForTypes.BindManager:
{
var user = db.Users.Find(requestMessage.EventKey);
user.OpenId = requestMessage.FromUserName;
db.SaveChanges();
responseMessage.Content = "已成功绑定微信管理员!";
}
break;
}
}
}
return responseMessage;
}
/// <summary>
/// 上报地理位置事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_LocationRequest(RequestMessageEvent_Location requestMessage)
{
//回复日志
var log = new WeiChat_LocationEventLog()
{
CreateTime = DateTime.Now,
TenantId = TenantId,
From = requestMessage.FromUserName,
To = requestMessage.ToUserName,
Latitude = requestMessage.Latitude,
Longitude = requestMessage.Longitude,
Precision = requestMessage.Precision
};
db.WeiChat_LocationEventLogs.Add(log);
db.SaveChanges();
//这里是微信客户端(通过微信服务器)自动发送过来的位置信息
var responseMessage = CreateResponseMessage<ResponseMessageText>();
//requestMessage.Latitude
//requestMessage.Longitude
responseMessage.Content = "您的位置已被记录(PS:如果您不需要告知用户,请返回NULL,以免打扰用户)。";
return responseMessage;//这里也可以返回null(需要注意写日志时候null的问题)
}
#region 菜单事件
public override IResponseMessageBase OnTextOrEventRequest(RequestMessageText requestMessage)
{
// 预处理文字或事件类型请求。
// 这个请求是一个比较特殊的请求,通常用于统一处理来自文字或菜单按钮的同一个执行逻辑,
// 会在执行OnTextRequest或OnEventRequest之前触发,具有以下一些特征:
// 1、如果返回null,则继续执行OnTextRequest或OnEventRequest
// 2、如果返回不为null,则终止执行OnTextRequest或OnEventRequest,返回最终ResponseMessage
// 3、如果是事件,则会将RequestMessageEvent自动转为RequestMessageText类型,其中RequestMessageText.Content就是RequestMessageEvent.EventKey
return null;//返回null,则继续执行OnTextRequest或OnEventRequest
}
/// <summary>
/// 菜单点击事件处理
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ClickRequest(RequestMessageEvent_Click requestMessage)
{
IResponseMessageBase responseMessage = null;
//回复日志
var replylog = new WeiChat_KeyWordReplyLog()
{
ReceiveWords = requestMessage.EventKey,
CreateTime = DateTime.Now,
TenantId = TenantId,
From = requestMessage.FromUserName,
To = requestMessage.ToUserName,
EventKey = requestMessage.EventKey,
MsgId = requestMessage.MsgId
};
try
{
#region 事件关键字回复,仅支持等于
var keyword = db.WeiChat_KeyWordAutoReplies.FirstOrDefault(p => p.AllowEventKey && p.KeyWord == requestMessage.EventKey);
if (keyword != null)
{
replylog.ContentId = keyword.ContentId;
replylog.KeyWord = keyword.KeyWord;
replylog.WeiChat_KeyWordAutoReplyId = keyword.Id;
responseMessage = this.ReplyResponseMessage(db, keyword.KeyWord, keyword.ContentId, keyword.KeyWordContentType, (key, contentId, type) =>
{
replylog.IsSuccess = true;
});
}
#endregion
else
{
return null;
}
}
catch (Exception ex)
{
replylog.Error = ex.ToString();
responseMessage = CreateResponseMessage<ResponseMessageText>();
((ResponseMessageText)responseMessage).Content = HttpContext.Current.IsDebuggingEnabled ? ex.ToString() : "出现错误,无法处理请求!";
}
//记录日志
db.WeiChat_KeyWordReplyLogs.Add(replylog);
db.SaveChanges();
return responseMessage;
}
#endregion
public override IResponseMessageBase OnEvent_ViewRequest(RequestMessageEvent_View requestMessage)
{
//说明:这条消息只作为接收,下面的responseMessage到达不了客户端,类似OnEvent_UnsubscribeRequest
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "您点击了view按钮,将打开网页:" + requestMessage.EventKey;
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_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_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_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 消息(文本、图片、视频、音频)处理
/// <summary>
/// 处理文字请求
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)
{
IResponseMessageBase responseMessage = null;
//客服用于唤醒多客服
if (requestMessage.Content == "客服")
{
responseMessage = CreateResponseMessage<ResponseMessageTransfer_Customer_Service>();
return responseMessage;
}
//回复日志
var replylog = new WeiChat_KeyWordReplyLog()
{
ReceiveWords = requestMessage.Content,
CreateTime = DateTime.Now,
TenantId = TenantId,
From = requestMessage.FromUserName,
To = requestMessage.ToUserName,
//EventKey = requestMessage.EventKey,
MsgId = requestMessage.MsgId
};
{
try
{
#region 关键字回复
var keyword = db.WeiChat_KeyWordAutoReplies.FirstOrDefault(p => p.MatchType == KeyWordMatchTypes.Equals && p.KeyWord == requestMessage.Content);
if (keyword == null)
{
keyword = db.WeiChat_KeyWordAutoReplies.FirstOrDefault(p => p.MatchType == KeyWordMatchTypes.Contains && requestMessage.Content.Contains(p.KeyWord));
}
if (keyword != null)
{
replylog.ContentId = keyword.ContentId;
replylog.KeyWord = keyword.KeyWord;
replylog.WeiChat_KeyWordAutoReplyId = keyword.Id;
responseMessage = this.ReplyResponseMessage(db, keyword.KeyWord, keyword.ContentId, keyword.KeyWordContentType, (key, contentId, type) =>
{
replylog.IsSuccess = true;
});
}
#endregion
else
{
#region 答不上来配置
//答不上来
try
{
var notAnswerReply = db.WeiChat_NotAnswerReplies.FirstOrDefault();
if (notAnswerReply != null)
{
replylog.ContentId = notAnswerReply.ContentId;
replylog.KeyWord = "";
replylog.WeiChat_KeyWordAutoReplyId = notAnswerReply.Id;
responseMessage = this.ReplyResponseMessage(db, requestMessage.Content, notAnswerReply.ContentId, notAnswerReply.KeyWordContentType, (key, contentId, type) =>
{
replylog.IsSuccess = true;
});
}
else
{
var sb = new StringBuilder();
sb.AppendLine("您好,该关键字本公众号尚不支持/(ㄒoㄒ)/~~。");
var keywords = db.WeiChat_KeyWordAutoReplies.Take(10).Select(p => p.KeyWord).ToArray();
if (keywords != null && keywords.Length > 0)
{
sb.AppendLine("不过,您可以试试以下关键字哦:");
sb.AppendLine(string.Join("、", keywords));
}
responseMessage = CreateResponseMessage<ResponseMessageText>();
((ResponseMessageText)responseMessage).Content = sb.ToString();
}
}
catch (Exception ex)
{
replylog.Error = ex.ToString();
responseMessage = CreateResponseMessage<ResponseMessageText>();
((ResponseMessageText)responseMessage).Content = HttpContext.Current.IsDebuggingEnabled ? ex.ToString() : "出现错误,无法处理请求!";
}
#endregion
}
}
catch (Exception ex)
{
replylog.Error = ex.ToString();
responseMessage = CreateResponseMessage<ResponseMessageText>();
((ResponseMessageText)responseMessage).Content = HttpContext.Current.IsDebuggingEnabled ? ex.ToString() : "出现错误,无法处理请求!";
}
//记录日志
db.WeiChat_KeyWordReplyLogs.Add(replylog);
db.SaveChanges();
}
return responseMessage;
}
///// <summary>
///// 处理图片请求
///// </summary>
///// <param name="requestMessage"></param>
///// <returns></returns>
//public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)
//{
// var responseMessage = CreateResponseMessage<ResponseMessageNews>();
// responseMessage.Articles.Add(new Article()
// {
// Title = "您刚才发送了图片信息",
// Description = "您发送的图片将会显示在边上",
// PicUrl = requestMessage.PicUrl,
// Url = "http://www.hp.com"
// });
// responseMessage.Articles.Add(new Article()
// {
// Title = "第二条",
// Description = "第二条带连接的内容",
// PicUrl = requestMessage.PicUrl,
// Url = "http://www.hp.com"
// });
// return responseMessage;
//}
/// <summary>
/// 处理语音请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage)
{
//var responseMessage = CreateResponseMessage<ResponseMessageVoice>();
//responseMessage.Voice.MediaId = "HXIy1CJD5Qt12D9XBuSx0sDA8YS_82zS3zdaJsZUYOc";
//return responseMessage;
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = string.Empty;
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;
return responseMessage;
}
/// <summary>
/// 处理小视频请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnShortVideoRequest(RequestMessageShortVideo requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "您刚才发送的是小视频";
return responseMessage;
}
/// 处理位置请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnLocationRequest(RequestMessageLocation requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = string.Format("您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}",
requestMessage.Location_X, requestMessage.Location_Y,
requestMessage.Scale, requestMessage.Label);
return responseMessage;
}
/// <summary>
/// 处理链接消息请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnLinkRequest(RequestMessageLink requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = string.Format(@"您发送了一条连接信息:
Title:{0}
Description:{1}
Url:{2}", requestMessage.Title, requestMessage.Description, requestMessage.Url);
return responseMessage;
}
#endregion
}
}
客服消息
在用户给公众号发消息后的48小时内,公众号可以给用户发送不限数量的消息,主要用于客服场景。用户的行为会触发事件推送,某些事件推送是支持公众号据此发送客服消息的,详见微信推送消息与事件说明文档。
客服消息可以在微信在线客服平台登录收发相关消息。
地址:https://mp.weixin.qq.com/misc/kf?action=list&token=1996838014&lang=zh_CN
概要图
客服消息流程
开发实践
开发思路
框架设计
为了简化接口调用,Magicodes.WeChat.SDK对此进行了封装。
客服消息接口
注意:多图文群发时,图片地址必须为上传图片素材时获取到的微信服务器地址。如果图文数超过8,则将会无响应。
Demo
using Magicodes.WeiChat.Data;
using Magicodes.WeChat.SDK.Apis.CustomerService;
using Magicodes.WeChat.SDK.Apis.Menu;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using Magicodes.WeChat.SDK.Apis.CustomMessage;
namespace Magicodes.WeChat.SDK.Test.ApiTests
{
[TestClass]
public class CustomMessageApiTest : ApiTestBase
{
CustomMessageApi weChatApi = new CustomMessageApi();
public CustomMessageApiTest()
{
weChatApi.SetKey(1);
}
private string GetTestOpenId()
{
var weiChatUser = db.WeiChat_Users.FirstOrDefault(p => p.AllowTest);
if (weiChatUser != null)
return weiChatUser.OpenId;
throw new Exception("请设置测试粉丝用户");
}
[TestMethod]
public void CustomMessageApiTest_SendTextMessage()
{
//发送文本客服消息
var result = weChatApi.SendTextMessage(new TextMessage()
{
TextContent = new TextMessage.Text()
{
Content = "Test_SendTextMessage",
},
Touser = GetTestOpenId()
});
if (!result.IsSuccess())
{
Assert.Fail("发送文本客服消息失败,返回结果如下:" + result.DetailResult);
}
}
}
}
模板消息
在需要对用户发送服务通知(如刷卡提醒、服务预约成功通知等)时,公众号可以用特定内容模板,主动向用户发送消息。
注意:模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。
概要图
效果
使用规则
- 所有服务号都可以在功能->添加功能插件处看到申请模板消息功能的入口,但只有认证后的服务号才可以申请模板消息的使用权限并获得该权限;
- 需要选择公众账号服务所处的2个行业,每月可更改1次所选行业;
- 在所选择行业的模板库中选用已有的模板进行调用;
- 每个账号可以同时使用25个模板。
- 当前每个账号的模板消息的日调用上限为10万次,单个模板没有特殊限制。当账号粉丝数超过10W/100W/1000W时,模板消息的日调用上限会相应提升,以公众号MP后台开发者中心页面中标明的数字为准。
开发实践
开发思路
框架设计
为了简化接口调用,Magicodes.WeChat.SDK对此进行了封装。
模板消息接口
Demo
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Magicodes.WeiChat.Data;
using System.Linq;
using Magicodes.WeChat.SDK.Apis.TemplateMessage;
using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace Magicodes.WeChat.SDK.Test.ApiTests
{
[TestClass]
public class TemplateMessageApiTest : ApiTestBase
{
TemplateMessageApi api = new TemplateMessageApi();
public TemplateMessageApiTest()
{
api.SetKey(1);
}
[TestMethod]
public void TemplateMessageApiTest_AddTemplate()
{
var result = api.AddTemplate("TM00003");
Assert.IsTrue(result.IsSuccess());
Assert.IsNotNull(result.TemplateId);
}
[TestMethod]
public void TemplateMessageApiTest_Get()
{
var result = api.Get();
Assert.IsTrue(result.IsSuccess());
Assert.IsNotNull(result.Templates);
}
[TestMethod]
public void TemplateMessageApiTest_Create()
{
//获取测试用户
var testUsersOpenIds = db.WeiChat_Users.Where(p => p.AllowTest).Select(p => p.OpenId).ToList();
if (testUsersOpenIds.Count == 0)
{
Assert.Fail("测试失败,必须设置测试账户,见WeiChat_User中的AllowTest!");
}
var receiverIds = string.Join(";", testUsersOpenIds);
var testMessageTemplates = db.WeiChat_MessagesTemplates.Where(p => p.AllowTest).ToList();
if (testMessageTemplates.Count == 0)
{
Assert.Fail("测试失败,必须设置测试模板,见WeiChat_MessagesTemplates中的AllowTest!");
}
var count = 0;
var successCount = 0;
foreach (var template in testMessageTemplates)
{
count += testUsersOpenIds.Count;
//模板消息模型
var tmm = new TemplateMessageCreateModel()
{
MessagesTemplateNo = template.TemplateNo,
Data = http://www.mamicode.com/new Dictionary<string, TemplateDataItem>(),
ReceiverIds = receiverIds,
Url = "www.magicodes.net"
};
var rm = Regex.Matches(template.Content, @"\{\{(.+?)\}\}");
if (rm.Count > 0)
{
foreach (Match item in rm)
{
tmm.Data.Add(Regex.Split(item.Value.Trim(‘{‘).Trim(‘}‘), ".DATA")[0], new TemplateDataItem("测试"));
}
}
var batchNumber = api.Create(tmm);
successCount +=
db.WeiChat_MessagesTemplateSendLogs.Count(p => p.IsSuccess && p.BatchNumber == batchNumber);
}
Assert.AreEqual<int>(count, successCount, "部分消息发送未成功,请检查!{0}/{1}", successCount, count);
}
}
}
公众号消息会话开发