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.

biz_service.py 13KB

1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
3 weken geleden
1 maand geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import aiohttp
  2. import asyncio
  3. import json
  4. import base64
  5. import io
  6. import json
  7. import os
  8. import threading
  9. import time
  10. import uuid,random
  11. from fastapi import FastAPI, Depends
  12. from common.log import logger
  13. from common.singleton import singleton
  14. from services.kafka_service import KafkaService
  15. from fastapi import Request
  16. from common.utils import *
  17. @singleton
  18. class BizService():
  19. def __init__(self,app:FastAPI):
  20. if not hasattr(self, 'initialized'):
  21. #self.kafka_service =kafka_service # 获取 KafkaService 单例
  22. self.kafka_service =app.state.kafka_service
  23. self.wxchat=app.state.gewe_service
  24. self.redis_service=app.state.redis_service
  25. self.initialized = True
  26. def setup_handlers(self):
  27. """设置默认的消息处理器"""
  28. # 这里可以添加业务逻辑
  29. # 注册默认处理器
  30. self.kafka_service.add_handler(
  31. self.kafka_service.consumer_topic,
  32. self.ops_messages_process_handler
  33. )
  34. async def ops_messages_process_handler(self, message: str):
  35. """消息处理器"""
  36. #print(f"BizService handling message: {message}")
  37. try:
  38. msg_content = message
  39. cleaned_content = clean_json_string(msg_content)
  40. content = json.loads(cleaned_content)
  41. data = content.get("data", {})
  42. msg_type_data = data.get("msg_type", None)
  43. content_data = data.get("content", {})
  44. if msg_type_data=="login":
  45. await self.login_handler_async(content_data)
  46. elif msg_type_data == 'group-sending':
  47. print(f'处理消息类型group-sending')
  48. await self.group_sending_handler_async(content_data)
  49. elif msg_type_data == 'login_wx_captch_code':
  50. pass
  51. else:
  52. print(f'kakfa 未处理息类型 {msg_type_data}')
  53. except Exception as e:
  54. print(f"处理消息时发生错误: {e}, 消息内容: {message}")
  55. async def login_handler_async(self, content_data: dict):
  56. tel=content_data.get('tel', '18733438393')
  57. token_id=content_data.get('token_id', 'c50b7d57-2efa-4a53-8c11-104a06d1e1fa')
  58. region_id=content_data.get('region_id', '440000')
  59. agent_token_id=content_data.get('agent_token_id', 'sk-fAOIdANeGXjWKW5mFybnsNZZGYU2lFLmqVY9rVFaFmjiOaWt3tcWMi')
  60. loginfo= await self.wxchat.get_login_info_from_cache_async(tel,token_id,region_id,agent_token_id)
  61. print(loginfo)
  62. status=loginfo.get('status','0')
  63. if status=='1':
  64. logger.info(f'手机号{tel},wx_token{token_id} 已经微信登录,终止登录流程')
  65. return
  66. async def group_sending_handler_async(self,content_data: dict):
  67. agent_tel=content_data.get('agent_tel', '18733438393')
  68. hash_key = f"__AI_OPS_WX__:LOGININFO:{agent_tel}"
  69. logininfo = await self.redis_service.get_hash(hash_key)
  70. if not logininfo:
  71. logger.warning(f"未找到 {agent_tel} 的登录信息")
  72. return
  73. token_id = logininfo.get('tokenId')
  74. app_id = logininfo.get('appId')
  75. agent_wxid = logininfo.get('wxid')
  76. # 获取联系人列表并计算交集
  77. hash_key = f"__AI_OPS_WX__:CONTACTS_BRIEF:{agent_wxid}"
  78. cache_friend_wxids_str=await self.redis_service.get_hash_field(hash_key,"data")
  79. cache_friend_wxids_list=json.loads(cache_friend_wxids_str) if cache_friend_wxids_str else []
  80. cache_friend_wxids=[f["userName"] for f in cache_friend_wxids_list]
  81. # 获取群交集
  82. hash_key = f"__AI_OPS_WX__:GROUPS_INFO:{agent_wxid}"
  83. cache_chatrooms = await self.redis_service.get_hash(hash_key)
  84. cache_chatroom_ids=cache_chatrooms.keys()
  85. wxid_contact_list_content_data = [c['wxid'] for c in content_data.get("contact_list", [])]
  86. intersection_friend_wxids = list(set(cache_friend_wxids) & set(wxid_contact_list_content_data))
  87. intersection_chatroom_ids = list(set(cache_chatroom_ids) & set(wxid_contact_list_content_data))
  88. intersection_wxids=intersection_friend_wxids+intersection_chatroom_ids
  89. # 发送消息
  90. wx_content_list = content_data.get("wx_content", [])
  91. self.wxchat.forward_video_aeskey = ''
  92. self.wxchat.forward_video_cdnvideourl = ''
  93. self.wxchat.forward_video_length = 0
  94. self.wxchat.video_duration = 0
  95. for intersection_wxid in intersection_wxids:
  96. for wx_content in wx_content_list:
  97. if wx_content["type"] == "text":
  98. await self.send_text_message_async(token_id, app_id, agent_wxid, [intersection_wxid], wx_content["text"])
  99. elif wx_content["type"] == "image_url":
  100. await self.send_image_messagae_sync(token_id, app_id, agent_wxid, [intersection_wxid], wx_content.get("image_url", {}).get("url"))
  101. elif wx_content["type"] == "tts":
  102. await self.send_tts_message(token_id, app_id, agent_wxid, [intersection_wxid], wx_content["text"])
  103. elif wx_content["type"] == "file":
  104. await self.send_file_message(token_id, app_id, agent_wxid, [intersection_wxid], wx_content.get("file_url", {}).get("url"))
  105. async def send_text_message_async(self, token_id, app_id, agent_wxid, intersection_wxids, text):
  106. for t in intersection_wxids:
  107. # 发送文本消息
  108. ret,ret_msg,res = await self.wxchat.post_text_async(token_id, app_id, t, text)
  109. logger.info(f'{agent_wxid} 向 {t} 发送文字【{text}】')
  110. # 构造对话消息并发送到 Kafka
  111. input_wx_content_dialogue_message = [{"type": "text", "text": text}]
  112. input_message = dialogue_message(agent_wxid, t, input_wx_content_dialogue_message)
  113. await self.kafka_service.send_message_async(input_message)
  114. logger.info("发送对话 %s", input_message)
  115. # 等待随机时间
  116. await asyncio.sleep(random.uniform(1.5, 3))
  117. async def send_image_messagae_sync(self,token_id, app_id, agent_wxid, intersection_wxids, image_url):
  118. aeskey, cdnthumburl, cdnthumblength, cdnthumbheight, cdnthumbwidth, length, md5 = "", "", 0, 0, 0, 0, ""
  119. for t in intersection_wxids:
  120. if t == intersection_wxids[0]:
  121. # 发送图片
  122. ret,ret_msg,res = await self.wxchat.post_image_async(token_id, app_id, t, image_url)
  123. if ret==200:
  124. aeskey = res["aesKey"]
  125. cdnthumburl = res["fileId"]
  126. cdnthumblength = res["cdnThumbLength"]
  127. cdnthumbheight = res["height"]
  128. cdnthumbwidth = res["width"]
  129. length = res["length"]
  130. md5 = res["md5"]
  131. logger.info(f'{agent_wxid} 向 {t} 发送图片【{image_url}】{ret_msg}')
  132. else:
  133. logger.warning(f'{agent_wxid} 向 {t} 发送图片【{image_url}】{ret_msg}')
  134. else:
  135. if aeskey !="":
  136. # 转发图片
  137. ret,ret_msg,res = await self.wxchat.forward_image_async(token_id, app_id, t, aeskey, cdnthumburl, cdnthumblength, cdnthumbheight, cdnthumbwidth, length, md5)
  138. logger.info(f'{agent_wxid} 向 {t} 转发图片【{image_url}】{ret_msg}')
  139. else:
  140. # 发送图片
  141. ret,ret_msg,res = await self.wxchat.post_image_async(token_id, app_id, t, image_url)
  142. if ret==200:
  143. aeskey = res["aesKey"]
  144. cdnthumburl = res["fileId"]
  145. cdnthumblength = res["cdnThumbLength"]
  146. cdnthumbheight = res["height"]
  147. cdnthumbwidth = res["width"]
  148. length = res["length"]
  149. md5 = res["md5"]
  150. logger.info(f'{agent_wxid} 向 {t} 发送图片【{image_url}】{ret_msg}')
  151. else:
  152. logger.warning(f'{agent_wxid} 向 {t} 发送图片【{image_url}】{ret_msg}')
  153. # 构造对话消息并发送到 Kafka
  154. wx_content_dialogue_message = [{"type": "image_url", "image_url": {"url": image_url}}]
  155. input_message = dialogue_message(agent_wxid, t, wx_content_dialogue_message)
  156. await self.kafka_service.send_message_async(input_message)
  157. logger.info("发送对话 %s", input_message)
  158. # 等待随机时间
  159. await asyncio.sleep(random.uniform(1.5, 3))
  160. async def send_tts_message(self, token_id, app_id, agent_wxid, intersection_wxids, text):
  161. voice_during,voice_url=wx_voice(text)
  162. for t in intersection_wxids:
  163. # 发送送语音消息
  164. if voice_url:
  165. ret,ret_msg,res = await self.wxchat.post_voice_async(token_id, app_id, t, voice_url,voice_during)
  166. if ret==200:
  167. logger.info(f'{agent_wxid} 向 {t} 发送语音文本【{text}】{ret_msg}')
  168. # 构造对话消息并发送到 Kafka
  169. input_wx_content_dialogue_message = [{"type": "text", "text": text}]
  170. input_message = dialogue_message(agent_wxid, t, input_wx_content_dialogue_message)
  171. await self.kafka_service.send_message_async(input_message)
  172. logger.info("发送对话 %s", input_message)
  173. else:
  174. logger.warning((f'{agent_wxid} 向 {t} 发送语音文本【{text}】{ret_msg}'))
  175. else:
  176. logger.warning((f'{agent_wxid} 向 {t} 发送语音文本【{text}】出错'))
  177. # 等待随机时间
  178. await asyncio.sleep(random.uniform(1.5, 3))
  179. async def send_file_message(self,token_id, app_id, agent_wxid, intersection_wxids, file_url):
  180. parsed_url = urlparse(file_url)
  181. path = parsed_url.path
  182. # 从路径中提取文件名
  183. filename = path.split('/')[-1]
  184. # 获取扩展名
  185. _, ext = os.path.splitext(filename)
  186. if ext == '.mp4':
  187. await self.send_video_message(token_id, app_id, agent_wxid, intersection_wxids, file_url)
  188. else:
  189. await self.send_other_file_message(token_id, app_id, agent_wxid, intersection_wxids, file_url)
  190. #time.sleep(random.uniform(1.5, 3))
  191. async def send_video_message(self, token_id, app_id, agent_wxid, intersection_wxids, file_url):
  192. for t in intersection_wxids:
  193. # 发送视频消息
  194. parsed_url = urlparse(file_url)
  195. filename = os.path.basename(parsed_url.path)
  196. tmp_file_path = os.path.join(os.getcwd(),'tmp', filename) # 拼接完整路径
  197. thumbnail_path=tmp_file_path.replace('.mp4','.jpg')
  198. if self.wxchat.forward_video_aeskey == '':
  199. video_thumb_url,video_duration =download_video_and_get_thumbnail(file_url,thumbnail_path)
  200. print(f'视频缩略图 {video_thumb_url} 时长 {video_duration}')
  201. ret,ret_msg,res = await self.wxchat.post_video_async(token_id, app_id, t, file_url,video_thumb_url,video_duration)
  202. if ret==200:
  203. self.wxchat.forward_video_aeskey = res["aesKey"]
  204. self.wxchat.forward_video_cdnvideourl = res["cdnThumbUrl"]
  205. self.wxchat.forward_video_length = res["length"]
  206. self.wxchat.video_duration=int(video_duration)
  207. else:
  208. ret,ret_msg,res = await self.wxchat.forward_video_async(token_id, app_id, t, self.wxchat.forward_video_aeskey, self.wxchat.forward_video_cdnvideourl, self.wxchat.forward_video_length,self.wxchat.video_duration)
  209. print('转发视频')
  210. if ret==200:
  211. logger.info(f'{agent_wxid} 向 {t} 发送视频【{file_url}】{ret_msg}')
  212. # 构造对话消息并发送到 Kafka
  213. input_wx_content_dialogue_message = [{"type": "file", "file_url": {"url": file_url}}]
  214. input_message = dialogue_message(agent_wxid, t, input_wx_content_dialogue_message)
  215. await self.kafka_service.send_message_async(input_message)
  216. logger.info("发送对话 %s", input_message)
  217. else:
  218. logger.warning((f'{agent_wxid} 向 {t} 发送视频【{file_url}】{ret_msg}'))
  219. # 等待随机时间
  220. await asyncio.sleep(random.uniform(1.5, 3))
  221. async def send_other_file_message(self, token_id, app_id, agent_wxid, intersection_wxids, file_url):
  222. print('send_otherfile_message')