在很多場合下,我們可能需要利用微信公眾號的優勢,定期給指定用戶群發送一些推廣消息或者新聞內容,以便給關注客戶一種經常更新公眾號內容的感覺,同時也方便我們經常和用戶進行互動。微信公眾號的高級群發接口就是為了處理這個場景的,本文介紹在C#代碼中如何封裝消息的群發和預覽等功能。
對於公眾號中的服務號和訂閱號,群發的消息有一定的限制,具體規則如下所示。
1、對於認證訂閱號,群發接口每天可成功調用1次,此次群發可選擇發送給全部用戶或某個分組; 2、對於認證服務號雖然開發者使用高級群發接口的每日調用限制為100次,但是用戶每月只能接收4條,無論在公眾平台網站上,還是使用接口群發,用戶每月只能接收4條群發消息,多於4條的群發將對該用戶發送失敗; 3、具備微信支付權限的公眾號,在使用群發接口上傳、群發圖文消息類型時,可使用<a>標簽加入外鏈; 4、開發者可以使用預覽接口校對消息樣式和排版,通過預覽接口可發送編輯好的消息給指定用戶校驗效果。
群發圖文消息的過程如下:
1、首先,預先將圖文消息中需要用到的圖片,使用上傳圖文消息內圖片接口,上傳成功並獲得圖片URL 2、上傳圖文消息素材,需要用到圖片時,請使用上一步獲取的圖片URL 3、使用對用戶分組的群發,或對OpenID列表的群發,將圖文消息群發出去 4、在上述過程中,如果需要,還可以預覽圖文消息、查詢群發狀態,或刪除已群發的消息等
群發圖片、文本等其他消息類型的過程如下:
1、如果是群發文本消息,則直接根據下面的接口說明進行群發即可 2、如果是群發圖片、視頻等消息,則需要預先通過素材管理接口准備好mediaID
雖然群發的消息類型有幾種,如包括圖文消息、文本消息、圖片、視頻、語音、卡劵等等,不過消息群發方式分為兩類:根據群組發送消息和根據OpenID發送消息兩種。
根據微信接口的定義,我們設計了對上面兩種不同方式的發送接口,我們把不同類型的消息放到枚舉MassMessageType 進行定義。
/// <summary> /// 根據分組進行群發消息(圖文消息、文本消息、語音消息、視頻消息、圖片、卡劵等) /// </summary> /// <param name="accessToken">訪問憑證</param> /// <param name="mediaIdOrContent">群發媒體文件時傳入mediaId,群發文本消息時傳入content,群發卡券時傳入cardId</param> /// <param name="groupId">群發到的分組的group_id</param> /// <param name="isToAll"> /// 使用is_to_all為true且成功群發,會使得此次群發進入歷史消息列表。 /// 設置is_to_all為false時是可以多次群發的,但每個用戶只會收到最多4條,且這些群發不會進入歷史消息列表</param> /// <returns></returns> MassMessageResult SendByGroup(string accessToken, MassMessageType messageType, string mediaIdOrContent, string groupId, bool isToAll = false); /// <summary> /// 根據OpenId進行群發消息(視頻消息需要單獨) /// </summary> /// <param name="accessToken">訪問憑證</param> /// <param name="messageType">消息類型</param> /// <param name="mediaIdOrContent">用於群發的消息的media_id</param> /// <param name="openIdList">openId字符串數組</param> /// <returns></returns> MassMessageResult SendByOpenId(string accessToken, MassMessageType messageType, string mediaIdOrContent, List<string> openIdList);
其中枚舉MassMessageType定義代碼如下所示。
/// <summary> /// 群發消息的類型 /// </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 }
然後我們根據上面的接口實現相關的處理函數,群發消息的類定義代碼如下所示。
/// <summary> /// 消息群發. /// 在公眾平台網站上,為訂閱號提供了每天1條的群發權限,為服務號提供每月(自然月)4條的群發權限。 /// 而對於某些具備開發能力的公眾號運營者,可以通過高級群發接口,實現更靈活的群發能力。 /// </summary> public class MassSendApi : IMassSendApi
對於圖文消息的群發規則,微信接口定義如下。
接口調用請求說明
http請求方式: POST https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN
POST數據說明
POST數據示例如下:
圖文消息(注意圖文消息的media_id需要通過上述方法來得到):
{ "filter":{ "is_to_all":false, "group_id":2 }, "mpnews":{ "media_id":"123dsdajkasd231jhksad" }, "msgtype":"mpnews" }
其他類似文本消息、圖片、視頻、語音、卡劵等發送方式類似,都是提供一個不同的JSON字符串,然後提交到對應的連接地址就可以了,因此我們可以把它們進行統一的封裝處理。
我們可以在一個條件語句裡面對內容進行組裝,例如對於圖文消息的處理代碼如下所示。
switch (messageType) { case MassMessageType.mpnews://圖文消息 postData = new { filter = new { is_to_all = isToAll, //是否讓此次群發進入歷史消息列表 group_id = groupId //群發到的分組的group_id }, mpnews = new { media_id = mediaIdOrContent //用於群發的消息的media_id }, msgtype = "mpnews" }.ToJson(); break;
對於文本消息的組裝如下所示。
case MassMessageType.text://文本消息 postData = new { filter = new { is_to_all = isToAll, //是否讓此次群發進入歷史消息列表 group_id = groupId //群發到的分組的group_id }, text = new { content = mediaIdOrContent //用於群發的消息的內容 }, msgtype = "text" }.ToJson(); break;
最後我們通過代碼進行提交JSON數據,並獲取返回結果即可,如下代碼所示。
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;
這樣,整合各個消息類型的處理,我們就可以得到一個完整的消息群發操作了。
群發給openid的操作也是類似上面的處理方式,也是通過一個switch的條件語句,進行不同內容的構建,然後統一發送即可。
請注意:在返回成功時,意味著群發任務提交成功,並不意味著此時群發已經結束,所以,仍有可能在後續的發送過程中出現異常情況導致用戶未收到消息,如消息有時會進行審核、服務器不穩定等。此外,群發任務一般需要較長的時間才能全部發送完畢,請耐心等待。
由於群發任務提交後,群發任務可能在一定時間後才完成,因此,群發接口調用時,僅會給出群發任務是否提交成功的提示,若群發任務提交成功,則在群發任務結束時,會向開發者在公眾平台填寫的開發者URL(callback URL)推送事件。
推送的XML結構如下(發送成功時):
<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>
對應的字段說明如下所示。
因此我們需要通過處理消息群發的發送完成操作,定義一個實體類來承載這個消息。
public class RequestMassSendJobFinish : BaseEvent { public RequestMassSendJobFinish() { this.MsgType = RequestMsgType.Event.ToString().ToLower(); this.Event = RequestEvent.MASSSENDJOBFINISH.ToString(); } /// <summary> /// 群發的消息ID /// </summary> public int MsgID { get; set; } /// <summary> /// 返回狀態。 /// </summary> public string Status { get; set; } /// <summary> /// group_id下粉絲數;或者openid_list中的粉絲數 /// </summary> public int TotalCount { get; set; } /// <summary> /// 過濾(過濾是指,有些用戶在微信設置不接收該公眾號的消息)後,准備發送的粉絲數,原則上,FilterCount = SentCount + ErrorCount /// </summary> public int FilterCount { get; set; } /// <summary> /// 發送成功的粉絲數 /// </summary> public int SendCount { get; set; } /// <summary> /// 發送失敗的粉絲數 /// </summary> public int ErrorCount { get; set; } }
在我們需要記錄或者更新處理這種群發消息的狀態的時候,我們可以在整個微信的消息鏈裡面對這樣的請求事件進行處理,如下代碼是處理這種群發消息的通知的。
case RequestEvent.MASSSENDJOBFINISH: { //由於群發任務徹底完成需要較長時間,將會在群發任務即將完成的時候,就推送群發結果,此時的推送人數數據將會與實際情形存在一定誤差 RequestMassSendJobFinish info = XmlConvertor.XmlToObject(postStr, typeof(RequestMassSendJobFinish)) as RequestMassSendJobFinish; if(info != null) { //在此記錄群發完成的處理 } LogTextHelper.Info(eventName + ((info == null) ? "info is null" : info.ToJson())); } break;
在很多時候,我們群發消息之前,我們希望通過自己的微信號來看看具體的群發消息效果,如果沒有問題我們在統一群發,相當於一個真實的審核過程,這樣對於我們發送高質量的消息是一個很好的習慣。
對於普通的消息預覽,我們定義的接口如下所示。
/// <summary> /// 預覽接口【訂閱號與服務號認證後均可用】。 /// 開發者可通過該接口發送消息給指定用戶,在手機端查看消息的樣式和排版。 /// 為了滿足第三方平台開發者的需求,在保留對openID預覽能力的同時,增加了對指定微信號發送預覽的能力,但該能力每日調用次數有限制(100次),請勿濫用。 /// </summary> /// <param name="accessToken">訪問憑證</param> /// <param name="messageType">消息類型</param> /// <param name="media_id">用於群發的消息的media_id</param> /// <param name="touserOpenId">接收消息用戶對應該公眾號的openid</param> /// <param name="towxname">可以針對微信號進行預覽(而非openID),towxname和touser同時賦值時,以towxname優先</param> /// <returns></returns> MassMessageResult PreviewMessage(string accessToken, MassMessageType messageType, string media_id, string touserOpenId, string towxname = null);
具體的實現也就是針對不同的消息類型,構建一個不同的處理機制,把它們差異性的JSON構造出來,然後統一調用就可以了,具體代碼如下所示。
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("發送卡券息請使用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); }
消息的預覽在我們正式群發消息前的審核是比較有用的,我們可以通過接口進行一個消息的預覽,可以在微信公眾號上看到的效果與正式群發後的消息是一樣的。
例如我們通過下面的代碼進行一個簡單的預覽消息操作。
/// <summary> /// 群發消息的預覽 /// </summary> private void btnPreviewMass_Click(object sender, EventArgs e) { //上傳圖片 btnUpload_Click(null, null); //上傳圖文消息 btnUploadNews_Click(null, null); //消息群發前的預覽操作 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); } }
最後可以看到例子代碼的預覽效果如下所示。
如果對這個《C#開發微信門戶及應用》系列感興趣,可以關注我的其他文章,系列隨筆如下所示:
C#開發微信門戶及應用(29)--微信個性化菜單的實現
C#開發微信門戶及應用(28)--微信“搖一搖·周邊”功能的使用和接口的實現
C#開發微信門戶及應用(27)-公眾號模板消息管理
C#開發微信門戶及應用(26)-公眾號微信素材管理
C#開發微信門戶及應用(25)-微信企業號的客戶端管理功能
C#開發微信門戶及應用(24)-微信小店貨架信息管理
C#開發微信門戶及應用(23)-微信小店商品管理接口的封裝和測試
C#開發微信門戶及應用(22)-微信小店的開發和使用
C#開發微信門戶及應用(21)-微信企業號的消息和事件的接收處理及解密
C#開發微信門戶及應用(20)-微信企業號的菜單管理
C#開發微信門戶及應用(19)-微信企業號的消息發送(文本、圖片、文件、語音、視頻、圖文消息等)
C#開發微信門戶及應用(18)-微信企業號的通訊錄管理開發之成員管理
C#開發微信門戶及應用(17)-微信企業號的通訊錄管理開發之部門管理
C#開發微信門戶及應用(16)-微信企業號的配置和使用
C#開發微信門戶及應用(15)-微信菜單增加掃一掃、發圖片、發地理位置功能
C#開發微信門戶及應用(14)-在微信菜單中采用重定向獲取用戶數據
C#開發微信門戶及應用(13)-使用地理位置擴展相關應用
C#開發微信門戶及應用(12)-使用語音處理
C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹
C#開發微信門戶及應用(10)--在管理系統中同步微信用戶分組信息
C#開發微信門戶及應用(9)-微信門戶菜單管理及提交到微信服務器
C#開發微信門戶及應用(8)-微信門戶應用管理系統功能介紹
C#開發微信門戶及應用(7)-微信多客服功能及開發集成
C#開發微信門戶及應用(6)--微信門戶菜單的管理操作
C#開發微信門戶及應用(5)--用戶分組信息管理
C#開發微信門戶及應用(4)--關注用戶列表及詳細信息管理
C#開發微信門戶及應用(3)--文本消息和圖文消息的應答
C#開發微信門戶及應用(2)--微信消息的處理和應答
C#開發微信門戶及應用(1)--開始使用微信接口