在很多場合下,我們可能需要利用微信公眾號的優(yōu)勢钙蒙,定期給指定用戶群發(fā)送一些推廣消息或者新聞內(nèi)容茵瀑,以便給關(guān)注客戶一種經(jīng)常更新公眾號內(nèi)容的感覺,同時也方便我們經(jīng)常和用戶進(jìn)行互動躬厌。微信公眾號的高級群發(fā)接口就是為了處理這個場景的马昨,本文介紹在C#代碼中如何封裝消息的群發(fā)和預(yù)覽等功能。
1扛施、消息群發(fā)的功能和限制
對于公眾號中的服務(wù)號和訂閱號鸿捧,群發(fā)的消息有一定的限制,具體規(guī)則如下所示疙渣。
1笛谦、對于認(rèn)證訂閱號,群發(fā)接口每天可成功調(diào)用1次昌阿,此次群發(fā)可選擇發(fā)送給全部用戶或某個分組;2恳邀、對于認(rèn)證服務(wù)號雖然開發(fā)者使用高級群發(fā)接口的每日調(diào)用限制為100次懦冰,但是用戶每月只能接收4條,無論在公眾平臺網(wǎng)站上谣沸,還是使用接口群發(fā)刷钢,用戶每月只能接收4條群發(fā)消息,多于4條的群發(fā)將對該用戶發(fā)送失敗乳附;3内地、具備微信支付權(quán)限的公眾號伴澄,在使用群發(fā)接口上傳、群發(fā)圖文消息類型時阱缓,可使用<a>標(biāo)簽加入外鏈非凌;4、開發(fā)者可以使用預(yù)覽接口校對消息樣式和排版荆针,通過預(yù)覽接口可發(fā)送編輯好的消息給指定用戶校驗效果敞嗡。
群發(fā)圖文消息的過程如下:
1、首先航背,預(yù)先將圖文消息中需要用到的圖片喉悴,使用上傳圖文消息內(nèi)圖片接口,上傳成功并獲得圖片URL2玖媚、上傳圖文消息素材箕肃,需要用到圖片時,請使用上一步獲取的圖片URL3今魔、使用對用戶分組的群發(fā)勺像,或?qū)penID列表的群發(fā),將圖文消息群發(fā)出去4涡贱、在上述過程中咏删,如果需要,還可以預(yù)覽圖文消息问词、查詢?nèi)喊l(fā)狀態(tài)督函,或刪除已群發(fā)的消息等
群發(fā)圖片、文本等其他消息類型的過程如下:
1激挪、如果是群發(fā)文本消息辰狡,則直接根據(jù)下面的接口說明進(jìn)行群發(fā)即可2、如果是群發(fā)圖片垄分、視頻等消息宛篇,則需要預(yù)先通過素材管理接口準(zhǔn)備好mediaID
2、消息的群發(fā)處理
雖然群發(fā)的消息類型有幾種薄湿,如包括圖文消息叫倍、文本消息、圖片豺瘤、視頻吆倦、語音、卡劵等等坐求,不過消息群發(fā)方式分為兩類:根據(jù)群組發(fā)送消息和根據(jù)OpenID發(fā)送消息兩種蚕泽。
根據(jù)微信接口的定義,我們設(shè)計了對上面兩種不同方式的發(fā)送接口桥嗤,我們把不同類型的消息放到枚舉MassMessageType 進(jìn)行定義须妻。
/// <summary>
/// 根據(jù)分組進(jìn)行群發(fā)消息(圖文消息仔蝌、文本消息、語音消息荒吏、視頻消息敛惊、圖片、卡劵等)
/// </summary>
/// <param name="accessToken">訪問憑證</param>
/// <param name="mediaIdOrContent">群發(fā)媒體文件時傳入mediaId,群發(fā)文本消息時傳入content,群發(fā)卡券時傳入cardId</param>
/// <param name="groupId">群發(fā)到的分組的group_id</param>
/// <param name="isToAll">
/// 使用is_to_all為true且成功群發(fā)司倚,會使得此次群發(fā)進(jìn)入歷史消息列表豆混。
/// 設(shè)置is_to_all為false時是可以多次群發(fā)的,但每個用戶只會收到最多4條动知,且這些群發(fā)不會進(jìn)入歷史消息列表</param>
/// <returns></returns>
MassMessageResult SendByGroup(string accessToken, MassMessageType messageType, string mediaIdOrContent, string groupId, bool isToAll = false);
/// <summary>
/// 根據(jù)OpenId進(jìn)行群發(fā)消息(視頻消息需要單獨)
/// </summary>
/// <param name="accessToken">訪問憑證</param>
/// <param name="messageType">消息類型</param>
/// <param name="mediaIdOrContent">用于群發(fā)的消息的media_id</param>
/// <param name="openIdList">openId字符串?dāng)?shù)組</param>
/// <returns></returns>
MassMessageResult SendByOpenId(string accessToken, MassMessageType messageType, string mediaIdOrContent, List<string> openIdList);
其中枚舉MassMessageType定義代碼如下所示皿伺。
/// <summary>
/// 群發(fā)消息的類型
/// </summary>
public enum MassMessageType
{
/// <summary>
/// 圖文消息
/// </summary>
mpnews,
/// <summary>
/// 文本消息
/// </summary>
text,
/// <summary>
/// 圖片
/// </summary>
image,
/// <summary>
/// 語音
/// </summary>
voice,
/// <summary>
/// 音樂
/// </summary>
music,
/// <summary>
/// 視頻
/// </summary>
video,
/// <summary>
/// 卡劵
/// </summary>
wxcard
}
然后我們根據(jù)上面的接口實現(xiàn)相關(guān)的處理函數(shù),群發(fā)消息的類定義代碼如下所示盒粮。
/// <summary>
/// 消息群發(fā).
/// 在公眾平臺網(wǎng)站上鸵鸥,為訂閱號提供了每天1條的群發(fā)權(quán)限,為服務(wù)號提供每月(自然月)4條的群發(fā)權(quán)限丹皱。
/// 而對于某些具備開發(fā)能力的公眾號運營者妒穴,可以通過高級群發(fā)接口,實現(xiàn)更靈活的群發(fā)能力摊崭。
/// </summary>
public class MassSendApi : IMassSendApi
對于圖文消息的群發(fā)規(guī)則讼油,微信接口定義如下。
接口調(diào)用請求說明
http請求方式: POST
https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN
POST數(shù)據(jù)說明
POST數(shù)據(jù)示例如下:
圖文消息(注意圖文消息的media_id需要通過上述方法來得到):
{
"filter":{
"is_to_all":false,
"group_id":2
},
"mpnews":{
"media_id":"123dsdajkasd231jhksad"
},
"msgtype":"mpnews"
}
其他類似文本消息呢簸、圖片矮台、視頻、語音根时、卡劵等發(fā)送方式類似瘦赫,都是提供一個不同的JSON字符串,然后提交到對應(yīng)的連接地址就可以了蛤迎,因此我們可以把它們進(jìn)行統(tǒng)一的封裝處理确虱。
我們可以在一個條件語句里面對內(nèi)容進(jìn)行組裝,例如對于圖文消息的處理代碼如下所示替裆。
switch (messageType)
{
case MassMessageType.mpnews://圖文消息
postData = new
{
filter = new
{
is_to_all = isToAll, //是否讓此次群發(fā)進(jìn)入歷史消息列表
group_id = groupId //群發(fā)到的分組的group_id
},
mpnews = new
{
media_id = mediaIdOrContent //用于群發(fā)的消息的media_id
},
msgtype = "mpnews"
}.ToJson();
break;
對于文本消息的組裝如下所示校辩。
case MassMessageType.text://文本消息
postData = new
{
filter = new
{
is_to_all = isToAll, //是否讓此次群發(fā)進(jìn)入歷史消息列表
group_id = groupId //群發(fā)到的分組的group_id
},
text = new
{
content = mediaIdOrContent //用于群發(fā)的消息的內(nèi)容
},
msgtype = "text"
}.ToJson();
break;
最后我們通過代碼進(jìn)行提交JSON數(shù)據(jù),并獲取返回結(jié)果即可辆童,如下代碼所示召川。
string url = string.Format("https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={0}", accessToken);
MassMessageResult result = JsonHelper<MassMessageResult>.ConvertJson(url, postData);
return result;
這樣,整合各個消息類型的處理胸遇,我們就可以得到一個完整的消息群發(fā)操作了。
群發(fā)給openid的操作也是類似上面的處理方式汉形,也是通過一個switch的條件語句纸镊,進(jìn)行不同內(nèi)容的構(gòu)建倍阐,然后統(tǒng)一發(fā)送即可。
請注意:在返回成功時逗威,意味著群發(fā)任務(wù)提交成功峰搪,并不意味著此時群發(fā)已經(jīng)結(jié)束,所以凯旭,仍有可能在后續(xù)的發(fā)送過程中出現(xiàn)異常情況導(dǎo)致用戶未收到消息概耻,如消息有時會進(jìn)行審核、服務(wù)器不穩(wěn)定等罐呼。此外鞠柄,群發(fā)任務(wù)一般需要較長的時間才能全部發(fā)送完畢,請耐心等待嫉柴。
由于群發(fā)任務(wù)提交后厌杜,群發(fā)任務(wù)可能在一定時間后才完成,因此计螺,群發(fā)接口調(diào)用時夯尽,僅會給出群發(fā)任務(wù)是否提交成功的提示,若群發(fā)任務(wù)提交成功登馒,則在群發(fā)任務(wù)結(jié)束時匙握,會向開發(fā)者在公眾平臺填寫的開發(fā)者URL(callback URL)推送事件。
推送的XML結(jié)構(gòu)如下(發(fā)送成功時):
<xml>
<ToUserName><![CDATA[gh_3e8adccde292]]></ToUserName>
<FromUserName><![CDATA[oR5Gjjl_eiZoUpGozMo7dbBJ362A]]></FromUserName>
<CreateTime>1394524295</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[MASSSENDJOBFINISH]]></Event>
<MsgID>1988</MsgID>
<Status><![CDATA[sendsuccess]]></Status>
<TotalCount>100</TotalCount>
<FilterCount>80</FilterCount>
<SentCount>75</SentCount>
<ErrorCount>5</ErrorCount>
</xml>
對應(yīng)的字段說明如下所示陈轿。
ToUserName 公眾號的微信號
FromUserName 公眾號群發(fā)助手的微信號圈纺,為mphelper
CreateTime 創(chuàng)建時間的時間戳
MsgType 消息類型,此處為event
Event 事件信息济欢,此處為MASSSENDJOBFINISH
MsgID 群發(fā)的消息ID
Status 群發(fā)的結(jié)構(gòu)赠堵,為“send success”或“send fail”或“err(num)”。但send success時法褥,也有可能因用戶拒收公眾號的消息茫叭、系統(tǒng)錯誤等原因造成少量用戶接收失敗。err(num)是審核失敗的具體原因
TotalCount group_id下粉絲數(shù)半等;或者openid_list中的粉絲數(shù)
FilterCount 過濾(過濾是指特定地區(qū)揍愁、性別的過濾、用戶設(shè)置拒收的過濾杀饵,用戶接收已超4條的過濾)后莽囤,準(zhǔn)備發(fā)送的粉絲數(shù),原則上切距,F(xiàn)ilterCount = SentCount + ErrorCount
SentCount 發(fā)送成功的粉絲數(shù)
ErrorCount 發(fā)送失敗的粉絲數(shù)
因此我們需要通過處理消息群發(fā)的發(fā)送完成操作朽缎,定義一個實體類來承載這個消息。
public class RequestMassSendJobFinish : BaseEvent
{
public RequestMassSendJobFinish()
{
this.MsgType = RequestMsgType.Event.ToString().ToLower();
this.Event = RequestEvent.MASSSENDJOBFINISH.ToString();
}
/// <summary>
/// 群發(fā)的消息ID
/// </summary>
public int MsgID { get; set; }
/// <summary>
/// 返回狀態(tài)。
/// </summary>
public string Status { get; set; }
/// <summary>
/// group_id下粉絲數(shù)话肖;或者openid_list中的粉絲數(shù)
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 過濾(過濾是指北秽,有些用戶在微信設(shè)置不接收該公眾號的消息)后,準(zhǔn)備發(fā)送的粉絲數(shù)最筒,原則上断盛,F(xiàn)ilterCount = SentCount + ErrorCount
/// </summary>
public int FilterCount { get; set; }
/// <summary>
/// 發(fā)送成功的粉絲數(shù)
/// </summary>
public int SendCount { get; set; }
/// <summary>
/// 發(fā)送失敗的粉絲數(shù)
/// </summary>
public int ErrorCount { get; set; }
}
在我們需要記錄或者更新處理這種群發(fā)消息的狀態(tài)的時候暑竟,我們可以在整個微信的消息鏈里面對這樣的請求事件進(jìn)行處理繁扎,如下代碼是處理這種群發(fā)消息的通知的油航。
case RequestEvent.MASSSENDJOBFINISH:
{
//由于群發(fā)任務(wù)徹底完成需要較長時間,將會在群發(fā)任務(wù)即將完成的時候邢锯,就推送群發(fā)結(jié)果扬蕊,此時的推送人數(shù)數(shù)據(jù)將會與實際情形存在一定誤差
RequestMassSendJobFinish info = XmlConvertor.XmlToObject(postStr, typeof(RequestMassSendJobFinish)) as RequestMassSendJobFinish;
if(info != null)
{
//在此記錄群發(fā)完成的處理
}
LogTextHelper.Info(eventName + ((info == null) ? "info is null" : info.ToJson()));
}
break;
3、待群發(fā)消息的預(yù)覽
在很多時候弹囚,我們?nèi)喊l(fā)消息之前厨相,我們希望通過自己的微信號來看看具體的群發(fā)消息效果,如果沒有問題我們在統(tǒng)一群發(fā)鸥鹉,相當(dāng)于一個真實的審核過程蛮穿,這樣對于我們發(fā)送高質(zhì)量的消息是一個很好的習(xí)慣。
對于普通的消息預(yù)覽毁渗,我們定義的接口如下所示践磅。
/// <summary>
/// 預(yù)覽接口【訂閱號與服務(wù)號認(rèn)證后均可用】。
/// 開發(fā)者可通過該接口發(fā)送消息給指定用戶灸异,在手機(jī)端查看消息的樣式和排版府适。
/// 為了滿足第三方平臺開發(fā)者的需求,在保留對openID預(yù)覽能力的同時肺樟,增加了對指定微信號發(fā)送預(yù)覽的能力檐春,但該能力每日調(diào)用次數(shù)有限制(100次),請勿濫用么伯。
/// </summary>
/// <param name="accessToken">訪問憑證</param>
/// <param name="messageType">消息類型</param>
/// <param name="media_id">用于群發(fā)的消息的media_id</param>
/// <param name="touserOpenId">接收消息用戶對應(yīng)該公眾號的openid</param>
/// <param name="towxname">可以針對微信號進(jìn)行預(yù)覽(而非openID)疟暖,towxname和touser同時賦值時,以towxname優(yōu)先</param>
/// <returns></returns>
MassMessageResult PreviewMessage(string accessToken, MassMessageType messageType, string media_id, string touserOpenId, string towxname = null);
具體的實現(xiàn)也就是針對不同的消息類型田柔,構(gòu)建一個不同的處理機(jī)制俐巴,把它們差異性的JSON構(gòu)造出來,然后統(tǒng)一調(diào)用就可以了硬爆,具體代碼如下所示欣舵。
public MassMessageResult PreviewMessage(string accessToken, MassMessageType messageType, string media_id, string touserOpenId, string towxname = null)
{
string postData = "";
switch (messageType)
{
case MassMessageType.mpnews://圖文消息
postData = new
{
touser = touserOpenId,
towxname = towxname,
mpnews = new
{
media_id = media_id
},
msgtype = "mpnews"
}.ToJson();
break;
case MassMessageType.text://文本消息
postData = new
{
touser = touserOpenId,
towxname = towxname,
text = new
{
content = media_id
},
msgtype = "text"
}.ToJson();
break;
case MassMessageType.voice://語音
postData = new
{
touser = touserOpenId,
towxname = towxname,
voice = new
{
media_id = media_id
},
msgtype = "voice"
}.ToJson();
break;
case MassMessageType.image://圖片
postData = new
{
touser = touserOpenId,
towxname = towxname,
image = new
{
media_id = media_id
},
msgtype = "image"
}.ToJson();
break;
case MassMessageType.video://視頻
postData = new
{
touser = touserOpenId,
towxname = towxname,
mpvideo = new
{
media_id = media_id
},
msgtype = "mpvideo"
}.ToJson();
break;
case MassMessageType.wxcard: //卡劵
throw new WeixinException("發(fā)送卡券息請使用PreviewCardMessage方法。");
break;
}
var url = string.Format("https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}", accessToken);
return JsonHelper<MassMessageResult>.ConvertJson(url, postData);
}
消息的預(yù)覽在我們正式群發(fā)消息前的審核是比較有用的缀磕,我們可以通過接口進(jìn)行一個消息的預(yù)覽缘圈,可以在微信公眾號上看到的效果與正式群發(fā)后的消息是一樣的劣光。
例如我們通過下面的代碼進(jìn)行一個簡單的預(yù)覽消息操作。
/// <summary>
/// 群發(fā)消息的預(yù)覽
/// </summary>
private void btnPreviewMass_Click(object sender, EventArgs e)
{
//上傳圖片
btnUpload_Click(null, null);
//上傳圖文消息
btnUploadNews_Click(null, null);
//消息群發(fā)前的預(yù)覽操作
List<string> list = new List<string>() { openId };
IMassSendApi api = new MassSendApi();
var mediaId = this.news_mediaId;
MassMessageResult result = api.PreviewMessage(token, MassMessageType.mpnews, mediaId, openId);
if (result != null)
{
Console.WriteLine(result.msg_id);
}
}
最后可以看到例子代碼的預(yù)覽效果如下所示糟把。
如果對這個《C#開發(fā)微信門戶及應(yīng)用》系列感興趣赎线,可以關(guān)注我的其他文章