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.

216 lines
7.9KB

  1. # encoding:utf-8
  2. """
  3. wechat channel
  4. """
  5. import os
  6. import pathlib
  7. import itchat
  8. import json
  9. from itchat.content import *
  10. from channel.channel import Channel
  11. from concurrent.futures import ThreadPoolExecutor
  12. from common.log import logger
  13. from config import conf
  14. import requests
  15. import io
  16. thread_pool = ThreadPoolExecutor(max_workers=8)
  17. @itchat.msg_register(TEXT)
  18. def handler_single_msg(msg):
  19. WechatChannel().handle_text(msg)
  20. return None
  21. @itchat.msg_register(TEXT, isGroupChat=True)
  22. def handler_group_msg(msg):
  23. WechatChannel().handle_group(msg)
  24. return None
  25. @itchat.msg_register(VOICE)
  26. def handler_single_voice(msg):
  27. WechatChannel().handle_voice(msg)
  28. return None
  29. class WechatChannel(Channel):
  30. tmpFilePath = pathlib.Path('./tmp/')
  31. def __init__(self):
  32. pathExists = os.path.exists(self.tmpFilePath)
  33. if not pathExists and conf().get('speech_recognition') == True:
  34. os.makedirs(self.tmpFilePath)
  35. def startup(self):
  36. # login by scan QRCode
  37. itchat.auto_login(enableCmdQR=2)
  38. # start message listener
  39. itchat.run()
  40. def handle_voice(self, msg):
  41. if conf().get('speech_recognition') != True :
  42. return
  43. logger.debug("[WX]receive voice msg: " + msg['FileName'])
  44. thread_pool.submit(self._do_handle_voice, msg)
  45. def _do_handle_voice(self, msg):
  46. fileName = self.tmpFilePath+msg['FileName']
  47. msg.download(fileName)
  48. content = super().build_voice_to_text(fileName)
  49. self._handle_single_msg(msg, content, False)
  50. def handle_text(self, msg):
  51. logger.debug("[WX]receive text msg: " + json.dumps(msg, ensure_ascii=False))
  52. content = msg['Text']
  53. self._handle_single_msg(msg, content, False)
  54. def _handle_single_msg(self, msg, content, is_voice):
  55. from_user_id = msg['FromUserName']
  56. to_user_id = msg['ToUserName'] # 接收人id
  57. other_user_id = msg['User']['UserName'] # 对手方id
  58. match_prefix = self.check_prefix(content, conf().get('single_chat_prefix'))
  59. if "」\n- - - - - - - - - - - - - - -" in content:
  60. logger.debug("[WX]reference query skipped")
  61. return
  62. if from_user_id == other_user_id and match_prefix is not None:
  63. # 好友向自己发送消息
  64. if match_prefix != '':
  65. str_list = content.split(match_prefix, 1)
  66. if len(str_list) == 2:
  67. content = str_list[1].strip()
  68. img_match_prefix = self.check_prefix(content, conf().get('image_create_prefix'))
  69. if img_match_prefix:
  70. content = content.split(img_match_prefix, 1)[1].strip()
  71. thread_pool.submit(self._do_send_img, content, from_user_id)
  72. elif is_voice:
  73. thread_pool.submit(self._do_send_voice, content, from_user_id)
  74. else :
  75. thread_pool.submit(self._do_send_text, content, from_user_id)
  76. elif to_user_id == other_user_id and match_prefix:
  77. # 自己给好友发送消息
  78. str_list = content.split(match_prefix, 1)
  79. if len(str_list) == 2:
  80. content = str_list[1].strip()
  81. img_match_prefix = self.check_prefix(content, conf().get('image_create_prefix'))
  82. if img_match_prefix:
  83. content = content.split(img_match_prefix, 1)[1].strip()
  84. thread_pool.submit(self._do_send_img, content, to_user_id)
  85. elif is_voice:
  86. thread_pool.submit(self._do_send_voice, content, to_user_id)
  87. else:
  88. thread_pool.submit(self._do_send_text, content, to_user_id)
  89. def handle_group(self, msg):
  90. logger.debug("[WX]receive group msg: " + json.dumps(msg, ensure_ascii=False))
  91. group_name = msg['User'].get('NickName', None)
  92. group_id = msg['User'].get('UserName', None)
  93. if not group_name:
  94. return ""
  95. origin_content = msg['Content']
  96. content = msg['Content']
  97. content_list = content.split(' ', 1)
  98. context_special_list = content.split('\u2005', 1)
  99. if len(context_special_list) == 2:
  100. content = context_special_list[1]
  101. elif len(content_list) == 2:
  102. content = content_list[1]
  103. if "」\n- - - - - - - - - - - - - - -" in content:
  104. logger.debug("[WX]reference query skipped")
  105. return ""
  106. config = conf()
  107. match_prefix = (msg['IsAt'] and not config.get("group_at_off", False)) or self.check_prefix(origin_content, config.get('group_chat_prefix')) \
  108. or self.check_contain(origin_content, config.get('group_chat_keyword'))
  109. if ('ALL_GROUP' in config.get('group_name_white_list') or group_name in config.get('group_name_white_list') or self.check_contain(group_name, config.get('group_name_keyword_white_list'))) and match_prefix:
  110. img_match_prefix = self.check_prefix(content, conf().get('image_create_prefix'))
  111. if img_match_prefix:
  112. content = content.split(img_match_prefix, 1)[1].strip()
  113. thread_pool.submit(self._do_send_img, content, group_id)
  114. else:
  115. thread_pool.submit(self._do_send_group, content, msg)
  116. def send(self, msg, receiver):
  117. itchat.send(msg, toUserName=receiver)
  118. logger.info('[WX] sendMsg={}, receiver={}'.format(msg, receiver))
  119. def _do_send_voice(self, query, reply_user_id):
  120. try:
  121. if not query:
  122. return
  123. context = dict()
  124. context['from_user_id'] = reply_user_id
  125. reply_text = super().build_reply_content(query, context)
  126. if reply_text:
  127. replyFile = super().build_text_to_voice(reply_text)
  128. itchat.send_file(replyFile, toUserName=reply_user_id)
  129. logger.info('[WX] sendFile={}, receiver={}'.format(replyFile, reply_user_id))
  130. except Exception as e:
  131. logger.exception(e)
  132. def _do_send_text(self, query, reply_user_id):
  133. try:
  134. if not query:
  135. return
  136. context = dict()
  137. context['from_user_id'] = reply_user_id
  138. reply_text = super().build_reply_content(query, context)
  139. if reply_text:
  140. self.send(conf().get("single_chat_reply_prefix") + reply_text, reply_user_id)
  141. except Exception as e:
  142. logger.exception(e)
  143. def _do_send_img(self, query, reply_user_id):
  144. try:
  145. if not query:
  146. return
  147. context = dict()
  148. context['type'] = 'IMAGE_CREATE'
  149. img_url = super().build_reply_content(query, context)
  150. if not img_url:
  151. return
  152. # 图片下载
  153. pic_res = requests.get(img_url, stream=True)
  154. image_storage = io.BytesIO()
  155. for block in pic_res.iter_content(1024):
  156. image_storage.write(block)
  157. image_storage.seek(0)
  158. # 图片发送
  159. itchat.send_image(image_storage, reply_user_id)
  160. logger.info('[WX] sendImage, receiver={}'.format(reply_user_id))
  161. except Exception as e:
  162. logger.exception(e)
  163. def _do_send_group(self, query, msg):
  164. if not query:
  165. return
  166. context = dict()
  167. context['from_user_id'] = msg['ActualUserName']
  168. reply_text = super().build_reply_content(query, context)
  169. if reply_text:
  170. reply_text = '@' + msg['ActualNickName'] + ' ' + reply_text.strip()
  171. self.send(conf().get("group_chat_reply_prefix", "") + reply_text, msg['User']['UserName'])
  172. def check_prefix(self, content, prefix_list):
  173. for prefix in prefix_list:
  174. if content.startswith(prefix):
  175. return prefix
  176. return None
  177. def check_contain(self, content, keyword_list):
  178. if not keyword_list:
  179. return None
  180. for ky in keyword_list:
  181. if content.find(ky) != -1:
  182. return True
  183. return None