message.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. from __future__ import absolute_import, unicode_literals
  4. import re
  5. import six
  6. from optionaldict import optionaldict
  7. from library.wechatpy.client.api.base import BaseWeChatAPI
  8. from library import random_string
  9. class WeChatMessage(BaseWeChatAPI):
  10. OPENID_RE = re.compile(r'^[\w\-]{28}$', re.I)
  11. def _send_custom_message(self, data, account=None):
  12. data = data or {}
  13. if account:
  14. data['customservice'] = {'kf_account': account}
  15. return self._post(
  16. 'message/custom/send',
  17. data=data
  18. )
  19. def send_text(self, user_id, content, account=None):
  20. """
  21. 发送文本消息
  22. 详情请参考
  23. http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
  24. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  25. :param content: 消息正文
  26. :param account: 可选,客服账号
  27. :return: 返回的 JSON 数据包
  28. 使用示例::
  29. from wechatpy import WeChatClient
  30. client = WeChatClient('appid', 'secret')
  31. res = client.message.send_text('openid', 'text')
  32. """
  33. data = {
  34. 'touser': user_id,
  35. 'msgtype': 'text',
  36. 'text': {'content': content}
  37. }
  38. return self._send_custom_message(data, account=account)
  39. def send_image(self, user_id, media_id, account=None):
  40. """
  41. 发送图片消息
  42. 详情请参考
  43. http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
  44. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  45. :param media_id: 图片的媒体ID。 可以通过 :func:`upload_media` 上传。
  46. :param account: 可选,客服账号
  47. :return: 返回的 JSON 数据包
  48. 使用示例::
  49. from wechatpy import WeChatClient
  50. client = WeChatClient('appid', 'secret')
  51. res = client.message.send_image('openid', 'media_id')
  52. """
  53. data = {
  54. 'touser': user_id,
  55. 'msgtype': 'image',
  56. 'image': {
  57. 'media_id': media_id
  58. }
  59. }
  60. return self._send_custom_message(data, account=account)
  61. def send_voice(self, user_id, media_id, account=None):
  62. """
  63. 发送语音消息
  64. 详情请参考
  65. http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
  66. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  67. :param media_id: 发送的语音的媒体ID。 可以通过 :func:`upload_media` 上传。
  68. :param account: 可选,客服账号
  69. :return: 返回的 JSON 数据包
  70. 使用示例::
  71. from wechatpy import WeChatClient
  72. client = WeChatClient('appid', 'secret')
  73. res = client.message.send_voice('openid', 'media_id')
  74. """
  75. data = {
  76. 'touser': user_id,
  77. 'msgtype': 'voice',
  78. 'voice': {
  79. 'media_id': media_id
  80. }
  81. }
  82. return self._send_custom_message(data, account=account)
  83. def send_video(self, user_id, media_id, title=None,
  84. description=None, account=None):
  85. """
  86. 发送视频消息
  87. 详情请参考
  88. http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
  89. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  90. :param media_id: 发送的视频的媒体ID。 可以通过 :func:`upload_media` 上传。
  91. :param title: 视频消息的标题
  92. :param description: 视频消息的描述
  93. :param account: 可选,客服账号
  94. :return: 返回的 JSON 数据包
  95. 使用示例::
  96. from wechatpy import WeChatClient
  97. client = WeChatClient('appid', 'secret')
  98. res = client.message.send_video('openid', 'media_id', 'title', 'description')
  99. """
  100. video_data = {
  101. 'media_id': media_id,
  102. }
  103. if title:
  104. video_data['title'] = title
  105. if description:
  106. video_data['description'] = description
  107. data = {
  108. 'touser': user_id,
  109. 'msgtype': 'video',
  110. 'video': video_data
  111. }
  112. return self._send_custom_message(data, account=account)
  113. def send_music(self, user_id, url, hq_url, thumb_media_id,
  114. title=None, description=None, account=None):
  115. """
  116. 发送音乐消息
  117. 详情请参考
  118. http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
  119. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  120. :param url: 音乐链接
  121. :param hq_url: 高品质音乐链接,wifi环境优先使用该链接播放音乐
  122. :param thumb_media_id: 缩略图的媒体ID。 可以通过 :func:`upload_media` 上传。
  123. :param title: 音乐标题
  124. :param description: 音乐描述
  125. :param account: 可选,客服账号
  126. :return: 返回的 JSON 数据包
  127. """
  128. music_data = {
  129. 'musicurl': url,
  130. 'hqmusicurl': hq_url,
  131. 'thumb_media_id': thumb_media_id
  132. }
  133. if title:
  134. music_data['title'] = title
  135. if description:
  136. music_data['description'] = description
  137. data = {
  138. 'touser': user_id,
  139. 'msgtype': 'music',
  140. 'music': music_data
  141. }
  142. return self._send_custom_message(data, account=account)
  143. def send_articles(self, user_id, articles, account=None):
  144. """
  145. 发送图文消息
  146. 详情请参考
  147. http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
  148. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  149. :param articles: 一个包含至多10个图文的数组, 或者微信图文消息素材 media_id
  150. :param account: 可选,客服账号
  151. :return: 返回的 JSON 数据包
  152. """
  153. if isinstance(articles, (tuple, list)):
  154. articles_data = []
  155. for article in articles:
  156. articles_data.append({
  157. 'title': article['title'],
  158. 'description': article['description'],
  159. 'url': article['url'],
  160. 'picurl': article.get('image', article.get('picurl')),
  161. })
  162. data = {
  163. 'touser': user_id,
  164. 'msgtype': 'news',
  165. 'news': {
  166. 'articles': articles_data
  167. }
  168. }
  169. else:
  170. data = {
  171. 'touser': user_id,
  172. 'msgtype': 'mpnews',
  173. 'mpnews': {
  174. 'media_id': articles,
  175. }
  176. }
  177. return self._send_custom_message(data, account=account)
  178. def send_card(self, user_id, card_id, card_ext=None, account=None):
  179. """
  180. 发送卡券消息
  181. 详情请参参考
  182. https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547
  183. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  184. :param card_id: 卡券 ID
  185. :param card_ext: 可选,卡券扩展信息
  186. :param account: 可选,客服账号
  187. :return: 返回的 JSON 数据包
  188. """
  189. wxcard = {
  190. 'card_id': card_id,
  191. }
  192. if card_ext:
  193. wxcard['card_ext'] = card_ext
  194. data = {
  195. 'touser': user_id,
  196. 'msgtype': 'wxcard',
  197. 'wxcard': wxcard,
  198. }
  199. return self._send_custom_message(data, account=account)
  200. def send_mini_program_page(self, user_id, miniprogrampage, account=None):
  201. """发送小程序卡片(要求小程序与公众号已关联)
  202. 详情请参参考
  203. https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547
  204. :param user_id: 用户 ID openid
  205. :param miniprogrampage: 小程序卡片信息
  206. :param account: 可选,客服账号
  207. :return: 返回的 JSON 数据包
  208. """
  209. data = {
  210. 'touser': user_id,
  211. 'msgtype': 'miniprogrampage',
  212. 'miniprogrampage': miniprogrampage
  213. }
  214. return self._send_custom_message(data, account=account)
  215. def delete_mass(self, msg_id):
  216. """
  217. 删除群发消息
  218. 详情请参考
  219. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  220. :param msg_id: 要删除的群发消息 ID
  221. :return: 返回的 JSON 数据包
  222. 使用示例::
  223. from wechatpy import WeChatClient
  224. client = WeChatClient('appid', 'secret')
  225. res = client.message.delete_mass('message id')
  226. """
  227. return self._post(
  228. 'message/mass/delete',
  229. data={
  230. 'msg_id': msg_id
  231. }
  232. )
  233. def _send_mass_message(self, group_or_users, msg_type, msg,
  234. is_to_all=False, preview=False,
  235. send_ignore_reprint=0, client_msg_id=None):
  236. data = {
  237. 'msgtype': msg_type,
  238. 'send_ignore_reprint': send_ignore_reprint,
  239. }
  240. if client_msg_id is not None:
  241. data['clientmsgid'] = client_msg_id
  242. if not preview:
  243. if isinstance(group_or_users, (tuple, list)):
  244. # send by user ids
  245. data['touser'] = group_or_users
  246. endpoint = 'message/mass/send'
  247. else:
  248. # send by group id
  249. data['filter'] = {
  250. 'group_id': group_or_users,
  251. 'is_to_all': is_to_all,
  252. }
  253. endpoint = 'message/mass/sendall'
  254. else:
  255. if not isinstance(group_or_users, six.string_types):
  256. raise ValueError('group_or_users should be string types')
  257. # 预览接口
  258. if self.OPENID_RE.match(group_or_users):
  259. # 按照 openid 预览群发
  260. data['touser'] = group_or_users
  261. else:
  262. # 按照微信号预览群发
  263. data['towxname'] = group_or_users
  264. endpoint = 'message/mass/preview'
  265. data.update(msg)
  266. return self._post(
  267. endpoint,
  268. data=data
  269. )
  270. def send_mass_text(self, group_or_users, content,
  271. is_to_all=False, preview=False,
  272. send_ignore_reprint=0, client_msg_id=None):
  273. """
  274. 群发文本消息
  275. 详情请参考
  276. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  277. :param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
  278. 当 is_to_all 为 True 时,传入 None 即对所有用户发送。
  279. :param content: 消息正文
  280. :param is_to_all: 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户
  281. 选择false可根据group_id发送给指定群组的用户
  282. :type is_to_all: bool
  283. :param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
  284. :type preview: bool
  285. :param send_ignore_reprint: 指定待群发的文章被判定为转载时,是否继续群发。
  286. 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。
  287. 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。
  288. send_ignore_reprint 默认为0。
  289. :type send_ignore_reprint: int
  290. :param client_msg_id: 开发者侧群发 msgid,长度限制 64 字节
  291. :type client_msg_id: str
  292. :return: 返回的 JSON 数据包
  293. """
  294. return self._send_mass_message(
  295. group_or_users,
  296. 'text',
  297. {
  298. 'text': {
  299. 'content': content
  300. }
  301. },
  302. is_to_all,
  303. preview,
  304. send_ignore_reprint,
  305. client_msg_id,
  306. )
  307. def send_mass_image(self, group_or_users, media_id,
  308. is_to_all=False, preview=False,
  309. send_ignore_reprint=0, client_msg_id=None):
  310. """
  311. 群发图片消息
  312. 详情请参考
  313. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  314. :param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
  315. 当 is_to_all 为 True 时,传入 None 即对所有用户发送。
  316. :param media_id: 图片的媒体 ID。 可以通过 :func:`upload_media` 上传。
  317. :param is_to_all: 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户
  318. 选择false可根据group_id发送给指定群组的用户
  319. :type is_to_all: bool
  320. :param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
  321. :type preview: bool
  322. :param send_ignore_reprint: 指定待群发的文章被判定为转载时,是否继续群发。
  323. 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。
  324. 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。
  325. send_ignore_reprint 默认为0。
  326. :type send_ignore_reprint: int
  327. :param client_msg_id: 开发者侧群发 msgid,长度限制 64 字节
  328. :type client_msg_id: str
  329. :return: 返回的 JSON 数据包
  330. """
  331. return self._send_mass_message(
  332. group_or_users,
  333. 'image',
  334. {
  335. 'image': {
  336. 'media_id': media_id
  337. }
  338. },
  339. is_to_all,
  340. preview,
  341. send_ignore_reprint,
  342. client_msg_id,
  343. )
  344. def send_mass_voice(self, group_or_users, media_id,
  345. is_to_all=False, preview=False,
  346. send_ignore_reprint=0, client_msg_id=None):
  347. """
  348. 群发语音消息
  349. 详情请参考
  350. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  351. :param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
  352. 当 is_to_all 为 True 时,传入 None 即对所有用户发送。
  353. :param media_id: 语音的媒体 ID。可以通过 :func:`upload_media` 上传。
  354. :param is_to_all: 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户
  355. 选择false可根据group_id发送给指定群组的用户
  356. :type is_to_all: bool
  357. :param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
  358. :type preview: bool
  359. :param send_ignore_reprint: 指定待群发的文章被判定为转载时,是否继续群发。
  360. 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。
  361. 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。
  362. send_ignore_reprint 默认为0。
  363. :type send_ignore_reprint: int
  364. :param client_msg_id: 开发者侧群发 msgid,长度限制 64 字节
  365. :type client_msg_id: str
  366. :return: 返回的 JSON 数据包
  367. """
  368. return self._send_mass_message(
  369. group_or_users,
  370. 'voice',
  371. {
  372. 'voice': {
  373. 'media_id': media_id
  374. }
  375. },
  376. is_to_all,
  377. preview,
  378. send_ignore_reprint,
  379. client_msg_id,
  380. )
  381. def send_mass_video(self, group_or_users, media_id, title=None,
  382. description=None, is_to_all=False, preview=False,
  383. send_ignore_reprint=0, client_msg_id=None):
  384. """
  385. 群发视频消息
  386. 详情请参考
  387. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  388. :param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
  389. 当 is_to_all 为 True 时,传入 None 即对所有用户发送。
  390. :param media_id: 视频的媒体 ID。可以通过 :func:`upload_video` 上传。
  391. :param title: 视频标题
  392. :param description: 视频描述
  393. :param is_to_all: 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户
  394. 选择false可根据group_id发送给指定群组的用户
  395. :type is_to_all: bool
  396. :param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
  397. :type preview: bool
  398. :param send_ignore_reprint: 指定待群发的文章被判定为转载时,是否继续群发。
  399. 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。
  400. 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。
  401. send_ignore_reprint 默认为0。
  402. :type send_ignore_reprint: int
  403. :param client_msg_id: 开发者侧群发 msgid,长度限制 64 字节
  404. :type client_msg_id: str
  405. :return: 返回的 JSON 数据包
  406. """
  407. video_data = {
  408. 'media_id': media_id
  409. }
  410. if title:
  411. video_data['title'] = title
  412. if description:
  413. video_data['description'] = description
  414. return self._send_mass_message(
  415. group_or_users,
  416. 'mpvideo',
  417. {
  418. 'mpvideo': video_data
  419. },
  420. is_to_all,
  421. preview,
  422. send_ignore_reprint,
  423. client_msg_id,
  424. )
  425. def send_mass_article(self, group_or_users, media_id,
  426. is_to_all=False, preview=False,
  427. send_ignore_reprint=0, client_msg_id=None):
  428. """
  429. 群发图文消息
  430. 详情请参考
  431. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  432. :param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
  433. 当 is_to_all 为 True 时,传入 None 即对所有用户发送。
  434. :param media_id: 图文的媒体 ID。可以通过 :func:`upload_articles` 上传。
  435. :param is_to_all: 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户
  436. 选择false可根据group_id发送给指定群组的用户
  437. :type is_to_all: bool
  438. :param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
  439. :type preview: bool
  440. :param send_ignore_reprint: 指定待群发的文章被判定为转载时,是否继续群发。
  441. 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。
  442. 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。
  443. send_ignore_reprint 默认为0。
  444. :type send_ignore_reprint: int
  445. :param client_msg_id: 开发者侧群发 msgid,长度限制 64 字节
  446. :type client_msg_id: str
  447. :return: 返回的 JSON 数据包
  448. """
  449. return self._send_mass_message(
  450. group_or_users,
  451. 'mpnews',
  452. {
  453. 'mpnews': {
  454. 'media_id': media_id
  455. }
  456. },
  457. is_to_all,
  458. preview,
  459. send_ignore_reprint,
  460. client_msg_id,
  461. )
  462. def get_mass(self, msg_id):
  463. """
  464. 查询群发消息发送状态
  465. 详情请参考
  466. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  467. :param msg_id: 群发消息后返回的消息id
  468. :return: 返回的 JSON 数据包
  469. 使用示例::
  470. from wechatpy import WeChatClient
  471. client = WeChatClient('appid', 'secret')
  472. res = client.message.get_mass('mass message id')
  473. """
  474. return self._post(
  475. 'message/mass/get',
  476. data={
  477. 'msg_id': msg_id
  478. }
  479. )
  480. def send_template(self, user_id, template_id, data, url=None, mini_program=None):
  481. """
  482. 发送模板消息
  483. 详情请参考
  484. https://mp.weixin.qq.com/wiki?id=mp1445241432&lang=zh_CN
  485. :param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
  486. :param template_id: 模板 ID。在公众平台线上模板库中选用模板获得
  487. :param url: 链接地址
  488. :param data: 模板消息数据
  489. :param mini_program: 跳小程序所需数据, 如:`{'appid': 'appid', 'pagepath': 'index?foo=bar'}`
  490. :return: 返回的 JSON 数据包
  491. """
  492. tpl_data = optionaldict(
  493. touser = user_id,
  494. template_id = template_id,
  495. url = url,
  496. miniprogram = mini_program,
  497. data = data,
  498. )
  499. return self._post(
  500. 'message/template/send',
  501. data = tpl_data
  502. )
  503. def get_autoreply_info(self):
  504. """
  505. 获取自动回复规则
  506. 详情请参考
  507. http://mp.weixin.qq.com/wiki/7/7b5789bb1262fb866d01b4b40b0efecb.html
  508. :return: 返回的 JSON 数据包
  509. 使用示例::
  510. from wechatpy import WeChatClient
  511. client = WeChatClient('appid', 'secret')
  512. info = client.message.get_autoreply_info()
  513. """
  514. return self._get('get_current_autoreply_info')
  515. def send_mass_card(self, group_or_users, card_id,
  516. is_to_all=False, preview=False,
  517. send_ignore_reprint=0, client_msg_id=None):
  518. """
  519. 群发卡券消息
  520. 详情请参考
  521. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  522. :param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
  523. 当 is_to_all 为 True 时,传入 None 即对所有用户发送。
  524. :param card_id: 卡券 ID
  525. :param is_to_all: 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户
  526. 选择false可根据group_id发送给指定群组的用户
  527. :type is_to_all: bool
  528. :param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
  529. :type preview: bool
  530. :param send_ignore_reprint: 指定待群发的文章被判定为转载时,是否继续群发。
  531. 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。
  532. 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。
  533. send_ignore_reprint 默认为0。
  534. :type send_ignore_reprint: int
  535. :param client_msg_id: 开发者侧群发 msgid,长度限制 64 字节
  536. :type client_msg_id: str
  537. :return: 返回的 JSON 数据包
  538. """
  539. return self._send_mass_message(
  540. group_or_users,
  541. 'wxcard',
  542. {
  543. 'wxcard': {
  544. 'card_id': card_id
  545. }
  546. },
  547. is_to_all,
  548. preview,
  549. send_ignore_reprint,
  550. client_msg_id,
  551. )
  552. def get_subscribe_authorize_url(self, scene, template_id, redirect_url, reserved=None):
  553. """
  554. 构造请求用户授权的url
  555. 详情请参阅:
  556. https://mp.weixin.qq.com/wiki?id=mp1500374289_66bvB
  557. :param scene: 订阅场景值,开发者可以填0-10000的整形值,用来标识订阅场景值
  558. :type scene: int
  559. :param template_id: 订阅消息模板ID,登录公众平台后台,在接口权限列表处可查看订阅模板ID
  560. :param redirect_url: 授权后重定向的回调地址
  561. :param reserved: 用于保持请求和回调的状态,授权请后原样带回给第三方。该参数可用于防止csrf攻击。若不指定则随机生成。
  562. """
  563. if reserved is None:
  564. reserved = random_string()
  565. base_url = 'https://mp.weixin.qq.com/mp/subscribemsg'
  566. params = [
  567. ('action', 'get_confirm'),
  568. ('appid', self.appid),
  569. ('scene', scene),
  570. ('template_id', template_id),
  571. ('redirect_url', redirect_url),
  572. ('reserved', reserved),
  573. ]
  574. from six.moves.urllib import parse
  575. encoded_params = parse.urlencode(params)
  576. url = '{base}?{params}#wechat_redirect'.format(base=base_url, params=encoded_params)
  577. return url
  578. def send_subscribe_template(self, openid, template_id, scene, title, data, url=None):
  579. """
  580. 一次性订阅消息,通过API推送订阅模板消息给到授权微信用户。
  581. 详情请参阅:
  582. https://mp.weixin.qq.com/wiki?id=mp1500374289_66bvB
  583. :param openid: 填接收消息的用户openid
  584. :param template_id: 订阅消息模板ID
  585. :param scene: 订阅场景值,开发者可以填0-10000的整形值,用来标识订阅场景值
  586. :type scene: int
  587. :param title: 消息标题,15字以内
  588. :param data: 消息正文,value为消息内容,color为颜色,200字以内
  589. :type data: dict
  590. :param url: 点击消息跳转的链接,需要有ICP备案
  591. """
  592. post_data = {
  593. 'touser': openid,
  594. 'template_id': template_id,
  595. 'url': url,
  596. 'scene': scene,
  597. 'title': title,
  598. 'data': data,
  599. }
  600. if url is not None:
  601. post_data['url'] = url
  602. return self._post(
  603. 'message/template/subscribe',
  604. data=post_data,
  605. )
  606. def send_subscribe_bizsend(self, temp):
  607. """
  608. 推送用户订阅的模板固定模板
  609. """
  610. self._post('message/subscribe/bizsend', data=temp)