Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

759 Zeilen
33KB

  1. from flask_restful import Resource, reqparse
  2. from flask import jsonify,request
  3. import requests,json,re
  4. from wechat import gewe_chat
  5. from voice.ali.ali_voice import AliVoice
  6. from common import utils,redis_helper,memory,kafka_helper
  7. from common.log import logger
  8. import xml.etree.ElementTree as ET
  9. import os
  10. from voice import audio_convert
  11. class MessagesResource(Resource):
  12. def __init__(self):
  13. self.parser = reqparse.RequestParser()
  14. def post(self):
  15. msg = request.get_json()
  16. logger.info(f"收到微信回调消息: {msg}")
  17. type_name =msg.get("TypeName")
  18. app_id = msg.get("Appid")
  19. # token_id = "f828cb3c-1039-489f-b9ae-7494d1778a15"
  20. token_id=get_token_id_by_app_id(app_id)
  21. if token_id=="":
  22. logger.warning('找不到登录信息,不处理')
  23. return jsonify({"message": "收到微信回调消息"})
  24. if type_name=='AddMsg':
  25. wxid = msg.get("Wxid")
  26. msg_data = msg.get("Data")
  27. msg_type = msg_data.get("MsgType",None)
  28. from_wxid = msg_data["FromUserName"]["string"]
  29. to_wxid = msg_data["ToUserName"]["string"]
  30. msg_push_content=msg_data.get("PushContent") #群发
  31. handlers = {
  32. 1: handle_text,
  33. 3: handle_image,
  34. 34: handle_voice,
  35. 49: handle_xml,
  36. 37: handle_add_friend_notice,
  37. 10002: handle_10002_msg
  38. }
  39. # (扫码进群情况)判断受否是群聊,并添加到通信录,
  40. if check_chatroom(from_wxid) or check_chatroom(to_wxid):
  41. logger.info('群信息')
  42. chatroom_id=from_wxid
  43. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,3)
  44. logger.info(f'保存到通讯录 chatroom_id {chatroom_id} {msg}')
  45. gewe_chat.wxchat.update_groups_info_to_cache(token_id,app_id,wxid,chatroom_id)
  46. handlers[1]=handle_text_group
  47. handlers[3]=handle_image_group
  48. handlers[34]=handle_voice_group
  49. handler = handlers.get(msg_type)
  50. if handler:
  51. return handler(token_id,app_id, wxid,msg_data,from_wxid, to_wxid)
  52. else:
  53. logger.warning(f"微信回调消息类型 {msg_type} 未处理")
  54. elif type_name=='ModContacts':
  55. '''
  56. 好友通过验证及好友资料变更的通知消息
  57. '''
  58. wxid = msg.get("Wxid")
  59. msg_data = msg.get("Data")
  60. handle_mod_contacts(token_id,app_id,wxid,msg_data)
  61. elif type_name=="DelContacts":
  62. '''
  63. 删除好友通知/退出群聊
  64. '''
  65. msg_data = msg.get("Data")
  66. username=msg_data["UserName"]["string"]
  67. if check_chatroom(username):
  68. logger.info('退出群聊')
  69. wxid = msg.get("Wxid")
  70. chatroom_id=username
  71. redis_helper.redis_helper.delete_hash_field(f'__AI_OPS_WX__:GROUPS_INFO:{wxid}',chatroom_id)
  72. logger.info(f'清除 chatroom_id{chatroom_id} 数据')
  73. else:
  74. logger.info('删除好友通知')
  75. elif type_name=="Offline":
  76. '''
  77. 已经离线
  78. '''
  79. wxid = msg.get("Wxid")
  80. logger.warning(f'微信ID {wxid}在设备{app_id}已经离线')
  81. k,r=get_login_info_by_app_id(app_id)
  82. print(k)
  83. redis_helper.redis_helper.update_hash_field(k,'status',0)
  84. else:
  85. logger.warning(f"未知消息类型")
  86. return jsonify({"message": "收到微信回调消息"})
  87. def handle_text(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  88. '''
  89. 私聊文本消息
  90. '''
  91. msg_content=msg_data["Content"]["string"]
  92. if wxid == from_wxid: #手动发送消息
  93. logger.info("Active message sending detected")
  94. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id,wxid,[to_wxid])
  95. callback_to_user=msg_data["ToUserName"]["string"]
  96. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  97. input_message=utils.dialogue_message(from_wxid,to_wxid,input_wx_content_dialogue_message)
  98. kafka_helper.kafka_client.produce_message(input_message)
  99. logger.info("发送对话 %s",input_message)
  100. else:
  101. callback_to_user=msg_data["FromUserName"]["string"]
  102. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  103. prompt={"role": "user", "content": [{
  104. "type": "text",
  105. "text": msg_content
  106. }]}
  107. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  108. # 收到的对话
  109. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  110. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  111. kafka_helper.kafka_client.produce_message(input_message)
  112. logger.info("发送对话 %s",input_message)
  113. cache_data = memory.USER_INTERACTIVE_CACHE.get(wxid)
  114. if cache_data and cache_data.get('interactive') :
  115. o=get_first_char_if_digit(msg_content)
  116. if o is not None:
  117. userSelectOptions=cache_data.get('options')
  118. if o < len(userSelectOptions):
  119. o=o-1
  120. msg_content=userSelectOptions[o].get("value")
  121. messages_to_send=[{"role": "user", "content": msg_content}]
  122. else:
  123. messages_to_send=[{"role": "user", "content": msg_content}]
  124. else:
  125. messages_to_send=[{"role": "user", "content": msg_content}]
  126. res=fast_gpt_api(messages_to_send,f'{wxid}-{callback_to_user}')
  127. reply_content=res["choices"][0]["message"]["content"]
  128. description = ''
  129. userSelectOptions = []
  130. if isinstance(reply_content, list) and any(item.get("type") == "interactive" for item in reply_content):
  131. for item in reply_content:
  132. if item["type"] == "interactive" and item["interactive"]["type"] == "userSelect":
  133. params = item["interactive"]["params"]
  134. description = params.get("description")
  135. userSelectOptions = params.get("userSelectOptions", [])
  136. values_string = "\n".join(option["value"] for option in userSelectOptions)
  137. if description is not None:
  138. memory.USER_INTERACTIVE_CACHE[wxid] = {
  139. "interactive":True,
  140. "options": userSelectOptions,
  141. }
  142. reply_content=description + '------------------------------\n'+values_string
  143. elif isinstance(reply_content, list) and any(item.get("type") == "text" for item in reply_content):
  144. memory.USER_INTERACTIVE_CACHE[wxid] = {
  145. "interactive":False
  146. }
  147. text=''
  148. for item in reply_content:
  149. if item["type"] == "text":
  150. text=item["text"]["content"]
  151. if text=='':
  152. # 去除上次上一轮对话再次请求
  153. cache_messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  154. cache_messages = json.loads(cache_messages_str) if cache_messages_str else []
  155. if len(cache_messages) >= 3:
  156. cache_messages = cache_messages[:-3]
  157. redis_helper.redis_helper.update_hash_field(hash_key,"data",json.dumps(cache_messages,ensure_ascii=False))
  158. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  159. res=fast_gpt_api(messages_to_send,f'{wxid}-{callback_to_user}')
  160. reply_content=res["choices"][0]["message"]["content"]
  161. # print('-------------------------------------------------')
  162. # print(reply_content)
  163. # print('-------------------------------------------------')
  164. if isinstance(reply_content, list) :
  165. reply_content=reply_content[0].get('text').get("content")
  166. # print(reply_content)
  167. # print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
  168. # try:
  169. # json_data = json.loads(reply_content)
  170. # reply_content=json_data[0].get('text').get("content")
  171. # except ValueError:
  172. # print("reply_content is a string, but not a valid JSON.")
  173. else:
  174. reply_content=text
  175. else:
  176. memory.USER_INTERACTIVE_CACHE[wxid] = {
  177. "interactive":False
  178. }
  179. reply_content=res["choices"][0]["message"]["content"]
  180. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  181. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  182. # 回复的对话
  183. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  184. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  185. kafka_helper.kafka_client.produce_message(input_message)
  186. logger.info("发送对话 %s",input_message)
  187. def handle_text_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  188. '''
  189. 群聊文本消息
  190. '''
  191. msg_content=msg_data["Content"]["string"]
  192. msg_push_content=msg_data.get("PushContent")
  193. k,login_info=get_login_info_by_app_id(app_id)
  194. nickname=login_info.get("nickName")
  195. if wxid == from_wxid: #手动发送消息
  196. logger.info("Active message sending detected")
  197. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id,wxid,[to_wxid])
  198. callback_to_user=msg_data["ToUserName"]["string"]
  199. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  200. input_message=utils.dialogue_message(from_wxid,to_wxid,input_wx_content_dialogue_message)
  201. kafka_helper.kafka_client.produce_message(input_message)
  202. logger.info("发送对话 %s",input_message)
  203. else:
  204. c = gewe_chat.wxchat.get_wxchat_config_from_cache(wxid)
  205. chatroom_id_white_list = c.get("chatroomIdWhiteList", [])
  206. if not chatroom_id_white_list:
  207. logger.info('白名单为空或未定义,不处理')
  208. return
  209. if from_wxid not in chatroom_id_white_list:
  210. logger.info('群ID不在白名单中,不处理')
  211. return
  212. if '在群聊中@了你' in msg_push_content or '@'+nickname in msg_push_content:
  213. callback_to_user=msg_data["FromUserName"]["string"]
  214. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  215. prompt={"role": "user", "content": [{
  216. "type": "text",
  217. "text": msg_content
  218. }]}
  219. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  220. # 收到的对话
  221. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  222. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  223. kafka_helper.kafka_client.produce_message(input_message)
  224. logger.info("发送对话 %s",input_message)
  225. cache_data = memory.USER_INTERACTIVE_CACHE.get(wxid)
  226. if cache_data and cache_data.get('interactive') :
  227. o=get_first_char_if_digit(msg_content)
  228. if o is not None:
  229. userSelectOptions=cache_data.get('options')
  230. if o < len(userSelectOptions):
  231. o=o-1
  232. msg_content=userSelectOptions[o].get("value")
  233. messages_to_send=[{"role": "user", "content": msg_content}]
  234. else:
  235. messages_to_send=[{"role": "user", "content": msg_content}]
  236. else:
  237. messages_to_send=[{"role": "user", "content": msg_content}]
  238. res=fast_gpt_api(messages_to_send,f'{wxid}-{callback_to_user}')
  239. reply_content=res["choices"][0]["message"]["content"]
  240. description = ''
  241. userSelectOptions = []
  242. if isinstance(reply_content, list) and any(item.get("type") == "interactive" for item in reply_content):
  243. for item in reply_content:
  244. if item["type"] == "interactive" and item["interactive"]["type"] == "userSelect":
  245. params = item["interactive"]["params"]
  246. description = params.get("description")
  247. userSelectOptions = params.get("userSelectOptions", [])
  248. values_string = "\n".join(option["value"] for option in userSelectOptions)
  249. if description is not None:
  250. memory.USER_INTERACTIVE_CACHE[wxid] = {
  251. "interactive":True,
  252. "options": userSelectOptions,
  253. }
  254. reply_content=description + '------------------------------\n'+values_string
  255. elif isinstance(reply_content, list) and any(item.get("type") == "text" for item in reply_content):
  256. memory.USER_INTERACTIVE_CACHE[wxid] = {
  257. "interactive":False
  258. }
  259. text=''
  260. for item in reply_content:
  261. if item["type"] == "text":
  262. text=item["text"]["content"]
  263. if text=='':
  264. # 去除上次上一轮对话再次请求
  265. cache_messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  266. cache_messages = json.loads(cache_messages_str) if cache_messages_str else []
  267. if len(cache_messages) >= 3:
  268. cache_messages = cache_messages[:-3]
  269. redis_helper.redis_helper.update_hash_field(hash_key,"data",json.dumps(cache_messages,ensure_ascii=False))
  270. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  271. res=fast_gpt_api(messages_to_send,f'{wxid}-{callback_to_user}')
  272. reply_content=res["choices"][0]["message"]["content"]
  273. else:
  274. reply_content=text
  275. else:
  276. memory.USER_INTERACTIVE_CACHE[wxid] = {
  277. "interactive":False
  278. }
  279. reply_content=res["choices"][0]["message"]["content"]
  280. reply_content='@'+extract_nickname(msg_push_content) + reply_content
  281. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  282. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  283. # 回复的对话
  284. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  285. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  286. kafka_helper.kafka_client.produce_message(input_message)
  287. logger.info("发送对话 %s",input_message)
  288. else:
  289. logger.info('群聊公开消息')
  290. callback_to_user=msg_data["FromUserName"]["string"]
  291. dialogue_message=[{"type": "text", "text": msg_content}]
  292. input_message=utils.dialogue_message(callback_to_user,wxid,dialogue_message)
  293. kafka_helper.kafka_client.produce_message(input_message)
  294. logger.info("发送对话 %s",input_message)
  295. return
  296. def handle_image(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  297. '''
  298. 图片消息
  299. '''
  300. msg_content=msg_data["Content"]["string"]
  301. callback_to_user=from_wxid
  302. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  303. wx_img_url=gewe_chat.wxchat.download_image_msg(token_id,app_id,msg_content)
  304. oss_access_key_id="LTAI5tRTG6pLhTpKACJYoPR5"
  305. oss_access_key_secret="E7dMzeeMxq4VQvLg7Tq7uKf3XWpYfN"
  306. oss_endpoint="http://oss-cn-shanghai.aliyuncs.com"
  307. oss_bucket_name="cow-agent"
  308. oss_prefix="cow"
  309. img_url=utils.upload_oss(oss_access_key_id, oss_access_key_secret, oss_endpoint, oss_bucket_name, wx_img_url, oss_prefix)
  310. prompt={
  311. "role": "user",
  312. "content": [{
  313. "type": "image_url",
  314. "image_url": {"url": img_url}
  315. }]
  316. }
  317. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  318. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,'已经上传了图片,有什么可以为您服务')
  319. logger.info(f"上传图片 URL: {img_url}")
  320. wx_content_dialogue_message=[{"type": "image_url", "image_url": {"url": img_url}}]
  321. input_message=utils.dialogue_message(wxid,callback_to_user,wx_content_dialogue_message)
  322. kafka_helper.kafka_client.produce_message(input_message)
  323. logger.info("发送对话 %s",input_message)
  324. def handle_image_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  325. logger.info('群聊图片消息')
  326. def handle_voice(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  327. '''
  328. 语音消息
  329. '''
  330. callback_to_user=from_wxid
  331. msg_content=msg_data["Content"]["string"]
  332. msg_id=msg_data["MsgId"]
  333. file_url=gewe_chat.wxchat.download_audio_msg(token_id,app_id,msg_id,msg_content)
  334. react_silk_path=utils.save_to_local_from_url(file_url)
  335. react_wav_path = os.path.splitext(react_silk_path)[0] + ".wav"
  336. audio_convert.any_to_wav(react_silk_path,react_wav_path)
  337. react_voice_text=AliVoice().voiceToText(react_wav_path)
  338. os.remove(react_silk_path)
  339. os.remove(react_wav_path)
  340. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  341. messages=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "user", "content": react_voice_text})
  342. ai_res=fast_gpt_api(messages,f'{wxid}-{callback_to_user}')
  343. ai_res_content=ai_res["choices"][0]["message"]["content"]
  344. has_url=contains_url(ai_res_content)
  345. if not has_url:
  346. voice_during,voice_url=utils.wx_voice(ai_res_content)
  347. ret,ret_msg,res=gewe_chat.wxchat.post_voice(token_id,app_id,callback_to_user,voice_url,voice_during)
  348. if ret==200:
  349. logger.info((f'{wxid} 向 {callback_to_user} 发送语音文本【{ai_res_content}】{ret_msg}'))
  350. else:
  351. logger.warning((f'{wxid} 向 {callback_to_user} 发送语音文本【{ai_res_content}】{ret_msg}'))
  352. ret,ret_msg,res=gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,ai_res_content)
  353. logger.info((f'{wxid} 向 {callback_to_user} 发送文本【{ai_res_content}】{ret_msg}'))
  354. else:
  355. logger.info(f"回复内容包含网址,不发送语音,回复文字内容:{ai_res_content}")
  356. ret,ret_msg,res=gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,ai_res_content)
  357. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": ai_res_content})
  358. # 构造对话消息并发送到 Kafka
  359. input_wx_content_dialogue_message = [{"type": "text", "text": ai_res_content}]
  360. input_message = utils.dialogue_message(wxid, callback_to_user, input_wx_content_dialogue_message,True)
  361. kafka_helper.kafka_client.produce_message(input_message)
  362. logger.info("发送对话 %s", input_message)
  363. def handle_voice_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  364. logger.info('语音消息')
  365. def handle_xml(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  366. '''
  367. 处理xml
  368. '''
  369. msg_content_xml=msg_data["Content"]["string"]
  370. root = ET.fromstring(msg_content_xml)
  371. type_value = root.find(".//appmsg/type").text
  372. handlers = {
  373. 57: handle_xml_reference,
  374. }
  375. handler = handlers.get(type_value)
  376. if handler:
  377. return handler(token_id,app_id, wxid,msg_data,from_wxid, to_wxid)
  378. elif "邀请你加入了群聊" in msg_content_xml: # 邀请加入群聊
  379. logger.warning(f"xml消息 {type_value} 邀请你加入了群聊.todo")
  380. else:
  381. print(f"xml消息 {type_value} 未解析")
  382. def handle_xml_reference(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  383. '''
  384. 判断此类消息的逻辑:$.Data.MsgType=49 并且 解析$.Data.Content.string中的xml msg.appmsg.type=57
  385. '''
  386. callback_to_user=from_wxid
  387. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  388. msg_content= msg_data["PushContent"]
  389. prompt={"role": "user", "content": [{
  390. "type": "text",
  391. "text": msg_content
  392. }]}
  393. # 收到的对话
  394. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  395. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  396. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  397. kafka_helper.kafka_client.produce_message(input_message)
  398. logger.info("发送对话 %s",input_message)
  399. # 回复的对话
  400. res=fast_gpt_api(messages_to_send,f'{wxid}-{callback_to_user}')
  401. reply_content=res["choices"][0]["message"]["content"]
  402. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  403. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  404. kafka_helper.kafka_client.produce_message(input_message)
  405. logger.info("发送对话 %s",input_message)
  406. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  407. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  408. def handle_add_friend_notice(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  409. '''
  410. 好友添加请求通知
  411. '''
  412. logger.info('好友添加请求通知')
  413. msg_content_xml=msg_data["Content"]["string"]
  414. root = ET.fromstring(msg_content_xml)
  415. msg_content = root.attrib.get('content', None)
  416. v3= root.attrib.get('encryptusername', None)
  417. v4= root.attrib.get('ticket', None)
  418. scene=root.attrib.get('scene', None)
  419. to_contact_wxid=root.attrib.get('fromusername', None)
  420. wxid=msg_data["ToUserName"]["string"]
  421. # 自动同意好友
  422. # print(v3)
  423. # print(v4)
  424. # print(scene)
  425. # print(msg_content)
  426. # 操作类型,2添加好友 3同意好友 4拒绝好友
  427. #option=2
  428. option=3
  429. reply_add_contact_contact="亲,我是你的美丽顾问"
  430. ret,ret_msg=gewe_chat.wxchat.add_contacts(token_id,app_id,scene,option,v3,v4,reply_add_contact_contact)
  431. if ret==200:
  432. logger.info('自动添加好友成功')
  433. # 好友发送的文字
  434. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{to_contact_wxid}'
  435. prompt={"role": "user", "content": [{"type": "text","text": msg_content}]}
  436. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  437. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  438. input_message=utils.dialogue_message(to_contact_wxid,wxid,input_wx_content_dialogue_message)
  439. kafka_helper.kafka_client.produce_message(input_message)
  440. logger.info("发送对话 %s",input_message)
  441. callback_to_user=to_contact_wxid
  442. res=fast_gpt_api(messages_to_send,f'{wxid}-{callback_to_user}')
  443. reply_content=res["choices"][0]["message"]["content"]
  444. #保存好友信息
  445. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id, wxid,[to_contact_wxid])
  446. # 保存到缓存
  447. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  448. # 发送信息
  449. gewe_chat.wxchat.post_text(token_id,app_id, to_contact_wxid,reply_content)
  450. # 发送到kafka
  451. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  452. input_message=utils.dialogue_message(wxid,to_contact_wxid,input_wx_content_dialogue_message,True)
  453. kafka_helper.kafka_client.produce_message(input_message)
  454. logger.info("发送对话 %s",input_message)
  455. else:
  456. logger.warning("添加好友失败")
  457. def handle_10002_msg(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  458. '''
  459. 群聊邀请
  460. 撤回消息
  461. 拍一拍消息
  462. 地理位置
  463. 踢出群聊通知
  464. 解散群聊通知
  465. 发布群公告
  466. '''
  467. msg_content_xml=msg_data["Content"]["string"]
  468. # 群聊邀请
  469. if '邀请你加入了群聊' in msg_content_xml and check_chatroom(msg_data["FromUserName"]["string"]):
  470. chatroom_id=msg_data["FromUserName"]["string"]
  471. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,3)
  472. logger.info(f'群聊邀请,保存到通讯录 chatroom_id {chatroom_id} {msg}')
  473. gewe_chat.wxchat.update_groups_info_to_cache(token_id,app_id,wxid,chatroom_id)
  474. if '移出了群聊' in msg_content_xml and 'sysmsgtemplate' in msg_content_xml :
  475. chatroom_id=msg_data["FromUserName"]["string"]
  476. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,2)
  477. logger.info(f'踢出群聊,移除从通讯录 chatroom_id {chatroom_id} {msg}')
  478. redis_helper.redis_helper.delete_hash_field(f'__AI_OPS_WX__:GROUPS_INFO:{wxid}',chatroom_id)
  479. logger.info(f'清除 chatroom_id{chatroom_id} 数据')
  480. if '已解散该群聊' in msg_content_xml and 'sysmsgtemplate' in msg_content_xml :
  481. chatroom_id=msg_data["FromUserName"]["string"]
  482. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,2)
  483. logger.info(f'解散群聊,移除从通讯录 chatroom_id {chatroom_id} {msg}')
  484. redis_helper.redis_helper.delete_hash_field(f'__AI_OPS_WX__:GROUPS_INFO:{wxid}',chatroom_id)
  485. logger.info(f'清除 chatroom_id{chatroom_id} 数据')
  486. print('撤回消息,拍一拍消息,地理位置')
  487. def handle_mod_contacts(token_id,app_id,wxid,msg_data):
  488. '''
  489. 好友通过验证及好友资料变更的通知消息
  490. '''
  491. logger.info('好友通过验证及好友资料变更的通知消息')
  492. if not check_chatroom(msg_data["UserName"]["string"]):
  493. contact_wxid = msg_data["UserName"]["string"]
  494. # 更新好友信息
  495. ret,msg,contacts_list = gewe_chat.wxchat.fetch_contacts_list(token_id, app_id)
  496. friend_wxids = contacts_list['friends'][3:] # 可以调整截取范围
  497. print(friend_wxids)
  498. #friend_wxids.remove('weixin')
  499. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id, app_id, wxid, friend_wxids)
  500. # keys=redis_helper.redis_helper.client.keys('__AI_OPS_WX__:LOGININFO:*')
  501. # wxid=""
  502. # for k in keys:
  503. # key=k.decode('utf-8')
  504. # hash_key=f"__AI_OPS_WX__:LOGININFO:{key}"
  505. # cache_app_id=redis_helper.redis_helper.get_hash_field(hash_key,"appId")
  506. # if app_id==cache_app_id:
  507. # wxid=redis_helper.redis_helper.get_hash_field(hash_key,"wxid")
  508. # break
  509. # print(wxid)
  510. # print('~~~~~~~~~~~~~~~~~~~~~~~~~~~\n')
  511. # if wxid!="":
  512. # print('~~~~~~~~~~~~~~~~~~~~~~~~~~~\n')
  513. # hash_key=f"__AI_OPS_WX__:CONTACTS_BRIEF:{wxid}"
  514. # ret,msg,contacts_list = gewe_chat.wxchat.fetch_contacts_list(token_id, app_id)
  515. # friend_wxids = contacts_list['friends'][3:] # 可以调整截取范围
  516. # print(friend_wxids)
  517. # #friend_wxids.remove('weixin')
  518. # gewe_chat.wxchat.save_contacts_brief_to_cache(token_id, app_id, wxid, friend_wxids)
  519. # cache_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  520. # cache = json.loads(cache_str) if cache_str else []
  521. # for c in cache:
  522. # if c["userName"]==contact_wxid:
  523. # c["nickName"] = msg_data["NickName"]
  524. # c["snsBgImg"] = msg_data["SnsBgimgId"]
  525. # c["smallHeadImgUrl"] = msg_data["SmallHeadImgUrl"]
  526. # c["signature"]= msg_data["Signature"]
  527. # # 更新缓存,如果有修改过的话
  528. # if cache:
  529. # updated_cache_str = json.dumps(cache)
  530. # redis_helper.redis_helper.set_hash_field(hash_key, "data", updated_cache_str)
  531. else:
  532. logger.info('群聊好友通过验证及好友资料变更的通知消息')
  533. def get_messages_from_cache(hash_key,object:object)->list:
  534. '''
  535. 对话列表
  536. '''
  537. messages=redis_helper.redis_helper.get_hash(hash_key)
  538. wxid=hash_key.split(':')[-1]
  539. if not messages:
  540. messages=[{"role": "system", "content": ""}]
  541. messages.append(object)
  542. redis_helper.redis_helper.set_hash(hash_key,{"data":json.dumps(messages,ensure_ascii=False)},3600)
  543. else:
  544. messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  545. messages = json.loads(messages_str) if messages_str else []
  546. #判断是否含有图片
  547. last_message = messages[-1]
  548. content = last_message.get("content", [])
  549. if isinstance(content, list) and content:
  550. last_content_type = content[-1].get("type")
  551. if last_content_type == 'image_url':
  552. content.append(object['content'][0])
  553. messages[-1]['content']=content
  554. else:
  555. messages.append(object)
  556. else:
  557. messages.append(object)
  558. redis_helper.redis_helper.set_hash(hash_key,{"data":json.dumps(messages,ensure_ascii=False)},3600)
  559. return messages
  560. def fast_gpt_api(messages:list,session_id:str):
  561. api_key="sk-jr69ONIehfGKe9JFphuNk4DU5Y5wooHKHhQv7oSnFzVbwCnW65fXO9kvH" #测试
  562. #api_key="sk-uJDBdKmJVb2cmfldGOvlIY6Qx0AzqWMPD3lS1IzgQYzHNOXv9SKNI" #开发2
  563. api_url = "http://106.15.182.218:3000/api/v1/chat/completions"
  564. headers = {
  565. "Content-Type": "application/json",
  566. "Authorization": f"Bearer {api_key}"
  567. }
  568. data={
  569. "model": "",
  570. "messages":messages,
  571. "chatId": session_id,
  572. "detail": True
  573. }
  574. #print(json.dumps(data,ensure_ascii=False))
  575. logger.info("[CHATGPT] 请求={}".format(json.dumps(data, ensure_ascii=False)))
  576. response = requests.post(url=api_url, headers=headers, data=json.dumps(data), timeout=600)
  577. response.raise_for_status()
  578. response_data = response.json()
  579. logger.info("[CHATGPT] 响应={}".format(json.dumps(response_data, separators=(',', ':'),ensure_ascii=False)))
  580. print(response_data)
  581. return response_data
  582. def get_token_id_by_app_id(app_id: str) -> str:
  583. # 使用 SCAN 避免一次性返回所有的匹配键,逐步扫描
  584. cursor = 0
  585. while True:
  586. cursor, login_keys = redis_helper.redis_helper.client.scan(cursor, match='__AI_OPS_WX__:LOGININFO:*')
  587. # 批量获取所有键的 hash 数据
  588. for k in login_keys:
  589. r = redis_helper.redis_helper.get_hash(k)
  590. if r.get("appId") == app_id:
  591. return r.get("tokenId", "")
  592. # 如果游标为 0,则表示扫描完成
  593. if cursor == 0:
  594. break
  595. return ""
  596. def get_login_info_by_app_id(app_id: str) ->dict:
  597. # 使用 SCAN 避免一次性返回所有的匹配键,逐步扫描
  598. cursor = 0
  599. while True:
  600. cursor, login_keys = redis_helper.redis_helper.client.scan(cursor, match='__AI_OPS_WX__:LOGININFO:*')
  601. # 批量获取所有键的 hash 数据
  602. for k in login_keys:
  603. r = redis_helper.redis_helper.get_hash(k)
  604. if r.get("appId") == app_id:
  605. return k,r
  606. # 如果游标为 0,则表示扫描完成
  607. if cursor == 0:
  608. break
  609. return ""
  610. def contains_url(text):
  611. # 定义检测网址的正则表达式
  612. url_pattern = re.compile(
  613. r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
  614. )
  615. # 检查字符串是否包含网址
  616. return bool(url_pattern.search(text))
  617. def get_first_char_if_digit(s):
  618. if s and s[0].isdigit(): # 判断字符串是否非空且首字符为数字
  619. return int(s[0]) # 返回数字形式
  620. return None # 如果不是数字则返回 None
  621. def remove_at_mention_regex(text):
  622. # 使用正则表达式去掉“在群聊中@了你”
  623. return re.sub(r"在群聊中@了你", "", text)
  624. def extract_nickname(text)->str:
  625. if "在群聊中@了你" in text:
  626. # 如果包含 "在群聊中@了你",提取其前面的名字
  627. match = re.search(r"^(.*?)在群聊中@了你", text)
  628. if match:
  629. return match.group(1).strip()
  630. elif ": @" in text:
  631. # 如果包含 ": @",提取其前面的名字
  632. return text.split(": @")[0].strip()
  633. return ''
  634. def check_chatroom(userName):
  635. pattern = r'^\d+@chatroom$'
  636. if re.match(pattern, userName):
  637. return True
  638. return False