No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

220 líneas
10KB

  1. # -*- coding: utf-8 -*-
  2. import io
  3. import os
  4. import time
  5. import imghdr
  6. import requests
  7. from bridge.context import *
  8. from bridge.reply import *
  9. from channel.chat_channel import ChatChannel
  10. from channel.wechatmp.wechatmp_client import WechatMPClient
  11. from channel.wechatmp.common import *
  12. from common.log import logger
  13. from common.singleton import singleton
  14. from config import conf
  15. from wechatpy.exceptions import WeChatClientException
  16. import asyncio
  17. from threading import Thread
  18. import web
  19. from voice.audio_convert import any_to_mp3
  20. # If using SSL, uncomment the following lines, and modify the certificate path.
  21. # from cheroot.server import HTTPServer
  22. # from cheroot.ssl.builtin import BuiltinSSLAdapter
  23. # HTTPServer.ssl_adapter = BuiltinSSLAdapter(
  24. # certificate='/ssl/cert.pem',
  25. # private_key='/ssl/cert.key')
  26. @singleton
  27. class WechatMPChannel(ChatChannel):
  28. def __init__(self, passive_reply=True):
  29. super().__init__()
  30. self.passive_reply = passive_reply
  31. self.NOT_SUPPORT_REPLYTYPE = []
  32. appid = conf().get("wechatmp_app_id")
  33. secret = conf().get("wechatmp_app_secret")
  34. self.client = WechatMPClient(appid, secret)
  35. if self.passive_reply:
  36. # Cache the reply to the user's first message
  37. self.cache_dict = dict()
  38. # Record whether the current message is being processed
  39. self.running = set()
  40. # Count the request from wechat official server by message_id
  41. self.request_cnt = dict()
  42. # The permanent media need to be deleted to avoid media number limit
  43. self.delete_media_loop = asyncio.new_event_loop()
  44. t = Thread(target=self.start_loop, args=(self.delete_media_loop,))
  45. t.setDaemon(True)
  46. t.start()
  47. def startup(self):
  48. if self.passive_reply:
  49. urls = ("/wx", "channel.wechatmp.passive_reply.Query")
  50. else:
  51. urls = ("/wx", "channel.wechatmp.active_reply.Query")
  52. app = web.application(urls, globals(), autoreload=False)
  53. port = conf().get("wechatmp_port", 8080)
  54. web.httpserver.runsimple(app.wsgifunc(), ("0.0.0.0", port))
  55. def start_loop(self, loop):
  56. asyncio.set_event_loop(loop)
  57. loop.run_forever()
  58. async def delete_media(self, media_id):
  59. logger.debug("[wechatmp] permanent media {} will be deleted in 10s".format(media_id))
  60. await asyncio.sleep(10)
  61. self.client.material.delete(media_id)
  62. logger.info("[wechatmp] permanent media {} has been deleted".format(media_id))
  63. def send(self, reply: Reply, context: Context):
  64. receiver = context["receiver"]
  65. if self.passive_reply:
  66. if reply.type == ReplyType.TEXT or reply.type == ReplyType.INFO or reply.type == ReplyType.ERROR:
  67. reply_text = reply.content
  68. logger.info("[wechatmp] reply to {} cached:\n{}".format(receiver, reply_text))
  69. self.cache_dict[receiver] = ("text", reply_text)
  70. elif reply.type == ReplyType.VOICE:
  71. try:
  72. file_path = reply.content
  73. response = self.client.material.add("voice", open(file_path, "rb"))
  74. logger.debug("[wechatmp] upload voice response: {}".format(response))
  75. except WeChatClientException as e:
  76. logger.error("[wechatmp] upload voice failed: {}".format(e))
  77. return
  78. time.sleep(3) # todo check media_id
  79. media_id = response["media_id"]
  80. logger.info("[wechatmp] voice reply to {} uploaded: {}".format(receiver, media_id))
  81. self.cache_dict[receiver] = ("voice", media_id)
  82. elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片
  83. img_url = reply.content
  84. pic_res = requests.get(img_url, stream=True)
  85. print(pic_res.headers)
  86. image_storage = io.BytesIO()
  87. for block in pic_res.iter_content(1024):
  88. image_storage.write(block)
  89. image_storage.seek(0)
  90. image_type = imghdr.what(image_storage)
  91. filename = receiver + "-" + str(context['msg'].msg_id) + "." + image_type
  92. content_type = "image/" + image_type
  93. try:
  94. response = self.client.material.add("image", (filename, image_storage, content_type))
  95. logger.debug("[wechatmp] upload image response: {}".format(response))
  96. except WeChatClientException as e:
  97. logger.error("[wechatmp] upload image failed: {}".format(e))
  98. return
  99. media_id = response["media_id"]
  100. logger.info(
  101. "[wechatmp] image reply url={}, receiver={}".format(img_url, receiver)
  102. )
  103. self.cache_dict[receiver] = ("image", media_id)
  104. elif reply.type == ReplyType.IMAGE: # 从文件读取图片
  105. image_storage = reply.content
  106. image_storage.seek(0)
  107. image_type = imghdr.what(image_storage)
  108. filename = receiver + "-" + str(context['msg'].msg_id) + "." + image_type
  109. content_type = "image/" + image_type
  110. try:
  111. response = self.client.material.add("image", (filename, image_storage, content_type))
  112. logger.debug("[wechatmp] upload image response: {}".format(response))
  113. except WeChatClientException as e:
  114. logger.error("[wechatmp] upload image failed: {}".format(e))
  115. return
  116. media_id = response["media_id"]
  117. logger.info(
  118. "[wechatmp] image reply url={}, receiver={}".format(img_url, receiver)
  119. )
  120. self.cache_dict[receiver] = ("image", media_id)
  121. else:
  122. if reply.type == ReplyType.TEXT or reply.type == ReplyType.INFO or reply.type == ReplyType.ERROR:
  123. reply_text = reply.content
  124. self.client.message.send_text(receiver, reply_text)
  125. logger.info("[wechatmp] Do send to {}: {}".format(receiver, reply_text))
  126. elif reply.type == ReplyType.VOICE:
  127. try:
  128. file_path = reply.content
  129. file_name = os.path.basename(file_path)
  130. file_type = os.path.splitext(file_name)[1]
  131. if file_type == ".mp3":
  132. file_type = "audio/mpeg"
  133. elif file_type == ".amr":
  134. file_type = "audio/amr"
  135. else:
  136. mp3_file = os.path.splitext(file_path)[0] + ".mp3"
  137. any_to_mp3(file_path, mp3_file)
  138. file_path = mp3_file
  139. file_name = os.path.basename(file_path)
  140. file_type = "audio/mpeg"
  141. logger.info("[wechatmp] file_name: {}, file_type: {} ".format(file_name, file_type))
  142. response = self.client.media.upload("voice", (file_name, open(file_path, "rb"), file_type))
  143. logger.debug("[wechatmp] upload voice response: {}".format(response))
  144. except WeChatClientException as e:
  145. logger.error("[wechatmp] upload voice failed: {}".format(e))
  146. return
  147. self.client.message.send_voice(receiver, response["media_id"])
  148. logger.info("[wechatmp] Do send voice to {}".format(receiver))
  149. elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片
  150. img_url = reply.content
  151. pic_res = requests.get(img_url, stream=True)
  152. print(pic_res.headers)
  153. image_storage = io.BytesIO()
  154. for block in pic_res.iter_content(1024):
  155. image_storage.write(block)
  156. image_storage.seek(0)
  157. image_type = imghdr.what(image_storage)
  158. filename = receiver + "-" + str(context['msg'].msg_id) + "." + image_type
  159. content_type = "image/" + image_type
  160. try:
  161. response = self.client.media.upload("image", (filename, image_storage, content_type))
  162. logger.debug("[wechatmp] upload image response: {}".format(response))
  163. except WeChatClientException as e:
  164. logger.error("[wechatmp] upload image failed: {}".format(e))
  165. return
  166. self.client.message.send_image(
  167. receiver, response["media_id"]
  168. )
  169. logger.info(
  170. "[wechatmp] sendImage url={}, receiver={}".format(img_url, receiver)
  171. )
  172. elif reply.type == ReplyType.IMAGE: # 从文件读取图片
  173. image_storage = reply.content
  174. image_storage.seek(0)
  175. image_type = imghdr.what(image_storage)
  176. filename = receiver + "-" + str(context['msg'].msg_id) + "." + image_type
  177. content_type = "image/" + image_type
  178. try:
  179. response = self.client.media.upload("image", (filename, image_storage, content_type))
  180. logger.debug("[wechatmp] upload image response: {}".format(response))
  181. except WeChatClientException as e:
  182. logger.error("[wechatmp] upload image failed: {}".format(e))
  183. return
  184. self.client.message.send_image(
  185. receiver, response["media_id"]
  186. )
  187. logger.info(
  188. "[wechatmp] sendImage, receiver={}".format(receiver)
  189. )
  190. return
  191. def _success_callback(self, session_id, context, **kwargs): # 线程异常结束时的回调函数
  192. logger.debug(
  193. "[wechatmp] Success to generate reply, msgId={}".format(
  194. context["msg"].msg_id
  195. )
  196. )
  197. if self.passive_reply:
  198. self.running.remove(session_id)
  199. def _fail_callback(self, session_id, exception, context, **kwargs): # 线程异常结束时的回调函数
  200. logger.exception(
  201. "[wechatmp] Fail to generate reply to user, msgId={}, exception={}".format(
  202. context["msg"].msg_id, exception
  203. )
  204. )
  205. if self.passive_reply:
  206. assert session_id not in self.cache_dict
  207. self.running.remove(session_id)