添加自动点击发货按钮

添加自动点击发货按钮
This commit is contained in:
zhinianboke 2025-07-30 16:16:02 +08:00
parent c7bcc72b98
commit 97dda678c8
7 changed files with 457 additions and 351 deletions

View File

@ -3,8 +3,9 @@ FROM python:3.11-slim
# 设置标签信息
LABEL maintainer="Xianyu Auto Reply System"
LABEL version="1.0.0"
LABEL description="闲鱼自动回复系统 - Docker版本"
LABEL version="2.0.0"
LABEL description="闲鱼自动回复系统 - 企业级多用户版本"
LABEL repository="https://github.com/zhinianboke/xianyu-auto-reply"
# 设置工作目录
WORKDIR /app

View File

@ -1,282 +0,0 @@
# Docker快速部署指南 - 多用户版本
## 🚀 一键部署
### 1. 克隆项目
```bash
git clone https://github.com/zhinianboke/xianyu-auto-reply.git
cd xianyu-auto-reply
```
### 2. 配置环境变量
```bash
# 复制环境变量模板
cp .env.example .env
# 编辑配置文件(重要!)
nano .env
```
**必须修改的配置**
```bash
# 修改管理员密码
ADMIN_PASSWORD=your-secure-password
# 修改JWT密钥
JWT_SECRET_KEY=your-very-long-and-random-secret-key
# 多用户功能配置
MULTIUSER_ENABLED=true
USER_REGISTRATION_ENABLED=true
EMAIL_VERIFICATION_ENABLED=true
CAPTCHA_ENABLED=true
```
### 3. 启动服务
```bash
# 构建并启动
docker-compose up -d --build
# 查看启动状态
docker-compose ps
# 查看日志
docker-compose logs -f
```
### 4. 验证部署
```bash
# 健康检查
curl http://localhost:8080/health
# 访问注册页面
curl http://localhost:8080/register.html
```
## 🎯 快速测试
### 访问地址
- **主页**: http://localhost:8080
- **登录页面**: http://localhost:8080/login.html
- **注册页面**: http://localhost:8080/register.html
### 默认管理员账号
- **用户名**: admin
- **密码**: admin123请立即修改
### 测试多用户功能
1. 访问注册页面
2. 输入用户信息
3. 验证图形验证码
4. 接收邮箱验证码
5. 完成注册
6. 登录测试数据隔离
## 🔧 常用命令
### 服务管理
```bash
# 启动服务
docker-compose up -d
# 停止服务
docker-compose down
# 重启服务
docker-compose restart
# 查看状态
docker-compose ps
# 查看日志
docker-compose logs -f
# 查看特定服务日志
docker-compose logs -f xianyu-app
```
### 数据管理
```bash
# 备份数据
docker-compose exec xianyu-app cp /app/data/xianyu_data.db /app/data/backup_$(date +%Y%m%d_%H%M%S).db
# 进入容器
docker-compose exec xianyu-app bash
# 查看数据目录
docker-compose exec xianyu-app ls -la /app/data/
```
### SQL日志调试
```bash
# SQL日志默认已启用INFO级别
# 查看SQL执行日志
docker-compose logs -f xianyu-app | grep "SQL"
# 如需禁用SQL日志
export SQL_LOG_ENABLED=false
docker-compose up -d
# 如需调整日志级别
export SQL_LOG_LEVEL=DEBUG # 更详细的日志
# 或
export SQL_LOG_LEVEL=WARNING # 更少的日志
docker-compose up -d
# 在.env文件中配置
echo "SQL_LOG_ENABLED=false" >> .env
echo "SQL_LOG_LEVEL=DEBUG" >> .env
```
### 故障排除
```bash
# 重新构建镜像
docker-compose build --no-cache
# 查看容器资源使用
docker stats
# 清理未使用的镜像
docker image prune
# 查看详细错误
docker-compose logs --tail=50 xianyu-app
```
## 🔍 故障排除
### 1. 容器启动失败
```bash
# 查看详细日志
docker-compose logs xianyu-app
# 检查端口占用
netstat -tulpn | grep 8080
# 重新构建
docker-compose down
docker-compose build --no-cache
docker-compose up -d
```
### 2. 图形验证码不显示
```bash
# 检查Pillow安装
docker-compose exec xianyu-app python -c "from PIL import Image; print('OK')"
# 检查字体
docker-compose exec xianyu-app ls /usr/share/fonts/
# 重新构建镜像
docker-compose build --no-cache
```
### 3. 数据库问题
```bash
# 检查数据库文件
docker-compose exec xianyu-app ls -la /app/data/
# 运行数据迁移
docker-compose exec xianyu-app python migrate_to_multiuser.py
# 检查数据库状态
docker-compose exec xianyu-app python migrate_to_multiuser.py check
```
### 4. 权限问题
```bash
# 检查数据目录权限
ls -la ./data/
# 修复权限Linux/Mac
sudo chown -R 1000:1000 ./data ./logs
# Windows用户通常不需要修改权限
```
## 📊 监控和维护
### 性能监控
```bash
# 查看资源使用
docker stats --no-stream
# 查看容器详情
docker-compose exec xianyu-app ps aux
# 查看磁盘使用
docker-compose exec xianyu-app df -h
```
### 日志管理
```bash
# 查看日志大小
docker-compose exec xianyu-app du -sh /app/logs/
# 清理旧日志保留最近7天
docker-compose exec xianyu-app find /app/logs/ -name "*.log" -mtime +7 -delete
# 实时监控日志
docker-compose logs -f --tail=100
```
### 数据备份
```bash
# 创建备份脚本
cat > backup.sh << 'EOF'
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
docker-compose exec -T xianyu-app cp /app/data/xianyu_data.db /app/data/backup_$DATE.db
echo "备份完成: backup_$DATE.db"
EOF
chmod +x backup.sh
./backup.sh
```
## 🔐 安全建议
### 1. 修改默认配置
- ✅ 修改管理员密码
- ✅ 修改JWT密钥
- ✅ 禁用调试模式
- ✅ 配置防火墙
### 2. 网络安全
```bash
# 只允许本地访问(如果不需要外部访问)
# 修改 docker-compose.yml 中的端口映射
ports:
- "127.0.0.1:8080:8080" # 只绑定本地
```
### 3. 数据安全
- 定期备份数据库
- 使用HTTPS通过反向代理
- 限制用户注册(如不需要)
- 监控异常登录
## 🎉 部署完成
部署完成后,您的系统将支持:
- ✅ **多用户注册和登录**
- ✅ **图形验证码保护**
- ✅ **邮箱验证码验证**
- ✅ **完整的数据隔离**
- ✅ **企业级安全保护**
现在可以安全地支持多个用户同时使用系统!
## 📞 获取帮助
如果遇到问题:
1. 查看日志:`docker-compose logs -f`
2. 检查状态:`docker-compose ps`
3. 健康检查:`curl http://localhost:8080/health`
4. 运行测试:`python test_docker_deployment.sh`Windows用户需要WSL或Git Bash
---
**提示**: 首次部署后建议运行数据迁移脚本将历史数据绑定到admin用户。

110
README.md
View File

@ -2,8 +2,10 @@
[![GitHub](https://img.shields.io/badge/GitHub-zhinianboke%2Fxianyu--auto--reply-blue?logo=github)](https://github.com/zhinianboke/xianyu-auto-reply)
[![Docker](https://img.shields.io/badge/Docker-一键部署-blue?logo=docker)](https://github.com/zhinianboke/xianyu-auto-reply#-快速开始)
[![Python](https://img.shields.io/badge/Python-3.11+-green?logo=python)](https://www.python.org/)
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
一个功能完整的闲鱼自动回复和管理系统,支持多用户、多账号管理,具备智能回复、自动发货、商品管理等企业级功能。
一个功能完整的闲鱼自动回复和管理系统,支持多用户、多账号管理,具备智能回复、自动发货、自动确认发货、商品管理等企业级功能。
## ✨ 核心特性
@ -34,6 +36,8 @@
- **多种触发** - 支持付款消息、小刀消息等多种触发条件
- **防重复发货** - 智能防重复机制,避免重复发货
- **多种发货方式** - 支持文本内容、卡密文件、API调用等发货方式
- **自动确认发货** - 检测到付款后自动调用闲鱼API确认发货
- **防重复确认** - 智能防重复确认机制避免重复API调用
- **发货统计** - 完整的发货记录和统计功能
### 🛍️ 商品管理
@ -73,7 +77,8 @@ xianyu-auto-reply/
│ ├── cookie_manager.py # Cookie和账号管理
│ ├── ai_reply_engine.py # AI智能回复引擎
│ ├── file_log_collector.py # 日志收集和管理
│ └── config.py # 配置文件管理
│ ├── config.py # 配置文件管理
│ └── secure_confirm_ultra.py # 自动确认发货模块(加密保护)
├── 🛠️ 工具模块
│ └── utils/
│ ├── xianyu_utils.py # 闲鱼API工具函数
@ -207,7 +212,7 @@ docker rm -f xianyu-auto-reply
### 4. 设置自动发货
- 添加发货规则,设置商品关键词和发货内容
- 支持文本内容和卡密文件两种发货方式
- 系统检测到付款消息时自动发货
- 系统检测到付款消息时自动确认发货并自动发货
### 5. 使用商品搜索功能
- 访问商品搜索页面(需要登录)
@ -240,40 +245,45 @@ docker rm -f xianyu-auto-reply
└─────────────────────────────────────┘
```
## 📁 项目结构
## 📁 核心文件功能说明
```
xianyu-auto-reply/
├── Start.py # 主启动文件
├── XianyuAutoAsync.py # 闲鱼WebSocket客户端核心
├── reply_server.py # FastAPI Web服务器
├── db_manager.py # 数据库管理模块
├── cookie_manager.py # Cookie和任务管理
├── ai_reply_engine.py # AI回复引擎
├── config.py # 配置管理
├── file_log_collector.py # 日志收集器
├── global_config.yml # 全局配置文件
├── requirements.txt # Python依赖
├── docker-compose.yml # Docker编排配置
├── Dockerfile # Docker镜像构建
├── utils/ # 工具模块
│ ├── item_search.py # 商品搜索功能
│ └── ... # 其他工具模块
├── static/ # 前端静态文件
│ ├── index.html # 主界面
│ ├── login.html # 登录页面
│ ├── register.html # 注册页面
│ ├── item_search.html # 商品搜索页面
│ ├── user_management.html # 用户管理页面
│ ├── data_management.html # 数据管理页面
│ ├── log_management.html # 日志管理页面
│ └── lib/ # 本地静态资源库
│ ├── bootstrap/ # Bootstrap框架
│ └── bootstrap-icons/ # Bootstrap图标
├── logs/ # 日志文件目录
├── data/ # 数据库文件目录
└── backups/ # 备份文件目录
```
### 🚀 启动和核心模块
- **`Start.py`** - 项目启动入口,初始化所有服务和组件
- **`XianyuAutoAsync.py`** - 闲鱼WebSocket连接核心处理消息收发和自动回复
- **`reply_server.py`** - FastAPI Web服务器提供管理界面和API接口
- **`cookie_manager.py`** - 多账号Cookie管理负责账号任务的启动和停止
### 🗄️ 数据和配置管理
- **`db_manager.py`** - SQLite数据库管理处理用户数据、商品信息、关键词等
- **`config.py`** - 配置文件管理,加载和管理全局配置
- **`global_config.yml`** - 全局配置文件,包含所有系统配置项
### 🤖 智能功能模块
- **`ai_reply_engine.py`** - AI智能回复引擎支持多种AI模型
- **`secure_confirm_ultra.py`** - 自动确认发货模块(加密保护)
- **`file_log_collector.py`** - 日志收集和管理,提供实时日志查看
### 🛠️ 工具模块
- **`utils/xianyu_utils.py`** - 闲鱼API工具函数包含加密解密、签名生成等
- **`utils/message_utils.py`** - 消息格式化工具
- **`utils/ws_utils.py`** - WebSocket客户端工具
- **`utils/item_search.py`** - 商品搜索功能基于Playwright技术
### 🌐 前端界面
- **`static/index.html`** - 主管理界面,账号管理和系统监控
- **`static/login.html`** - 用户登录页面
- **`static/register.html`** - 用户注册页面,支持邮箱验证
- **`static/user_management.html`** - 用户管理页面(管理员功能)
- **`static/data_management.html`** - 数据管理页面,关键词导入导出
- **`static/log_management.html`** - 日志管理页面,实时日志查看
- **`static/item_search.html`** - 商品搜索页面,获取真实闲鱼数据
### 🐳 部署配置
- **`Dockerfile`** - Docker镜像构建文件包含完整运行环境
- **`docker-compose.yml`** - Docker Compose配置支持一键部署
- **`docker-deploy.sh`** - Docker部署脚本提供完整的部署管理功能
- **`.env`** - 环境变量配置文件,包含所有可配置项
- **`requirements.txt`** - Python依赖包列表
## ⚙️ 配置说明
@ -363,6 +373,34 @@ curl http://localhost:8080/health
- **数据隔离**:用户数据完全隔离
- **会话管理**:安全的会话超时机制
- **操作日志**:完整的用户操作记录
- **代码加密**:核心业务逻辑采用多层加密保护
## 🛡️ 技术特性
### 🏗️ 架构设计
- **微服务架构**:模块化设计,易于维护和扩展
- **异步编程**基于asyncio的高性能异步处理
- **WebSocket长连接**:实时消息处理,低延迟响应
- **RESTful API**标准化的API接口设计
### 🔧 技术栈
- **后端框架**FastAPI + Uvicorn
- **数据库**SQLite轻量级无需额外配置
- **前端技术**原生HTML/CSS/JavaScript + Bootstrap
- **WebSocket**:实时双向通信
- **容器化**Docker + Docker Compose
### 🚀 性能优化
- **连接池管理**:高效的数据库连接管理
- **异步处理**非阻塞I/O操作
- **内存优化**:智能缓存和垃圾回收
- **资源限制**Docker容器资源限制和监控
### 🔐 安全机制
- **多层加密**敏感代码采用5层编码混淆
- **变量名随机化**:防止静态分析
- **运行时解密**:代码在内存中动态解密执行
- **防重复机制**:智能防重复确认和发货
## 🤝 贡献指南

View File

@ -1,5 +1,6 @@
import asyncio
import json
import re
import time
import base64
import os
@ -101,6 +102,10 @@ class XianyuLive:
# 自动发货防重复机制
self.last_delivery_time = {} # 记录每个商品的最后发货时间
self.delivery_cooldown = 60 # 1分钟内不重复发货
# 自动确认发货防重复机制
self.confirmed_orders = {} # 记录已确认发货的订单,防止重复确认
self.order_confirm_cooldown = 300 # 5分钟内不重复确认同一订单
# 人工接管功能已禁用,永远走自动模式
# self.manual_mode_conversations = set() # 存储处于人工接管模式的会话ID
@ -623,7 +628,6 @@ class XianyuLive:
message_1 = message.get('1')
if isinstance(message_1, str):
# 尝试从字符串中提取数字ID
import re
id_match = re.search(r'(\d{10,})', message_1)
if id_match:
logger.info(f"从message[1]字符串中提取商品ID: {id_match.group(1)}")
@ -662,7 +666,6 @@ class XianyuLive:
# 从消息内容中提取数字ID
content = message_3.get('content', '')
if isinstance(content, str) and content:
import re
id_match = re.search(r'(\d{10,})', content)
if id_match:
logger.info(f"{self.cookie_id}】从消息内容中提取商品ID: {id_match.group(1)}")
@ -687,7 +690,6 @@ class XianyuLive:
elif isinstance(obj, str):
# 从字符串中提取可能的商品ID
import re
id_match = re.search(r'(\d{10,})', obj)
if id_match:
logger.info(f"{path}字符串中提取商品ID: {id_match.group(1)}")
@ -873,7 +875,6 @@ class XianyuLive:
if not price_str:
return 0.0
# 移除非数字字符,保留小数点
import re
price_clean = re.sub(r'[^\d.]', '', str(price_str))
return float(price_clean) if price_clean else 0.0
except:
@ -884,7 +885,6 @@ class XianyuLive:
try:
from db_manager import db_manager
import aiohttp
import json
# 获取当前账号的通知配置
@ -926,7 +926,6 @@ class XianyuLive:
"""发送QQ通知"""
try:
import aiohttp
import json
# 解析配置QQ号码
qq_number = config.strip()
@ -1075,6 +1074,28 @@ class XianyuLive:
except Exception as e:
logger.error(f"发送自动发货通知异常: {self._safe_str(e)}")
async def auto_confirm(self, order_id, retry_count=0):
"""自动确认发货 - 使用加密模块"""
try:
# 导入超级混淆模块
from secure_confirm_ultra import SecureConfirm
# 创建加密确认实例
secure_confirm = SecureConfirm(self.session, self.cookies_str, self.cookie_id)
# 传递必要的属性
secure_confirm.current_token = self.current_token
secure_confirm.last_token_refresh_time = self.last_token_refresh_time
secure_confirm.token_refresh_interval = self.token_refresh_interval
secure_confirm.refresh_token = self.refresh_token # 传递refresh_token方法
# 调用加密的确认方法
return await secure_confirm.auto_confirm(order_id, retry_count)
except Exception as e:
logger.error(f"{self.cookie_id}】加密确认模块调用失败: {self._safe_str(e)}")
return {"error": f"加密确认模块调用失败: {self._safe_str(e)}", "order_id": order_id}
async def _auto_delivery(self, item_id: str, item_title: str = None):
"""自动发货功能"""
try:
@ -1099,7 +1120,6 @@ class XianyuLive:
# 解析 shareInfoJsonString 并提取 content 内容
try:
import json
share_info = json.loads(shareInfoJsonString)
content = share_info.get('contentParams', {}).get('mainParams', {}).get('content', '')
if content:
@ -1262,8 +1282,6 @@ class XianyuLive:
try:
import aiohttp
import json
import asyncio
api_config = rule.get('card_api_config')
if not api_config:
@ -1978,12 +1996,117 @@ class XianyuLive:
elif send_message == '快给ta一个评价吧~' or send_message == '快给ta一个评价吧':
logger.info(f'[{msg_time}] 【{self.cookie_id}】评价提醒消息不处理')
return
elif send_message == '卖家人不错送Ta闲鱼小红花':
logger.info(f'[{msg_time}] 【{self.cookie_id}】小红花提醒消息不处理')
return
elif send_message == '[你已确认收货,交易成功]':
logger.info(f'[{msg_time}] 【{self.cookie_id}】买家确认收货消息不处理')
return
elif send_message == '[你已发货]':
logger.info(f'[{msg_time}] 【{self.cookie_id}】发货确认消息不处理')
return
elif send_message == '[我已付款,等待你发货]':
logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】买家已付款,准备自动发货')
# 提取orderId并打印
try:
order_id = None
# 先查看消息的完整结构
logger.info(f"{self.cookie_id}】🔍 完整消息结构: {message}")
# 检查message['1']的结构
message_1 = message.get('1', {})
logger.info(f"{self.cookie_id}】🔍 message['1'] keys: {list(message_1.keys()) if message_1 else 'None'}")
# 检查message['1']['6']的结构
message_1_6 = message_1.get('6', {}) if message_1 else {}
logger.info(f"{self.cookie_id}】🔍 message['1']['6'] keys: {list(message_1_6.keys()) if message_1_6 else 'None'}")
# 方法1: 从button的targetUrl中提取orderId
content_json_str = message.get('1', {}).get('6', {}).get('3', {}).get('5', '')
logger.info(f"{self.cookie_id}】🔍 content_json_str: {content_json_str[:200] if content_json_str else 'None'}...")
if content_json_str:
try:
content_data = json.loads(content_json_str)
logger.info(f"{self.cookie_id}】🔍 content_data keys: {list(content_data.keys())}")
# 方法1a: 从button的targetUrl中提取orderId
target_url = content_data.get('dxCard', {}).get('item', {}).get('main', {}).get('exContent', {}).get('button', {}).get('targetUrl', '')
logger.info(f"{self.cookie_id}】🔍 button targetUrl: {target_url}")
if target_url:
# 从URL中提取orderId参数
order_match = re.search(r'orderId=(\d+)', target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 从button提取到订单ID: {order_id}')
# 方法1b: 从main的targetUrl中提取order_detail的id
if not order_id:
main_target_url = content_data.get('dxCard', {}).get('item', {}).get('main', {}).get('targetUrl', '')
logger.info(f"{self.cookie_id}】🔍 main targetUrl: {main_target_url}")
if main_target_url:
order_match = re.search(r'order_detail\?id=(\d+)', main_target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 从main targetUrl提取到订单ID: {order_id}')
except Exception as parse_e:
logger.error(f"解析内容JSON失败: {parse_e}")
# 方法2: 从dynamicOperation中的order_detail URL提取orderId
if not order_id and content_json_str:
try:
content_data = json.loads(content_json_str)
dynamic_target_url = content_data.get('dynamicOperation', {}).get('changeContent', {}).get('dxCard', {}).get('item', {}).get('main', {}).get('exContent', {}).get('button', {}).get('targetUrl', '')
if dynamic_target_url:
# 从order_detail URL中提取id参数
order_match = re.search(r'order_detail\?id=(\d+)', dynamic_target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 从order_detail提取到订单ID: {order_id}')
except Exception as parse_e:
logger.debug(f"解析dynamicOperation JSON失败: {parse_e}")
# 如果成功获取到orderId进行自动确认发货
if order_id:
# 检查是否已经确认过这个订单
current_time = time.time()
if order_id in self.confirmed_orders:
last_confirm_time = self.confirmed_orders[order_id]
if current_time - last_confirm_time < self.order_confirm_cooldown:
logger.info(f'[{msg_time}] 【{self.cookie_id}】⏭️ 订单 {order_id} 已在 {self.order_confirm_cooldown} 秒内确认过,跳过重复确认')
else:
# 超过冷却时间,可以重新确认
try:
logger.info(f'[{msg_time}] 【{self.cookie_id}】开始自动确认发货订单ID: {order_id}')
confirm_result = await self.auto_confirm(order_id)
if confirm_result.get('success'):
self.confirmed_orders[order_id] = current_time
logger.info(f'[{msg_time}] 【{self.cookie_id}】🎉 自动确认发货成功订单ID: {order_id}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 自动确认发货失败: {confirm_result.get("error", "未知错误")}')
except Exception as confirm_e:
logger.error(f'[{msg_time}] 【{self.cookie_id}】自动确认发货异常: {self._safe_str(confirm_e)}')
else:
# 首次确认这个订单
try:
logger.info(f'[{msg_time}] 【{self.cookie_id}】开始自动确认发货订单ID: {order_id}')
confirm_result = await self.auto_confirm(order_id)
if confirm_result.get('success'):
self.confirmed_orders[order_id] = current_time
logger.info(f'[{msg_time}] 【{self.cookie_id}】🎉 自动确认发货成功订单ID: {order_id}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 自动确认发货失败: {confirm_result.get("error", "未知错误")}')
except Exception as confirm_e:
logger.error(f'[{msg_time}] 【{self.cookie_id}】自动确认发货异常: {self._safe_str(confirm_e)}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 未能提取到订单ID')
except Exception as extract_e:
logger.error(f"提取订单ID失败: {self._safe_str(extract_e)}")
# 检查是否可以进行自动发货(防重复)
if not self.can_auto_delivery(item_id):
return
@ -2023,6 +2146,89 @@ class XianyuLive:
elif send_message == '[已付款,待发货]':
logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】买家已付款,准备自动发货')
# 提取orderId并打印
try:
order_id = None
# 方法1: 从button的targetUrl中提取orderId
content_json_str = message.get('1', {}).get('6', {}).get('3', {}).get('5', '')
if content_json_str:
try:
content_data = json.loads(content_json_str)
# 方法1a: 从button的targetUrl中提取orderId
target_url = content_data.get('dxCard', {}).get('item', {}).get('main', {}).get('exContent', {}).get('button', {}).get('targetUrl', '')
if target_url:
# 从URL中提取orderId参数
order_match = re.search(r'orderId=(\d+)', target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 从button提取到订单ID: {order_id}')
# 方法1b: 从main的targetUrl中提取order_detail的id
if not order_id:
main_target_url = content_data.get('dxCard', {}).get('item', {}).get('main', {}).get('targetUrl', '')
if main_target_url:
order_match = re.search(r'order_detail\?id=(\d+)', main_target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 从main targetUrl提取到订单ID: {order_id}')
except Exception as parse_e:
logger.debug(f"解析内容JSON失败: {parse_e}")
# 方法2: 从dynamicOperation中的order_detail URL提取orderId
if not order_id and content_json_str:
try:
content_data = json.loads(content_json_str)
dynamic_target_url = content_data.get('dynamicOperation', {}).get('changeContent', {}).get('dxCard', {}).get('item', {}).get('main', {}).get('exContent', {}).get('button', {}).get('targetUrl', '')
if dynamic_target_url:
# 从order_detail URL中提取id参数
order_match = re.search(r'order_detail\?id=(\d+)', dynamic_target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 从order_detail提取到订单ID: {order_id}')
except Exception as parse_e:
logger.debug(f"解析dynamicOperation JSON失败: {parse_e}")
# 如果成功获取到orderId进行自动确认发货
if order_id:
# 检查是否已经确认过这个订单
current_time = time.time()
if order_id in self.confirmed_orders:
last_confirm_time = self.confirmed_orders[order_id]
if current_time - last_confirm_time < self.order_confirm_cooldown:
logger.info(f'[{msg_time}] 【{self.cookie_id}】⏭️ 订单 {order_id} 已在 {self.order_confirm_cooldown} 秒内确认过,跳过重复确认')
else:
# 超过冷却时间,可以重新确认
try:
logger.info(f'[{msg_time}] 【{self.cookie_id}】开始自动确认发货订单ID: {order_id}')
confirm_result = await self.auto_confirm(order_id)
if confirm_result.get('success'):
self.confirmed_orders[order_id] = current_time
logger.info(f'[{msg_time}] 【{self.cookie_id}】🎉 自动确认发货成功订单ID: {order_id}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 自动确认发货失败: {confirm_result.get("error", "未知错误")}')
except Exception as confirm_e:
logger.error(f'[{msg_time}] 【{self.cookie_id}】自动确认发货异常: {self._safe_str(confirm_e)}')
else:
# 首次确认这个订单
try:
logger.info(f'[{msg_time}] 【{self.cookie_id}】开始自动确认发货订单ID: {order_id}')
confirm_result = await self.auto_confirm(order_id)
if confirm_result.get('success'):
self.confirmed_orders[order_id] = current_time
logger.info(f'[{msg_time}] 【{self.cookie_id}】🎉 自动确认发货成功订单ID: {order_id}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 自动确认发货失败: {confirm_result.get("error", "未知错误")}')
except Exception as confirm_e:
logger.error(f'[{msg_time}] 【{self.cookie_id}】自动确认发货异常: {self._safe_str(confirm_e)}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 未能提取到订单ID')
except Exception as extract_e:
logger.error(f"提取订单ID失败: {self._safe_str(extract_e)}")
# 检查是否可以进行自动发货(防重复)
if not self.can_auto_delivery(item_id):
return
@ -2086,6 +2292,89 @@ class XianyuLive:
if card_title == "我已小刀,待刀成":
logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】检测到"我已小刀,待刀成",准备自动发货')
# 提取orderId并打印
try:
order_id = None
# 方法1: 从button的targetUrl中提取orderId
content_json_str = message.get('1', {}).get('6', {}).get('3', {}).get('5', '')
if content_json_str:
try:
content_data = json.loads(content_json_str)
# 方法1a: 从button的targetUrl中提取orderId
target_url = content_data.get('dxCard', {}).get('item', {}).get('main', {}).get('exContent', {}).get('button', {}).get('targetUrl', '')
if target_url:
# 从URL中提取orderId参数
order_match = re.search(r'orderId=(\d+)', target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 小刀成功从button提取到订单ID: {order_id}')
# 方法1b: 从main的targetUrl中提取order_detail的id
if not order_id:
main_target_url = content_data.get('dxCard', {}).get('item', {}).get('main', {}).get('targetUrl', '')
if main_target_url:
order_match = re.search(r'order_detail\?id=(\d+)', main_target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 小刀成功从main targetUrl提取到订单ID: {order_id}')
except Exception as parse_e:
logger.debug(f"解析内容JSON失败: {parse_e}")
# 方法2: 从dynamicOperation中的order_detail URL提取orderId
if not order_id and content_json_str:
try:
content_data = json.loads(content_json_str)
dynamic_target_url = content_data.get('dynamicOperation', {}).get('changeContent', {}).get('dxCard', {}).get('item', {}).get('main', {}).get('exContent', {}).get('button', {}).get('targetUrl', '')
if dynamic_target_url:
# 从order_detail URL中提取id参数
order_match = re.search(r'order_detail\?id=(\d+)', dynamic_target_url)
if order_match:
order_id = order_match.group(1)
logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 小刀成功从order_detail提取到订单ID: {order_id}')
except Exception as parse_e:
logger.debug(f"解析dynamicOperation JSON失败: {parse_e}")
# 如果成功获取到orderId进行自动确认发货
if order_id:
# 检查是否已经确认过这个订单
current_time = time.time()
if order_id in self.confirmed_orders:
last_confirm_time = self.confirmed_orders[order_id]
if current_time - last_confirm_time < self.order_confirm_cooldown:
logger.info(f'[{msg_time}] 【{self.cookie_id}】⏭️ 订单 {order_id} 已在 {self.order_confirm_cooldown} 秒内确认过,跳过重复确认')
else:
# 超过冷却时间,可以重新确认
try:
logger.info(f'[{msg_time}] 【{self.cookie_id}】小刀成功开始自动确认发货订单ID: {order_id}')
confirm_result = await self.auto_confirm(order_id)
if confirm_result.get('success'):
self.confirmed_orders[order_id] = current_time
logger.info(f'[{msg_time}] 【{self.cookie_id}】🎉 小刀成功自动确认发货成功订单ID: {order_id}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 小刀成功,自动确认发货失败: {confirm_result.get("error", "未知错误")}')
except Exception as confirm_e:
logger.error(f'[{msg_time}] 【{self.cookie_id}】小刀成功,自动确认发货异常: {self._safe_str(confirm_e)}')
else:
# 首次确认这个订单
try:
logger.info(f'[{msg_time}] 【{self.cookie_id}】小刀成功开始自动确认发货订单ID: {order_id}')
confirm_result = await self.auto_confirm(order_id)
if confirm_result.get('success'):
self.confirmed_orders[order_id] = current_time
logger.info(f'[{msg_time}] 【{self.cookie_id}】🎉 小刀成功自动确认发货成功订单ID: {order_id}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 小刀成功,自动确认发货失败: {confirm_result.get("error", "未知错误")}')
except Exception as confirm_e:
logger.error(f'[{msg_time}] 【{self.cookie_id}】小刀成功,自动确认发货异常: {self._safe_str(confirm_e)}')
else:
logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 小刀成功但未能提取到订单ID')
except Exception as extract_e:
logger.error(f"提取订单ID失败: {self._safe_str(extract_e)}")
# 检查是否可以进行自动发货(防重复)
if not self.can_auto_delivery(item_id):
return

View File

@ -54,19 +54,14 @@ check_dependencies() {
# 初始化配置
init_config() {
print_info "初始化配置文件..."
if [ ! -f "$ENV_FILE" ]; then
if [ -f ".env.example" ]; then
cp .env.example "$ENV_FILE"
print_success "已创建 $ENV_FILE 配置文件"
else
print_error ".env.example 文件不存在"
exit 1
fi
print_warning "$ENV_FILE 文件不存在,将使用默认配置"
print_info "如需自定义配置,请创建 $ENV_FILE 文件"
else
print_warning "$ENV_FILE 已存在,跳过创建"
print_success "$ENV_FILE 配置文件已存在"
fi
# 创建必要的目录
mkdir -p data logs backups
print_success "已创建必要的目录"
@ -328,9 +323,7 @@ main() {
"cleanup")
cleanup
;;
"fix-playwright")
fix_playwright
;;
"help"|"--help"|"-h")
show_help
;;

View File

@ -1,17 +1,21 @@
# ================================
# 闲鱼自动回复系统 - Python依赖包
# ================================
# Web框架和API相关
fastapi>=0.111
uvicorn[standard]>=0.29
pydantic>=2.7
fastapi>=0.111.0
uvicorn[standard]>=0.29.0
pydantic>=2.7.0
# 日志记录
loguru>=0.7
loguru>=0.7.0
# 网络通信
websockets>=10.0,<13.0 # 兼容性版本范围
aiohttp>=3.9
websockets>=10.0,<13.0
aiohttp>=3.9.0
# 配置文件处理
PyYAML>=6.0
PyYAML>=6.0.0
# JavaScript执行引擎
PyExecJS>=1.5.1
@ -22,7 +26,7 @@ blackboxprotobuf>=1.0.1
# 系统监控
psutil>=5.9.0
# HTTP客户端(用于测试)
# HTTP客户端
requests>=2.31.0
# 文件上传支持
@ -42,6 +46,7 @@ playwright>=1.40.0
PyJWT>=2.8.0
passlib>=1.7.4
bcrypt>=4.0.1
cryptography>=41.0.0
# 时间处理
python-dateutil>=2.8.2
@ -51,4 +56,10 @@ regex>=2023.10.3
# Excel文件处理导入导出功能
pandas>=2.0.0
openpyxl>=3.1.0
openpyxl>=3.1.0
# 数据库相关
sqlite3 # Python内置无需安装
# 其他工具库
typing-extensions>=4.7.0

56
secure_confirm_ultra.py Normal file
View File

@ -0,0 +1,56 @@
"""
自动确认发货模块 - 超级混淆版本
代码经过多层编码和混淆处理
"""
import base64 as APWhvGwho
import zlib as ZUMHeUctH
import types as YEYVefZAzSK
import binascii as xjJqeneb
class MyCvbqb:
"""解码器类"""
def __init__(self):
self.GUqVZNquc = "d355231467a733244345c6a5172787b43346f48436563463f245833377a6e6c693b23376862713c477b6360367264386747517976747a467363775a545a52717672485a4a64464f607b48355647334d48514b4074563835767b463a4940345f275a5c4051457c433633605a52733d4a73316933756b273d4a55625270583b69594f666b4134516a593d603d64427234303768756352773747605d6938773951493a466946534572646973396f483451576575356f64674a7a63794a436a795d6a74437467585454463b2f253f603a4a4976474838754930325b413176303a64405645427b286e43593038635a47325254746b27505b61674175656243756579394c623263747863324c4a69354466666849434a7a7b63353a53477765766d4f66413f68403056535c4d484f6a6d66614f44734c464758677466733536607647733a77305f2033727148456431745352795d484965307f4c4c413d463d6a505a586052594a677a625453613655575961726b616852427161445d6a656b4537436252756a7163747e454858643f63725136673563316877595a7333726a4a4452756135307b4075763f246233764664675b4e425374557b6139383945656c4275594a4647456b49405979743a7f295e61544870563465337234705a7e6e626a7748354b457245416a74655036373340324f297c4e63393335654c6e6762396353616f64524642584a537a646149515c496d44415a74444942735550556f646143735957424f2e655239627a456668756e683f6a7339467d6166554b64717c4d6137544a69605f47773a5c6a61767743525b4942615f40767232666b407a69777b2d63364558767861735754314159397f6c64705135786374686238386d483943593f283077427035616a44625a4d4d4363534837773b496e6360385445587c474537744f4f267f276c4f275e47633266427b44793b496c6566614650323553663c67703b67346649473e405c496c4e485c4148574747753b67333342437c663140305855565a6a5c46625a7742797a4666574a545c637672553f6d43623231574842415647784151337958405f4d4356344a5738666b24754374366a6f264657693633616b273a447b243573586e6a6d4a457a6353334963457e4a7f42597754507e41615b243b6872794479324f4c64773748456a514639394e44313c464832524c63486c4f47643569444472336a584a6274396b234b636e4c6577385752443a7e6a5972794a485a78476d643334687469627449637b6e6852436078585b49343a5b2c637b6269793c6e497f2943584b686659347361666c69666b424a4e6174463d405f6e686a755a703f464434696268464f687a4755397f487d686d65436d415b2e4a5a51477c4541426b6852535c6f6f43576a4a4130336c6f4b6650794534314f234950377c647172686d62525d416e663446535a5b495a7777584642623375457c676e4a736169437168514353797c4536527544634441305231487573413f4a62573171674470745765447b455c6167314532676d6d4f2b2742614a50305a51454f4b46523e6563387334705b257a7071487a7a507c46407e626774535348527774517f2a68355c6b2a427f2e4149577e48414135507a5352736a5b2d4c63666a525a4d66356567477d443e6b6536705d49407463675443417a6649387b23356f4a637535507959397968675238795a5538365369425c4d696249545b62653a7778403a6532397f264e697c6559307569774857344d6a5c62663d427148524346357532784f21424239785c42473f4170523a4e4455556656643641505e425f234834627435417178317b6f6135503a577f47514753494477513757325b25365d4f613753745566366339427941455a7574386845495e497d6532435c613272393337376a663667737447494f4e444946435e674f23787477456f60335141495943515d6a586072394449605944677f2b2445707f683a75454f664272615976735a56594f6b48526f64545161643b243a675353337a6943476762723c4344434b613f493a5b6137556a5738736546617a4e6644465152437a624b45714a453d4842775636325b4930525a4469427968534f4875394f4a543368674c667842625c4638394a686371523a597e4a474f415135737133425e4f276c6b415243303d605d6a4f67353f497d60777f4031705c6131426a457e4e483d447b2b2e4661384330364c44474f28355954447842603c6753746f4741554b4a4b484c454833316e4b656a755f22445a6477345a7338363e46536b627848643653355e41474a5a44497f434a5871377632333168764537744c45697841665269607a4569436146345533336255705f285a5c69503b4e6363324a644a73595052405c6f487b6642614331507131597761447d664169327271657a42614a6b426072365143797133426d613337505541443773754148353a5a7950755a65535777563c4845667d65547255464758464372527662413e4141686478593f243f695337526c673447563c613742617e637c6335597745687a7748557551376535413073695b41387c64626441633153754077705c63423d674d6d6364784756654f69636854754c46796866567a677845693a6c485265375734765563484a5079693247337531633a57344d695e6445626632764751384f42394845615f23775956616f685b24616b2367414833795a7d6a7c6a4c407744434b4b295c6e4b44516d6b605d4a5a674736617539617e464f21376f615c64687b44475a554557624161797637614256345937497a543f63527a7453475a48455146566c6c446247365f696962427039547557643e4b28603a6537455356473c4f494a643a7034475244617a6a6a6c4f683860745257723948496f2e4331513648613972535c675136546f474a5a494a7654674a6968694a717f6a4631536b6866694f496a527b62557173796f216077707c4a66317b6b455239675039795b4a7050557173713666545246357a6a74665557635577524a607d4a413a7f6961345c6a557f695355757b4b6f2f6e4c4767575a44597c6c4343557e455655517e69796352797b467b227573797141384a446e67354a7c6861413d4657563963514e4b27403b6e6149726b264261693557394a5c44716743723d673169747549775f22543472675f2b646352794858387360373a7a713a5b266a79393538383563574765324450563d446a6a537334633576656f666e4539733948507664434b4275564f28774a46464a424f6f694371434756547d4d4d4449423462326b4271634737545e6a57474667415e60735e443730574a607a7b62316445335d6b4e426b2d4c457e6b28366553613544593647565a7a456"
def ScRcLgANY(self):
"""解码方法"""
# 第一步:反转
CbYwjgc = self.GUqVZNquc[::-1]
# 第二步:十六进制解码
lfudkUg = bytes.fromhex(CbYwjgc)
# 第三步Base64解码
BPHsSqoTwa = APWhvGwho.b64decode(lfudkUg)
# 第四步zlib解压
XgrjgRdyihoN = ZUMHeUctH.decompress(BPHsSqoTwa)
# 第五步UTF-8解码
tjIfqnQS = XgrjgRdyihoN.decode('utf-8')
return tjIfqnQS
def FfLPbc(self):
"""创建模块"""
TgvtbRCbyCZn = self.ScRcLgANY()
AntZnm = YEYVefZAzSK.ModuleType('secure_confirm')
exec(TgvtbRCbyCZn, AntZnm.__dict__)
return AntZnm
# 创建解码器实例
LgIDlnLUPliI = MyCvbqb()
# 加载模块
OnrJVdsaLdM = LgIDlnLUPliI.FfLPbc()
# 导出类
SecureConfirm = OnrJVdsaLdM.SecureConfirm
# 清理所有变量
del LgIDlnLUPliI
del OnrJVdsaLdM
del MyCvbqb