Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

wechatmp_client.py 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import time
  2. import json
  3. import requests
  4. import threading
  5. from channel.wechatmp.common import *
  6. from common.log import logger
  7. from config import conf
  8. class WechatMPClient:
  9. def __init__(self):
  10. self.app_id = conf().get("wechatmp_app_id")
  11. self.app_secret = conf().get("wechatmp_app_secret")
  12. self.access_token = None
  13. self.access_token_expires_time = 0
  14. self.access_token_lock = threading.Lock()
  15. self.get_access_token()
  16. def wechatmp_request(self, method, url, **kwargs):
  17. r = requests.request(method=method, url=url, **kwargs)
  18. r.raise_for_status()
  19. r.encoding = "utf-8"
  20. ret = r.json()
  21. if "errcode" in ret and ret["errcode"] != 0:
  22. if ret["errcode"] == 45009:
  23. self.clear_quota_v2()
  24. raise WeChatAPIException("{}".format(ret))
  25. return ret
  26. def get_access_token(self):
  27. # return the access_token
  28. if self.access_token:
  29. if self.access_token_expires_time - time.time() > 60:
  30. return self.access_token
  31. # Get new access_token
  32. # Do not request access_token in parallel! Only the last obtained is valid.
  33. if self.access_token_lock.acquire(blocking=False):
  34. # Wait for other threads that have previously obtained access_token to complete the request
  35. # This happens every 2 hours, so it doesn't affect the experience very much
  36. time.sleep(1)
  37. self.access_token = None
  38. url = "https://api.weixin.qq.com/cgi-bin/token"
  39. params = {
  40. "grant_type": "client_credential",
  41. "appid": self.app_id,
  42. "secret": self.app_secret,
  43. }
  44. ret = self.wechatmp_request(method="get", url=url, params=params)
  45. self.access_token = ret["access_token"]
  46. self.access_token_expires_time = int(time.time()) + ret["expires_in"]
  47. logger.info("[wechatmp] access_token: {}".format(self.access_token))
  48. self.access_token_lock.release()
  49. else:
  50. # Wait for token update
  51. while self.access_token_lock.locked():
  52. time.sleep(0.1)
  53. return self.access_token
  54. def send_text(self, receiver, reply_text):
  55. url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
  56. params = {"access_token": self.get_access_token()}
  57. json_data = {
  58. "touser": receiver,
  59. "msgtype": "text",
  60. "text": {"content": reply_text},
  61. }
  62. self.wechatmp_request(
  63. method="post",
  64. url=url,
  65. params=params,
  66. data=json.dumps(json_data, ensure_ascii=False).encode("utf8"),
  67. )
  68. def send_voice(self, receiver, media_id):
  69. url="https://api.weixin.qq.com/cgi-bin/message/custom/send"
  70. params = {"access_token": self.get_access_token()}
  71. json_data = {
  72. "touser": receiver,
  73. "msgtype": "voice",
  74. "voice": {
  75. "media_id": media_id
  76. }
  77. }
  78. self.wechatmp_request(
  79. method="post",
  80. url=url,
  81. params=params,
  82. data=json.dumps(json_data, ensure_ascii=False).encode("utf8"),
  83. )
  84. def send_image(self, receiver, media_id):
  85. url="https://api.weixin.qq.com/cgi-bin/message/custom/send"
  86. params = {"access_token": self.get_access_token()}
  87. json_data = {
  88. "touser": receiver,
  89. "msgtype": "image",
  90. "image": {
  91. "media_id": media_id
  92. }
  93. }
  94. self.wechatmp_request(
  95. method="post",
  96. url=url,
  97. params=params,
  98. data=json.dumps(json_data, ensure_ascii=False).encode("utf8"),
  99. )
  100. def upload_media(self, media_type, media_file):
  101. url="https://api.weixin.qq.com/cgi-bin/media/upload"
  102. params={
  103. "access_token": self.get_access_token(),
  104. "type": media_type
  105. }
  106. files={"media": media_file}
  107. ret = self.wechatmp_request(
  108. method="post",
  109. url=url,
  110. params=params,
  111. files=files
  112. )
  113. logger.debug("[wechatmp] media {} uploaded".format(media_file))
  114. return ret["media_id"]
  115. def upload_permanent_media(self, media_type, media_file):
  116. url="https://api.weixin.qq.com/cgi-bin/material/add_material"
  117. params={
  118. "access_token": self.get_access_token(),
  119. "type": media_type
  120. }
  121. files={"media": media_file}
  122. ret = self.wechatmp_request(
  123. method="post",
  124. url=url,
  125. params=params,
  126. files=files
  127. )
  128. logger.debug("[wechatmp] permanent media {} uploaded".format(media_file))
  129. return ret["media_id"]
  130. def delete_permanent_media(self, media_id):
  131. url="https://api.weixin.qq.com/cgi-bin/material/del_material"
  132. params={
  133. "access_token": self.get_access_token()
  134. }
  135. self.wechatmp_request(
  136. method="post",
  137. url=url,
  138. params=params,
  139. data=json.dumps({"media_id": media_id}, ensure_ascii=False).encode("utf8")
  140. )
  141. logger.debug("[wechatmp] permanent media {} deleted".format(media_id))
  142. def clear_quota(self):
  143. url="https://api.weixin.qq.com/cgi-bin/clear_quota"
  144. params = {
  145. "access_token": self.get_access_token()
  146. }
  147. self.wechatmp_request(
  148. method="post",
  149. url=url,
  150. params=params,
  151. data={"appid": self.app_id}
  152. )
  153. logger.debug("[wechatmp] API quata has been cleard")
  154. def clear_quota_v2(self):
  155. url="https://api.weixin.qq.com/cgi-bin/clear_quota/v2"
  156. self.wechatmp_request(
  157. method="post",
  158. url=url,
  159. data={"appid": self.app_id, "appsecret": self.app_secret}
  160. )
  161. logger.debug("[wechatmp] API quata has been cleard")