mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-02 04:27:36 +08:00
添加自动点击发货按钮
添加自动点击发货按钮
This commit is contained in:
parent
c7bcc72b98
commit
97dda678c8
@ -3,8 +3,9 @@ FROM python:3.11-slim
|
|||||||
|
|
||||||
# 设置标签信息
|
# 设置标签信息
|
||||||
LABEL maintainer="Xianyu Auto Reply System"
|
LABEL maintainer="Xianyu Auto Reply System"
|
||||||
LABEL version="1.0.0"
|
LABEL version="2.0.0"
|
||||||
LABEL description="闲鱼自动回复系统 - Docker版本"
|
LABEL description="闲鱼自动回复系统 - 企业级多用户版本"
|
||||||
|
LABEL repository="https://github.com/zhinianboke/xianyu-auto-reply"
|
||||||
|
|
||||||
# 设置工作目录
|
# 设置工作目录
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
282
Docker快速启动指南.md
282
Docker快速启动指南.md
@ -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
110
README.md
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
[](https://github.com/zhinianboke/xianyu-auto-reply)
|
[](https://github.com/zhinianboke/xianyu-auto-reply)
|
||||||
[](https://github.com/zhinianboke/xianyu-auto-reply#-快速开始)
|
[](https://github.com/zhinianboke/xianyu-auto-reply#-快速开始)
|
||||||
|
[](https://www.python.org/)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
一个功能完整的闲鱼自动回复和管理系统,支持多用户、多账号管理,具备智能回复、自动发货、商品管理等企业级功能。
|
一个功能完整的闲鱼自动回复和管理系统,支持多用户、多账号管理,具备智能回复、自动发货、自动确认发货、商品管理等企业级功能。
|
||||||
|
|
||||||
## ✨ 核心特性
|
## ✨ 核心特性
|
||||||
|
|
||||||
@ -34,6 +36,8 @@
|
|||||||
- **多种触发** - 支持付款消息、小刀消息等多种触发条件
|
- **多种触发** - 支持付款消息、小刀消息等多种触发条件
|
||||||
- **防重复发货** - 智能防重复机制,避免重复发货
|
- **防重复发货** - 智能防重复机制,避免重复发货
|
||||||
- **多种发货方式** - 支持文本内容、卡密文件、API调用等发货方式
|
- **多种发货方式** - 支持文本内容、卡密文件、API调用等发货方式
|
||||||
|
- **自动确认发货** - 检测到付款后自动调用闲鱼API确认发货
|
||||||
|
- **防重复确认** - 智能防重复确认机制,避免重复API调用
|
||||||
- **发货统计** - 完整的发货记录和统计功能
|
- **发货统计** - 完整的发货记录和统计功能
|
||||||
|
|
||||||
### 🛍️ 商品管理
|
### 🛍️ 商品管理
|
||||||
@ -73,7 +77,8 @@ xianyu-auto-reply/
|
|||||||
│ ├── cookie_manager.py # Cookie和账号管理
|
│ ├── cookie_manager.py # Cookie和账号管理
|
||||||
│ ├── ai_reply_engine.py # AI智能回复引擎
|
│ ├── ai_reply_engine.py # AI智能回复引擎
|
||||||
│ ├── file_log_collector.py # 日志收集和管理
|
│ ├── file_log_collector.py # 日志收集和管理
|
||||||
│ └── config.py # 配置文件管理
|
│ ├── config.py # 配置文件管理
|
||||||
|
│ └── secure_confirm_ultra.py # 自动确认发货模块(加密保护)
|
||||||
├── 🛠️ 工具模块
|
├── 🛠️ 工具模块
|
||||||
│ └── utils/
|
│ └── utils/
|
||||||
│ ├── xianyu_utils.py # 闲鱼API工具函数
|
│ ├── xianyu_utils.py # 闲鱼API工具函数
|
||||||
@ -207,7 +212,7 @@ docker rm -f xianyu-auto-reply
|
|||||||
### 4. 设置自动发货
|
### 4. 设置自动发货
|
||||||
- 添加发货规则,设置商品关键词和发货内容
|
- 添加发货规则,设置商品关键词和发货内容
|
||||||
- 支持文本内容和卡密文件两种发货方式
|
- 支持文本内容和卡密文件两种发货方式
|
||||||
- 系统检测到付款消息时自动发货
|
- 系统检测到付款消息时自动确认发货并自动发货
|
||||||
|
|
||||||
### 5. 使用商品搜索功能
|
### 5. 使用商品搜索功能
|
||||||
- 访问商品搜索页面(需要登录)
|
- 访问商品搜索页面(需要登录)
|
||||||
@ -240,40 +245,45 @@ docker rm -f xianyu-auto-reply
|
|||||||
└─────────────────────────────────────┘
|
└─────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📁 项目结构
|
## 📁 核心文件功能说明
|
||||||
|
|
||||||
```
|
### 🚀 启动和核心模块
|
||||||
xianyu-auto-reply/
|
- **`Start.py`** - 项目启动入口,初始化所有服务和组件
|
||||||
├── Start.py # 主启动文件
|
- **`XianyuAutoAsync.py`** - 闲鱼WebSocket连接核心,处理消息收发和自动回复
|
||||||
├── XianyuAutoAsync.py # 闲鱼WebSocket客户端核心
|
- **`reply_server.py`** - FastAPI Web服务器,提供管理界面和API接口
|
||||||
├── reply_server.py # FastAPI Web服务器
|
- **`cookie_manager.py`** - 多账号Cookie管理,负责账号任务的启动和停止
|
||||||
├── db_manager.py # 数据库管理模块
|
|
||||||
├── cookie_manager.py # Cookie和任务管理
|
### 🗄️ 数据和配置管理
|
||||||
├── ai_reply_engine.py # AI回复引擎
|
- **`db_manager.py`** - SQLite数据库管理,处理用户数据、商品信息、关键词等
|
||||||
├── config.py # 配置管理
|
- **`config.py`** - 配置文件管理,加载和管理全局配置
|
||||||
├── file_log_collector.py # 日志收集器
|
- **`global_config.yml`** - 全局配置文件,包含所有系统配置项
|
||||||
├── global_config.yml # 全局配置文件
|
|
||||||
├── requirements.txt # Python依赖
|
### 🤖 智能功能模块
|
||||||
├── docker-compose.yml # Docker编排配置
|
- **`ai_reply_engine.py`** - AI智能回复引擎,支持多种AI模型
|
||||||
├── Dockerfile # Docker镜像构建
|
- **`secure_confirm_ultra.py`** - 自动确认发货模块(加密保护)
|
||||||
├── utils/ # 工具模块
|
- **`file_log_collector.py`** - 日志收集和管理,提供实时日志查看
|
||||||
│ ├── item_search.py # 商品搜索功能
|
|
||||||
│ └── ... # 其他工具模块
|
### 🛠️ 工具模块
|
||||||
├── static/ # 前端静态文件
|
- **`utils/xianyu_utils.py`** - 闲鱼API工具函数,包含加密解密、签名生成等
|
||||||
│ ├── index.html # 主界面
|
- **`utils/message_utils.py`** - 消息格式化工具
|
||||||
│ ├── login.html # 登录页面
|
- **`utils/ws_utils.py`** - WebSocket客户端工具
|
||||||
│ ├── register.html # 注册页面
|
- **`utils/item_search.py`** - 商品搜索功能,基于Playwright技术
|
||||||
│ ├── item_search.html # 商品搜索页面
|
|
||||||
│ ├── user_management.html # 用户管理页面
|
### 🌐 前端界面
|
||||||
│ ├── data_management.html # 数据管理页面
|
- **`static/index.html`** - 主管理界面,账号管理和系统监控
|
||||||
│ ├── log_management.html # 日志管理页面
|
- **`static/login.html`** - 用户登录页面
|
||||||
│ └── lib/ # 本地静态资源库
|
- **`static/register.html`** - 用户注册页面,支持邮箱验证
|
||||||
│ ├── bootstrap/ # Bootstrap框架
|
- **`static/user_management.html`** - 用户管理页面(管理员功能)
|
||||||
│ └── bootstrap-icons/ # Bootstrap图标
|
- **`static/data_management.html`** - 数据管理页面,关键词导入导出
|
||||||
├── logs/ # 日志文件目录
|
- **`static/log_management.html`** - 日志管理页面,实时日志查看
|
||||||
├── data/ # 数据库文件目录
|
- **`static/item_search.html`** - 商品搜索页面,获取真实闲鱼数据
|
||||||
└── backups/ # 备份文件目录
|
|
||||||
```
|
### 🐳 部署配置
|
||||||
|
- **`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层编码混淆
|
||||||
|
- **变量名随机化**:防止静态分析
|
||||||
|
- **运行时解密**:代码在内存中动态解密执行
|
||||||
|
- **防重复机制**:智能防重复确认和发货
|
||||||
|
|
||||||
## 🤝 贡献指南
|
## 🤝 贡献指南
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
@ -101,6 +102,10 @@ class XianyuLive:
|
|||||||
# 自动发货防重复机制
|
# 自动发货防重复机制
|
||||||
self.last_delivery_time = {} # 记录每个商品的最后发货时间
|
self.last_delivery_time = {} # 记录每个商品的最后发货时间
|
||||||
self.delivery_cooldown = 60 # 1分钟内不重复发货
|
self.delivery_cooldown = 60 # 1分钟内不重复发货
|
||||||
|
|
||||||
|
# 自动确认发货防重复机制
|
||||||
|
self.confirmed_orders = {} # 记录已确认发货的订单,防止重复确认
|
||||||
|
self.order_confirm_cooldown = 300 # 5分钟内不重复确认同一订单
|
||||||
|
|
||||||
# 人工接管功能已禁用,永远走自动模式
|
# 人工接管功能已禁用,永远走自动模式
|
||||||
# self.manual_mode_conversations = set() # 存储处于人工接管模式的会话ID
|
# self.manual_mode_conversations = set() # 存储处于人工接管模式的会话ID
|
||||||
@ -623,7 +628,6 @@ class XianyuLive:
|
|||||||
message_1 = message.get('1')
|
message_1 = message.get('1')
|
||||||
if isinstance(message_1, str):
|
if isinstance(message_1, str):
|
||||||
# 尝试从字符串中提取数字ID
|
# 尝试从字符串中提取数字ID
|
||||||
import re
|
|
||||||
id_match = re.search(r'(\d{10,})', message_1)
|
id_match = re.search(r'(\d{10,})', message_1)
|
||||||
if id_match:
|
if id_match:
|
||||||
logger.info(f"从message[1]字符串中提取商品ID: {id_match.group(1)}")
|
logger.info(f"从message[1]字符串中提取商品ID: {id_match.group(1)}")
|
||||||
@ -662,7 +666,6 @@ class XianyuLive:
|
|||||||
# 从消息内容中提取数字ID
|
# 从消息内容中提取数字ID
|
||||||
content = message_3.get('content', '')
|
content = message_3.get('content', '')
|
||||||
if isinstance(content, str) and content:
|
if isinstance(content, str) and content:
|
||||||
import re
|
|
||||||
id_match = re.search(r'(\d{10,})', content)
|
id_match = re.search(r'(\d{10,})', content)
|
||||||
if id_match:
|
if id_match:
|
||||||
logger.info(f"【{self.cookie_id}】从消息内容中提取商品ID: {id_match.group(1)}")
|
logger.info(f"【{self.cookie_id}】从消息内容中提取商品ID: {id_match.group(1)}")
|
||||||
@ -687,7 +690,6 @@ class XianyuLive:
|
|||||||
|
|
||||||
elif isinstance(obj, str):
|
elif isinstance(obj, str):
|
||||||
# 从字符串中提取可能的商品ID
|
# 从字符串中提取可能的商品ID
|
||||||
import re
|
|
||||||
id_match = re.search(r'(\d{10,})', obj)
|
id_match = re.search(r'(\d{10,})', obj)
|
||||||
if id_match:
|
if id_match:
|
||||||
logger.info(f"从{path}字符串中提取商品ID: {id_match.group(1)}")
|
logger.info(f"从{path}字符串中提取商品ID: {id_match.group(1)}")
|
||||||
@ -873,7 +875,6 @@ class XianyuLive:
|
|||||||
if not price_str:
|
if not price_str:
|
||||||
return 0.0
|
return 0.0
|
||||||
# 移除非数字字符,保留小数点
|
# 移除非数字字符,保留小数点
|
||||||
import re
|
|
||||||
price_clean = re.sub(r'[^\d.]', '', str(price_str))
|
price_clean = re.sub(r'[^\d.]', '', str(price_str))
|
||||||
return float(price_clean) if price_clean else 0.0
|
return float(price_clean) if price_clean else 0.0
|
||||||
except:
|
except:
|
||||||
@ -884,7 +885,6 @@ class XianyuLive:
|
|||||||
try:
|
try:
|
||||||
from db_manager import db_manager
|
from db_manager import db_manager
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
# 获取当前账号的通知配置
|
# 获取当前账号的通知配置
|
||||||
@ -926,7 +926,6 @@ class XianyuLive:
|
|||||||
"""发送QQ通知"""
|
"""发送QQ通知"""
|
||||||
try:
|
try:
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
|
||||||
|
|
||||||
# 解析配置(QQ号码)
|
# 解析配置(QQ号码)
|
||||||
qq_number = config.strip()
|
qq_number = config.strip()
|
||||||
@ -1075,6 +1074,28 @@ class XianyuLive:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送自动发货通知异常: {self._safe_str(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):
|
async def _auto_delivery(self, item_id: str, item_title: str = None):
|
||||||
"""自动发货功能"""
|
"""自动发货功能"""
|
||||||
try:
|
try:
|
||||||
@ -1099,7 +1120,6 @@ class XianyuLive:
|
|||||||
|
|
||||||
# 解析 shareInfoJsonString 并提取 content 内容
|
# 解析 shareInfoJsonString 并提取 content 内容
|
||||||
try:
|
try:
|
||||||
import json
|
|
||||||
share_info = json.loads(shareInfoJsonString)
|
share_info = json.loads(shareInfoJsonString)
|
||||||
content = share_info.get('contentParams', {}).get('mainParams', {}).get('content', '')
|
content = share_info.get('contentParams', {}).get('mainParams', {}).get('content', '')
|
||||||
if content:
|
if content:
|
||||||
@ -1262,8 +1282,6 @@ class XianyuLive:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
api_config = rule.get('card_api_config')
|
api_config = rule.get('card_api_config')
|
||||||
if not api_config:
|
if not api_config:
|
||||||
@ -1978,12 +1996,117 @@ class XianyuLive:
|
|||||||
elif send_message == '快给ta一个评价吧~' or send_message == '快给ta一个评价吧~':
|
elif send_message == '快给ta一个评价吧~' or send_message == '快给ta一个评价吧~':
|
||||||
logger.info(f'[{msg_time}] 【{self.cookie_id}】评价提醒消息不处理')
|
logger.info(f'[{msg_time}] 【{self.cookie_id}】评价提醒消息不处理')
|
||||||
return
|
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 == '[你已发货]':
|
elif send_message == '[你已发货]':
|
||||||
logger.info(f'[{msg_time}] 【{self.cookie_id}】发货确认消息不处理')
|
logger.info(f'[{msg_time}] 【{self.cookie_id}】发货确认消息不处理')
|
||||||
return
|
return
|
||||||
elif send_message == '[我已付款,等待你发货]':
|
elif send_message == '[我已付款,等待你发货]':
|
||||||
logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】买家已付款,准备自动发货')
|
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):
|
if not self.can_auto_delivery(item_id):
|
||||||
return
|
return
|
||||||
@ -2023,6 +2146,89 @@ class XianyuLive:
|
|||||||
elif send_message == '[已付款,待发货]':
|
elif send_message == '[已付款,待发货]':
|
||||||
logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】买家已付款,准备自动发货')
|
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):
|
if not self.can_auto_delivery(item_id):
|
||||||
return
|
return
|
||||||
@ -2086,6 +2292,89 @@ class XianyuLive:
|
|||||||
if card_title == "我已小刀,待刀成":
|
if card_title == "我已小刀,待刀成":
|
||||||
logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】检测到"我已小刀,待刀成",准备自动发货')
|
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):
|
if not self.can_auto_delivery(item_id):
|
||||||
return
|
return
|
||||||
|
@ -54,19 +54,14 @@ check_dependencies() {
|
|||||||
# 初始化配置
|
# 初始化配置
|
||||||
init_config() {
|
init_config() {
|
||||||
print_info "初始化配置文件..."
|
print_info "初始化配置文件..."
|
||||||
|
|
||||||
if [ ! -f "$ENV_FILE" ]; then
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
if [ -f ".env.example" ]; then
|
print_warning "$ENV_FILE 文件不存在,将使用默认配置"
|
||||||
cp .env.example "$ENV_FILE"
|
print_info "如需自定义配置,请创建 $ENV_FILE 文件"
|
||||||
print_success "已创建 $ENV_FILE 配置文件"
|
|
||||||
else
|
|
||||||
print_error ".env.example 文件不存在"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
print_warning "$ENV_FILE 已存在,跳过创建"
|
print_success "$ENV_FILE 配置文件已存在"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 创建必要的目录
|
# 创建必要的目录
|
||||||
mkdir -p data logs backups
|
mkdir -p data logs backups
|
||||||
print_success "已创建必要的目录"
|
print_success "已创建必要的目录"
|
||||||
@ -328,9 +323,7 @@ main() {
|
|||||||
"cleanup")
|
"cleanup")
|
||||||
cleanup
|
cleanup
|
||||||
;;
|
;;
|
||||||
"fix-playwright")
|
|
||||||
fix_playwright
|
|
||||||
;;
|
|
||||||
"help"|"--help"|"-h")
|
"help"|"--help"|"-h")
|
||||||
show_help
|
show_help
|
||||||
;;
|
;;
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
|
# ================================
|
||||||
|
# 闲鱼自动回复系统 - Python依赖包
|
||||||
|
# ================================
|
||||||
|
|
||||||
# Web框架和API相关
|
# Web框架和API相关
|
||||||
fastapi>=0.111
|
fastapi>=0.111.0
|
||||||
uvicorn[standard]>=0.29
|
uvicorn[standard]>=0.29.0
|
||||||
pydantic>=2.7
|
pydantic>=2.7.0
|
||||||
|
|
||||||
# 日志记录
|
# 日志记录
|
||||||
loguru>=0.7
|
loguru>=0.7.0
|
||||||
|
|
||||||
# 网络通信
|
# 网络通信
|
||||||
websockets>=10.0,<13.0 # 兼容性版本范围
|
websockets>=10.0,<13.0
|
||||||
aiohttp>=3.9
|
aiohttp>=3.9.0
|
||||||
|
|
||||||
# 配置文件处理
|
# 配置文件处理
|
||||||
PyYAML>=6.0
|
PyYAML>=6.0.0
|
||||||
|
|
||||||
# JavaScript执行引擎
|
# JavaScript执行引擎
|
||||||
PyExecJS>=1.5.1
|
PyExecJS>=1.5.1
|
||||||
@ -22,7 +26,7 @@ blackboxprotobuf>=1.0.1
|
|||||||
# 系统监控
|
# 系统监控
|
||||||
psutil>=5.9.0
|
psutil>=5.9.0
|
||||||
|
|
||||||
# HTTP客户端(用于测试)
|
# HTTP客户端
|
||||||
requests>=2.31.0
|
requests>=2.31.0
|
||||||
|
|
||||||
# 文件上传支持
|
# 文件上传支持
|
||||||
@ -42,6 +46,7 @@ playwright>=1.40.0
|
|||||||
PyJWT>=2.8.0
|
PyJWT>=2.8.0
|
||||||
passlib>=1.7.4
|
passlib>=1.7.4
|
||||||
bcrypt>=4.0.1
|
bcrypt>=4.0.1
|
||||||
|
cryptography>=41.0.0
|
||||||
|
|
||||||
# 时间处理
|
# 时间处理
|
||||||
python-dateutil>=2.8.2
|
python-dateutil>=2.8.2
|
||||||
@ -51,4 +56,10 @@ regex>=2023.10.3
|
|||||||
|
|
||||||
# Excel文件处理(导入导出功能)
|
# Excel文件处理(导入导出功能)
|
||||||
pandas>=2.0.0
|
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
56
secure_confirm_ultra.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user