Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

289 rindas
12KB

  1. from flask import Flask, send_from_directory, request,jsonify
  2. from flask_restful import Api,got_request_exception
  3. from resources.messages_resource import MessagesResource
  4. from resources.contacts_resources import DeleteFriendResource,GetFriendsInfoResource
  5. from resources.config_reources import GetWxchatConfigResource ,SaveWxchatConfigResource
  6. from resources.groups_resources import GetGroupsInfoResource
  7. from resources.login_resources import GetLoginInfoResource,GetLoginWxQRCodeResource,LoginWxCaptchCodeResource
  8. from resources.sns_resources import SendSNSTextResource,SendSNSImageResource, SendSNSVideoResource
  9. from common.log import logger, log_exception
  10. from common.interceptors import before_request, after_request, handle_exception
  11. import threading
  12. from common import kafka_helper, redis_helper,utils
  13. from model import Models
  14. import logging
  15. from config import load_config
  16. from wechat.biz import start_kafka_consumer_thread
  17. from wechat import gewe_chat
  18. import os,time,json,time
  19. from voice.ali.ali_voice import AliVoice
  20. # 自定义错误消息
  21. errors = {
  22. 'UserAlreadyExistsError': {
  23. 'message': "A user with that username already exists.",
  24. 'status': 409,
  25. },
  26. 'ResourceDoesNotExist': {
  27. 'message': "A resource with that ID no longer exists.",
  28. 'status': 410,
  29. 'extra': "Any extra information you want.",
  30. },
  31. }
  32. def fetch_and_save_contacts():
  33. """
  34. 获取联系人列表并保存到缓存
  35. """
  36. wxchat=gewe_chat.wxchat
  37. while True:
  38. try:
  39. login_keys = list(redis_helper.redis_helper.client.scan_iter(match='__AI_OPS_WX__:LOGININFO:*'))
  40. logger.info(f"Fetching login keys: {login_keys}")
  41. # 遍历每一个获取到的登录键
  42. for k in login_keys:
  43. try:
  44. r= redis_helper.redis_helper.get_hash(k)
  45. # print(r)
  46. token_id = r.get('tokenId')
  47. app_id = r.get('appId')
  48. wxid = r.get('wxid')
  49. status=r.get('status')
  50. if status=='1':
  51. ret,msg,contacts_list = wxchat.fetch_contacts_list(token_id, app_id)
  52. if ret==200:
  53. friend_wxids = [c for c in contacts_list['friends'] if c not in ['fmessage', 'medianote','weixin','weixingongzhong']] # 可以调整截取范围
  54. print(f'{wxid}的好友数量 {len(friend_wxids)}')
  55. wxchat.save_contacts_brief_to_cache(token_id, app_id, wxid, friend_wxids)
  56. logger.info(f'微信ID {wxid} 登录APPID {app_id} 成功,联系人已定时保存')
  57. chatrooms=contacts_list['chatrooms']
  58. wxchat.save_groups_info_to_cache(token_id, app_id, wxid, chatrooms)
  59. wxchat.save_groups_members_to_cache(token_id, app_id, wxid, chatrooms)
  60. logger.info(f'微信ID {wxid} 登录APPID {app_id} 成功,群信息已定时保存')
  61. else:
  62. logger.warning(f'{msg}-微信ID {wxid} 登录APPID {app_id} 不能获取好友和群资料')
  63. else:
  64. logger.info(f'微信ID {wxid} 未登录 {app_id} ,联系人不能定时保存')
  65. time.sleep(3)
  66. except Exception as e:
  67. logger.error(f'处理好友和群资料出错 login key {k}: {str(e)}', exc_info=True)
  68. except Exception as e:
  69. logger.error(f'发送错误 {str(e)}', exc_info=True)
  70. time.sleep(3600*1)
  71. def auto_add_contacts_from_chatrooms():
  72. logger.info('自动从群添加好友')
  73. wxchat=gewe_chat.wxchat
  74. while True:
  75. login_keys = list(redis_helper.redis_helper.client.scan_iter(match='__AI_OPS_WX__:LOGININFO:*'))
  76. #logger.info(f"Fetching login keys: {login_keys}")
  77. # 遍历每一个获取到的登录键
  78. for k in login_keys:
  79. r= redis_helper.redis_helper.get_hash(k)
  80. # print(r)
  81. token_id = r.get('tokenId')
  82. app_id = r.get('appId')
  83. nickname=r.get('nickName')
  84. wxid = r.get('wxid')
  85. status=r.get('status')
  86. # 启动线程处理登录信息
  87. thread = threading.Thread(target=process_add_contacts_from_chatrooms, args=(wxchat,status, wxid, token_id, app_id))
  88. thread.start()
  89. time.sleep(3)
  90. #time.sleep(60*10)
  91. time.sleep(3600*24)
  92. def process_add_contacts_from_chatrooms(wxchat:gewe_chat.GeWeChatCom,status, wxid, token_id, app_id):
  93. if status == '1':
  94. c = wxchat.get_wxchat_config_from_cache(wxid)
  95. contacts = wxchat.get_contacts_brief_from_cache(wxid)
  96. contact_wxids = [c.get('userName') for c in contacts]
  97. chatrooms = c.get('addContactsFromChatroomIdWhiteList', [])
  98. for chatroom_id in chatrooms:
  99. chatroom = wxchat.get_group_info_from_cache(wxid, chatroom_id)
  100. chatroom_member=wxchat.get_group_members_from_cache(wxid, chatroom_id)
  101. chatroom_nickname = chatroom.get('nickName')
  102. chatroom_owner_wxid = chatroom_member.get('chatroomOwner', None)
  103. admin_wxid = chatroom_member.get('adminWxid', None)
  104. logger.info(f'{chatroom_nickname} 的群主是 {chatroom_owner_wxid},管理员是{admin_wxid}')
  105. contact_wxids_set = set(contact_wxids)
  106. if admin_wxid is not None:
  107. contact_wxids_set.add(admin_wxid)
  108. if chatroom_owner_wxid is not None:
  109. contact_wxids_set.add(chatroom_owner_wxid)
  110. contact_wxids_set.add(wxid)
  111. unavailable_wixds=wxchat.check_wixd_group_add_contacts_history(wxid,chatroom_id)
  112. contact_wxids_set.update(unavailable_wixds)
  113. chatroot_member_list = chatroom.get('memberList', [])
  114. remaining_chatroot_members = [x for x in chatroot_member_list if x.get('wxid') not in contact_wxids_set]
  115. nickname = next((member['nickName'] for member in chatroot_member_list if member['wxid'] == wxid), None)
  116. logger.info(f'{nickname}-{wxid} 在 {chatroom_nickname} 群里还可以邀请的好友有:{[x.get("nickName") for x in remaining_chatroot_members]}')
  117. for m in remaining_chatroot_members:
  118. ret, msg, data = wxchat.add_group_member_as_friend(token_id, app_id, chatroom_id, m.get('wxid'), f'我是群聊"{chatroom_nickname}"群的{nickname}')
  119. if ret==200:
  120. contact_wxids= m.get('wxid')
  121. history=Models.AddGroupContactsHistory.model_validate({
  122. "chatroomId":chatroom_id,
  123. "wxid":wxid,
  124. "contactWixd":contact_wxids,
  125. "addTime":int(time.time())
  126. })
  127. wxchat.save_group_add_contacts_history(wxid,chatroom_id,contact_wxids,history)
  128. else:
  129. logger.info(f'群好友邀请失败原因:{data}')
  130. logger.info(f'{nickname} 向 {chatroom_nickname}-{chatroom_id} 群的 {m.get("nickName")}-{m.get("wxid")} 发送好友邀请 {msg}')
  131. time.sleep(10)
  132. time.sleep(20)
  133. else:
  134. logger.info(f'微信ID {wxid} 未登录 {app_id} ,群成员不能定时定时')
  135. # def background_wxchat_thread():
  136. # scan_wx_login_info()
  137. # # 启动同步联系人线程
  138. # threading.Thread(target=fetch_and_save_contacts).start()
  139. # threading.Thread(target=auto_add_contacts_from_chatrooms).start()
  140. def background_wxchat_thread():
  141. lock_name = "background_wxchat_thread_lock"
  142. lock_identifier = str(time.time()) # 使用时间戳作为唯一标识
  143. # 尝试获取分布式锁
  144. if redis_helper.redis_helper.acquire_lock(lock_name, timeout=60):
  145. try:
  146. logger.info("分布式锁已成功获取")
  147. # 启动任务
  148. scan_wx_login_info()
  149. threading.Thread(target=fetch_and_save_contacts).start()
  150. threading.Thread(target=auto_add_contacts_from_chatrooms).start()
  151. # 保持锁的续期
  152. while True:
  153. time.sleep(30) # 每30秒检查一次锁的状态
  154. if not redis_helper.redis_helper.renew_lock(lock_name, lock_identifier, timeout=60):
  155. break # 如果无法续期锁,退出循环
  156. finally:
  157. # 释放锁
  158. redis_helper.redis_helper.release_lock(lock_name, lock_identifier)
  159. else:
  160. # 如果获取锁失败,等待一段时间后重试
  161. time.sleep(10)
  162. background_wxchat_thread()
  163. def scan_wx_login_info():
  164. wxchat = gewe_chat.wxchat
  165. cursor = 0
  166. while True:
  167. cursor, login_keys = redis_helper.redis_helper.client.scan(cursor, match='__AI_OPS_WX__:LOGININFO:*')
  168. for k in login_keys:
  169. r = redis_helper.redis_helper.get_hash(k)
  170. app_id=r.get("appId")
  171. #tel=r.get("mobile")
  172. token_id=r.get("tokenId")
  173. wxid=r.get("wxid")
  174. is_online = wxchat.check_online(token_id, app_id)
  175. if is_online:
  176. logger.info(f'微信ID {wxid} 在APPID {app_id} 已经在线')
  177. else:
  178. # 尝试重连
  179. res = wxchat.reconnection(token_id, app_id)
  180. if res.get('ret') == 200:
  181. logger.info(f'微信ID {wxid} 在APPID {app_id} 重连成功')
  182. # 同步联系人
  183. #fetch_and_save_contacts(wxchat, token_id, app_id, k)
  184. else:
  185. print("重连失败,重新登录...")
  186. print("发送离线消息到kafka")
  187. redis_helper.redis_helper.update_hash_field(k,'status',0)
  188. time.sleep(3)
  189. # 如果游标为 0,则表示扫描完成
  190. if cursor == 0:
  191. break
  192. app = Flask(__name__)
  193. # api = Api(app)
  194. flask_api = Api(app,errors=errors, catch_all_404s=True)
  195. # 设置日志(logger 已在 log.py 中配置)
  196. app.logger.handlers.clear() # 清除 Flask 默认的日志处理器
  197. app.logger.addHandler(logger.handlers[1]) # 使用文件日志处理器
  198. app.logger.setLevel(logging.DEBUG) # 设置日志级别
  199. # 添加拦截器
  200. app.before_request(before_request)
  201. app.after_request(after_request)
  202. app.register_error_handler(Exception, handle_exception)
  203. # 定义路由
  204. flask_api.add_resource(MessagesResource, '/messages')
  205. flask_api.add_resource(DeleteFriendResource, '/api/contacts/deletefriend')
  206. flask_api.add_resource(GetFriendsInfoResource, '/api/contacts/getfriends')
  207. flask_api.add_resource(GetWxchatConfigResource, '/api/wxchat/getconfig')
  208. flask_api.add_resource(SaveWxchatConfigResource, '/api/wxchat/saveconfig')
  209. flask_api.add_resource(GetGroupsInfoResource, '/api/groups/getchatroominfo')
  210. flask_api.add_resource(GetLoginInfoResource, '/api/agent/getlogin')
  211. flask_api.add_resource(GetLoginWxQRCodeResource, '/api/agent/getwxqrcode')
  212. flask_api.add_resource(LoginWxCaptchCodeResource, '/api/agent/logincaptchcode')
  213. flask_api.add_resource(SendSNSTextResource, '/api/sns/sendtext')
  214. flask_api.add_resource(SendSNSImageResource, '/api/sns/sendimages')
  215. flask_api.add_resource(SendSNSVideoResource, '/api/sns/sendvideo')
  216. load_config()
  217. kafka_helper.start()
  218. redis_helper.start()
  219. gewe_chat.start()
  220. start_kafka_consumer_thread()
  221. # background_wxchat_thread()
  222. threading.Thread(target=background_wxchat_thread).start()
  223. if __name__ == '__main__':
  224. # 获取环境变量
  225. environment = os.environ.get('environment', 'default')
  226. port = 80 if environment == 'default' else 5000
  227. app.run(debug=False, host='0.0.0.0', port=port)