選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

207 行
8.8KB

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