MassMessage.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Net;
  4. using System.Dynamic;
  5. using Newtonsoft.Json;
  6. using Newtonsoft.Json.Linq;
  7. using Common;
  8. using System.Threading.Tasks;
  9. namespace ZcPeng.weixin.PublicAccount
  10. {
  11. /// <summary>
  12. /// 群发消息
  13. /// </summary>
  14. public static class MassMessage
  15. {
  16. /// <summary>
  17. /// 群发消息所用的http方法
  18. /// </summary>
  19. private const string httpMethod = WebRequestMethods.Http.Post;
  20. /// <summary>
  21. /// 根据分组群发消息的地址
  22. /// </summary>
  23. private const string urlForSendingAll = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={0}";
  24. /// <summary>
  25. /// 按OpenId列表群发消息的地址
  26. /// </summary>
  27. private const string urlForSending = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token={0}";
  28. /// <summary>
  29. /// 按OpenId列表群发消息的最大用户数
  30. /// </summary>
  31. private const int maxUserCount = 10000;
  32. /// <summary>
  33. /// 删除群发消息的地址
  34. /// </summary>
  35. private const string urlForDeleting = "https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token={0}";
  36. /// <summary>
  37. /// 预览群发消息的地址
  38. /// </summary>
  39. private const string urlForPreviewing = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}";
  40. /// <summary>
  41. /// 查询群发消息发送状态的地址
  42. /// </summary>
  43. private const string urlForGettingStatus = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token={0}";
  44. /// <summary>
  45. /// 消息发送状态为发送成功的提示
  46. /// </summary>
  47. private const string sendSuccess = "SEND_SUCCESS";
  48. /// <summary>
  49. /// 根据分组群发消息
  50. /// </summary>
  51. /// <param name="userName">公众号</param>
  52. /// <param name="isToAll">是否群发给所有用户</param>
  53. /// <param name="groupId">如果群发给所有用户,忽略该参数;否则群发给该组中的用户</param>
  54. /// <param name="messageType">群发消息类型</param>
  55. /// <param name="mediaIdOrContent">多媒体id或者文本内容</param>
  56. /// <param name="errorMessage">返回发送是否成功</param>
  57. /// <returns>如果发送成功,返回消息ID;否则,返回-1。</returns>
  58. public static async Task<long> Send(string userName, bool isToAll, string groupId, MassMessageTypeEnum messageType, string mediaIdOrContent, ErrorMessage errorMessage)
  59. {
  60. long messageId = -1;
  61. if (!isToAll && string.IsNullOrWhiteSpace(groupId))
  62. {
  63. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "缺少分组ID。");
  64. return messageId;
  65. }
  66. if (string.IsNullOrWhiteSpace(mediaIdOrContent))
  67. {
  68. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode,
  69. string.Format("缺少{0}。", messageType == MassMessageTypeEnum.text ? "文本内容" : "媒体ID"));
  70. return messageId;
  71. }
  72. string json = GetMassMessageJsonString(isToAll, groupId, messageType, mediaIdOrContent);
  73. return await Send(userName, urlForSendingAll, json, errorMessage);
  74. }
  75. /// <summary>
  76. /// 根据OpenId列表群发消息
  77. /// </summary>
  78. /// <param name="userName">公众号</param>
  79. /// <param name="tousers">OpenId列表</param>
  80. /// <param name="messageType">群发消息类型</param>
  81. /// <param name="mediaIdOrContent">多媒体id或者文本内容</param>
  82. /// <param name="errorMessage">返回发送是否成功</param>
  83. /// <returns>如果发送成功,返回消息ID;否则,返回-1。</returns>
  84. public static async Task<long> Send(string userName, IEnumerable<string> tousers, MassMessageTypeEnum messageType, string mediaIdOrContent, ErrorMessage errorMessage)
  85. {
  86. long messageId = -1;
  87. if (tousers == null)
  88. {
  89. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, string.Format("接收者不正确,数目必须大于0,小于等于{0}。", maxUserCount));
  90. return messageId;
  91. }
  92. List<string> touserList = new List<string>(tousers);
  93. if (touserList.Count == 0 || touserList.Count > maxUserCount)
  94. {
  95. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, string.Format("接收者不正确,数目必须大于0,小于等于{0}。", maxUserCount));
  96. return messageId;
  97. }
  98. if (string.IsNullOrWhiteSpace(mediaIdOrContent))
  99. {
  100. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode,
  101. string.Format("缺少{0}。", messageType == MassMessageTypeEnum.text ? "文本内容" : "媒体ID"));
  102. return messageId;
  103. }
  104. string json = GetMassMessageJsonString(touserList.ToArray(), messageType, mediaIdOrContent);
  105. return await Send(userName, urlForSending, json, errorMessage);
  106. }
  107. /// <summary>
  108. /// 群发消息
  109. /// </summary>
  110. /// <param name="userName">公众号</param>
  111. /// <param name="urlFormat">包含格式的服务器地址</param>
  112. /// <param name="json">消息json字符串</param>
  113. /// <param name="errorMessage">返回发送是否成功</param>
  114. /// <returns>如果发送成功,返回消息id;否则,返回-1。</returns>
  115. private static async Task<Tuple<long, ErrorMessage>> SendImpl(string userName, string urlFormat, string json)
  116. {
  117. long messageId = -1;
  118. ErrorMessage errorMessage;
  119. AccessToken token = AccessToken.Get(userName);
  120. if (token == null)
  121. {
  122. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "获取许可令牌失败。");
  123. return new Tuple<long, ErrorMessage>(messageId, errorMessage);
  124. }
  125. string url = string.Format(urlFormat, token.access_token);
  126. string responseContent = await HttpHelper.HttpPost(url, json, "application/text");
  127. //var data = JsonConvert.DeserializeAnonymousType(responseContent, new { msg_id = 0, msg_data_id = 0, errmsg = "",errcode = 0 });
  128. if (responseContent == null|| responseContent == "")
  129. {
  130. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "提交数据到微信服务器失败。");
  131. return new Tuple<long, ErrorMessage>(messageId, errorMessage);
  132. }
  133. JObject jo = JObject.Parse(responseContent);
  134. JToken jt;
  135. if (jo.TryGetValue("errcode", out jt) && jo.TryGetValue("errmsg", out jt))
  136. {
  137. errorMessage = new ErrorMessage((int)jo["errcode"], (string)jo["errmsg"]);
  138. if (jo.TryGetValue("msg_id", out jt))
  139. messageId = (long)jt;
  140. }
  141. else
  142. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "解析返回结果出错。");
  143. return new Tuple<long, ErrorMessage>(messageId, errorMessage);
  144. }
  145. private static async Task<long> Send(string userName, string urlFormat, string json, ErrorMessage errorMessage)
  146. {
  147. var result = await SendImpl(userName, urlFormat, json);
  148. var msgId = result.Item1;
  149. errorMessage = result.Item2;
  150. return msgId;
  151. }
  152. /// <summary>
  153. /// 获取群发消息的json字符串。
  154. /// </summary>
  155. /// <param name="isToAll">是否群发给所有用户</param>
  156. /// <param name="groupId">如果群发给所有用户,忽略该参数;否则群发给该组中的用户</param>
  157. /// <param name="messageType">群发消息类型</param>
  158. /// <param name="mediaIdOrContent">多媒体id或者文本内容</param>
  159. /// <returns>返回群发消息的json字符串。</returns>
  160. private static string GetMassMessageJsonString(bool isToAll, string groupId, MassMessageTypeEnum messageType, string mediaIdOrContent)
  161. {
  162. dynamic msg = new ExpandoObject();
  163. msg.filter = new
  164. {
  165. is_to_all = isToAll,
  166. group_id = isToAll ? string.Empty : groupId
  167. };
  168. if (messageType == MassMessageTypeEnum.text)
  169. ((IDictionary<string, object>)msg).Add(messageType.ToString(), new { content = mediaIdOrContent });
  170. else
  171. ((IDictionary<string, object>)msg).Add(messageType.ToString(), new { media_id = mediaIdOrContent });
  172. msg.msgtype = messageType.ToString();
  173. return JsonConvert.SerializeObject(msg);
  174. }
  175. /// <summary>
  176. /// 获取群发消息的json字符串。
  177. /// </summary>
  178. /// <param name="tousers">OpenId列表</param>
  179. /// <param name="messageType">群发消息类型</param>
  180. /// <param name="mediaIdOrContent">多媒体id或者文本内容</param>
  181. /// <returns>返回群发消息的json字符串。</returns>
  182. private static string GetMassMessageJsonString(string[] tousers, MassMessageTypeEnum messageType, string mediaIdOrContent)
  183. {
  184. dynamic msg = new ExpandoObject();
  185. msg.touser = tousers;
  186. if (messageType == MassMessageTypeEnum.text)
  187. ((IDictionary<string, object>)msg).Add(messageType.ToString(), new { content = mediaIdOrContent });
  188. else
  189. ((IDictionary<string, object>)msg).Add(messageType.ToString(), new { media_id = mediaIdOrContent });
  190. msg.msgtype = messageType.ToString();
  191. return JsonConvert.SerializeObject(msg);
  192. }
  193. /// <summary>
  194. /// 获取群发消息的json字符串。
  195. /// </summary>
  196. /// <param name="touser">OpenId</param>
  197. /// <param name="messageType">群发消息类型</param>
  198. /// <param name="mediaIdOrContent">多媒体id或者文本内容</param>
  199. /// <returns>返回群发消息的json字符串。</returns>
  200. private static string GetMassMessageJsonString(string touser, MassMessageTypeEnum messageType, string mediaIdOrContent)
  201. {
  202. dynamic msg = new ExpandoObject();
  203. msg.touser = touser;
  204. if (messageType == MassMessageTypeEnum.text)
  205. ((IDictionary<string, object>)msg).Add(messageType.ToString(), new { content = mediaIdOrContent });
  206. else
  207. ((IDictionary<string, object>)msg).Add(messageType.ToString(), new { media_id = mediaIdOrContent });
  208. msg.msgtype = messageType.ToString();
  209. return JsonConvert.SerializeObject(msg);
  210. }
  211. /// <summary>
  212. /// 删除群发消息。
  213. /// 注:只能删除图文消息和视频消息。
  214. /// </summary>
  215. /// <param name="userName">公众号</param>
  216. /// <param name="messageId">消息id</param>
  217. /// <returns>返回删除是否成功</returns>
  218. //public static ErrorMessage Delete(string userName, long messageId)
  219. //{
  220. // string json = JsonConvert.SerializeObject(new { msg_id = messageId });
  221. // return HttpHelper.RequestErrorMessage(urlForDeleting, userName, null, httpMethod, json);
  222. //}
  223. public static async Task<Tuple<long, ErrorMessage>> PreviewImpl(string userName, string touser, MassMessageTypeEnum messageType, string mediaIdOrContent)
  224. {
  225. long messageId = -1;
  226. ErrorMessage errorMessage = null;
  227. if (string.IsNullOrWhiteSpace(mediaIdOrContent))
  228. {
  229. errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode,
  230. string.Format("缺少{0}。", messageType == MassMessageTypeEnum.text ? "文本内容" : "媒体ID"));
  231. return new Tuple<long, ErrorMessage>(messageId, errorMessage );
  232. }
  233. string json = GetMassMessageJsonString(touser, messageType, mediaIdOrContent);
  234. var result = await Send(userName, urlForPreviewing, json, errorMessage);
  235. return new Tuple<long, ErrorMessage>(result, errorMessage);
  236. }
  237. /// <summary>
  238. /// 预览群发消息
  239. /// </summary>
  240. /// <param name="userName">公众号</param>
  241. /// <param name="touser">OpenId</param>
  242. /// <param name="messageType">群发消息类型</param>
  243. /// <param name="mediaIdOrContent">多媒体id或者文本内容</param>
  244. /// <param name="errorMessage">返回发送是否成功</param>
  245. /// <returns>如果发送成功,返回消息ID;否则,返回-1。</returns>
  246. public static async Task<long> Preview(string userName, string touser, MassMessageTypeEnum messageType, string mediaIdOrContent, ErrorMessage errorMessage)
  247. {
  248. var tuple = await PreviewImpl(userName, touser, messageType, mediaIdOrContent);
  249. var result = tuple.Item1;
  250. errorMessage = tuple.Item2;
  251. return result;
  252. }
  253. /// <summary>
  254. /// 查询群发消息的发送状态
  255. /// </summary>
  256. /// <param name="userName">公众号</param>
  257. /// <param name="messageId">消息id</param>
  258. /// <param name="errorMessage">返回查询是否成功</param>
  259. /// <returns>返回消息是否发送成功</returns>
  260. //public static bool GetStatus(string userName, long messageId, out ErrorMessage errorMessage)
  261. //{
  262. // string json = JsonConvert.SerializeObject(new { msg_id = messageId });
  263. // string responseContent = HttpHelper.RequestResponseContent(urlForGettingStatus, userName, null, httpMethod, json);
  264. // if (string.IsNullOrWhiteSpace(responseContent))
  265. // {
  266. // errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "从微信服务器获取响应失败。");
  267. // return false;
  268. // }
  269. // else
  270. // {
  271. // JObject jo = JObject.Parse(responseContent);
  272. // JToken jt;
  273. // if (jo.TryGetValue("msg_status", out jt))
  274. // {
  275. // errorMessage = new ErrorMessage(ErrorMessage.SuccessCode, "查询群发消息发送状态成功。");
  276. // return (string)jt == sendSuccess;
  277. // }
  278. // else
  279. // {
  280. // errorMessage = ErrorMessage.Parse(responseContent);
  281. // return false;
  282. // }
  283. // }
  284. //}
  285. }
  286. }