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.

192 linhas
7.9KB

  1. #!/usr/bin/env python
  2. # -*- coding=utf-8 -*-
  3. import io
  4. import os
  5. import textwrap
  6. import requests
  7. import web
  8. from wechatpy.enterprise import WeChatClient, create_reply, parse_message
  9. from wechatpy.enterprise.crypto import WeChatCrypto
  10. from wechatpy.enterprise.exceptions import InvalidCorpIdException
  11. from wechatpy.exceptions import InvalidSignatureException, WeChatClientException
  12. from bridge.context import Context
  13. from bridge.reply import Reply, ReplyType
  14. from channel.chat_channel import ChatChannel
  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
  19. from config import conf
  20. from voice.audio_convert import any_to_amr
  21. @singleton
  22. class WechatComAppChannel(ChatChannel):
  23. NOT_SUPPORT_REPLYTYPE = []
  24. def __init__(self):
  25. super().__init__()
  26. self.corp_id = conf().get("wechatcom_corp_id")
  27. self.secret = conf().get("wechatcomapp_secret")
  28. self.agent_id = conf().get("wechatcomapp_agent_id")
  29. self.token = conf().get("wechatcomapp_token")
  30. self.aes_key = conf().get("wechatcomapp_aes_key")
  31. print(self.corp_id, self.secret, self.agent_id, self.token, self.aes_key)
  32. logger.info(
  33. "[wechatcom] init: corp_id: {}, secret: {}, agent_id: {}, token: {}, aes_key: {}".format(
  34. self.corp_id, self.secret, self.agent_id, self.token, self.aes_key
  35. )
  36. )
  37. self.crypto = WeChatCrypto(self.token, self.aes_key, self.corp_id)
  38. self.client = WeChatClient(self.corp_id, self.secret) # todo: 这里可能有线程安全问题
  39. def startup(self):
  40. # start message listener
  41. urls = ("/wxcomapp", "channel.wechatcom.wechatcomapp_channel.Query")
  42. app = web.application(urls, globals(), autoreload=False)
  43. port = conf().get("wechatcomapp_port", 8080)
  44. web.httpserver.runsimple(app.wsgifunc(), ("0.0.0.0", port))
  45. def send(self, reply: Reply, context: Context):
  46. receiver = context["receiver"]
  47. if reply.type in [ReplyType.TEXT, ReplyType.ERROR, ReplyType.INFO]:
  48. self.client.message.send_text(self.agent_id, receiver, reply.content)
  49. logger.info("[wechatcom] sendMsg={}, receiver={}".format(reply, receiver))
  50. elif reply.type == ReplyType.VOICE:
  51. try:
  52. file_path = reply.content
  53. amr_file = os.path.splitext(file_path)[0] + ".amr"
  54. any_to_amr(file_path, amr_file)
  55. response = self.client.media.upload("voice", open(amr_file, "rb"))
  56. logger.debug("[wechatcom] upload voice response: {}".format(response))
  57. except WeChatClientException as e:
  58. logger.error("[wechatcom] upload voice failed: {}".format(e))
  59. return
  60. try:
  61. os.remove(file_path)
  62. if amr_file != file_path:
  63. os.remove(amr_file)
  64. except Exception:
  65. pass
  66. self.client.message.send_voice(
  67. self.agent_id, receiver, response["media_id"]
  68. )
  69. logger.info(
  70. "[wechatcom] sendVoice={}, receiver={}".format(reply.content, receiver)
  71. )
  72. elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片
  73. img_url = reply.content
  74. pic_res = requests.get(img_url, stream=True)
  75. image_storage = io.BytesIO()
  76. for block in pic_res.iter_content(1024):
  77. image_storage.write(block)
  78. if (sz := fsize(image_storage)) >= 10 * 1024 * 1024:
  79. logger.info(
  80. "[wechatcom] image too large, ready to compress, sz={}".format(sz)
  81. )
  82. image_storage = compress_imgfile(image_storage, 10 * 1024 * 1024 - 1)
  83. logger.info(
  84. "[wechatcom] image compressed, sz={}".format(fsize(image_storage))
  85. )
  86. image_storage.seek(0)
  87. try:
  88. response = self.client.media.upload("image", image_storage)
  89. logger.debug("[wechatcom] upload image response: {}".format(response))
  90. except WeChatClientException as e:
  91. logger.error("[wechatcom] upload image failed: {}".format(e))
  92. return
  93. self.client.message.send_image(
  94. self.agent_id, receiver, response["media_id"]
  95. )
  96. logger.info(
  97. "[wechatcom] sendImage url={}, receiver={}".format(img_url, receiver)
  98. )
  99. elif reply.type == ReplyType.IMAGE: # 从文件读取图片
  100. image_storage = reply.content
  101. if (sz := fsize(image_storage)) >= 10 * 1024 * 1024:
  102. logger.info(
  103. "[wechatcom] image too large, ready to compress, sz={}".format(sz)
  104. )
  105. image_storage = compress_imgfile(image_storage, 10 * 1024 * 1024 - 1)
  106. logger.info(
  107. "[wechatcom] image compressed, sz={}".format(fsize(image_storage))
  108. )
  109. image_storage.seek(0)
  110. try:
  111. response = self.client.media.upload("image", image_storage)
  112. logger.debug("[wechatcom] upload image response: {}".format(response))
  113. except WeChatClientException as e:
  114. logger.error("[wechatcom] upload image failed: {}".format(e))
  115. return
  116. self.client.message.send_image(
  117. self.agent_id, receiver, response["media_id"]
  118. )
  119. logger.info("[wechatcom] sendImage, receiver={}".format(receiver))
  120. class Query:
  121. def GET(self):
  122. channel = WechatComAppChannel()
  123. params = web.input()
  124. signature = params.msg_signature
  125. timestamp = params.timestamp
  126. nonce = params.nonce
  127. echostr = params.echostr
  128. print(params)
  129. try:
  130. echostr = channel.crypto.check_signature(
  131. signature, timestamp, nonce, echostr
  132. )
  133. except InvalidSignatureException:
  134. raise web.Forbidden()
  135. return echostr
  136. def POST(self):
  137. channel = WechatComAppChannel()
  138. params = web.input()
  139. signature = params.msg_signature
  140. timestamp = params.timestamp
  141. nonce = params.nonce
  142. try:
  143. message = channel.crypto.decrypt_message(
  144. web.data(), signature, timestamp, nonce
  145. )
  146. except (InvalidSignatureException, InvalidCorpIdException):
  147. raise web.Forbidden()
  148. msg = parse_message(message)
  149. logger.debug("[wechatcom] receive message: {}, msg= {}".format(message, msg))
  150. if msg.type == "event":
  151. if msg.event == "subscribe":
  152. trigger_prefix = conf().get("single_chat_prefix", [""])[0]
  153. reply_content = textwrap.dedent(
  154. f"""\
  155. 感谢您的关注!
  156. 这里是ChatGPT,可以自由对话。
  157. 支持语音对话。
  158. 支持通用表情输入。
  159. 支持图片输入输出。
  160. 支持角色扮演和文字冒险两种定制模式对话。
  161. 输入'{trigger_prefix}#help' 查看详细指令。"""
  162. )
  163. reply = create_reply(reply_content, msg).render()
  164. res = channel.crypto.encrypt_message(reply, nonce, timestamp)
  165. return res
  166. else:
  167. try:
  168. wechatcom_msg = WechatComAppMessage(msg, client=channel.client)
  169. except NotImplementedError as e:
  170. logger.debug("[wechatcom] " + str(e))
  171. return "success"
  172. context = channel._compose_context(
  173. wechatcom_msg.ctype,
  174. wechatcom_msg.content,
  175. isgroup=False,
  176. msg=wechatcom_msg,
  177. )
  178. if context:
  179. channel.produce(context)
  180. return "success"