Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

117 rindas
4.3KB

  1. # -*- coding: utf-8 -*-
  2. import sys
  3. import web
  4. import math
  5. import time
  6. import json
  7. import requests
  8. import threading
  9. from common.singleton import singleton
  10. from common.log import logger
  11. from config import conf
  12. from bridge.reply import *
  13. from bridge.context import *
  14. from channel.chat_channel import ChatChannel
  15. from channel.wechatmp.common import *
  16. # If using SSL, uncomment the following lines, and modify the certificate path.
  17. # from cheroot.server import HTTPServer
  18. # from cheroot.ssl.builtin import BuiltinSSLAdapter
  19. # HTTPServer.ssl_adapter = BuiltinSSLAdapter(
  20. # certificate='/ssl/cert.pem',
  21. # private_key='/ssl/cert.key')
  22. @singleton
  23. class WechatMPChannel(ChatChannel):
  24. def __init__(self, passive_reply = True):
  25. super().__init__()
  26. self.passive_reply = passive_reply
  27. if self.passive_reply:
  28. self.cache_dict = dict()
  29. self.query1 = dict()
  30. self.query2 = dict()
  31. self.query3 = dict()
  32. else:
  33. self.app_id = conf().get('wechatmp_app_id')
  34. self.app_secret = conf().get('wechatmp_app_secret')
  35. self.access_token = None
  36. self.access_token_expires_time = 0
  37. self.access_token_lock = threading.Lock()
  38. self.get_access_token()
  39. def startup(self):
  40. if self.passive_reply:
  41. urls = ('/wx', 'channel.wechatmp.SubscribeAccount.Query')
  42. else:
  43. urls = ('/wx', 'channel.wechatmp.ServiceAccount.Query')
  44. app = web.application(urls, globals())
  45. app.run()
  46. def wechatmp_request(self, method, url, **kwargs):
  47. r = requests.request(method=method, url=url, **kwargs)
  48. r.raise_for_status()
  49. r.encoding = "utf-8"
  50. ret = r.json()
  51. if "errcode" in ret and ret["errcode"] != 0:
  52. raise WeChatAPIException("{}".format(ret))
  53. return ret
  54. def get_access_token(self):
  55. # return the access_token
  56. if self.access_token:
  57. if self.access_token_expires_time - time.time() > 60:
  58. return self.access_token
  59. # Get new access_token
  60. # Do not request access_token in parallel! Only the last obtained is valid.
  61. if self.access_token_lock.acquire(blocking=False):
  62. # Wait for other threads that have previously obtained access_token to complete the request
  63. # This happens every 2 hours, so it doesn't affect the experience very much
  64. time.sleep(1)
  65. self.access_token = None
  66. url="https://api.weixin.qq.com/cgi-bin/token"
  67. params={
  68. "grant_type": "client_credential",
  69. "appid": self.app_id,
  70. "secret": self.app_secret
  71. }
  72. data = self.wechatmp_request(method='get', url=url, params=params)
  73. self.access_token = data['access_token']
  74. self.access_token_expires_time = int(time.time()) + data['expires_in']
  75. logger.info("[wechatmp] access_token: {}".format(self.access_token))
  76. self.access_token_lock.release()
  77. else:
  78. # Wait for token update
  79. while self.access_token_lock.locked():
  80. time.sleep(0.1)
  81. return self.access_token
  82. def send(self, reply: Reply, context: Context):
  83. if self.passive_reply:
  84. receiver = context["receiver"]
  85. reply_text = reply.content
  86. reply_cnt = math.ceil(len(reply_text) / 600)
  87. self.cache_dict[receiver] = (reply_cnt, reply_text)
  88. logger.debug("[send] reply to {} saved to cache: {}".format(receiver, reply_text))
  89. else:
  90. receiver = context["receiver"]
  91. reply_text = reply.content
  92. url="https://api.weixin.qq.com/cgi-bin/message/custom/send"
  93. params = {
  94. "access_token": self.get_access_token()
  95. }
  96. json_data = {
  97. "touser": receiver,
  98. "msgtype": "text",
  99. "text": {"content": reply_text}
  100. }
  101. self.wechatmp_request(method='post', url=url, params=params, data=json.dumps(json_data, ensure_ascii=False).encode('utf8'))
  102. logger.info("[send] Do send to {}: {}".format(receiver, reply_text))
  103. return
  104. # Last import to avoid circular import
  105. import channel.wechatmp.SubscribeAccount
  106. import channel.wechatmp.ServiceAccount