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.

1109 line
47KB

  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 threading,time
  10. from model import Models
  11. import os
  12. from voice import audio_convert
  13. timeout_duration = 8.0
  14. WX_BACKLIST=['fmessage', 'medianote','weixin','weixingongzhong','tmessage']
  15. class MessagesResource(Resource):
  16. def __init__(self):
  17. self.parser = reqparse.RequestParser()
  18. def post(self):
  19. msg = request.get_json()
  20. logger.info(f"收到微信回调消息: {json.dumps(msg, separators=(',', ':'),ensure_ascii=False)}")
  21. type_name =msg.get("TypeName")
  22. app_id = msg.get("Appid")
  23. # token_id = "f828cb3c-1039-489f-b9ae-7494d1778a15"
  24. token_id=get_token_id_by_app_id(app_id)
  25. if token_id=="":
  26. logger.warning('找不到登录信息,不处理')
  27. return jsonify({"message": "收到微信回调消息"})
  28. wxid = msg.get("Wxid",'')
  29. # 自发命令
  30. # if type_name=='AddMsg':
  31. # from_wxid = msg_data["FromUserName"]["string"]
  32. # if wxid=='from_wxid':
  33. # pass
  34. if type_name == 'AddMsg':
  35. handle_self_cmd(wxid,msg)
  36. msg_data = msg.get("Data")
  37. from_wxid = msg_data["FromUserName"]["string"]
  38. config=redis_helper.redis_helper.get_hash(f"__AI_OPS_WX__:WXCHAT_CONFIG")
  39. wxids=config.keys()
  40. WX_BACKLIST.extend(wxids)
  41. if from_wxid in WX_BACKLIST:
  42. logger.warning(f'微信ID {wxid} 在黑名单,不处理')
  43. return jsonify({"message": "收到微信回调消息"})
  44. wx_config = gewe_chat.wxchat.get_wxchat_config_from_cache(wxid)
  45. if type_name=='AddMsg':
  46. if not bool(wx_config.get("agentEnabled",False)):
  47. logger.info(f'微信ID {wxid} 未托管,不处理')
  48. return jsonify({"message": "收到微信回调消息"})
  49. #wxid = msg.get("Wxid")
  50. msg_data = msg.get("Data")
  51. msg_type = msg_data.get("MsgType",None)
  52. from_wxid = msg_data["FromUserName"]["string"]
  53. to_wxid = msg_data["ToUserName"]["string"]
  54. msg_push_content=msg_data.get("PushContent") #群发
  55. #wx_config = gewe_chat.wxchat.get_wxchat_config_from_cache(wxid)
  56. handlers = {
  57. 1: handle_text,
  58. 3: handle_image,
  59. 34: handle_voice,
  60. 42: handle_name_card,
  61. 49: handle_xml,
  62. 37: handle_add_friend_notice,
  63. 10002: handle_10002_msg
  64. }
  65. # (扫码进群情况)判断受否是群聊,并添加到通信录
  66. if check_chatroom(from_wxid) or check_chatroom(to_wxid):
  67. logger.info('群信息')
  68. chatroom_id=from_wxid
  69. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,3)
  70. logger.info(f'保存到通讯录 chatroom_id {chatroom_id} {msg}')
  71. gewe_chat.wxchat.update_group_info_to_cache(token_id,app_id,wxid,chatroom_id)
  72. gewe_chat.wxchat.update_group_members_to_cache(token_id,app_id,wxid,chatroom_id)
  73. handlers[1]=handle_text_group
  74. handlers[3]=handle_image_group
  75. handlers[34]=handle_voice_group
  76. handler = handlers.get(msg_type)
  77. if handler:
  78. return handler(token_id,app_id, wxid,msg_data,from_wxid, to_wxid)
  79. else:
  80. logger.warning(f"微信回调消息类型 {msg_type} 未处理")
  81. elif type_name=='ModContacts':
  82. '''
  83. 好友通过验证及好友资料变更的通知消息
  84. '''
  85. wxid = msg.get("Wxid")
  86. msg_data = msg.get("Data")
  87. #
  88. #handle_mod_contacts(token_id,app_id,wxid,msg_data)
  89. #
  90. threading.Thread(target=handle_mod_contacts, args=(token_id,app_id,wxid,msg_data)).start()
  91. contact_wxid = msg_data["UserName"]["string"]
  92. nickname=msg_data["NickName"]["string"]
  93. city=msg_data["City"]
  94. signature=msg_data["Signature"]
  95. province=msg_data["Province"]
  96. bigHeadImgUrl=msg_data["SnsUserInfo"]["SnsBgimgId"]
  97. country=msg_data["Country"]
  98. sex=msg_data["Sex"]
  99. pyInitial=msg_data["PyInitial"]["string"]
  100. quanPin=msg_data["QuanPin"]["string"]
  101. remark=msg_data.get("Remark").get("string","")
  102. remarkPyInitial=msg_data.get("RemarkPyInitial").get("string","")
  103. remarkQuanPin=msg_data.get("RemarkQuanPin").get("string","")
  104. smallHeadImgUrl=msg_data["smallHeadImgUrl"]
  105. # data=gewe_chat.wxchat.get_brief_info(token_id,app_id,[contact_wxid])
  106. # contact=data[0]
  107. # alias=contact.get("alias")
  108. #推动到kafka
  109. contact_data = {
  110. "alias": None,
  111. "bigHeadImgUrl": bigHeadImgUrl,
  112. "cardImgUrl": None,
  113. "city": city,
  114. "country": country,
  115. "description": None,
  116. "labelList": None,
  117. "nickName": nickname,
  118. "phoneNumList": None,
  119. "province": province,
  120. "pyInitial": pyInitial,
  121. "quanPin": quanPin,
  122. "remark": remark,
  123. "remarkPyInitial": remarkPyInitial,
  124. "remarkQuanPin": remarkQuanPin,
  125. "sex": sex,
  126. "signature": signature,
  127. "smallHeadImgUrl": smallHeadImgUrl,
  128. "snsBgImg": None,
  129. "userName": contact_wxid
  130. }
  131. input_message=utils.wx_mod_contact_message(wxid,contact_data)
  132. kafka_helper.kafka_client.produce_message(input_message)
  133. elif type_name=="DelContacts":
  134. '''
  135. 删除好友通知/退出群聊
  136. '''
  137. msg_data = msg.get("Data")
  138. username=msg_data["UserName"]["string"]
  139. if check_chatroom(username):
  140. logger.info('退出群聊')
  141. wxid = msg.get("Wxid")
  142. chatroom_id=username
  143. redis_helper.redis_helper.delete_hash_field(f'__AI_OPS_WX__:GROUPS_INFO:{wxid}',chatroom_id)
  144. logger.info(f'清除 chatroom_id{chatroom_id} 数据')
  145. else:
  146. logger.info('删除好友通知')
  147. # 推送到kafka
  148. input_message=utils.wx_del_contact_message(wxid,username)
  149. kafka_helper.kafka_client.produce_message(input_message)
  150. elif type_name=="Offline":
  151. '''
  152. 已经离线
  153. '''
  154. wxid = msg.get("Wxid")
  155. logger.warning(f'微信ID {wxid}在设备{app_id}已经离线')
  156. k,r=get_login_info_by_app_id(app_id)
  157. print(k)
  158. redis_helper.redis_helper.update_hash_field(k,'status',0)
  159. redis_helper.redis_helper.update_hash_field(k,'modify_at',int(time.time()))
  160. # 推送到kafka
  161. input_message=utils.wx_offline_message(app_id,wxid)
  162. kafka_helper.kafka_client.produce_message(input_message)
  163. else:
  164. logger.warning(f"未知消息类型")
  165. return jsonify({"message": "收到微信回调消息"})
  166. def handle_name_card(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  167. logger.info('名片消息')
  168. try:
  169. msg_content_xml=msg_data["Content"]["string"]
  170. # 解析XML字符串
  171. root = ET.fromstring(msg_content_xml)
  172. # 提取alias属性
  173. alias_value = root.get("alias")
  174. # 加好友资料
  175. scene = int(root.get("scene"))
  176. v3 = root.get("username")
  177. v4 = root.get("antispamticket")
  178. logger.info(f"alias_value: {alias_value}, scene: {scene}, v3: {v3}, v4: {v4}")
  179. # 判断appid 是否已经创建3天
  180. k, login_info = utils.get_login_info_by_wxid(wxid)
  181. creation_timestamp=int(login_info.get('create_at',time.time()))
  182. current_timestamp = time.time()
  183. three_days_seconds = 3 * 24 * 60 * 60 # 三天的秒数
  184. diff_flag=(current_timestamp - creation_timestamp) >= three_days_seconds
  185. if not diff_flag:
  186. log_content=f'名片添加好友功能,{wxid} 用户创建不够三天,不能使用该功能'
  187. logger.warning(log_content)
  188. response=jsonify({'code': 401, 'message':log_content})
  189. response.status_code = 401
  190. return response
  191. # 将加好友资料添加到待加好友队列
  192. #gewe_chat.wxchat.enqueue_to_add_contacts(wxid,scene,v3,v4)
  193. _,loginfo=utils.get_login_info_by_wxid(wxid)
  194. nickname=loginfo.get('nickName')
  195. add_contact_content=f'您好,我是{nickname}'
  196. #gewe_chat.wxchat.add_contacts(token_id,app_id,scene,Models.OperationType.ADD_FRIEND,v3,v4,add_contact_content)
  197. gewe_chat.wxchat.add_contacts(token_id,app_id,scene,Models.OperationType.ADD_FRIEND.value,v3,v4,add_contact_content)
  198. except ET.ParseError as e:
  199. logger.error(f"XML解析错误: {e}")
  200. except KeyError as e:
  201. logger.error(f"字典键错误: {e}")
  202. except Exception as e:
  203. logger.error(f"未知错误: {e}")
  204. def handle_self_cmd(wxid,msg):
  205. '''
  206. 个人微信命令处理
  207. 如果是个人微信的指令,wxid == from_wxid
  208. commands = {
  209. '启用托管': True,
  210. '关闭托管': False
  211. }
  212. '''
  213. msg_data=msg.get("Data")
  214. from_wxid=msg_data["FromUserName"]["string"]
  215. msg_content=msg_data["Content"]["string"]
  216. if wxid == from_wxid:
  217. commands = {
  218. '启用托管': True,
  219. '关闭托管': False
  220. }
  221. if msg_content in commands:
  222. c_dict = gewe_chat.wxchat.get_wxchat_config_from_cache(wxid)
  223. if c_dict:
  224. from model import Models
  225. agent_config=Models.AgentConfig.model_validate(c_dict)
  226. agent_config.agentEnabled=commands[msg_content]
  227. gewe_chat.wxchat.save_wxchat_config(wxid, agent_config.model_dump())
  228. logger.info(f'{wxid} {"启动" if commands[msg_content] else "关闭"}托管')
  229. def handle_text(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  230. '''
  231. 私聊文本消息
  232. '''
  233. msg_content=msg_data["Content"]["string"]
  234. if wxid == from_wxid: #手动发送消息
  235. logger.info("Active message sending detected")
  236. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id,wxid,[to_wxid])
  237. callback_to_user=msg_data["ToUserName"]["string"]
  238. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  239. input_message=utils.dialogue_message(from_wxid,to_wxid,input_wx_content_dialogue_message)
  240. kafka_helper.kafka_client.produce_message(input_message)
  241. logger.info("发送对话 %s",input_message)
  242. else:
  243. callback_to_user=msg_data["FromUserName"]["string"]
  244. # hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  245. # prompt={"role": "user", "content": [{
  246. # "type": "text",
  247. # "text": msg_content
  248. # }]}
  249. # messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  250. # # 收到的对话
  251. # input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  252. # input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  253. # kafka_helper.kafka_client.produce_message(input_message)
  254. # logger.info("发送对话 %s",input_message)
  255. # cache_data = memory.USER_INTERACTIVE_CACHE.get(wxid)
  256. # if cache_data and cache_data.get('interactive') :
  257. # o=get_first_char_if_digit(msg_content)
  258. # if o is not None:
  259. # userSelectOptions=cache_data.get('options')
  260. # if o < len(userSelectOptions):
  261. # o=o-1
  262. # msg_content=userSelectOptions[o].get("value")
  263. # messages_to_send=[{"role": "user", "content": msg_content}]
  264. # else:
  265. # messages_to_send=[{"role": "user", "content": msg_content}]
  266. # else:
  267. # messages_to_send=[{"role": "user", "content": msg_content}]
  268. # res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  269. # reply_content=res["choices"][0]["message"]["content"]
  270. # description = ''
  271. # userSelectOptions = []
  272. # if isinstance(reply_content, list) and any(item.get("type") == "interactive" for item in reply_content):
  273. # for item in reply_content:
  274. # if item["type"] == "interactive" and item["interactive"]["type"] == "userSelect":
  275. # params = item["interactive"]["params"]
  276. # description = params.get("description")
  277. # userSelectOptions = params.get("userSelectOptions", [])
  278. # values_string = "\n".join(option["value"] for option in userSelectOptions)
  279. # if description is not None:
  280. # memory.USER_INTERACTIVE_CACHE[wxid] = {
  281. # "interactive":True,
  282. # "options": userSelectOptions,
  283. # }
  284. # reply_content=description + '------------------------------\n'+values_string
  285. # elif isinstance(reply_content, list) and any(item.get("type") == "text" for item in reply_content):
  286. # memory.USER_INTERACTIVE_CACHE[wxid] = {
  287. # "interactive":False
  288. # }
  289. # text=''
  290. # for item in reply_content:
  291. # if item["type"] == "text":
  292. # text=item["text"]["content"]
  293. # if text=='':
  294. # # 去除上次上一轮对话再次请求
  295. # cache_messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  296. # cache_messages = json.loads(cache_messages_str) if cache_messages_str else []
  297. # if len(cache_messages) >= 3:
  298. # cache_messages = cache_messages[:-3]
  299. # redis_helper.redis_helper.update_hash_field(hash_key,"data",json.dumps(cache_messages,ensure_ascii=False))
  300. # messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  301. # res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  302. # reply_content=res["choices"][0]["message"]["content"]
  303. # if isinstance(reply_content, list) :
  304. # reply_content=reply_content[0].get('text').get("content")
  305. # else:
  306. # reply_content=text
  307. # else:
  308. # memory.USER_INTERACTIVE_CACHE[wxid] = {
  309. # "interactive":False
  310. # }
  311. # reply_content=res["choices"][0]["message"]["content"]
  312. # gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  313. # gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  314. # # 回复的对话
  315. # input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  316. # input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  317. # kafka_helper.kafka_client.produce_message(input_message)
  318. # logger.info("发送对话 %s",input_message)
  319. # 创建并启动任务线程,将参数传递给 ai_chat_text 函数
  320. task_thread = threading.Thread(
  321. target=ai_chat_text,
  322. args=(token_id,app_id,wxid,msg_data,msg_content)
  323. )
  324. task_thread.start()
  325. # 设置定时器,1秒后检查任务是否超时。这里需要使用 lambda 来传递参数
  326. timeout_timer = threading.Timer(
  327. timeout_duration,
  328. lambda:check_timeout(task_thread, token_id, wxid,app_id, callback_to_user)
  329. )
  330. timeout_timer.daemon=True
  331. timeout_timer.start()
  332. # 等待任务线程完成
  333. task_thread.join()
  334. # 取消定时器
  335. timeout_timer.cancel()
  336. def check_timeout(task_thread:threading.Thread, token_id,wxid, app_id, callback_to_user):
  337. if task_thread.is_alive():
  338. print(f"任务运行时间超过{timeout_duration}秒,token_id={token_id}, app_id={app_id}, callback_to_user={callback_to_user}")
  339. wx_config = gewe_chat.wxchat.get_wxchat_config_from_cache(wxid)
  340. if bool(wx_config.get("chatWaitingMsgEnabled",True)):
  341. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,"亲,我正在组织回复的信息,请稍等一会")
  342. def ai_chat_text(token_id,app_id,wxid,msg_data,msg_content):
  343. start_time = time.time() # 记录任务开始时间
  344. callback_to_user=msg_data["FromUserName"]["string"]
  345. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  346. prompt={"role": "user", "content": [{
  347. "type": "text",
  348. "text": msg_content
  349. }]}
  350. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  351. # 收到的对话
  352. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  353. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  354. kafka_helper.kafka_client.produce_message(input_message)
  355. logger.info("发送对话 %s",input_message)
  356. cache_data = memory.USER_INTERACTIVE_CACHE.get(wxid)
  357. if cache_data and cache_data.get('interactive') :
  358. o=get_first_char_if_digit(msg_content)
  359. if o is not None:
  360. userSelectOptions=cache_data.get('options')
  361. if o < len(userSelectOptions):
  362. o=o-1
  363. msg_content=userSelectOptions[o].get("value")
  364. messages_to_send=[{"role": "user", "content": msg_content}]
  365. else:
  366. messages_to_send=[{"role": "user", "content": msg_content}]
  367. else:
  368. messages_to_send=[{"role": "user", "content": msg_content}]
  369. res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  370. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  371. description = ''
  372. userSelectOptions = []
  373. if isinstance(reply_content, list) and any(item.get("type") == "interactive" for item in reply_content):
  374. for item in reply_content:
  375. if item["type"] == "interactive" and item["interactive"]["type"] == "userSelect":
  376. params = item["interactive"]["params"]
  377. description = params.get("description")
  378. userSelectOptions = params.get("userSelectOptions", [])
  379. values_string = "\n".join(option["value"] for option in userSelectOptions)
  380. if description is not None:
  381. memory.USER_INTERACTIVE_CACHE[wxid] = {
  382. "interactive":True,
  383. "options": userSelectOptions,
  384. }
  385. reply_content=description + '------------------------------\n'+values_string
  386. elif isinstance(reply_content, list) and any(item.get("type") == "text" for item in reply_content):
  387. memory.USER_INTERACTIVE_CACHE[wxid] = {
  388. "interactive":False
  389. }
  390. text=''
  391. for item in reply_content:
  392. if item["type"] == "text":
  393. text=item["text"]["content"]
  394. if text=='':
  395. # 去除上次上一轮对话再次请求
  396. cache_messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  397. cache_messages = json.loads(cache_messages_str) if cache_messages_str else []
  398. if len(cache_messages) >= 3:
  399. cache_messages = cache_messages[:-3]
  400. redis_helper.redis_helper.update_hash_field(hash_key,"data",json.dumps(cache_messages,ensure_ascii=False))
  401. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  402. res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  403. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  404. if isinstance(reply_content, list) :
  405. reply_content=remove_markdown_symbol(reply_content[0].get('text').get("content"))
  406. else:
  407. reply_content=text
  408. else:
  409. memory.USER_INTERACTIVE_CACHE[wxid] = {
  410. "interactive":False
  411. }
  412. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  413. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  414. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  415. # 回复的对话
  416. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  417. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  418. kafka_helper.kafka_client.produce_message(input_message)
  419. logger.info("发送对话 %s",input_message)
  420. end_time = time.time() # 记录任务结束时间
  421. execution_time = end_time - start_time # 计算执行时间
  422. logger.info(f"AI回答任务完成,耗时 {execution_time:.2f} 秒")
  423. def handle_text_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  424. '''
  425. 群聊文本消息
  426. '''
  427. msg_content=msg_data["Content"]["string"]
  428. msg_push_content=msg_data.get("PushContent")
  429. k,login_info=get_login_info_by_app_id(app_id)
  430. nickname=login_info.get("nickName")
  431. if wxid == from_wxid: #手动发送消息
  432. logger.info("Active message sending detected")
  433. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id,wxid,[to_wxid])
  434. callback_to_user=msg_data["ToUserName"]["string"]
  435. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  436. input_message=utils.dialogue_message(from_wxid,to_wxid,input_wx_content_dialogue_message)
  437. kafka_helper.kafka_client.produce_message(input_message)
  438. logger.info("发送对话 %s",input_message)
  439. else:
  440. c = gewe_chat.wxchat.get_wxchat_config_from_cache(wxid)
  441. chatroom_id_white_list = c.get("chatroomIdWhiteList", [])
  442. if not chatroom_id_white_list:
  443. logger.info('白名单为空或未定义,不处理')
  444. return
  445. if from_wxid not in chatroom_id_white_list:
  446. logger.info(f'群ID {from_wxid} 不在白名单中,不处理')
  447. return
  448. if '在群聊中@了你' in msg_push_content or '@'+nickname in msg_push_content:
  449. callback_to_user=msg_data["FromUserName"]["string"]
  450. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  451. prompt={"role": "user", "content": [{
  452. "type": "text",
  453. "text": msg_content
  454. }]}
  455. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  456. # 收到的对话
  457. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  458. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  459. kafka_helper.kafka_client.produce_message(input_message)
  460. logger.info("发送对话 %s",input_message)
  461. cache_data = memory.USER_INTERACTIVE_CACHE.get(wxid)
  462. if cache_data and cache_data.get('interactive') :
  463. o=get_first_char_if_digit(msg_content)
  464. if o is not None:
  465. userSelectOptions=cache_data.get('options')
  466. if o < len(userSelectOptions):
  467. o=o-1
  468. msg_content=userSelectOptions[o].get("value")
  469. messages_to_send=[{"role": "user", "content": msg_content}]
  470. else:
  471. messages_to_send=[{"role": "user", "content": msg_content}]
  472. else:
  473. messages_to_send=[{"role": "user", "content": msg_content}]
  474. res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  475. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  476. description = ''
  477. userSelectOptions = []
  478. if isinstance(reply_content, list) and any(item.get("type") == "interactive" for item in reply_content):
  479. for item in reply_content:
  480. if item["type"] == "interactive" and item["interactive"]["type"] == "userSelect":
  481. params = item["interactive"]["params"]
  482. description = params.get("description")
  483. userSelectOptions = params.get("userSelectOptions", [])
  484. values_string = "\n".join(option["value"] for option in userSelectOptions)
  485. if description is not None:
  486. memory.USER_INTERACTIVE_CACHE[wxid] = {
  487. "interactive":True,
  488. "options": userSelectOptions,
  489. }
  490. reply_content=description + '------------------------------\n'+values_string
  491. elif isinstance(reply_content, list) and any(item.get("type") == "text" for item in reply_content):
  492. memory.USER_INTERACTIVE_CACHE[wxid] = {
  493. "interactive":False
  494. }
  495. text=''
  496. for item in reply_content:
  497. if item["type"] == "text":
  498. text=item["text"]["content"]
  499. if text=='':
  500. # 去除上次上一轮对话再次请求
  501. cache_messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  502. cache_messages = json.loads(cache_messages_str) if cache_messages_str else []
  503. if len(cache_messages) >= 3:
  504. cache_messages = cache_messages[:-3]
  505. redis_helper.redis_helper.update_hash_field(hash_key,"data",json.dumps(cache_messages,ensure_ascii=False))
  506. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  507. res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  508. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  509. else:
  510. reply_content=text
  511. else:
  512. memory.USER_INTERACTIVE_CACHE[wxid] = {
  513. "interactive":False
  514. }
  515. reply_content=res["choices"][0]["message"]["content"]
  516. reply_content='@'+extract_nickname(msg_push_content) + reply_content
  517. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  518. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  519. # 回复的对话
  520. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  521. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  522. kafka_helper.kafka_client.produce_message(input_message)
  523. logger.info("发送对话 %s",input_message)
  524. else:
  525. logger.info('群聊公开消息')
  526. callback_to_user=msg_data["FromUserName"]["string"]
  527. dialogue_message=[{"type": "text", "text": msg_content}]
  528. input_message=utils.dialogue_message(callback_to_user,wxid,dialogue_message)
  529. kafka_helper.kafka_client.produce_message(input_message)
  530. logger.info("发送对话 %s",input_message)
  531. return
  532. def handle_image(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  533. '''
  534. 图片消息
  535. '''
  536. msg_content=msg_data["Content"]["string"]
  537. callback_to_user=from_wxid
  538. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  539. wx_img_url=gewe_chat.wxchat.download_image_msg(token_id,app_id,msg_content)
  540. oss_access_key_id="LTAI5tRTG6pLhTpKACJYoPR5"
  541. oss_access_key_secret="E7dMzeeMxq4VQvLg7Tq7uKf3XWpYfN"
  542. oss_endpoint="http://oss-cn-shanghai.aliyuncs.com"
  543. oss_bucket_name="cow-agent"
  544. oss_prefix="cow"
  545. img_url=utils.upload_oss(oss_access_key_id, oss_access_key_secret, oss_endpoint, oss_bucket_name, wx_img_url, oss_prefix)
  546. prompt={
  547. "role": "user",
  548. "content": [{
  549. "type": "image_url",
  550. "image_url": {"url": img_url}
  551. }]
  552. }
  553. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  554. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,'已经上传了图片,有什么可以为您服务')
  555. logger.info(f"上传图片 URL: {img_url}")
  556. wx_content_dialogue_message=[{"type": "image_url", "image_url": {"url": img_url}}]
  557. input_message=utils.dialogue_message(wxid,callback_to_user,wx_content_dialogue_message)
  558. kafka_helper.kafka_client.produce_message(input_message)
  559. logger.info("发送对话 %s",input_message)
  560. def handle_image_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  561. logger.info('群聊图片消息')
  562. def handle_voice(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  563. '''
  564. 语音消息
  565. '''
  566. callback_to_user=from_wxid
  567. msg_content=msg_data["Content"]["string"]
  568. msg_id=msg_data["MsgId"]
  569. file_url=gewe_chat.wxchat.download_audio_msg(token_id,app_id,msg_id,msg_content)
  570. react_silk_path=utils.save_to_local_from_url(file_url)
  571. react_wav_path = os.path.splitext(react_silk_path)[0] + ".wav"
  572. audio_convert.any_to_wav(react_silk_path,react_wav_path)
  573. react_voice_text=AliVoice().voiceToText(react_wav_path)
  574. os.remove(react_silk_path)
  575. os.remove(react_wav_path)
  576. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  577. messages=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "user", "content": react_voice_text})
  578. ai_res=fast_gpt_api(messages,wxid,callback_to_user)
  579. ai_res_content=remove_markdown_symbol(ai_res["choices"][0]["message"]["content"])
  580. has_url=contains_url(ai_res_content)
  581. if not has_url:
  582. voice_during,voice_url=utils.wx_voice(ai_res_content)
  583. if voice_during < 60 * 1000:
  584. ret,ret_msg,res=gewe_chat.wxchat.post_voice(token_id,app_id,callback_to_user,voice_url,voice_during)
  585. else:
  586. ret,ret_msg,res=gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,ai_res_content)
  587. logger.warning(f'回应语音消息长度 {voice_during/1000}秒,超过60秒,转为文本回复')
  588. if ret==200:
  589. logger.info((f'{wxid} 向 {callback_to_user} 发送语音文本【{ai_res_content}】{ret_msg}'))
  590. else:
  591. logger.warning((f'{wxid} 向 {callback_to_user} 发送语音文本【{ai_res_content}】{ret_msg}'))
  592. ret,ret_msg,res=gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,ai_res_content)
  593. logger.info((f'{wxid} 向 {callback_to_user} 发送文本【{ai_res_content}】{ret_msg}'))
  594. else:
  595. logger.info(f"回复内容包含网址,不发送语音,回复文字内容:{ai_res_content}")
  596. ret,ret_msg,res=gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,ai_res_content)
  597. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": ai_res_content})
  598. # 构造对话消息并发送到 Kafka
  599. input_wx_content_dialogue_message = [{"type": "text", "text": ai_res_content}]
  600. input_message = utils.dialogue_message(wxid, callback_to_user, input_wx_content_dialogue_message,True)
  601. kafka_helper.kafka_client.produce_message(input_message)
  602. logger.info("发送对话 %s", input_message)
  603. def handle_voice_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  604. logger.info('语音消息')
  605. def handle_xml(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  606. '''
  607. 处理xml
  608. '''
  609. msg_content_xml=msg_data["Content"]["string"]
  610. root = ET.fromstring(msg_content_xml)
  611. type_value = int(root.find(".//appmsg/type").text)
  612. handlers = {
  613. 57: handle_xml_reference,
  614. 5: handle_xml_invite_group
  615. }
  616. handler = handlers.get(type_value)
  617. if handler:
  618. return handler(token_id,app_id, wxid,msg_data,from_wxid, to_wxid)
  619. # elif "邀请你加入了群聊" in msg_content_xml: # 邀请加入群聊
  620. # logger.warning(f"xml消息 {type_value} 邀请你加入了群聊.todo")
  621. else:
  622. print(f"xml消息 {type_value} 未解析")
  623. def handle_xml_reference(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  624. '''
  625. 引用消息
  626. 判断此类消息的逻辑:$.Data.MsgType=49 并且 解析$.Data.Content.string中的xml msg.appmsg.type=57
  627. '''
  628. callback_to_user=from_wxid
  629. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{callback_to_user}'
  630. msg_content= msg_data["PushContent"]
  631. prompt={"role": "user", "content": [{
  632. "type": "text",
  633. "text": msg_content
  634. }]}
  635. # 收到的对话
  636. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  637. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  638. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  639. kafka_helper.kafka_client.produce_message(input_message)
  640. logger.info("发送对话 %s",input_message)
  641. # 回复的对话
  642. res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  643. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  644. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  645. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  646. kafka_helper.kafka_client.produce_message(input_message)
  647. logger.info("发送对话 %s",input_message)
  648. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  649. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  650. def handle_xml_invite_group(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  651. '''
  652. 群聊邀请
  653. 判断此类消息的逻辑:$.Data.MsgType=49
  654. 并且 解析$.Data.Content.string中的xml msg.appmsg.title=邀请你加入群聊(根据手机设置的系统语言title会有调整,不同语言关键字不同)
  655. '''
  656. logger.info(f'{wxid} 群聊邀请')
  657. msg_content_xml=msg_data["Content"]["string"]
  658. root = ET.fromstring(msg_content_xml)
  659. title_value = root.find(".//appmsg/title").text
  660. if '邀请你加入群聊' in title_value:
  661. invite_url = root.find('.//url').text
  662. ret,msg,data=gewe_chat.wxchat.agree_join_room(token_id,app_id,invite_url)
  663. if ret==200:
  664. logger.info(f'群聊邀请,同意加入群聊 {msg} {data}')
  665. chatroom_id=data.get('chatroomId','')
  666. # if not chatroom_id:
  667. # logger.warning(f'群聊邀请,同意加入群聊失败 {msg} {data}')
  668. # return
  669. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,3)
  670. logger.info(f'群聊邀请,保存到通讯录 chatroom_id {chatroom_id} {msg}')
  671. gewe_chat.wxchat.update_group_info_to_cache(token_id,app_id,wxid,chatroom_id)
  672. gewe_chat.wxchat.update_group_members_to_cache(token_id,app_id,wxid,chatroom_id)
  673. else:
  674. logger.warning(f'群聊邀请,同意加入群聊失败 {msg} {data}')
  675. def handle_add_friend_notice(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  676. '''
  677. 好友添加请求通知
  678. '''
  679. logger.info('好友添加请求通知')
  680. msg_content_xml=msg_data["Content"]["string"]
  681. root = ET.fromstring(msg_content_xml)
  682. msg_content = root.attrib.get('content', None)
  683. v3= root.attrib.get('encryptusername', None)
  684. v4= root.attrib.get('ticket', None)
  685. scene=root.attrib.get('scene', None)
  686. to_contact_wxid=root.attrib.get('fromusername', None)
  687. wxid=msg_data["ToUserName"]["string"]
  688. # 自动同意好友
  689. # print(v3)
  690. # print(v4)
  691. # print(scene)
  692. # print(msg_content)
  693. # 操作类型,2添加好友 3同意好友 4拒绝好友
  694. #option=2
  695. option=3
  696. reply_add_contact_contact="亲,我是你的好友"
  697. ret,ret_msg=gewe_chat.wxchat.add_contacts(token_id,app_id,scene,option,v3,v4,reply_add_contact_contact)
  698. if ret==200:
  699. logger.info('自动添加好友成功')
  700. # 好友发送的文字
  701. hash_key = f'__AI_OPS_WX__:MESSAGES:{wxid}:{to_contact_wxid}'
  702. prompt={"role": "user", "content": [{"type": "text","text": msg_content}]}
  703. messages_to_send=gewe_chat.wxchat.save_session_messages_to_cache(hash_key, prompt)
  704. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  705. input_message=utils.dialogue_message(to_contact_wxid,wxid,input_wx_content_dialogue_message)
  706. kafka_helper.kafka_client.produce_message(input_message)
  707. logger.info("发送对话 %s",input_message)
  708. callback_to_user=to_contact_wxid
  709. res=fast_gpt_api(messages_to_send,wxid,callback_to_user)
  710. reply_content=remove_markdown_symbol(res["choices"][0]["message"]["content"])
  711. #保存好友信息
  712. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id, wxid,[to_contact_wxid])
  713. # 保存到缓存
  714. gewe_chat.wxchat.save_session_messages_to_cache(hash_key, {"role": "assistant", "content": reply_content})
  715. # 发送信息
  716. gewe_chat.wxchat.post_text(token_id,app_id, to_contact_wxid,reply_content)
  717. # 发送到kafka
  718. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  719. input_message=utils.dialogue_message(wxid,to_contact_wxid,input_wx_content_dialogue_message,True)
  720. kafka_helper.kafka_client.produce_message(input_message)
  721. logger.info("发送对话 %s",input_message)
  722. else:
  723. logger.warning("添加好友失败")
  724. def handle_10002_msg(token_id,app_id, wxid,msg_data,from_wxid, to_wxid):
  725. '''
  726. 群聊邀请
  727. 撤回消息
  728. 拍一拍消息
  729. 地理位置
  730. 踢出群聊通知
  731. 解散群聊通知
  732. 发布群公告
  733. '''
  734. msg_content_xml=msg_data["Content"]["string"]
  735. # 群聊邀请
  736. if '邀请你加入了群聊' in msg_content_xml and check_chatroom(msg_data["FromUserName"]["string"]):
  737. chatroom_id=msg_data["FromUserName"]["string"]
  738. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,3)
  739. logger.info(f'群聊邀请,保存到通讯录 chatroom_id {chatroom_id} {msg}')
  740. gewe_chat.wxchat.update_group_info_to_cache(token_id,app_id,wxid,chatroom_id)
  741. gewe_chat.wxchat.update_group_members_to_cache(token_id,app_id,wxid,chatroom_id)
  742. if '移出了群聊' in msg_content_xml and 'sysmsgtemplate' in msg_content_xml :
  743. chatroom_id=msg_data["FromUserName"]["string"]
  744. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,2)
  745. logger.info(f'踢出群聊,移除从通讯录 chatroom_id {chatroom_id} {msg}')
  746. redis_helper.redis_helper.delete_hash_field(f'__AI_OPS_WX__:GROUPS_INFO:{wxid}',chatroom_id)
  747. logger.info(f'清除 chatroom_id{chatroom_id} 数据')
  748. if '已解散该群聊' in msg_content_xml and 'sysmsgtemplate' in msg_content_xml :
  749. chatroom_id=msg_data["FromUserName"]["string"]
  750. ret,msg,data=gewe_chat.wxchat.save_contract_list(token_id,app_id,chatroom_id,2)
  751. logger.info(f'解散群聊,移除从通讯录 chatroom_id {chatroom_id} {msg}')
  752. redis_helper.redis_helper.delete_hash_field(f'__AI_OPS_WX__:GROUPS_INFO:{wxid}',chatroom_id)
  753. logger.info(f'清除 chatroom_id{chatroom_id} 数据')
  754. print('撤回消息,拍一拍消息,地理位置')
  755. def handle_mod_contacts(token_id,app_id,wxid,msg_data):
  756. '''
  757. 好友通过验证及好友资料变更的通知消息
  758. '''
  759. logger.info('好友通过验证及好友资料变更的通知消息')
  760. if not check_chatroom(msg_data["UserName"]["string"]):
  761. contact_wxid = msg_data["UserName"]["string"]
  762. # 更新好友信息
  763. # 检查好友关系,不是好友则删除
  764. # ret,msg,check_relation=gewe_chat.wxchat.check_relation(token_id, app_id,[contact_wxid])
  765. # first_item = check_relation[0]
  766. # check_relation_status=first_item.get('relation')
  767. # logger.info(f'{wxid} 好友 {contact_wxid} 关系检查:{check_relation_status}')
  768. # if check_relation_status != 0:
  769. # gewe_chat.wxchat.delete_contacts_brief_from_cache(wxid, [contact_wxid])
  770. # logger.info(f'好友关系异常:{check_relation_status},删除好友 {contact_wxid} 信息')
  771. # else:
  772. # gewe_chat.wxchat.save_contacts_brief_to_cache(token_id, app_id, wxid, [contact_wxid])
  773. ret,msg,contacts_list = gewe_chat.wxchat.fetch_contacts_list(token_id, app_id)
  774. # friend_wxids = contacts_list['friends'][3:] # 可以调整截取范围
  775. # print(friend_wxids)
  776. #friend_wxids.remove('fmessage')
  777. #friend_wxids.remove('weixin')
  778. friend_wxids = [c for c in contacts_list['friends'] if c not in ['fmessage', 'medianote','weixin','weixingongzhong','tmessage']] # 可以调整截取范围
  779. print(f'{wxid}的好友数量 {len(friend_wxids)}')
  780. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id, app_id, wxid, friend_wxids)
  781. else:
  782. logger.info('群聊好友通过验证及好友资料变更的通知消息')
  783. def get_messages_from_cache(hash_key,object:object)->list:
  784. '''
  785. 对话列表
  786. '''
  787. messages=redis_helper.redis_helper.get_hash(hash_key)
  788. wxid=hash_key.split(':')[-1]
  789. if not messages:
  790. messages=[{"role": "system", "content": ""}]
  791. messages.append(object)
  792. redis_helper.redis_helper.set_hash(hash_key,{"data":json.dumps(messages,ensure_ascii=False)},3600)
  793. else:
  794. messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  795. messages = json.loads(messages_str) if messages_str else []
  796. #判断是否含有图片
  797. last_message = messages[-1]
  798. content = last_message.get("content", [])
  799. if isinstance(content, list) and content:
  800. last_content_type = content[-1].get("type")
  801. if last_content_type == 'image_url':
  802. content.append(object['content'][0])
  803. messages[-1]['content']=content
  804. else:
  805. messages.append(object)
  806. else:
  807. messages.append(object)
  808. redis_helper.redis_helper.set_hash(hash_key,{"data":json.dumps(messages,ensure_ascii=False)},3600)
  809. return messages
  810. def fast_gpt_api(messages:list,wixd:str,friend_wxid:str):
  811. c=gewe_chat.wxchat.get_wxchat_config_from_cache(wixd)
  812. api_key=c.get('agentTokenId',"sk-jr69ONIehfGKe9JFphuNk4DU5Y5wooHKHhQv7oSnFzVbwCnW65fXO9kvH")
  813. print(f'流程key:{api_key}\n')
  814. #api_key="sk-jr69ONIehfGKe9JFphuNk4DU5Y5wooHKHhQv7oSnFzVbwCnW65fXO9kvH" #测试
  815. #api_key="sk-uJDBdKmJVb2cmfldGOvlIY6Qx0AzqWMPD3lS1IzgQYzHNOXv9SKNI" #开发2
  816. api_url = "http://106.15.182.218:3000/api/v1/chat/completions"
  817. headers = {
  818. "Content-Type": "application/json",
  819. "Authorization": f"Bearer {api_key}"
  820. }
  821. session_id=f'{wixd}-{friend_wxid}'
  822. data={
  823. "model": "",
  824. "messages":messages,
  825. "chatId": session_id,
  826. "detail": True
  827. }
  828. #print(json.dumps(data,ensure_ascii=False))
  829. logger.info("[CHATGPT] 请求={}".format(json.dumps(data, ensure_ascii=False)))
  830. response = requests.post(url=api_url, headers=headers, data=json.dumps(data), timeout=600)
  831. response.raise_for_status()
  832. response_data = response.json()
  833. logger.info("[CHATGPT] 响应={}".format(json.dumps(response_data, separators=(',', ':'),ensure_ascii=False)))
  834. #print(response_data)
  835. return response_data
  836. def get_token_id_by_app_id(app_id: str) -> str:
  837. # 使用 SCAN 避免一次性返回所有的匹配键,逐步扫描
  838. cursor = 0
  839. while True:
  840. cursor, login_keys = redis_helper.redis_helper.client.scan(cursor, match='__AI_OPS_WX__:LOGININFO:*')
  841. # 批量获取所有键的 hash 数据
  842. for k in login_keys:
  843. r = redis_helper.redis_helper.get_hash(k)
  844. if r.get("appId") == app_id:
  845. return r.get("tokenId", "")
  846. # 如果游标为 0,则表示扫描完成
  847. if cursor == 0:
  848. break
  849. return ""
  850. def get_login_info_by_app_id(app_id: str) ->dict:
  851. # 使用 SCAN 避免一次性返回所有的匹配键,逐步扫描
  852. cursor = 0
  853. while True:
  854. cursor, login_keys = redis_helper.redis_helper.client.scan(cursor, match='__AI_OPS_WX__:LOGININFO:*')
  855. # 批量获取所有键的 hash 数据
  856. for k in login_keys:
  857. r = redis_helper.redis_helper.get_hash(k)
  858. if r.get("appId") == app_id:
  859. return k,r
  860. # 如果游标为 0,则表示扫描完成
  861. if cursor == 0:
  862. break
  863. return ""
  864. def contains_url(text):
  865. # 定义检测网址的正则表达式
  866. url_pattern = re.compile(
  867. r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
  868. )
  869. # 检查字符串是否包含网址
  870. return bool(url_pattern.search(text))
  871. def get_first_char_if_digit(s):
  872. if s and s[0].isdigit(): # 判断字符串是否非空且首字符为数字
  873. return int(s[0]) # 返回数字形式
  874. return None # 如果不是数字则返回 None
  875. def remove_at_mention_regex(text):
  876. # 使用正则表达式去掉“在群聊中@了你”
  877. return re.sub(r"在群聊中@了你", "", text)
  878. def extract_nickname(text)->str:
  879. if "在群聊中@了你" in text:
  880. # 如果包含 "在群聊中@了你",提取其前面的名字
  881. match = re.search(r"^(.*?)在群聊中@了你", text)
  882. if match:
  883. return match.group(1).strip()
  884. elif ": @" in text:
  885. # 如果包含 ": @",提取其前面的名字
  886. return text.split(": @")[0].strip()
  887. return ''
  888. def check_chatroom(userName):
  889. pattern = r'^\d+@chatroom$'
  890. if re.match(pattern, userName):
  891. return True
  892. return False
  893. # def remove_markdown_symbol(text: str):
  894. # # 移除markdown格式,目前先移除**
  895. # if not text or not isinstance(text, str):
  896. # return text
  897. # return re.sub(r'\*\*(.*?)\*\*', r'\1', text)
  898. def remove_markdown_symbol(text: str):
  899. # 移除markdown格式,目前先移除**
  900. if not text or not isinstance(text, str):
  901. return text
  902. # 去除加粗、斜体等格式
  903. #text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text) # 去除加粗
  904. text=re.sub(r'\*\*(.*?)\*\*', r'\1', text)
  905. text = re.sub(r'\*([^*]+)\*', r'\1', text) # 去除斜体
  906. text = re.sub(r'__([^_]+)__', r'\1', text) # 去除加粗(下划线)
  907. text = re.sub(r'_(.*?)_', r'\1', text) # 去除斜体(下划线)
  908. # 去除行内代码块
  909. text = re.sub(r'`([^`]+)`', r'\1', text)
  910. # 去除换行符\n,或者多余的空格
  911. #text = re.sub(r'\n+', ' ', text)
  912. # 去除列表编号等
  913. #text = re.sub(r'^\d+\.\s*', '', text, flags=re.MULTILINE)
  914. #text = re.sub('[\\\`\*\_\[\]\#\+\-\!\>]', '', text)
  915. text = re.sub('[\\\`\*\_\[\]\#\+\!\>]', '', text)
  916. print(text)
  917. return text