message.py 26 KB

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