目錄
-
概要
博客使用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://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://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://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://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 == null)
{
CurrentMessageContext.StorageData = 0;
}
base.OnExecuting();
}
public override void OnExecuted()
{
base.OnExecuted();
CurrentMessageContext.StorageData = (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 == 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 = 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);
}
}
}