测试其他线程问题。
This commit is contained in:
@@ -84,6 +84,8 @@ source .venv/bin/activate # Linux/Mac
|
|||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
# 特别安装,pip 版本依赖不准确,需要单独安装
|
||||||
|
pip install pysilk-mod
|
||||||
```
|
```
|
||||||
|
|
||||||
### 主要依赖包
|
### 主要依赖包
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from flask import Blueprint, render_template, jsonify, request, current_app
|
from flask import Blueprint, render_template, jsonify, request, current_app
|
||||||
from .auth import login_required
|
from .auth import login_required
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -7,59 +8,49 @@ from loguru import logger
|
|||||||
# 创建联系人管理蓝图
|
# 创建联系人管理蓝图
|
||||||
contacts_bp = Blueprint('contacts', __name__, url_prefix='/contacts')
|
contacts_bp = Blueprint('contacts', __name__, url_prefix='/contacts')
|
||||||
|
|
||||||
|
# 创建线程池
|
||||||
|
message_thread_pool = ThreadPoolExecutor(max_workers=10, thread_name_prefix="message_sender_")
|
||||||
|
|
||||||
|
# 创建共享的事件循环
|
||||||
|
shared_loop = None
|
||||||
|
loop_lock = threading.Lock()
|
||||||
|
|
||||||
|
def get_or_create_loop():
|
||||||
|
"""获取或创建共享的事件循环"""
|
||||||
|
global shared_loop
|
||||||
|
with loop_lock:
|
||||||
|
if shared_loop is None:
|
||||||
|
shared_loop = asyncio.new_event_loop()
|
||||||
|
# 在新线程中运行事件循环
|
||||||
|
def run_loop():
|
||||||
|
asyncio.set_event_loop(shared_loop)
|
||||||
|
shared_loop.run_forever()
|
||||||
|
|
||||||
|
loop_thread = threading.Thread(target=run_loop, daemon=True)
|
||||||
|
loop_thread.start()
|
||||||
|
return shared_loop
|
||||||
|
|
||||||
def send_message_in_thread(func, *args, **kwargs):
|
def send_message_in_thread(func, *args, **kwargs):
|
||||||
"""在独立线程中发送消息"""
|
"""使用共享事件循环发送消息"""
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
loop = None
|
|
||||||
try:
|
try:
|
||||||
# 创建新的事件循环
|
loop = get_or_create_loop()
|
||||||
loop = asyncio.new_event_loop()
|
|
||||||
asyncio.set_event_loop(loop)
|
|
||||||
|
|
||||||
# 创建异步任务
|
# 创建异步任务
|
||||||
async def send():
|
async def send():
|
||||||
try:
|
try:
|
||||||
await func(*args, **kwargs)
|
await func(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送消息失败: {e}")
|
logger.error(f"发送消息失败: {e}")
|
||||||
finally:
|
|
||||||
# 发送完成后停止事件循环
|
|
||||||
loop.stop()
|
|
||||||
|
|
||||||
# 创建并运行任务
|
|
||||||
asyncio.run_coroutine_threadsafe(send(), loop)
|
|
||||||
|
|
||||||
# 运行事件循环直到停止
|
|
||||||
loop.run_forever()
|
|
||||||
|
|
||||||
|
# 在共享事件循环中运行任务
|
||||||
|
future = asyncio.run_coroutine_threadsafe(send(), loop)
|
||||||
|
# 等待任务完成,设置超时时间
|
||||||
|
future.result(timeout=10)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"线程执行失败: {e}")
|
logger.error(f"消息发送任务执行失败: {e}")
|
||||||
finally:
|
|
||||||
# 确保清理资源
|
|
||||||
if loop is not None:
|
|
||||||
try:
|
|
||||||
# 取消所有待处理的任务
|
|
||||||
pending = asyncio.all_tasks(loop)
|
|
||||||
for task in pending:
|
|
||||||
task.cancel()
|
|
||||||
|
|
||||||
# 运行事件循环直到所有任务都被取消
|
# 使用线程池提交任务
|
||||||
loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
message_thread_pool.submit(run)
|
||||||
|
|
||||||
# 停止事件循环
|
|
||||||
loop.stop()
|
|
||||||
|
|
||||||
# 关闭事件循环
|
|
||||||
loop.close()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"清理资源失败: {e}")
|
|
||||||
|
|
||||||
# 创建并启动线程
|
|
||||||
thread = threading.Thread(target=run)
|
|
||||||
thread.daemon = True # 设置为守护线程,这样主程序退出时线程会自动结束
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
|
|
||||||
# 联系人管理页面
|
# 联系人管理页面
|
||||||
|
|||||||
@@ -329,7 +329,6 @@ class GroupBotManager:
|
|||||||
# 手动添加的管理员ID列表
|
# 手动添加的管理员ID列表
|
||||||
manual_admin_list = [
|
manual_admin_list = [
|
||||||
"Jyunere", # 示例ID,请替换为实际的微信ID
|
"Jyunere", # 示例ID,请替换为实际的微信ID
|
||||||
"wxid_abcdef", # 示例ID,请替换为实际的微信ID
|
|
||||||
"filehelper" # 文件传输助手,方便自己测试
|
"filehelper" # 文件传输助手,方便自己测试
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
101
wechat_ipad/models/friend_circle_info.py
Normal file
101
wechat_ipad/models/friend_circle_info.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
FRIEND_CIRCLE_INFO = """
|
||||||
|
<TimelineObject>
|
||||||
|
<id>
|
||||||
|
<![CDATA[{id}]]>
|
||||||
|
</id>
|
||||||
|
<username>
|
||||||
|
<![CDATA[{wxid}]]>
|
||||||
|
</username>
|
||||||
|
<createTime>
|
||||||
|
<![CDATA[{time}]]>
|
||||||
|
</createTime>
|
||||||
|
<contentDescShowType>0</contentDescShowType>
|
||||||
|
<contentDescScene>0</contentDescScene>
|
||||||
|
<private>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</private>
|
||||||
|
<contentDesc>
|
||||||
|
<![CDATA[{content}]]>
|
||||||
|
</contentDesc>
|
||||||
|
<contentattr>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</contentattr>
|
||||||
|
<sourceUserName></sourceUserName>
|
||||||
|
<publicUserName></publicUserName>
|
||||||
|
<sourceNickName></sourceNickName>
|
||||||
|
<statisticsData></statisticsData>
|
||||||
|
<weappInfo>
|
||||||
|
<appUserName></appUserName>
|
||||||
|
<pagePath></pagePath>
|
||||||
|
<version>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</version>
|
||||||
|
<isHidden>0</isHidden>
|
||||||
|
<debugMode>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</debugMode>
|
||||||
|
<shareActionId></shareActionId>
|
||||||
|
<isGame>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</isGame>
|
||||||
|
<messageExtraData></messageExtraData>
|
||||||
|
<subType>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</subType>
|
||||||
|
<preloadResources></preloadResources>
|
||||||
|
</weappInfo>
|
||||||
|
<canvasInfoXml></canvasInfoXml>
|
||||||
|
<ContentObject>
|
||||||
|
<contentStyle>
|
||||||
|
<![CDATA[1]]>
|
||||||
|
</contentStyle>
|
||||||
|
<contentSubStyle>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</contentSubStyle>
|
||||||
|
<title></title>
|
||||||
|
<description></description>
|
||||||
|
<contentUrl></contentUrl>
|
||||||
|
<mediaList>
|
||||||
|
<media>
|
||||||
|
<id>
|
||||||
|
<![CDATA[14672447414385119864]]>
|
||||||
|
</id>
|
||||||
|
<type>
|
||||||
|
<![CDATA[2]]>
|
||||||
|
</type>
|
||||||
|
<title></title>
|
||||||
|
<description></description>
|
||||||
|
<private>
|
||||||
|
<![CDATA[0]]>
|
||||||
|
</private>
|
||||||
|
<url type=\"1\" md5=\"c661215f338618b3282a6ea5174a0fb5\">
|
||||||
|
<![CDATA[http://szmmsns.qpic.cn/mmsns/AcIhsXSWkDeNF6K5icia87OmccBSE2sDH5gib5UWibjTCU1snn0WANibQoy4Obad1ibmQO6aNQXOTP4Fc/0]]>
|
||||||
|
</url>
|
||||||
|
<thumb type=\"1\">
|
||||||
|
<![CDATA[http://szmmsns.qpic.cn/mmsns/AcIhsXSWkDeNF6K5icia87OmccBSE2sDH5gib5UWibjTCU1snn0WANibQoy4Obad1ibmQO6aNQXOTP4Fc/150]]>
|
||||||
|
</thumb>
|
||||||
|
<videoDuration>
|
||||||
|
<![CDATA[0.0]]>
|
||||||
|
</videoDuration>
|
||||||
|
<size totalSize=\"47880.0\" width=\"1080.0\" height=\"1080.0\"></size>
|
||||||
|
</media>
|
||||||
|
</mediaList>
|
||||||
|
</ContentObject>
|
||||||
|
<actionInfo>
|
||||||
|
<appMsg>
|
||||||
|
<mediaTagName></mediaTagName>
|
||||||
|
<messageExt></messageExt>
|
||||||
|
<messageAction></messageAction>
|
||||||
|
</appMsg>
|
||||||
|
</actionInfo>
|
||||||
|
<appInfo>
|
||||||
|
<id></id>
|
||||||
|
</appInfo>
|
||||||
|
<location poiClassifyId=\"\" poiName=\"\" poiAddress=\"\" poiClassifyType=\"0\" city=\"\"></location>
|
||||||
|
<streamvideo>
|
||||||
|
<streamvideourl></streamvideourl>
|
||||||
|
<streamvideothumburl></streamvideothumburl>
|
||||||
|
<streamvideoweburl></streamvideoweburl>
|
||||||
|
</streamvideo>
|
||||||
|
</TimelineObject>
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user