Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

214 lines
8.9KB

  1. # encoding:utf-8
  2. """
  3. wechaty channel
  4. Python Wechaty - https://github.com/wechaty/python-wechaty
  5. """
  6. import io
  7. import os
  8. import json
  9. import time
  10. import asyncio
  11. import requests
  12. from typing import Optional, Union
  13. from wechaty_puppet import MessageType, FileBox, ScanStatus # type: ignore
  14. from wechaty import Wechaty, Contact
  15. from wechaty.user import Message, Room, MiniProgram, UrlLink
  16. from channel.channel import Channel
  17. from common.log import logger
  18. from config import conf
  19. class WechatyChannel(Channel):
  20. def __init__(self):
  21. pass
  22. def startup(self):
  23. asyncio.run(self.main())
  24. async def main(self):
  25. config = conf()
  26. # 使用PadLocal协议 比较稳定(免费web协议 os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '127.0.0.1:8080')
  27. token = config.get('wechaty_puppet_service_token')
  28. os.environ['WECHATY_PUPPET_SERVICE_TOKEN'] = token
  29. global bot
  30. bot = Wechaty()
  31. bot.on('scan', self.on_scan)
  32. bot.on('login', self.on_login)
  33. bot.on('message', self.on_message)
  34. await bot.start()
  35. async def on_login(self, contact: Contact):
  36. logger.info('[WX] login user={}'.format(contact))
  37. async def on_scan(self, status: ScanStatus, qr_code: Optional[str] = None,
  38. data: Optional[str] = None):
  39. contact = self.Contact.load(self.contact_id)
  40. logger.info('[WX] scan user={}, scan status={}, scan qr_code={}'.format(contact, status.name, qr_code))
  41. # print(f'user <{contact}> scan status: {status.name} , 'f'qr_code: {qr_code}')
  42. async def on_message(self, msg: Message):
  43. """
  44. listen for message event
  45. """
  46. from_contact = msg.talker() # 获取消息的发送者
  47. to_contact = msg.to() # 接收人
  48. room = msg.room() # 获取消息来自的群聊. 如果消息不是来自群聊, 则返回None
  49. from_user_id = from_contact.contact_id
  50. to_user_id = to_contact.contact_id # 接收人id
  51. # other_user_id = msg['User']['UserName'] # 对手方id
  52. content = msg.text()
  53. mention_content = await msg.mention_text() # 返回过滤掉@name后的消息
  54. match_prefix = self.check_prefix(content, conf().get('single_chat_prefix'))
  55. conversation: Union[Room, Contact] = from_contact if room is None else room
  56. if room is None and msg.type() == MessageType.MESSAGE_TYPE_TEXT:
  57. if not msg.is_self() and match_prefix is not None:
  58. # 好友向自己发送消息
  59. if match_prefix != '':
  60. str_list = content.split(match_prefix, 1)
  61. if len(str_list) == 2:
  62. content = str_list[1].strip()
  63. img_match_prefix = self.check_prefix(content, conf().get('image_create_prefix'))
  64. if img_match_prefix:
  65. content = content.split(img_match_prefix, 1)[1].strip()
  66. await self._do_send_img(content, from_user_id)
  67. else:
  68. await self._do_send(content, from_user_id)
  69. elif msg.is_self() and match_prefix:
  70. # 自己给好友发送消息
  71. str_list = content.split(match_prefix, 1)
  72. if len(str_list) == 2:
  73. content = str_list[1].strip()
  74. img_match_prefix = self.check_prefix(content, conf().get('image_create_prefix'))
  75. if img_match_prefix:
  76. content = content.split(img_match_prefix, 1)[1].strip()
  77. await self._do_send_img(content, to_user_id)
  78. else:
  79. await self._do_send(content, to_user_id)
  80. elif room and msg.type() == MessageType.MESSAGE_TYPE_TEXT:
  81. # 群组&文本消息
  82. room_id = room.room_id
  83. room_name = await room.topic()
  84. from_user_id = from_contact.contact_id
  85. from_user_name = from_contact.name
  86. is_at = await msg.mention_self()
  87. content = mention_content
  88. config = conf()
  89. match_prefix = (is_at and not config.get("group_at_off", False)) \
  90. or self.check_prefix(content, config.get('group_chat_prefix')) \
  91. or self.check_contain(content, config.get('group_chat_keyword'))
  92. if ('ALL_GROUP' in config.get('group_name_white_list') or room_name in config.get(
  93. 'group_name_white_list') or self.check_contain(room_name, config.get(
  94. 'group_name_keyword_white_list'))) and match_prefix:
  95. img_match_prefix = self.check_prefix(content, conf().get('image_create_prefix'))
  96. if img_match_prefix:
  97. content = content.split(img_match_prefix, 1)[1].strip()
  98. await self._do_send_group_img(content, room_id)
  99. else:
  100. await self._do_send_group(content, room_id, room_name, from_user_id, from_user_name)
  101. async def send(self, message: Union[str, Message, FileBox, Contact, UrlLink, MiniProgram], receiver):
  102. logger.info('[WX] sendMsg={}, receiver={}'.format(message, receiver))
  103. if receiver:
  104. contact = await bot.Contact.find(receiver)
  105. await contact.say(message)
  106. async def send_group(self, message: Union[str, Message, FileBox, Contact, UrlLink, MiniProgram], receiver):
  107. logger.info('[WX] sendMsg={}, receiver={}'.format(message, receiver))
  108. if receiver:
  109. room = await bot.Room.find(receiver)
  110. await room.say(message)
  111. async def _do_send(self, query, reply_user_id):
  112. try:
  113. if not query:
  114. return
  115. context = dict()
  116. context['session_id'] = reply_user_id
  117. context['type'] = 'TEXT'
  118. context['content'] = query
  119. reply_text = super().build_reply_content(query, context)['content']
  120. if reply_text:
  121. await self.send(conf().get("single_chat_reply_prefix") + reply_text, reply_user_id)
  122. except Exception as e:
  123. logger.exception(e)
  124. async def _do_send_img(self, query, reply_user_id):
  125. try:
  126. if not query:
  127. return
  128. context = dict()
  129. context['type'] = 'IMAGE_CREATE'
  130. context['content'] = query
  131. img_url = super().build_reply_content(query, context)['content']
  132. if not img_url:
  133. return
  134. # 图片下载
  135. # pic_res = requests.get(img_url, stream=True)
  136. # image_storage = io.BytesIO()
  137. # for block in pic_res.iter_content(1024):
  138. # image_storage.write(block)
  139. # image_storage.seek(0)
  140. # 图片发送
  141. logger.info('[WX] sendImage, receiver={}'.format(reply_user_id))
  142. t = int(time.time())
  143. file_box = FileBox.from_url(url=img_url, name=str(t) + '.png')
  144. await self.send(file_box, reply_user_id)
  145. except Exception as e:
  146. logger.exception(e)
  147. async def _do_send_group(self, query, group_id, group_name, group_user_id, group_user_name):
  148. if not query:
  149. return
  150. context = dict()
  151. group_chat_in_one_session = conf().get('group_chat_in_one_session', [])
  152. if ('ALL_GROUP' in group_chat_in_one_session or \
  153. group_name in group_chat_in_one_session or \
  154. self.check_contain(group_name, group_chat_in_one_session)):
  155. context['session_id'] = str(group_id)
  156. else:
  157. context['session_id'] = str(group_id) + '-' + str(group_user_id)
  158. context['type'] = 'TEXT'
  159. context['content'] = query
  160. reply_text = super().build_reply_content(query, context)['content']
  161. if reply_text:
  162. reply_text = '@' + group_user_name + ' ' + reply_text.strip()
  163. await self.send_group(conf().get("group_chat_reply_prefix", "") + reply_text, group_id)
  164. async def _do_send_group_img(self, query, reply_room_id):
  165. try:
  166. if not query:
  167. return
  168. context = dict()
  169. context['type'] = 'IMAGE_CREATE'
  170. context['content'] = query
  171. img_url = super().build_reply_content(query, context)['content']
  172. if not img_url:
  173. return
  174. # 图片发送
  175. logger.info('[WX] sendImage, receiver={}'.format(reply_room_id))
  176. t = int(time.time())
  177. file_box = FileBox.from_url(url=img_url, name=str(t) + '.png')
  178. await self.send_group(file_box, reply_room_id)
  179. except Exception as e:
  180. logger.exception(e)
  181. def check_prefix(self, content, prefix_list):
  182. for prefix in prefix_list:
  183. if content.startswith(prefix):
  184. return prefix
  185. return None
  186. def check_contain(self, content, keyword_list):
  187. if not keyword_list:
  188. return None
  189. for ky in keyword_list:
  190. if content.find(ky) != -1:
  191. return True
  192. return None