backup: save local changes on 2025-09-26

This commit is contained in:
2025-09-26 15:30:05 +08:00
parent a5eca7b3f1
commit 68ebf173e3
7874 changed files with 2590582 additions and 9 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3160
Utils/Pdd/PddUtilsOld.py Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
D:/GUI_main/Utils/PythonNew32/Log/SaiNiu_202509180015.log
D:/GUI_main/Utils/PythonNew32/Log/SaiNiu_202509191651.log

View File

@@ -1,12 +1,12 @@
=== SaiNiu DLL 启动脚本开始 ===
Python版本: 3.13.5 (tags/v3.13.5:6cb20a2, Jun 11 2025, 15:56:57) [MSC v.1943 32 bit (Intel)]
当前工作目录: D:\GUI_main\Utils\PythonNew32
时间戳: 2025-09-17 18:52:56
时间戳: 2025-09-19 16:44:51
正在加载 SaiNiuApi.dll...
[OK] SaiNiuApi.dll 加载成功
[OK] DLL函数类型定义完成
正在启动服务器 - 端口: 3030
[OK] Access_ServerStart 服务器启动结果: {"code":200,"msg":"调用成功","app_v":"3.0.9.0","passkey":"1758106377811"}
[OK] Access_ServerStart 服务器启动结果: {"code":200,"msg":"调用成功","app_v":"3.0.9.0","passkey":"1758271492787"}
=== DLL服务启动完成进入监控模式 ===
[HEARTBEAT] DLL服务心跳 #1 - 服务正常运行中...
[HEARTBEAT] DLL服务心跳 #2 - 服务正常运行中...

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,982 @@
# WebSocket/BackendClient.py
import json
import threading
import websockets
import uuid
import asyncio
from typing import List, Dict, Any, Optional, Callable
from config import get_gui_ws_url
class BackendClient:
"""后端WebSocket客户端"""
def __init__(self, url: str, token: str = None):
self.token = token
self.uuid = str(uuid.uuid4())
self.url = url
# 消息处理回调函数
self.store_list_callback: Optional[Callable] = None
self.customers_callback: Optional[Callable] = None
self.message_callback: Optional[Callable] = None
self.transfer_callback: Optional[Callable] = None
self.close_callback: Optional[Callable] = None
self.error_callback: Optional[Callable] = None
self.login_callback: Optional[Callable] = None # 新增平台登录下发cookies回调
self.success_callback: Optional[Callable] = None # 新增:后端连接成功回调
self.is_connected = False
def connect(self):
"""连接到WebSocket服务器"""
if self.is_connected:
return
self.thread = threading.Thread(target=self._run_loop, daemon=True)
self.thread.start()
def disconnect(self):
"""断开WebSocket连接"""
if self.loop and self.loop.is_running():
asyncio.run_coroutine_threadsafe(self._close(), self.loop)
if self.thread and self.thread.is_alive():
self.thread.join(timeout=3)
self.is_connected = False
async def _close(self):
"""异步关闭连接"""
if self.websocket:
await self.websocket.close()
self.is_connected = False
def _run_loop(self):
"""在新线程中运行事件循环"""
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
try:
self.loop.run_until_complete(self._connect_and_listen())
except Exception as e:
print(f"WebSocket异常: {e}")
finally:
self.loop.close()
async def _connect_and_listen(self):
"""连接并监听消息"""
try:
self.websocket = await websockets.connect(self.url)
self.is_connected = True
self.on_connected()
async for message in self.websocket:
try:
# 打印原始文本帧与长度
try:
raw_len = len(message.encode('utf-8')) if isinstance(message, str) else len(message)
print(f"后端发送消息体内容:{message}")
except Exception:
pass
data = json.loads(message)
self.on_message_received(data)
except json.JSONDecodeError:
print(f"JSON解析错误: {message}")
except Exception as e:
self.is_connected = False
self.on_error(str(e))
@classmethod
def from_exe_token(cls, exe_token: str):
"""使用 exe_token 构造单连接客户端ws/gui/<token>/"""
url = get_gui_ws_url(exe_token)
return cls(url=url, token=exe_token)
def set_callbacks(self,
store_list: Callable = None,
customers: Callable = None,
message: Callable = None,
transfer: Callable = None,
close: Callable = None,
error: Callable = None,
login: Callable = None,
success: Callable = None):
"""设置各种消息类型的回调函数"""
if store_list:
self.store_list_callback = store_list
if customers:
self.customers_callback = customers
if message:
self.message_callback = message
if transfer:
self.transfer_callback = transfer
if close:
self.close_callback = close
if error:
self.error_callback = error
if login:
self.login_callback = login
if success:
self.success_callback = success
def on_connected(self):
"""连接成功时的处理"""
print("后端WebSocket连接成功")
# 不再主动请求 get_store避免与后端不兼容导致协程未完成
def on_message_received(self, message: Dict[str, Any]):
"""处理接收到的消息 - 根据WebSocket文档v2更新"""
# 统一打印后端下发的完整消息结构体
try:
import json as _json
print("=== Backend -> GUI Message ===")
print(_json.dumps(message, ensure_ascii=False, indent=2))
print("=== End Message ===")
except Exception:
pass
msg_type = message.get('type')
try:
if msg_type == 'get_store':
self._handle_store_list(message)
elif msg_type == 'get_customers':
self._handle_customers_list(message)
elif msg_type == 'success':
print("后端连接服务成功")
# 可在此触发上层UI通知
if self.success_callback:
try:
self.success_callback()
except Exception:
pass
elif msg_type == 'message':
self._handle_message(message)
elif msg_type == 'transfer':
self._handle_transfer(message)
elif msg_type == 'close':
self._handle_close(message)
elif msg_type == 'pong':
self._handle_pong(message)
elif msg_type == 'connect_success': # 兼容旧版
self._handle_connect_success(message)
elif msg_type == 'login': # 新版后台下发平台cookies
self._handle_login(message)
elif msg_type == 'error':
self._handle_error_message(message)
elif msg_type == 'staff_list':
self._handle_staff_list(message)
else:
print(f"未知消息类型: {msg_type}")
except Exception as e:
error_msg = f"处理消息异常: {e}"
print(error_msg)
if self.error_callback:
self.error_callback(error_msg, message)
def on_error(self, error: str):
"""错误处理"""
print(f"后端连接错误: {error}")
if self.error_callback:
self.error_callback(error, None)
# ==================== 发送消息方法 ====================
def send_message(self, message: Dict[str, Any]):
"""
发送消息到后端
Args:
message: 要发送的消息字典
"""
if not self.is_connected or not self.loop:
raise Exception("WebSocket未连接")
future = asyncio.run_coroutine_threadsafe(
self._send_to_backend(message), self.loop
)
return future.result(timeout=8)
async def _send_to_backend(self, message: Dict[str, Any]):
"""异步发送消息到后端"""
if not self.websocket:
raise Exception("WebSocket连接不存在")
import json
message_str = json.dumps(message, ensure_ascii=False)
await self.websocket.send(message_str)
print(f"发送消息到后端: {message}")
def send_ping(self, custom_uuid: str = None, custom_token: str = None):
"""
发送心跳包
如果接收到关闭的消息后心跳包要带上token
"""
ping_message = {
'type': 'ping',
'uuid': custom_uuid or self.uuid
}
token = custom_token or self.token
if token:
ping_message['token'] = token
return self.send_message(ping_message)
def get_store(self):
"""获取店铺信息"""
message = {
'type': 'get_store',
'token': self.token or ''
}
return self.send_message(message)
def send_text_message(self, content: str, sender_id: str, store_id: str, pin_image: str = None):
"""发送文本消息 - 根据WebSocket文档v2更新"""
message = {
'type': 'message',
'content': content,
'msg_type': 'text',
'sender': {'id': sender_id},
'store_id': store_id
}
if pin_image:
message['pin_image'] = pin_image
return self.send_message(message)
def send_image_message(self, image_url: str, sender_id: str, store_id: str, pin_image: str = None):
"""发送图片消息 - 根据WebSocket文档v2更新"""
message = {
'type': 'message',
'content': image_url,
'msg_type': 'image',
'sender': {'id': sender_id},
'store_id': store_id
}
if pin_image:
message['pin_image'] = pin_image
return self.send_message(message)
def send_video_message(self, video_url: str, sender_id: str, store_id: str, pin_image: str = None):
"""发送视频消息 - 根据WebSocket文档v2更新"""
message = {
'type': 'message',
'content': video_url,
'msg_type': 'video',
'sender': {'id': sender_id},
'store_id': store_id
}
if pin_image:
message['pin_image'] = pin_image
return self.send_message(message)
def send_order_card(self, product_id: str, order_number: str, sender_id: str, store_id: str, pin_image: str = None):
"""发送订单卡片 - 根据WebSocket文档v2更新"""
message = {
'type': 'message',
'content': f'商品id{product_id} 订单号:{order_number}',
'msg_type': 'order_card',
'sender': {'id': sender_id},
'store_id': store_id
}
if pin_image:
message['pin_image'] = pin_image
return self.send_message(message)
def send_product_card(self, product_url: str, sender_id: str, store_id: str, pin_image: str = None):
"""发送商品卡片 - 根据WebSocket文档v2更新"""
message = {
'type': 'message',
'content': product_url,
'msg_type': 'product_card',
'sender': {'id': sender_id},
'store_id': store_id
}
if pin_image:
message['pin_image'] = pin_image
return self.send_message(message)
def send_staff_list(self, staff_list: List[Dict], store_id: str):
"""发送客服列表 - 根据WebSocket文档v2更新"""
message = {
'type': 'staff_list',
'content': '客服列表更新',
'data': {'staff_list': staff_list},
'store_id': store_id
}
return self.send_message(message)
# 保持向后兼容的旧方法(标记为已废弃)
def send_file_message(self, content: str, uid: str, pin: str, store_id: str):
"""发送文件消息 - 已废弃请使用send_video_message或send_image_message"""
print("警告: send_file_message已废弃请根据文件类型使用send_video_message或send_image_message")
# 尝试自动检测文件类型
content_lower = content.lower()
if any(ext in content_lower for ext in ['.mp4', '.avi', '.mov', '.wmv', '.flv']):
return self.send_video_message(content, uid, store_id)
elif any(ext in content_lower for ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']):
return self.send_image_message(content, uid, store_id)
else:
# 默认作为文本消息发送
return self.send_text_message(content, uid, store_id)
def send_transfer(self, customer: str, pin: str, store_id: str):
"""发送转接消息"""
message = {
'type': 'transfer',
'customer': customer, # 客服名称
'pin': pin, # 顾客名称
'store_id': store_id
}
return self.send_message(message)
def close_store(self, store_id: str):
"""关闭店铺"""
message = {
'type': 'close',
'store_id': store_id
}
return self.send_message(message)
# ==================== 消息处理方法 ====================
def _handle_store_list(self, message: Dict[str, Any]):
"""处理店铺列表"""
store_list = message.get('store_list', [])
print(f"获取到{len(store_list)}个店铺:")
for store in store_list:
merchant_name = store.get('merchant_name', '')
store_id = store.get('store_id', '')
store_platform = store.get('store_platform', '')
print(f" - {merchant_name} (ID: {store_id}, 平台: {store_platform})")
if self.store_list_callback:
self.store_list_callback(store_list)
def _handle_customers_list(self, message: Dict[str, Any]):
"""处理客服列表"""
customers = message.get('customers', [])
store_id = message.get('store_id', '')
print(f"店铺{store_id}的客服列表,共{len(customers)}个客服:")
for customer in customers:
pin = customer.get('pin', '')
nickname = customer.get('nickname', '')
print(f" - {nickname} ({pin})")
if self.customers_callback:
self.customers_callback(customers, store_id)
def _handle_message(self, message: Dict[str, Any]):
"""处理消息"""
store_id = message.get('store_id', '')
data = message.get('data')
content = message.get('content', '')
print(f"[{store_id}] [{message.get('msg_type', 'unknown')}] : {content}")
# 尝试将后端AI/客服回复转发到对应平台
try:
receiver = message.get('receiver') or (data.get('receiver') if isinstance(data, dict) else None) or {}
recv_pin = receiver.get('id')
if recv_pin and store_id:
# 根据store_id动态确定平台类型
platform_type = self._get_platform_by_store_id(store_id)
if platform_type == "京东":
self._forward_to_jd(store_id, recv_pin, content)
elif platform_type == "抖音":
self._forward_to_douyin(store_id, recv_pin, content)
elif platform_type == "千牛":
self._forward_to_qianniu(store_id, recv_pin, content)
elif platform_type == "拼多多":
self._forward_to_pdd(store_id, recv_pin, content)
else:
print(f"[Forward] 未知平台类型或未找到店铺: {platform_type}, store_id={store_id}")
except Exception as e:
print(f"转发到平台失败: {e}")
if self.message_callback:
self.message_callback(data if data else message, store_id)
def _get_platform_by_store_id(self, store_id: str) -> str:
"""根据店铺ID获取平台类型"""
try:
# 从WebSocket管理器获取平台信息
from WebSocket.backend_singleton import get_websocket_manager
manager = get_websocket_manager()
if manager and hasattr(manager, 'platform_listeners'):
for key, listener_info in manager.platform_listeners.items():
if listener_info.get('store_id') == store_id:
return listener_info.get('platform', '')
return ""
except Exception as e:
print(f"获取平台类型失败: {e}")
return ""
def _forward_to_jd(self, store_id: str, recv_pin: str, content: str):
"""转发消息到京东平台"""
try:
from Utils.JD.JdUtils import WebsocketManager as JDWSManager
jd_mgr = JDWSManager()
shop_key = f"京东:{store_id}"
entry = jd_mgr.get_connection(shop_key)
if not entry:
print(f"[JD Forward] 未找到连接: {shop_key}")
return
platform_info = (entry or {}).get('platform') or {}
ws = platform_info.get('ws')
aid = platform_info.get('aid')
pin_zj = platform_info.get('pin_zj')
vender_id = platform_info.get('vender_id')
loop = platform_info.get('loop')
print(
f"[JD Forward] shop_key={shop_key} has_ws={bool(ws)} aid={aid} pin_zj={pin_zj} vender_id={vender_id} has_loop={bool(loop)} recv_pin={recv_pin}")
if ws and aid and pin_zj and vender_id and loop and content:
async def _send():
import hashlib as _hashlib
import time as _time
import json as _json
msg = {
"ver": "4.3",
"type": "chat_message",
"from": {"pin": pin_zj, "app": "im.waiter", "clientType": "comet"},
"to": {"app": "im.customer", "pin": recv_pin},
"id": _hashlib.md5(str(int(_time.time() * 1000)).encode()).hexdigest(),
"lang": "zh_CN",
"aid": aid,
"timestamp": int(_time.time() * 1000),
"readFlag": 0,
"body": {
"content": content,
"translated": False,
"param": {"cusVenderId": vender_id},
"type": "text"
}
}
await ws.send(_json.dumps(msg))
import asyncio as _asyncio
_future = _asyncio.run_coroutine_threadsafe(_send(), loop)
try:
_future.result(timeout=2)
print(f"[JD Forward] 已转发到平台: pin={recv_pin}, content_len={len(content)}")
except Exception as fe:
print(f"[JD Forward] 转发提交失败: {fe}")
else:
print("[JD Forward] 条件不足,未转发:",
{
'has_ws': bool(ws), 'has_aid': bool(aid), 'has_pin_zj': bool(pin_zj),
'has_vender_id': bool(vender_id), 'has_loop': bool(loop), 'has_content': bool(content)
})
except Exception as e:
print(f"[JD Forward] 转发失败: {e}")
def _forward_to_douyin(self, store_id: str, recv_pin: str, content: str):
"""转发消息到抖音平台"""
try:
from Utils.Dy.DyUtils import DouYinWebsocketManager
dy_mgr = DouYinWebsocketManager()
shop_key = f"抖音:{store_id}"
entry = dy_mgr.get_connection(shop_key)
if not entry:
print(f"[DY Forward] 未找到连接: {shop_key}")
return
platform_info = entry.get('platform', {})
douyin_bot = platform_info.get('douyin_bot')
message_handler = platform_info.get('message_handler')
print(
f"[DY Forward] shop_key={shop_key} has_bot={bool(douyin_bot)} has_handler={bool(message_handler)} recv_pin={recv_pin}")
if douyin_bot and message_handler and content:
# 在消息处理器的事件循环中发送消息
def send_in_loop():
try:
# 获取消息处理器的事件循环
loop = message_handler._loop
if loop and not loop.is_closed():
# 在事件循环中执行发送
future = asyncio.run_coroutine_threadsafe(
message_handler.send_message_external(recv_pin, content),
loop
)
# 等待结果
try:
result = future.result(timeout=5)
if result:
print(f"[DY Forward] 已转发到平台: pin={recv_pin}, content_len={len(content)}")
else:
print(f"[DY Forward] 转发失败: 消息处理器返回False")
except Exception as fe:
print(f"[DY Forward] 转发执行失败: {fe}")
else:
print(f"[DY Forward] 事件循环不可用")
except Exception as e:
print(f"[DY Forward] 发送过程异常: {e}")
# 在新线程中执行发送操作
import threading
send_thread = threading.Thread(target=send_in_loop, daemon=True)
send_thread.start()
else:
print("[DY Forward] 条件不足,未转发:",
{
'has_bot': bool(douyin_bot),
'has_handler': bool(message_handler),
'has_content': bool(content)
})
except Exception as e:
print(f"[DY Forward] 转发失败: {e}")
def _forward_to_qianniu(self, store_id: str, recv_pin: str, content: str):
"""转发消息到千牛平台"""
try:
# TODO: 实现千牛平台的消息转发逻辑
print(
f"[QN Forward] 千牛平台消息转发功能待实现: store_id={store_id}, recv_pin={recv_pin}, content={content}")
except Exception as e:
print(f"[QN Forward] 转发失败: {e}")
def _forward_to_pdd(self, store_id: str, recv_pin: str, content: str):
"""转发消息到拼多多平台"""
try:
from Utils.Pdd.PddUtils import WebsocketManager as PDDWSManager
pdd_mgr = PDDWSManager()
shop_key = f"拼多多:{store_id}"
entry = pdd_mgr.get_connection(shop_key)
if not entry:
print(f"[PDD Forward] 未找到连接: {shop_key}")
return
platform_info = entry.get('platform', {})
pdd_instance = platform_info.get('pdd_instance')
loop = platform_info.get('loop')
print(
f"[PDD Forward] shop_key={shop_key} has_pdd_instance={bool(pdd_instance)} has_loop={bool(loop)} recv_pin={recv_pin}")
if pdd_instance and loop and content:
# 在拼多多实例的事件循环中发送消息
def send_in_loop():
try:
# 在事件循环中执行发送
future = asyncio.run_coroutine_threadsafe(
pdd_instance.send_message_external(recv_pin, content),
loop
)
# 等待结果
try:
result = future.result(timeout=10) # 拼多多可能需要更长时间
if result:
print(f"[PDD Forward] 已转发到平台: uid={recv_pin}, content_len={len(content)}")
else:
print(f"[PDD Forward] 转发失败: 拼多多实例返回False")
except Exception as fe:
print(f"[PDD Forward] 转发执行失败: {fe}")
except Exception as e:
print(f"[PDD Forward] 发送过程异常: {e}")
# 在新线程中执行发送操作
import threading
send_thread = threading.Thread(target=send_in_loop, daemon=True)
send_thread.start()
else:
print("[PDD Forward] 条件不足,未转发:",
{
'has_pdd_instance': bool(pdd_instance),
'has_loop': bool(loop),
'has_content': bool(content)
})
except Exception as e:
print(f"[PDD Forward] 转发失败: {e}")
def _transfer_to_pdd(self, customer_service_id: str, user_id: str, store_id: str):
"""执行拼多多平台转接操作"""
try:
from Utils.Pdd.PddUtils import WebsocketManager as PDDWSManager
pdd_mgr = PDDWSManager()
shop_key = f"拼多多:{store_id}"
entry = pdd_mgr.get_connection(shop_key)
if not entry:
print(f"[PDD Transfer] 未找到拼多多连接: {shop_key}")
return
platform_info = entry.get('platform', {})
pdd_instance = platform_info.get('pdd_instance')
loop = platform_info.get('loop')
print(f"[PDD Transfer] 找到拼多多连接,准备执行转接: user_id={user_id}, cs_id={customer_service_id}")
if pdd_instance and loop:
# 设置目标客服ID并执行转接
def transfer_in_loop():
try:
# 设置目标客服ID
pdd_instance.csid = customer_service_id
# 在事件循环中执行转接
future = asyncio.run_coroutine_threadsafe(
pdd_instance.handle_transfer_message({
"content": customer_service_id,
"receiver": {"id": user_id}
}),
loop
)
# 等待转接结果
try:
result = future.result(timeout=15) # 转接可能需要更长时间
if result:
print(f"[PDD Transfer] ✅ 转接成功: user_id={user_id} -> cs_id={customer_service_id}")
else:
print(f"[PDD Transfer] ❌ 转接失败: user_id={user_id}")
except Exception as fe:
print(f"[PDD Transfer] 转接执行失败: {fe}")
except Exception as e:
print(f"[PDD Transfer] 转接过程异常: {e}")
# 在新线程中执行转接操作
import threading
transfer_thread = threading.Thread(target=transfer_in_loop, daemon=True)
transfer_thread.start()
else:
print(f"[PDD Transfer] 条件不足: has_pdd_instance={bool(pdd_instance)}, has_loop={bool(loop)}")
except Exception as e:
print(f"[PDD Transfer] 拼多多转接失败: {e}")
def _transfer_to_jd(self, customer_service_id: str, user_id: str, store_id: str):
"""执行京东平台转接操作"""
try:
from Utils.JD.JdUtils import WebsocketManager as JDWSManager
jd_mgr = JDWSManager()
shop_key = f"京东:{store_id}"
entry = jd_mgr.get_connection(shop_key)
if not entry:
print(f"[JD Transfer] 未找到京东连接: {shop_key}")
return
platform_info = entry.get('platform', {})
websocket = platform_info.get('ws') # 京东使用'ws'字段
aid = platform_info.get('aid', '')
pin_zj = platform_info.get('pin_zj', '')
print(f"[JD Transfer] 找到京东连接,准备执行转接: user_id={user_id}, cs_id={customer_service_id}")
print(f"[JD Transfer] 连接信息: has_ws={bool(websocket)}, aid={aid}, pin_zj={pin_zj}")
if websocket:
# 执行转接操作
def transfer_in_loop():
try:
# 导入京东工具类
from Utils.JD.JdUtils import FixJdCookie
# 创建临时实例用于转接
jd_instance = FixJdCookie()
# 执行转接操作
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
result = loop.run_until_complete(
jd_instance.transfer_customer(
websocket, aid, user_id, pin_zj, customer_service_id
)
)
if result:
print(f"[JD Transfer] ✅ 转接成功: user_id={user_id} -> cs_id={customer_service_id}")
else:
print(f"[JD Transfer] ❌ 转接失败: user_id={user_id}")
finally:
loop.close()
except Exception as e:
print(f"[JD Transfer] 转接过程异常: {e}")
# 在新线程中执行转接操作
import threading
transfer_thread = threading.Thread(target=transfer_in_loop, daemon=True)
transfer_thread.start()
else:
print(f"[JD Transfer] 条件不足: has_ws={bool(websocket)}, aid='{aid}', pin_zj='{pin_zj}'")
except Exception as e:
print(f"[JD Transfer] 京东转接失败: {e}")
def _transfer_to_dy(self, customer_service_id: str, user_id: str, store_id: str):
"""执行抖音平台转接操作"""
try:
from Utils.Dy.DyUtils import DouYinWebsocketManager as DYWSManager
dy_mgr = DYWSManager()
shop_key = f"抖音:{store_id}"
entry = dy_mgr.get_connection(shop_key)
if not entry:
print(f"[DY Transfer] 未找到抖音连接: {shop_key}")
return
platform_info = entry.get('platform', {})
dy_instance = platform_info.get('douyin_bot') # 修正字段名称
cookie_dict = platform_info.get('cookie', {})
print(f"[DY Transfer] 找到抖音连接,准备执行转接: user_id={user_id}, cs_id={customer_service_id}")
print(f"[DY Transfer] 连接信息: has_douyin_bot={bool(dy_instance)}, has_cookie={bool(cookie_dict)}")
if dy_instance:
# 执行转接操作
def transfer_in_loop():
try:
# 抖音转接通过message_handler执行
if dy_instance and hasattr(dy_instance, 'message_handler') and dy_instance.message_handler:
# 获取实际的抖音店铺ID从cookie中获取
shop_id = cookie_dict.get('SHOP_ID', store_id) # 优先使用cookie中的SHOP_ID
print(f"[DY Transfer] 使用shop_id: {shop_id}")
print(
f"[DY Transfer] 转接参数: receiver_id={user_id}, shop_id={shop_id}, staff_id={customer_service_id}")
# 检查是否是自己转给自己的情况
try:
# 获取可用客服列表来验证
staff_list = dy_instance.message_handler.get_casl()
if staff_list:
print(f"[DY Transfer] 当前可用客服数量: {len(staff_list)}")
if len(staff_list) <= 1:
print(f"[DY Transfer] ⚠️ 只有一个客服在线,可能无法转接")
# 查找目标客服信息
target_staff = None
for staff in staff_list:
if str(staff.get('staffId', '')) == str(customer_service_id):
target_staff = staff
break
if target_staff:
print(
f"[DY Transfer] 找到目标客服: {target_staff.get('staffName', 'Unknown')} (ID: {customer_service_id})")
else:
print(f"[DY Transfer] ⚠️ 未找到目标客服ID: {customer_service_id}")
print(
f"[DY Transfer] 可用客服列表: {[{'id': s.get('staffId'), 'name': s.get('staffName')} for s in staff_list]}")
else:
print(f"[DY Transfer] ⚠️ 无法获取客服列表")
except Exception as e:
print(f"[DY Transfer] 获取客服列表时出错: {e}")
# 执行同步转接操作
result = dy_instance.message_handler.transfer_conversation(
receiver_id=user_id,
shop_id=shop_id,
staff_id=customer_service_id
)
if result:
print(f"[DY Transfer] ✅ 转接成功: user_id={user_id} -> cs_id={customer_service_id}")
else:
print(f"[DY Transfer] ❌ 转接失败: user_id={user_id}")
print(
f"[DY Transfer] 💡 可能原因1) 只有一个客服无法转接 2) 客服ID不存在 3) 权限不足 4) 会话状态不允许转接")
else:
print(f"[DY Transfer] ⚠️ 抖音实例或message_handler不可用")
except Exception as e:
print(f"[DY Transfer] 转接过程异常: {e}")
# 在新线程中执行转接操作
import threading
transfer_thread = threading.Thread(target=transfer_in_loop, daemon=True)
transfer_thread.start()
else:
print(f"[DY Transfer] 条件不足: has_douyin_bot={bool(dy_instance)}")
except Exception as e:
print(f"[DY Transfer] 抖音转接失败: {e}")
def _transfer_to_qianniu(self, customer_service_id: str, user_id: str, store_id: str):
"""执行千牛平台转接操作"""
try:
# TODO: 实现千牛平台转接逻辑
print(f"[QN Transfer] 千牛平台转接功能待实现: user_id={user_id}, cs_id={customer_service_id}")
except Exception as e:
print(f"[QN Transfer] 千牛转接失败: {e}")
def _handle_transfer(self, message: Dict[str, Any]):
"""处理转接消息"""
# 新版转接消息格式: {"type": "transfer", "content": "客服ID", "receiver": {"id": "用户ID"}, "store_id": "店铺ID"}
customer_service_id = message.get('content', '') # 目标客服ID
receiver_info = message.get('receiver', {})
user_id = receiver_info.get('id', '') # 用户ID
store_id = message.get('store_id', '')
print(f"转接消息: 顾客{user_id}已转接给客服{customer_service_id} (店铺: {store_id})")
# 根据店铺ID确定平台类型并执行转接
try:
platform_type = self._get_platform_by_store_id(store_id)
if platform_type == "京东":
# 京东转接逻辑
self._transfer_to_jd(customer_service_id, user_id, store_id)
elif platform_type == "抖音":
# 抖音转接逻辑
self._transfer_to_dy(customer_service_id, user_id, store_id)
elif platform_type == "千牛":
# 千牛转接逻辑
self._transfer_to_qianniu(customer_service_id, user_id, store_id)
elif platform_type == "拼多多":
self._transfer_to_pdd(customer_service_id, user_id, store_id)
else:
print(f"[Transfer] 未知平台类型或未找到店铺: {platform_type}, store_id={store_id}")
except Exception as e:
print(f"执行转接操作失败: {e}")
# 保持旧版回调兼容性
if self.transfer_callback:
self.transfer_callback(customer_service_id, user_id, store_id)
def _handle_close(self, message: Dict[str, Any]):
"""处理店铺关闭"""
store_id = message.get('store_id', '')
print(f"店铺{store_id}已关闭")
if self.close_callback:
self.close_callback(store_id)
def _handle_pong(self, message: Dict[str, Any]):
"""处理心跳响应"""
uuid_received = message.get('uuid', '')
print(f"收到心跳响应: {uuid_received}")
def _handle_connect_success(self, message: Dict[str, Any]):
"""处理连接成功消息(旧版兼容)"""
print("后端连接成功(connect_success)")
if self.token:
self.get_store()
def _handle_login(self, message: Dict[str, Any]):
"""处理平台登录消息新版type=login, cookies/login_params, store_id, platform_name"""
cookies = message.get('cookies', '')
store_id = message.get('store_id', '')
platform_name = message.get('platform_name', '')
content = message.get('content', '')
data = message.get('data', {})
# 判断是拼多多登录参数还是普通Cookie
if platform_name == "拼多多" and ("拼多多登录" in content) and data.get('login_params'):
# 拼多多登录参数模式 - 传递完整的消息JSON给处理器
print(f"收到拼多多登录参数: 平台={platform_name}, 店铺={store_id}, 类型={content}")
if self.login_callback:
# 传递完整的JSON消息让拼多多处理器来解析login_params
import json
full_message = json.dumps(message)
self.login_callback(platform_name, store_id, full_message)
else:
# 普通Cookie模式
print(f"收到登录指令: 平台={platform_name}, 店铺={store_id}, cookies_len={len(cookies) if cookies else 0}")
if self.login_callback:
self.login_callback(platform_name, store_id, cookies)
def _handle_error_message(self, message: Dict[str, Any]):
"""处理错误消息"""
error_msg = message.get('error', '未知错误')
print(f"后端连接错误: {error_msg}")
if self.error_callback:
self.error_callback(error_msg, message)
def _handle_staff_list(self, message: Dict[str, Any]):
"""处理客服列表更新消息"""
staff_list = message.get('data', {}).get('staff_list', [])
store_id = message.get('store_id', '')
print(f"店铺{store_id}的客服列表已更新,共{len(staff_list)}个客服:")
for staff in staff_list:
pin = staff.get('pin', '')
nickname = staff.get('nickname', '')
print(f" - {nickname} ({pin})")
if self.customers_callback: # 假设客服列表更新也触发客服列表回调
self.customers_callback(staff_list, store_id)
# ==================== 辅助方法 ====================
def set_token(self, token: str):
"""设置或更新令牌"""
self.token = token
def get_connection_info(self) -> Dict[str, Any]:
"""获取连接信息"""
return {
'url': self.url,
'token': self.token,
'uuid': self.uuid,
'is_connected': self.is_connected
}
# 使用示例
if __name__ == '__main__':
pass
# import time
#
# def on_store_list(stores):
# print(f"回调: 收到{len(stores)}个店铺")
#
# def on_message(data, store_id):
# print(f"回调: 店铺{store_id}收到消息: {data.get('content')}")
#
# def on_error(error, message):
# print(f"回调: 发生错误: {error}")
#
# def on_login(platform_name, store_id, cookies):
# print(f"回调: 登录指令 平台={platform_name}, 店铺={store_id}, cookies_len={len(cookies) if cookies else 0}")
# # 此处触发对应平台的WS连接/更新cookies逻辑并将该平台WS与store_id绑定
#
# def on_success():
# print("回调: 后端连接成功")
#
# # 创建客户端(新版:单连接)
# client = BackendClient.from_exe_token("your_exe_token_here")
#
# # 设置回调
# client.set_callbacks(
# store_list=on_store_list,
# message=on_message,
# error=on_error,
# login=on_login,
# success=on_success
# )
#
# try:
# # 连接
# client.connect()
#
# # 等待连接
# time.sleep(2)
#
# # 发送心跳
# client.send_ping()
#
# # 保持运行
# while True:
# time.sleep(30)
# client.send_ping()
#
# except KeyboardInterrupt:
# print("用户中断")
# finally:
# client.disconnect()

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

50414
build/main/Analysis-00.toc Normal file

File diff suppressed because it is too large Load Diff

23925
build/main/COLLECT-00.toc Normal file

File diff suppressed because it is too large Load Diff

77
build/main/EXE-00.toc Normal file
View File

@@ -0,0 +1,77 @@
('D:\\GUI_main\\build\\main\\main.exe',
False,
False,
True,
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-windowed.ico',
None,
False,
False,
b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<assembly xmlns='
b'"urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\n <trustInfo x'
b'mlns="urn:schemas-microsoft-com:asm.v3">\n <security>\n <requested'
b'Privileges>\n <requestedExecutionLevel level="asInvoker" uiAccess='
b'"false"/>\n </requestedPrivileges>\n </security>\n </trustInfo>\n '
b'<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">\n <'
b'application>\n <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f'
b'0}"/>\n <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>\n '
b' <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>\n <s'
b'upportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>\n <supporte'
b'dOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>\n </application>\n <'
b'/compatibility>\n <application xmlns="urn:schemas-microsoft-com:asm.v3">'
b'\n <windowsSettings>\n <longPathAware xmlns="http://schemas.micros'
b'oft.com/SMI/2016/WindowsSettings">true</longPathAware>\n </windowsSett'
b'ings>\n </application>\n <dependency>\n <dependentAssembly>\n <ass'
b'emblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version='
b'"6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" langua'
b'ge="*"/>\n </dependentAssembly>\n </dependency>\n</assembly>',
True,
False,
None,
None,
None,
'D:\\GUI_main\\build\\main\\main.pkg',
[('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz', 'D:\\GUI_main\\build\\main\\PYZ-00.pyz', 'PYZ'),
('struct', 'D:\\GUI_main\\build\\main\\localpycs\\struct.pyc', 'PYMODULE'),
('pyimod01_archive',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'),
('pyimod02_importers',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'),
('pyimod03_ctypes',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'),
('pyimod04_pywin32',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'),
('pyiboot01_bootstrap',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'),
('pyi_rth_inspect',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'),
('pyi_rth_pkgutil',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('pyi_rth_setuptools',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_setuptools.py',
'PYSOURCE'),
('pyi_rth_cryptography_openssl',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py',
'PYSOURCE'),
('pyi_rth_pyqt5',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pyqt5.py',
'PYSOURCE'),
('main', 'D:\\GUI_main\\main.py', 'PYSOURCE')],
[],
False,
False,
1758767002,
[('runw.exe',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe',
'EXECUTABLE')],
'D:\\Python\\python313.dll')

55
build/main/PKG-00.toc Normal file
View File

@@ -0,0 +1,55 @@
('D:\\GUI_main\\build\\main\\main.pkg',
{'BINARY': True,
'DATA': True,
'EXECUTABLE': True,
'EXTENSION': True,
'PYMODULE': True,
'PYSOURCE': True,
'PYZ': False,
'SPLASH': True,
'SYMLINK': False},
[('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz', 'D:\\GUI_main\\build\\main\\PYZ-00.pyz', 'PYZ'),
('struct', 'D:\\GUI_main\\build\\main\\localpycs\\struct.pyc', 'PYMODULE'),
('pyimod01_archive',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'),
('pyimod02_importers',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'),
('pyimod03_ctypes',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'),
('pyimod04_pywin32',
'D:\\GUI_main\\build\\main\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'),
('pyiboot01_bootstrap',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'),
('pyi_rth_inspect',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'),
('pyi_rth_pkgutil',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('pyi_rth_setuptools',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_setuptools.py',
'PYSOURCE'),
('pyi_rth_cryptography_openssl',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py',
'PYSOURCE'),
('pyi_rth_pyqt5',
'D:\\Kris\\shuidrop_gui\\venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pyqt5.py',
'PYSOURCE'),
('main', 'D:\\GUI_main\\main.py', 'PYSOURCE')],
'python313.dll',
True,
False,
False,
[],
None,
None,
None)

BIN
build/main/PYZ-00.pyz Normal file

Binary file not shown.

3002
build/main/PYZ-00.toc Normal file

File diff suppressed because it is too large Load Diff

BIN
build/main/base_library.zip Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
build/main/main.exe Normal file

Binary file not shown.

BIN
build/main/main.pkg Normal file

Binary file not shown.

288
build/main/warn-main.txt Normal file
View File

@@ -0,0 +1,288 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean this module is required for running your program. Python and
Python 3rd-party packages include a lot of conditional or optional modules. For
example the module 'ntpath' only exists on Windows, whereas the module
'posixpath' only exists on Posix systems.
Types if import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), netrc (delayed, optional), getpass (delayed, optional), http.server (delayed, optional), psutil (optional), setuptools._distutils.util (delayed, conditional, optional), setuptools._vendor.backports.tarfile (optional), setuptools._distutils.archive_util (optional)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), setuptools._vendor.backports.tarfile (optional), setuptools._distutils.archive_util (optional)
missing module named 'collections.abc' - imported by traceback (top-level), typing (top-level), inspect (top-level), logging (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level), websockets.imports (top-level), asyncio.base_events (top-level), http.client (top-level), asyncio.coroutines (top-level), websockets.asyncio.client (top-level), websockets.client (top-level), websockets.datastructures (top-level), websockets.frames (top-level), websockets.extensions.base (top-level), websockets.http11 (top-level), websockets.headers (top-level), websockets.protocol (top-level), websockets.streams (top-level), websockets.extensions.permessage_deflate (top-level), websockets.asyncio.connection (top-level), websockets.asyncio.messages (top-level), werkzeug.wrappers.request (top-level), werkzeug.datastructures.accept (top-level), werkzeug.datastructures.structures (top-level), markupsafe (top-level), typing_extensions (top-level), werkzeug.datastructures.cache_control (top-level), werkzeug.datastructures.mixins (top-level), werkzeug.datastructures.auth (top-level), werkzeug.datastructures.csp (top-level), werkzeug.datastructures.etag (top-level), werkzeug.datastructures.file_storage (top-level), werkzeug.datastructures.headers (top-level), werkzeug.datastructures.range (top-level), werkzeug.middleware.shared_data (top-level), websockets.asyncio.server (top-level), websockets.server (top-level), multidict._abc (top-level), multidict._multidict_py (top-level), multidict (conditional), attr._compat (top-level), attr._make (top-level), yarl._query (top-level), yarl._url (top-level), propcache._helpers_py (top-level), yarl._path (top-level), aiohttp.web (top-level), aiohttp.abc (top-level), frozenlist (top-level), aiohttp.client_reqrep (top-level), aiohttp.multipart (top-level), aiohttp.compression_utils (conditional), aiohttp.payload (top-level), aiohttp.connector (conditional), aiohttp.web_response (top-level), aiohttp.client_middlewares (top-level), aiohttp.cookiejar (top-level), requests.compat (top-level), OpenSSL.SSL (top-level), OpenSSL.crypto (top-level), socks (optional), google.protobuf.internal.containers (optional), google.protobuf.internal.well_known_types (optional), numpy.lib._npyio_impl (top-level), numpy.lib._function_base_impl (top-level), numpy._typing._nested_sequence (conditional), numpy._typing._shape (top-level), numpy._typing._dtype_like (top-level), numpy._typing._array_like (top-level), numpy.random.bit_generator (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level), setuptools (top-level), setuptools._distutils.filelist (top-level), setuptools._distutils.util (top-level), setuptools._vendor.jaraco.functools (top-level), setuptools._vendor.more_itertools.more (top-level), setuptools._vendor.more_itertools.recipes (top-level), setuptools._distutils._modified (top-level), setuptools._distutils.compat (top-level), setuptools._distutils.spawn (top-level), setuptools._distutils.compilers.C.base (top-level), setuptools._distutils.fancy_getopt (top-level), setuptools._reqs (top-level), setuptools.discovery (top-level), setuptools.dist (top-level), setuptools._distutils.command.bdist (top-level), setuptools._distutils.core (top-level), setuptools._distutils.cmd (top-level), setuptools._distutils.dist (top-level), configparser (top-level), setuptools._distutils.extension (top-level), setuptools.config.setupcfg (top-level), setuptools.config.expand (top-level), setuptools.config.pyprojecttoml (top-level), setuptools.config._apply_pyprojecttoml (top-level), tomllib._parser (top-level), setuptools._vendor.tomli._parser (top-level), setuptools.command.egg_info (top-level), setuptools._distutils.command.build (top-level), setuptools._distutils.command.sdist (top-level), setuptools.glob (top-level), setuptools.command._requirestxt (top-level), setuptools.command.bdist_wheel (top-level), setuptools._vendor.wheel.cli.convert (top-level), setuptools._vendor.wheel.cli.tags (top-level), setuptools._distutils.command.build_ext (top-level), _pyrepl.types (top-level), _pyrepl.readline (top-level), setuptools._distutils.compilers.C.msvc (top-level), websockets.legacy.auth (top-level), websockets.legacy.server (top-level), websockets.legacy.protocol (top-level), websockets.legacy.framing (top-level), websockets.legacy.client (top-level), websockets.sync.client (top-level), websockets.sync.connection (top-level), websockets.sync.server (top-level)
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional), _pyrepl.unix_console (delayed, optional)
missing module named resource - imported by posix (top-level)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named annotationlib - imported by typing_extensions (conditional), attr._compat (conditional)
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
missing module named fcntl - imported by subprocess (optional), _pyrepl.unix_console (top-level)
missing module named vms_lib - imported by platform (delayed, optional)
missing module named 'java.lang' - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level), loguru._logger (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by getpass (optional), tty (top-level), _pyrepl.pager (delayed, optional), werkzeug._reloader (delayed, optional), _pyrepl.unix_console (top-level), _pyrepl.fancy_termios (top-level), _pyrepl.unix_eventqueue (top-level)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.current_process - imported by multiprocessing (top-level), loguru._logger (top-level)
missing module named multiprocessing.Value - imported by multiprocessing (top-level), werkzeug.debug (top-level)
missing module named usercustomize - imported by site (delayed, optional)
missing module named sitecustomize - imported by site (delayed, optional)
missing module named _curses - imported by curses (top-level), curses.has_key (top-level), _pyrepl.curses (optional)
missing module named readline - imported by code (delayed, conditional, optional), cmd (delayed, conditional, optional), rlcompleter (optional), pdb (delayed, optional), site (delayed, optional), websockets.cli (delayed, optional)
missing module named _typeshed - imported by werkzeug._internal (conditional), numpy.random.bit_generator (top-level), setuptools._distutils.dist (conditional), setuptools.glob (conditional), setuptools.compat.py311 (conditional)
missing module named _manylinux - imported by packaging._manylinux (delayed, optional), setuptools._vendor.packaging._manylinux (delayed, optional), setuptools._vendor.wheel.vendored.packaging._manylinux (delayed, optional)
missing module named importlib_resources - imported by setuptools._vendor.jaraco.text (optional)
missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional)
missing module named pyimod02_importers - imported by D:\Kris\shuidrop_gui\venv\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named aiocontextvars - imported by loguru._contextvars (delayed, conditional)
missing module named exceptiongroup - imported by loguru._better_exceptions (conditional, optional)
missing module named IPython - imported by loguru._colorama (delayed, conditional, optional)
missing module named ipykernel - imported by loguru._colorama (delayed, conditional, optional)
missing module named 'setuptools._distutils.msvc9compiler' - imported by cffi._shimmed_dist_utils (conditional, optional)
missing module named collections.Callable - imported by collections (optional), socks (optional), cffi.api (optional)
missing module named _dummy_thread - imported by numpy._core.arrayprint (optional), cffi.lock (conditional, optional)
missing module named dummy_thread - imported by cffi.lock (conditional, optional)
missing module named thread - imported by cffi.lock (conditional, optional), cffi.cparser (conditional, optional)
missing module named cStringIO - imported by cffi.ffiplatform (optional)
missing module named cPickle - imported by pycparser.ply.yacc (delayed, optional)
missing module named cffi._pycparser - imported by cffi (optional), cffi.cparser (optional)
missing module named imp - imported by Crypto.Util._raw_api (conditional), cffi.verifier (conditional), cffi._imp_emulation (optional)
missing module named StringIO - imported by six (conditional), Crypto.Util.py3compat (conditional)
missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.vecmat - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ushort - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.unsignedinteger - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ulonglong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ulong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uintp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uintc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uint64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ubyte - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.trunc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.true_divide - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.timedelta64 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.tanh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.tan - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.subtract - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.str_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.square - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.spacing - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.sinh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.signedinteger - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.short - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.rint - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.right_shift - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.remainder - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.radians - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.rad2deg - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.power - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.positive - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.pi - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.not_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.negative - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.modf - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.mod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.minimum - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.maximum - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.matvec - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.longdouble - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.long - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_xor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_or - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_not - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_and - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logaddexp2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logaddexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log1p - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.less_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.less - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.left_shift - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ldexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.lcm - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.integer - imported by numpy._core (conditional), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.int8 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.hypot - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.heaviside - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.half - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.greater_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.greater - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.gcd - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.frompyfunc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.frexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmin - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmax - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floor_divide - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floating - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.float_power - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.float16 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fabs - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.expm1 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.exp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.euler_gamma - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.e - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.divmod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.degrees - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.deg2rad - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.datetime64 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cosh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cos - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.copysign - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.conjugate - imported by numpy._core (conditional), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.conj - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.complex64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.clongdouble - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.character - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ceil - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cbrt - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bytes_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.byte - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bool_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_xor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_or - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_count - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_and - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctanh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctan2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctan - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arcsinh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arcsin - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arccosh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arccos - imported by numpy._core (conditional), numpy (conditional)
missing module named threadpoolctl - imported by numpy.lib._utils_impl (delayed, optional)
missing module named numpy._core.ones - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.hstack - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.atleast_1d - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.atleast_3d - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
missing module named numpy._core.vstack - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
missing module named numpy._core.linspace - imported by numpy._core (top-level), numpy.lib._index_tricks_impl (top-level), numpy (conditional)
missing module named numpy._core.result_type - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.number - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.max - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.array2string - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.signbit - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.isscalar - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.isnat - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
missing module named numpy._core.array_repr - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
missing module named numpy._core.arange - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.float32 - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.vecdot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.matrix_transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.matmul - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.tensordot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.outer - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cross - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.trace - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.diagonal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.reciprocal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.sort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.argsort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sign - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.isnan - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.count_nonzero - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.divide - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.swapaxes - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.object_ - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.asanyarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.intp - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.atleast_2d - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.prod - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.amax - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.amin - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.moveaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.errstate - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.finfo - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.isfinite - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sum - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sqrt - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.multiply - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.dot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.inf - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.all - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.newaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.complexfloating - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.inexact - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cdouble - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.csingle - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.double - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.single - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.intc - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.empty_like - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.empty - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.array - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.iinfo - imported by numpy._core (top-level), numpy.lib._twodim_base_impl (top-level), numpy (conditional)
missing module named numpy._core.transpose - imported by numpy._core (top-level), numpy.lib._function_base_impl (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.ndarray - imported by numpy._core (top-level), numpy.lib._utils_impl (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
missing module named numpy._core.asarray - imported by numpy._core (top-level), numpy.lib._array_utils_impl (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level), numpy.fft._helper (top-level)
missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named 'numpy_distutils.command' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named numpy_distutils - imported by numpy.f2py.diagnose (delayed, optional)
missing module named numpy.random.RandomState - imported by numpy.random (top-level), numpy.random._generator (top-level)
missing module named yaml - imported by numpy.__config__ (delayed)
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)
missing module named simplejson - imported by jsonpath (conditional, optional), requests.compat (conditional, optional)
missing module named dummy_threading - imported by requests.cookies (optional)
missing module named zstandard - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named compression - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named 'h2.events' - imported by urllib3.http2.connection (top-level)
missing module named 'h2.connection' - imported by urllib3.http2.connection (top-level)
missing module named h2 - imported by urllib3.http2.connection (top-level)
missing module named brotli - imported by aiohttp.compression_utils (optional), urllib3.util.request (optional), urllib3.response (optional)
missing module named brotlicffi - imported by aiohttp.compression_utils (optional), urllib3.util.request (optional), urllib3.response (optional)
missing module named win_inet_pton - imported by socks (conditional, optional)
missing module named bcrypt - imported by cryptography.hazmat.primitives.serialization.ssh (optional)
missing module named cryptography.x509.UnsupportedExtension - imported by cryptography.x509 (optional), urllib3.contrib.pyopenssl (optional)
missing module named 'pyodide.ffi' - imported by urllib3.contrib.emscripten.fetch (delayed, optional)
missing module named pyodide - imported by urllib3.contrib.emscripten.fetch (top-level)
missing module named js - imported by urllib3.contrib.emscripten.fetch (top-level)
missing module named PyV8 - imported by execjs._pyv8runtime (delayed, optional)
missing module named wsaccel - imported by websocket._utils (optional)
missing module named 'python_socks.sync' - imported by websocket._http (optional), websockets.sync.client (optional)
missing module named 'python_socks._types' - imported by websocket._http (optional)
missing module named 'python_socks._errors' - imported by websocket._http (optional)
missing module named 'wsaccel.xormask' - imported by websocket._abnf (optional)
missing module named google.protobuf.internal.use_pure_python - imported by google.protobuf.internal (conditional, optional), google.protobuf.internal.api_implementation (conditional, optional)
missing module named google.protobuf.internal._api_implementation - imported by google.protobuf.internal (optional), google.protobuf.internal.api_implementation (optional)
missing module named six.moves.range - imported by six.moves (top-level), google.protobuf.internal.python_message (top-level)
runtime module named six.moves - imported by google.protobuf.internal.python_message (top-level)
missing module named google.protobuf.pyext._message - imported by google.protobuf.pyext (conditional), google.protobuf.descriptor (conditional), google.protobuf.pyext.cpp_message (top-level)
missing module named google.protobuf.enable_deterministic_proto_serialization - imported by google.protobuf (optional), google.protobuf.internal.api_implementation (optional)
missing module named google.protobuf._use_fast_cpp_protos - imported by google.protobuf (conditional, optional), google.protobuf.internal.api_implementation (conditional, optional)
missing module named uvloop - imported by aiohttp.worker (delayed)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named async_timeout - imported by aiohttp.helpers (conditional), aiohttp.web_ws (conditional), aiohttp.client_ws (conditional)
missing module named 'gunicorn.workers' - imported by aiohttp.worker (top-level)
missing module named gunicorn - imported by aiohttp.worker (top-level)
missing module named aiodns - imported by aiohttp.resolver (optional)
missing module named '_typeshed.wsgi' - imported by werkzeug.exceptions (conditional), werkzeug.http (conditional), werkzeug.wsgi (conditional), werkzeug.utils (conditional), werkzeug.wrappers.response (conditional), werkzeug.test (conditional), werkzeug.datastructures.headers (conditional), werkzeug.formparser (conditional), werkzeug.wrappers.request (conditional), werkzeug.serving (conditional), werkzeug.debug (conditional), werkzeug.middleware.shared_data (conditional), werkzeug.routing.exceptions (conditional), werkzeug.routing.map (conditional)
missing module named 'watchdog.observers' - imported by werkzeug._reloader (delayed)
missing module named 'watchdog.events' - imported by werkzeug._reloader (delayed)
missing module named watchdog - imported by werkzeug._reloader (delayed)
missing module named python_socks - imported by websockets.asyncio.client (optional), websockets.sync.client (optional)
missing module named 'python_socks.async_' - imported by websockets.asyncio.client (optional)

43838
build/main/xref-main.html Normal file

File diff suppressed because it is too large Load Diff

230
check_chrome_config.py Normal file
View File

@@ -0,0 +1,230 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Chrome配置检查脚本
验证32位ChromeDriver是否能正常驱动64位Chrome
"""
import os
import sys
import platform
import subprocess
def check_system_info():
"""检查系统信息"""
print("🖥️ 系统信息检查")
print("=" * 40)
# 系统架构
system_arch = platform.architecture()[0]
machine = platform.machine()
print(f"系统架构: {system_arch}")
print(f"处理器: {machine}")
# 判断是否64位系统
is_64bit = system_arch == '64bit' or 'AMD64' in machine
print(f"64位系统: {'✅ 是' if is_64bit else '❌ 否'}")
return is_64bit
def check_chrome_installation():
"""检查Chrome安装情况"""
print("\n🌐 Chrome浏览器检查")
print("=" * 40)
# Chrome可能的安装路径
chrome_paths = [
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
r"C:\Users\{}\AppData\Local\Google\Chrome\Application\chrome.exe".format(os.getenv('USERNAME', ''))
]
chrome_found = False
chrome_path = None
chrome_is_64bit = False
for path in chrome_paths:
if os.path.exists(path):
chrome_found = True
chrome_path = path
# 如果Chrome在Program Files目录通常是64位
chrome_is_64bit = "Program Files" in path and "(x86)" not in path
print(f"✅ 找到Chrome: {path}")
print(f"Chrome位数: {'64位' if chrome_is_64bit else '32位或未确定'}")
break
if not chrome_found:
print("❌ 未找到Chrome浏览器")
return False, None, False
# 尝试获取Chrome版本
try:
result = subprocess.run([chrome_path, '--version'],
capture_output=True, text=True, timeout=5)
if result.returncode == 0:
version = result.stdout.strip()
print(f"Chrome版本: {version}")
else:
print("⚠️ 无法获取Chrome版本")
except Exception as e:
print(f"⚠️ 获取版本失败: {e}")
return True, chrome_path, chrome_is_64bit
def check_chromedriver():
"""检查ChromeDriver"""
print("\n🚗 ChromeDriver检查")
print("=" * 40)
# 可能的ChromeDriver路径
driver_paths = [
"chromedriver.exe",
"downloads/chromedriver.exe",
"./chromedriver.exe"
]
driver_found = False
driver_path = None
for path in driver_paths:
if os.path.exists(path):
driver_found = True
driver_path = path
print(f"✅ 找到ChromeDriver: {path}")
break
if not driver_found:
print("❌ 未找到ChromeDriver")
return False, None
# 获取ChromeDriver版本和架构信息
try:
result = subprocess.run([driver_path, '--version'],
capture_output=True, text=True, timeout=5)
if result.returncode == 0:
version_info = result.stdout.strip()
print(f"ChromeDriver版本: {version_info}")
# ChromeDriver通常是32位的这是正常的
print("📋 说明: ChromeDriver通常是32位程序这是正常的")
else:
print("⚠️ 无法获取ChromeDriver版本")
except Exception as e:
print(f"⚠️ 获取版本失败: {e}")
return True, driver_path
def test_selenium_connection():
"""测试Selenium连接"""
print("\n🧪 Selenium连接测试")
print("=" * 40)
try:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
print("✅ Selenium模块导入成功")
# 查找ChromeDriver
driver_paths = ["chromedriver.exe", "downloads/chromedriver.exe", "./chromedriver.exe"]
driver_path = None
for path in driver_paths:
if os.path.exists(path):
driver_path = path
break
if not driver_path:
print("❌ 未找到ChromeDriver跳过连接测试")
return False
# 配置Chrome选项
chrome_options = Options()
chrome_options.add_argument('--headless') # 无头模式,不显示浏览器
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
# 创建服务
service = Service(driver_path)
print("🚀 正在启动Chrome WebDriver...")
# 创建WebDriver
driver = webdriver.Chrome(service=service, options=chrome_options)
# 简单测试
driver.get("data:text/html,<html><body><h1>Test Page</h1></body></html>")
title = driver.title
# 获取浏览器信息
user_agent = driver.execute_script("return navigator.userAgent")
driver.quit()
print("✅ Chrome WebDriver连接成功")
print(f"📄 测试页面标题: {title}")
print(f"🔍 浏览器信息: {user_agent}")
# 分析浏览器位数
if "WOW64" in user_agent or "Win64; x64" in user_agent:
print("🎯 确认: 运行的是64位Chrome浏览器")
else:
print("🎯 运行的Chrome浏览器位数: 32位或未确定")
return True
except ImportError:
print("❌ 未安装Selenium模块")
print("💡 安装命令: pip install selenium")
return False
except Exception as e:
print(f"❌ 连接测试失败: {e}")
return False
def main():
"""主函数"""
print("🔍 Chrome配置全面检查")
print("=" * 50)
# 系统信息检查
is_64bit_system = check_system_info()
# Chrome检查
chrome_found, chrome_path, chrome_is_64bit = check_chrome_installation()
# ChromeDriver检查
driver_found, driver_path = check_chromedriver()
# Selenium测试
selenium_ok = test_selenium_connection()
# 总结
print("\n📋 检查总结")
print("=" * 50)
print(f"64位系统: {'' if is_64bit_system else ''}")
print(f"Chrome浏览器: {'' if chrome_found else ''}")
print(f"ChromeDriver: {'' if driver_found else ''}")
print(f"Selenium测试: {'' if selenium_ok else ''}")
print("\n💡 重要说明:")
print("• 32位ChromeDriver可以完美驱动64位Chrome浏览器")
print("• 这是Google官方的标准设计不存在兼容性问题")
print("• ChromeDriver通过网络协议与Chrome通信位数无关")
if chrome_found and driver_found:
print("\n🎉 配置正常可以开始使用Selenium了")
else:
print("\n⚠️ 需要安装缺失的组件")
if not chrome_found:
print("📥 下载Chrome: https://www.google.com/chrome/")
if not driver_found:
print("📥 下载ChromeDriver: https://chromedriver.chromium.org/downloads")
if __name__ == "__main__":
main()
print()
input("按Enter键退出...")

View File

@@ -0,0 +1,113 @@
@echo off
setlocal enabledelayedexpansion
echo.
echo ==========================================
echo Chrome Complete Cleaner
echo ==========================================
echo.
echo WARNING: This script will completely remove Chrome
echo Including: Program files, user data, registry entries
echo.
set /p confirm="Continue? (Y/N): "
if /i not "%confirm%"=="Y" (
echo Operation cancelled
pause
exit /b 0
)
echo.
echo Starting Chrome cleanup...
echo.
REM 1. Kill Chrome processes
echo Stopping Chrome processes...
taskkill /f /im chrome.exe >nul 2>&1
taskkill /f /im chromedriver.exe >nul 2>&1
taskkill /f /im GoogleUpdate.exe >nul 2>&1
taskkill /f /im GoogleCrashHandler.exe >nul 2>&1
taskkill /f /im GoogleCrashHandler64.exe >nul 2>&1
timeout /t 3 /nobreak >nul
REM 2. Stop Google services
echo Stopping Google services...
sc stop gupdate >nul 2>&1
sc stop gupdatem >nul 2>&1
sc stop GoogleChromeElevationService >nul 2>&1
REM 3. Delete program files
echo Deleting program files...
if exist "C:\Program Files\Google\" (
echo Removing: C:\Program Files\Google
rmdir /s /q "C:\Program Files\Google\" >nul 2>&1
)
if exist "C:\Program Files (x86)\Google\" (
echo Removing: C:\Program Files ^(x86^)\Google
rmdir /s /q "C:\Program Files (x86)\Google\" >nul 2>&1
)
REM 4. Delete current user data
echo Deleting user data...
if exist "%LOCALAPPDATA%\Google\" (
echo Removing current user data
rmdir /s /q "%LOCALAPPDATA%\Google\" >nul 2>&1
)
if exist "%APPDATA%\Google\" (
echo Removing current user config
rmdir /s /q "%APPDATA%\Google\" >nul 2>&1
)
REM 5. Clean registry
echo Cleaning registry...
reg delete "HKEY_CURRENT_USER\Software\Google" /f >nul 2>&1
reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Google" /f >nul 2>&1
reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Google" /f >nul 2>&1
reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google" /f >nul 2>&1
reg delete "HKEY_CURRENT_USER\Software\Policies\Google" /f >nul 2>&1
REM 6. Clean installer records
echo Cleaning installer records...
reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome" /f >nul 2>&1
reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome" /f >nul 2>&1
REM 7. Clean shortcuts
echo Cleaning shortcuts...
del "%PUBLIC%\Desktop\Google Chrome.lnk" >nul 2>&1
del "%USERPROFILE%\Desktop\Google Chrome.lnk" >nul 2>&1
rmdir /s /q "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Google Chrome\" >nul 2>&1
rmdir /s /q "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Google Chrome\" >nul 2>&1
REM 8. Clean scheduled tasks
echo Cleaning scheduled tasks...
schtasks /delete /tn "GoogleUpdateTaskMachineCore" /f >nul 2>&1
schtasks /delete /tn "GoogleUpdateTaskMachineUA" /f >nul 2>&1
REM 9. Delete services
echo Deleting services...
sc delete gupdate >nul 2>&1
sc delete gupdatem >nul 2>&1
sc delete GoogleChromeElevationService >nul 2>&1
REM 10. Clean temp files
echo Cleaning temp files...
del /s /q "%TEMP%\chrome*" >nul 2>&1
del /s /q "%TEMP%\google*" >nul 2>&1
REM 11. Flush DNS
echo Flushing DNS cache...
ipconfig /flushdns >nul 2>&1
echo.
echo Chrome cleanup completed!
echo.
echo Cleaned items:
echo - Program files
echo - User data and configs
echo - Registry entries
echo - Shortcuts
echo - Scheduled tasks
echo - System services
echo - Temp files
echo.
echo You can now reinstall Chrome!
echo.
pause

View File

@@ -9,13 +9,13 @@ import json # 用于将令牌保存为 JSON 格式
# 后端服务器配置
# BACKEND_HOST = "192.168.5.233"
BACKEND_HOST = "192.168.5.12"
# BACKEND_HOST = "shuidrop.com"
# BACKEND_HOST = "192.168.5.12"
BACKEND_HOST = "shuidrop.com"
# BACKEND_HOST = "test.shuidrop.com"
BACKEND_PORT = "8000"
# BACKEND_PORT = ""
BACKEND_WS_URL = f"ws://{BACKEND_HOST}:{BACKEND_PORT}"
# BACKEND_WS_URL = f"wss://{BACKEND_HOST}"
# BACKEND_PORT = "8000"
BACKEND_PORT = ""
# BACKEND_WS_URL = f"ws://{BACKEND_HOST}:{BACKEND_PORT}"
BACKEND_WS_URL = f"wss://{BACKEND_HOST}"
# WebSocket配置
WS_CONNECT_TIMEOUT = 16.0

183
diagnose_connection.py Normal file
View File

@@ -0,0 +1,183 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
WebSocket连接诊断工具
快速定位502错误的原因
"""
import asyncio
import websockets
import requests
import socket
from urllib.parse import urlparse
import ssl
import sys
from config import BACKEND_HOST, BACKEND_WS_URL, get_gui_ws_url, get_saved_token
def test_basic_connectivity():
"""测试基础网络连通性"""
print("=== 1. 基础连通性测试 ===")
# DNS解析测试
try:
ip = socket.gethostbyname(BACKEND_HOST)
print(f"✅ DNS解析成功: {BACKEND_HOST} -> {ip}")
except Exception as e:
print(f"❌ DNS解析失败: {e}")
return False
# HTTP连通性测试
try:
response = requests.get(f"https://{BACKEND_HOST}", timeout=10)
print(f"✅ HTTPS连接成功: 状态码 {response.status_code}")
except requests.exceptions.SSLError as e:
print(f"⚠️ SSL证书问题: {e}")
return False
except requests.exceptions.ConnectionError as e:
print(f"❌ 连接失败: {e}")
return False
except requests.exceptions.Timeout:
print(f"❌ 连接超时")
return False
except Exception as e:
print(f"⚠️ HTTPS测试异常: {e}")
return True
def test_websocket_endpoint():
"""测试WebSocket端点"""
print("\n=== 2. WebSocket端点测试 ===")
# 获取token
token = get_saved_token()
if not token:
print("❌ 未找到有效token")
return False
print(f"📝 使用token: {token[:20]}...")
# 构建WebSocket URL
ws_url = get_gui_ws_url(token)
print(f"🔗 WebSocket URL: {ws_url}")
return ws_url
async def test_websocket_connection(ws_url):
"""异步测试WebSocket连接"""
print("\n=== 3. WebSocket连接测试 ===")
try:
print("🔄 尝试连接WebSocket...")
# 添加连接超时
websocket = await asyncio.wait_for(
websockets.connect(ws_url),
timeout=15.0
)
print("✅ WebSocket连接成功!")
# 测试发送消息
test_message = {"type": "ping", "data": "connection_test"}
await websocket.send(str(test_message))
print("✅ 消息发送成功")
# 等待响应
try:
response = await asyncio.wait_for(websocket.recv(), timeout=5.0)
print(f"✅ 收到响应: {response}")
except asyncio.TimeoutError:
print("⚠️ 未收到响应(超时5秒)")
await websocket.close()
print("✅ 连接正常关闭")
return True
except websockets.exceptions.InvalidStatusCode as e:
if e.status_code == 502:
print("❌ HTTP 502 Bad Gateway - 后端服务器问题")
print("💡 可能原因:")
print(" - 后端服务未启动")
print(" - Nginx配置错误")
print(" - 服务器资源不足")
else:
print(f"❌ HTTP状态码错误: {e.status_code}")
return False
except websockets.exceptions.ConnectionClosedError as e:
print(f"❌ 连接被关闭: {e}")
return False
except websockets.exceptions.WebSocketException as e:
print(f"❌ WebSocket异常: {e}")
return False
except asyncio.TimeoutError:
print("❌ 连接超时 - 服务器可能未响应")
return False
except Exception as e:
print(f"❌ 未知错误: {type(e).__name__}: {e}")
return False
def test_alternative_endpoints():
"""测试备用连接方式"""
print("\n=== 4. 备用端点测试 ===")
# 测试HTTP API端点
try:
api_url = f"https://{BACKEND_HOST}/api/health"
response = requests.get(api_url, timeout=10)
print(f"✅ API端点响应: {response.status_code}")
except Exception as e:
print(f"⚠️ API端点不可用: {e}")
# 测试WebSocket根路径
try:
root_ws_url = f"wss://{BACKEND_HOST}/ws/"
print(f"🔗 测试根WebSocket路径: {root_ws_url}")
except Exception as e:
print(f"⚠️ 根路径测试失败: {e}")
async def main():
"""主诊断流程"""
print("🔍 WebSocket连接诊断工具")
print("=" * 50)
# 1. 基础连通性
if not test_basic_connectivity():
print("\n❌ 基础连通性测试失败,请检查网络连接")
return
# 2. WebSocket端点配置
ws_url = test_websocket_endpoint()
if not ws_url:
print("\n❌ WebSocket端点配置错误")
return
# 3. WebSocket连接测试
success = await test_websocket_connection(ws_url)
# 4. 备用测试
test_alternative_endpoints()
# 5. 总结和建议
print("\n" + "=" * 50)
if success:
print("🎉 诊断完成: WebSocket连接正常!")
else:
print("❌ 诊断完成: 发现连接问题")
print("\n🔧 建议解决方案:")
print("1. 联系服务器管理员检查后端服务状态")
print("2. 检查Nginx WebSocket配置")
print("3. 确认防火墙和安全组设置")
print("4. 检查服务器资源使用情况")
print("5. 验证SSL证书有效性")
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n\n⏹️ 诊断被用户中断")
except Exception as e:
print(f"\n\n💥 诊断工具异常: {e}")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/main/_internal/Crypto/Hash/_MD2.pyd vendored Normal file

Binary file not shown.

BIN
dist/main/_internal/Crypto/Hash/_MD4.pyd vendored Normal file

Binary file not shown.

BIN
dist/main/_internal/Crypto/Hash/_MD5.pyd vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,28 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,92 @@
Metadata-Version: 2.1
Name: MarkupSafe
Version: 3.0.2
Summary: Safely add untrusted strings to HTML/XML markup.
Maintainer-email: Pallets <contact@palletsprojects.com>
License: Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
Project-URL: Source, https://github.com/pallets/markupsafe/
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
# MarkupSafe
MarkupSafe implements a text object that escapes characters so it is
safe to use in HTML and XML. Characters that have special meanings are
replaced so that they display as the actual characters. This mitigates
injection attacks, meaning untrusted user input can safely be displayed
on a page.
## Examples
```pycon
>>> from markupsafe import Markup, escape
>>> # escape replaces special characters and wraps in Markup
>>> escape("<script>alert(document.cookie);</script>")
Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
>>> # wrap in Markup to mark text "safe" and prevent escaping
>>> Markup("<strong>Hello</strong>")
Markup('<strong>hello</strong>')
>>> escape(Markup("<strong>Hello</strong>"))
Markup('<strong>hello</strong>')
>>> # Markup is a str subclass
>>> # methods and operators escape their arguments
>>> template = Markup("Hello <em>{name}</em>")
>>> template.format(name='"World"')
Markup('Hello <em>&#34;World&#34;</em>')
```
## Donate
The Pallets organization develops and supports MarkupSafe and other
popular packages. In order to grow the community of contributors and
users, and allow the maintainers to devote more time to the projects,
[please donate today][].
[please donate today]: https://palletsprojects.com/donate

View File

@@ -0,0 +1,15 @@
MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503
MarkupSafe-3.0.2.dist-info/METADATA,sha256=nhoabjupBG41j_JxPCJ3ylgrZ6Fx8oMCFbiLF9Kafqc,4067
MarkupSafe-3.0.2.dist-info/RECORD,,
MarkupSafe-3.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
MarkupSafe-3.0.2.dist-info/WHEEL,sha256=-v_yZ08fSknsoT62oIKG9wp1eCBV9_ao2rO4BeIReTY,101
MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
markupsafe/__init__.py,sha256=pREerPwvinB62tNCMOwqxBS2YHV6R52Wcq1d-rB4Z5o,13609
markupsafe/__pycache__/__init__.cpython-313.pyc,,
markupsafe/__pycache__/_native.cpython-313.pyc,,
markupsafe/_native.py,sha256=2ptkJ40yCcp9kq3L1NqpgjfpZB-obniYKFFKUOkHh4Q,218
markupsafe/_speedups.c,sha256=SglUjn40ti9YgQAO--OgkSyv9tXq9vvaHyVhQows4Ok,4353
markupsafe/_speedups.cp313-win_amd64.pyd,sha256=7MA12j0aUiSeNpFy-98h_pPSqgCpLeRacgp3I-j00Yo,13312
markupsafe/_speedups.pyi,sha256=LSDmXYOefH4HVpAXuL8sl7AttLw0oXh1njVoVZp2wqQ,42
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: setuptools (75.2.0)
Root-Is-Purelib: false
Tag: cp313-cp313-win_amd64

View File

@@ -0,0 +1 @@
markupsafe

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More