message.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, unicode_literals
  3. from optionaldict import optionaldict
  4. from wechatpy.client.api.base import BaseWeChatAPI
  5. class WeChatMessage(BaseWeChatAPI):
  6. """
  7. 发送应用消息
  8. https://work.weixin.qq.com/api/doc#90000/90135/90236
  9. 支持:
  10. * 文本消息
  11. * 图片消息
  12. * 语音消息
  13. * 视频消息
  14. * 文件消息
  15. * 文本卡片消息
  16. * 图文消息
  17. * 图文消息(mpnews)
  18. * markdown消息
  19. * 小程序通知消息
  20. """
  21. def send(self, agent_id, user_ids, party_ids='',
  22. tag_ids='', msg=None):
  23. """
  24. 通用的消息发送接口。msg 内需要指定 msgtype 和对应类型消息必须的字段。
  25. 如果部分接收人无权限或不存在,发送仍然执行,但会返回无效的部分(即invaliduser或invalidparty或invalidtag),常见的原因是接收人不在应用的可见范围内。
  26. user_ids、party_ids、tag_ids 不能同时为空,后面不再强调。
  27. :param agent_id: 必填,企业应用的id,整型。可在应用的设置页面查看。
  28. :param user_ids: 成员ID列表。
  29. :param party_ids: 部门ID列表。
  30. :param tag_ids: 标签ID列表。
  31. :param msg: 发送消息的 dict 对象
  32. :type msg: dict | None
  33. :return: 接口调用结果
  34. """
  35. msg = msg or {}
  36. if isinstance(user_ids, (tuple, list)):
  37. user_ids = '|'.join(user_ids)
  38. if isinstance(party_ids, (tuple, list)):
  39. party_ids = '|'.join(party_ids)
  40. if isinstance(tag_ids, (tuple, list)):
  41. tag_ids = '|'.join(tag_ids)
  42. data = {
  43. 'touser': user_ids,
  44. 'toparty': party_ids,
  45. 'totag': tag_ids,
  46. 'agentid': agent_id
  47. }
  48. data.update(msg)
  49. return self._post('message/send', data=data)
  50. def send_text(self, agent_id, user_ids, content,
  51. party_ids='', tag_ids='', safe=0):
  52. return self.send(
  53. agent_id,
  54. user_ids,
  55. party_ids,
  56. tag_ids,
  57. msg={
  58. 'msgtype': 'text',
  59. 'text': {'content': content},
  60. 'safe': safe
  61. }
  62. )
  63. def send_text_card(self, agent_id, user_ids, title, description, url, btntxt='详情',
  64. party_ids='', tag_ids=''):
  65. """ 文本卡片消息
  66. https://work.weixin.qq.com/api/doc#90000/90135/90236/文本卡片消息
  67. 请求示例:
  68. {
  69. "touser" : "UserID1|UserID2|UserID3",
  70. "toparty" : "PartyID1 | PartyID2",
  71. "totag" : "TagID1 | TagID2",
  72. "msgtype" : "textcard",
  73. "agentid" : 1,
  74. "textcard" : {
  75. "title" : "领奖通知",
  76. "description" : "<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台,
  77. 领奖码:xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>",
  78. "url" : "URL",
  79. "btntxt":"更多"
  80. }
  81. }
  82. 特殊说明:
  83. 卡片消息的展现形式非常灵活,支持使用br标签或者空格来进行换行处理,也支持使用div标签来使用不同的字体颜色,
  84. 目前内置了3种文字颜色:灰色(gray)、高亮(highlight)、默认黑色(normal),将其作为div标签的class属性即可,
  85. 具体用法请参考上面的示例。
  86. :param agent_id: 必填,企业应用的id,整型。可在应用的设置页面查看。
  87. :param user_ids: 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。
  88. :param title: 标题,不超过128个字节,超过会自动截断。
  89. :param description: 必填,描述,不超过512个字节,超过会自动截断
  90. :param url: 必填,点击后跳转的链接。
  91. :param btntxt: 按钮文字。 默认为“详情”, 不超过4个文字,超过自动截断。
  92. :param party_ids: 部门ID列表。
  93. :param tag_ids: 标签ID列表。
  94. """
  95. return self.send(
  96. agent_id,
  97. user_ids,
  98. party_ids,
  99. tag_ids,
  100. msg={
  101. 'msgtype': 'textcard',
  102. 'textcard': {
  103. 'title': title,
  104. 'description': description,
  105. 'url': url,
  106. 'btntxt': btntxt,
  107. },
  108. }
  109. )
  110. def send_image(self, agent_id, user_ids, media_id,
  111. party_ids='', tag_ids='', safe=0):
  112. return self.send(
  113. agent_id,
  114. user_ids,
  115. party_ids,
  116. tag_ids,
  117. msg={
  118. 'msgtype': 'image',
  119. 'image': {
  120. 'media_id': media_id
  121. },
  122. 'safe': safe
  123. }
  124. )
  125. def send_voice(self, agent_id, user_ids, media_id,
  126. party_ids='', tag_ids='', safe=0):
  127. return self.send(
  128. agent_id,
  129. user_ids,
  130. party_ids,
  131. tag_ids,
  132. msg={
  133. 'msgtype': 'voice',
  134. 'voice': {
  135. 'media_id': media_id
  136. },
  137. 'safe': safe
  138. }
  139. )
  140. def send_video(self, agent_id, user_ids, media_id, title=None,
  141. description=None, party_ids='', tag_ids='', safe=0):
  142. video_data = optionaldict()
  143. video_data['media_id'] = media_id
  144. video_data['title'] = title
  145. video_data['description'] = description
  146. return self.send(
  147. agent_id,
  148. user_ids,
  149. party_ids,
  150. tag_ids,
  151. msg={
  152. 'msgtype': 'video',
  153. 'video': dict(video_data),
  154. 'safe': safe
  155. }
  156. )
  157. def send_file(self, agent_id, user_ids, media_id,
  158. party_ids='', tag_ids='', safe=0):
  159. return self.send(
  160. agent_id,
  161. user_ids,
  162. party_ids,
  163. tag_ids,
  164. msg={
  165. 'msgtype': 'file',
  166. 'file': {
  167. 'media_id': media_id
  168. },
  169. 'safe': safe
  170. }
  171. )
  172. def send_articles(self, agent_id, user_ids, articles,
  173. party_ids='', tag_ids=''):
  174. articles_data = []
  175. for article in articles:
  176. articles_data.append({
  177. 'title': article['title'],
  178. 'description': article['description'],
  179. 'url': article['url'],
  180. 'picurl': article['image']
  181. })
  182. return self.send(
  183. agent_id,
  184. user_ids,
  185. party_ids,
  186. tag_ids,
  187. msg={
  188. 'msgtype': 'news',
  189. 'news': {
  190. 'articles': articles_data
  191. }
  192. }
  193. )
  194. def send_mp_articles(self, agent_id, user_ids, articles,
  195. party_ids='', tag_ids='', safe=0):
  196. articles_data = []
  197. for article in articles:
  198. articles_data.append({
  199. 'thumb_media_id': article['thumb_media_id'],
  200. 'author': article['author'],
  201. 'title': article['title'],
  202. 'content': article['content'],
  203. 'content_source_url': article['content_source_url'],
  204. 'digest': article['digest'],
  205. 'show_cover_pic': article['show_cover_pic']
  206. })
  207. return self.send(
  208. agent_id,
  209. user_ids,
  210. party_ids,
  211. tag_ids,
  212. msg={
  213. 'msgtype': 'mpnews',
  214. 'mpnews': {
  215. 'articles': articles_data
  216. },
  217. 'safe': safe
  218. }
  219. )
  220. def send_markdown(self, agent_id, user_ids, content, party_ids='', tag_ids=''):
  221. """markdown消息
  222. https://work.weixin.qq.com/api/doc#90000/90135/90236/markdown%E6%B6%88%E6%81%AF
  223. > 目前仅支持markdown语法的子集
  224. > 微工作台(原企业号)不支持展示markdown消息
  225. :param agent_id: 企业应用的id,整型。可在应用的设置页面查看
  226. :type agent_id: string
  227. :param content: markdown内容,最长不超过2048个字节,必须是utf8编码
  228. :type content: string
  229. :param user_ids: 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。
  230. 特殊情况:指定为@all,则向关注该企业应用的全部成员发送
  231. :type user_ids: list or tuple or string
  232. :param party_ids: 部门ID列表,最多支持100个。当touser为@all时忽略本参数
  233. :type party_ids: list or tuple or string
  234. :param tag_ids: 标签ID列表,最多支持100个。当touser为@all时忽略本参数
  235. :type tag_ids: list or tuple or string
  236. :return: 接口调用结果
  237. :rtype: dict
  238. """
  239. msg = {
  240. "msgtype": "markdown",
  241. "markdown": {"content": content}
  242. }
  243. return self.send(
  244. agent_id,
  245. user_ids,
  246. party_ids,
  247. tag_ids,
  248. msg=msg
  249. )