Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

wechatcomapp_channel.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # -*- coding=utf-8 -*-
  2. import io
  3. import os
  4. import time
  5. import requests
  6. import web
  7. from wechatpy.enterprise import create_reply, parse_message
  8. from wechatpy.enterprise.crypto import WeChatCrypto
  9. from wechatpy.enterprise.exceptions import InvalidCorpIdException
  10. from wechatpy.exceptions import InvalidSignatureException, WeChatClientException
  11. from bridge.context import Context
  12. from bridge.reply import Reply, ReplyType
  13. from channel.chat_channel import ChatChannel
  14. from channel.wechatcom.wechatcomapp_client import WechatComAppClient
  15. from channel.wechatcom.wechatcomapp_message import WechatComAppMessage
  16. from common.log import logger
  17. from common.singleton import singleton
  18. from common.utils import compress_imgfile, fsize, split_string_by_utf8_length
  19. from config import conf, subscribe_msg
  20. from voice.audio_convert import any_to_amr
  21. MAX_UTF8_LEN = 2048
  22. @singleton
  23. class WechatComAppChannel(ChatChannel):
  24. NOT_SUPPORT_REPLYTYPE = []
  25. def __init__(self):
  26. super().__init__()
  27. self.corp_id = conf().get("wechatcom_corp_id")
  28. self.secret = conf().get("wechatcomapp_secret")
  29. self.agent_id = conf().get("wechatcomapp_agent_id")
  30. self.token = conf().get("wechatcomapp_token")
  31. self.aes_key = conf().get("wechatcomapp_aes_key")
  32. print(self.corp_id, self.secret, self.agent_id, self.token, self.aes_key)
  33. logger.info(
  34. "[wechatcom] init: corp_id: {}, secret: {}, agent_id: {}, token: {}, aes_key: {}".format(self.corp_id, self.secret, self.agent_id, self.token, self.aes_key)
  35. )
  36. self.crypto = WeChatCrypto(self.token, self.aes_key, self.corp_id)
  37. self.client = WechatComAppClient(self.corp_id, self.secret)
  38. def startup(self):
  39. # start message listener
  40. urls = ("/wxcomapp", "channel.wechatcom.wechatcomapp_channel.Query")
  41. app = web.application(urls, globals(), autoreload=False)
  42. port = conf().get("wechatcomapp_port", 9898)
  43. web.httpserver.runsimple(app.wsgifunc(), ("0.0.0.0", port))
  44. def send(self, reply: Reply, context: Context):
  45. receiver = context["receiver"]
  46. if reply.type in [ReplyType.TEXT, ReplyType.ERROR, ReplyType.INFO]:
  47. reply_text = reply.content
  48. texts = split_string_by_utf8_length(reply_text, MAX_UTF8_LEN)
  49. if len(texts) > 1:
  50. logger.info("[wechatcom] text too long, split into {} parts".format(len(texts)))
  51. for i, text in enumerate(texts):
  52. self.client.message.send_text(self.agent_id, receiver, text)
  53. if i != len(texts) - 1:
  54. time.sleep(0.5) # 休眠0.5秒,防止发送过快乱序
  55. logger.info("[wechatcom] Do send text to {}: {}".format(receiver, reply_text))
  56. elif reply.type == ReplyType.VOICE:
  57. try:
  58. file_path = reply.content
  59. amr_file = os.path.splitext(file_path)[0] + ".amr"
  60. any_to_amr(file_path, amr_file)
  61. response = self.client.media.upload("voice", open(amr_file, "rb"))
  62. logger.debug("[wechatcom] upload voice response: {}".format(response))
  63. except WeChatClientException as e:
  64. logger.error("[wechatcom] upload voice failed: {}".format(e))
  65. return
  66. try:
  67. os.remove(file_path)
  68. if amr_file != file_path:
  69. os.remove(amr_file)
  70. except Exception:
  71. pass
  72. self.client.message.send_voice(self.agent_id, receiver, response["media_id"])
  73. logger.info("[wechatcom] sendVoice={}, receiver={}".format(reply.content, receiver))
  74. elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片
  75. img_url = reply.content
  76. pic_res = requests.get(img_url, stream=True)
  77. image_storage = io.BytesIO()
  78. for block in pic_res.iter_content(1024):
  79. image_storage.write(block)
  80. if (sz := fsize(image_storage)) >= 10 * 1024 * 1024:
  81. logger.info("[wechatcom] image too large, ready to compress, sz={}".format(sz))
  82. image_storage = compress_imgfile(image_storage, 10 * 1024 * 1024 - 1)
  83. logger.info("[wechatcom] image compressed, sz={}".format(fsize(image_storage)))
  84. image_storage.seek(0)
  85. try:
  86. response = self.client.media.upload("image", image_storage)
  87. logger.debug("[wechatcom] upload image response: {}".format(response))
  88. except WeChatClientException as e:
  89. logger.error("[wechatcom] upload image failed: {}".format(e))
  90. return
  91. self.client.message.send_image(self.agent_id, receiver, response["media_id"])
  92. logger.info("[wechatcom] sendImage url={}, receiver={}".format(img_url, receiver))
  93. elif reply.type == ReplyType.IMAGE: # 从文件读取图片
  94. image_storage = reply.content
  95. if (sz := fsize(image_storage)) >= 10 * 1024 * 1024:
  96. logger.info("[wechatcom] image too large, ready to compress, sz={}".format(sz))
  97. image_storage = compress_imgfile(image_storage, 10 * 1024 * 1024 - 1)
  98. logger.info("[wechatcom] image compressed, sz={}".format(fsize(image_storage)))
  99. image_storage.seek(0)
  100. try:
  101. response = self.client.media.upload("image", image_storage)
  102. logger.debug("[wechatcom] upload image response: {}".format(response))
  103. except WeChatClientException as e:
  104. logger.error("[wechatcom] upload image failed: {}".format(e))
  105. return
  106. self.client.message.send_image(self.agent_id, receiver, response["media_id"])
  107. logger.info("[wechatcom] sendImage, receiver={}".format(receiver))
  108. class Query:
  109. def GET(self):
  110. channel = WechatComAppChannel()
  111. params = web.input()
  112. logger.info("[wechatcom] receive params: {}".format(params))
  113. try:
  114. signature = params.msg_signature
  115. timestamp = params.timestamp
  116. nonce = params.nonce
  117. echostr = params.echostr
  118. echostr = channel.crypto.check_signature(signature, timestamp, nonce, echostr)
  119. except InvalidSignatureException:
  120. raise web.Forbidden()
  121. return echostr
  122. def POST(self):
  123. channel = WechatComAppChannel()
  124. params = web.input()
  125. logger.info("[wechatcom] receive params: {}".format(params))
  126. try:
  127. signature = params.msg_signature
  128. timestamp = params.timestamp
  129. nonce = params.nonce
  130. message = channel.crypto.decrypt_message(web.data(), signature, timestamp, nonce)
  131. except (InvalidSignatureException, InvalidCorpIdException):
  132. raise web.Forbidden()
  133. msg = parse_message(message)
  134. logger.debug("[wechatcom] receive message: {}, msg= {}".format(message, msg))
  135. if msg.type == "event":
  136. if msg.event == "subscribe":
  137. reply_content = subscribe_msg()
  138. if reply_content:
  139. reply = create_reply(reply_content, msg).render()
  140. res = channel.crypto.encrypt_message(reply, nonce, timestamp)
  141. return res
  142. else:
  143. try:
  144. wechatcom_msg = WechatComAppMessage(msg, client=channel.client)
  145. except NotImplementedError as e:
  146. logger.debug("[wechatcom] " + str(e))
  147. return "success"
  148. context = channel._compose_context(
  149. wechatcom_msg.ctype,
  150. wechatcom_msg.content,
  151. isgroup=False,
  152. msg=wechatcom_msg,
  153. )
  154. if context:
  155. channel.produce(context)
  156. return "success"