You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
8.1KB

  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, split_audio
  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. media_ids = []
  59. file_path = reply.content
  60. amr_file = os.path.splitext(file_path)[0] + ".amr"
  61. any_to_amr(file_path, amr_file)
  62. duration, files = split_audio(amr_file, 60 * 1000)
  63. if len(files) > 1:
  64. logger.info("[wechatcom] voice too long {}s > 60s , split into {} parts".format(duration / 1000.0, len(files)))
  65. for path in files:
  66. response = self.client.media.upload("voice", open(path, "rb"))
  67. logger.debug("[wechatcom] upload voice response: {}".format(response))
  68. media_ids.append(response["media_id"])
  69. except WeChatClientException as e:
  70. logger.error("[wechatcom] upload voice failed: {}".format(e))
  71. return
  72. try:
  73. os.remove(file_path)
  74. if amr_file != file_path:
  75. os.remove(amr_file)
  76. except Exception:
  77. pass
  78. for media_id in media_ids:
  79. self.client.message.send_voice(self.agent_id, receiver, media_id)
  80. time.sleep(1)
  81. logger.info("[wechatcom] sendVoice={}, receiver={}".format(reply.content, receiver))
  82. elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片
  83. img_url = reply.content
  84. pic_res = requests.get(img_url, stream=True)
  85. image_storage = io.BytesIO()
  86. for block in pic_res.iter_content(1024):
  87. image_storage.write(block)
  88. if (sz := fsize(image_storage)) >= 10 * 1024 * 1024:
  89. logger.info("[wechatcom] image too large, ready to compress, sz={}".format(sz))
  90. image_storage = compress_imgfile(image_storage, 10 * 1024 * 1024 - 1)
  91. logger.info("[wechatcom] image compressed, sz={}".format(fsize(image_storage)))
  92. image_storage.seek(0)
  93. try:
  94. response = self.client.media.upload("image", image_storage)
  95. logger.debug("[wechatcom] upload image response: {}".format(response))
  96. except WeChatClientException as e:
  97. logger.error("[wechatcom] upload image failed: {}".format(e))
  98. return
  99. self.client.message.send_image(self.agent_id, receiver, response["media_id"])
  100. logger.info("[wechatcom] sendImage url={}, receiver={}".format(img_url, receiver))
  101. elif reply.type == ReplyType.IMAGE: # 从文件读取图片
  102. image_storage = reply.content
  103. if (sz := fsize(image_storage)) >= 10 * 1024 * 1024:
  104. logger.info("[wechatcom] image too large, ready to compress, sz={}".format(sz))
  105. image_storage = compress_imgfile(image_storage, 10 * 1024 * 1024 - 1)
  106. logger.info("[wechatcom] image compressed, sz={}".format(fsize(image_storage)))
  107. image_storage.seek(0)
  108. try:
  109. response = self.client.media.upload("image", image_storage)
  110. logger.debug("[wechatcom] upload image response: {}".format(response))
  111. except WeChatClientException as e:
  112. logger.error("[wechatcom] upload image failed: {}".format(e))
  113. return
  114. self.client.message.send_image(self.agent_id, receiver, response["media_id"])
  115. logger.info("[wechatcom] sendImage, receiver={}".format(receiver))
  116. class Query:
  117. def GET(self):
  118. channel = WechatComAppChannel()
  119. params = web.input()
  120. logger.info("[wechatcom] receive params: {}".format(params))
  121. try:
  122. signature = params.msg_signature
  123. timestamp = params.timestamp
  124. nonce = params.nonce
  125. echostr = params.echostr
  126. echostr = channel.crypto.check_signature(signature, timestamp, nonce, echostr)
  127. except InvalidSignatureException:
  128. raise web.Forbidden()
  129. return echostr
  130. def POST(self):
  131. channel = WechatComAppChannel()
  132. params = web.input()
  133. logger.info("[wechatcom] receive params: {}".format(params))
  134. try:
  135. signature = params.msg_signature
  136. timestamp = params.timestamp
  137. nonce = params.nonce
  138. message = channel.crypto.decrypt_message(web.data(), signature, timestamp, nonce)
  139. except (InvalidSignatureException, InvalidCorpIdException):
  140. raise web.Forbidden()
  141. msg = parse_message(message)
  142. logger.debug("[wechatcom] receive message: {}, msg= {}".format(message, msg))
  143. if msg.type == "event":
  144. if msg.event == "subscribe":
  145. reply_content = subscribe_msg()
  146. if reply_content:
  147. reply = create_reply(reply_content, msg).render()
  148. res = channel.crypto.encrypt_message(reply, nonce, timestamp)
  149. return res
  150. else:
  151. try:
  152. wechatcom_msg = WechatComAppMessage(msg, client=channel.client)
  153. except NotImplementedError as e:
  154. logger.debug("[wechatcom] " + str(e))
  155. return "success"
  156. context = channel._compose_context(
  157. wechatcom_msg.ctype,
  158. wechatcom_msg.content,
  159. isgroup=False,
  160. msg=wechatcom_msg,
  161. )
  162. if context:
  163. channel.produce(context)
  164. return "success"