Compare commits
3 Commits
7b40a54aaf
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ab6a2bb7ea | |||
| 69046d1ccc | |||
| f0bfd9dbbe |
+2
-13
@@ -1,23 +1,12 @@
|
|||||||
# Sensitive data - NEVER commit
|
# Sensitive
|
||||||
CLAUDE.md
|
|
||||||
cookies.json
|
cookies.json
|
||||||
xiaomi_raw_cookies.json
|
|
||||||
xiaomi_config.json
|
xiaomi_config.json
|
||||||
|
xiaomi_raw_cookies.json
|
||||||
order_data.json
|
order_data.json
|
||||||
*.mi.token
|
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# OS
|
|
||||||
Thumbs.db
|
|
||||||
Desktop.ini
|
|
||||||
|
|
||||||
# Claude
|
|
||||||
.claude/
|
.claude/
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# 丰享订单监控项目
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
自动监控丰享商家端(fs.szfx.com)的新订单,同时推送到企业微信、飞书群和本地TTS语音播报**。
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
| 文件 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `order_monitor.py` | 主程序:监控订单 + 企业微信 + 飞书 + 本地TTS播报 |
|
||||||
|
| `push_latest_order.py` | 单次获取最新订单并推送企业微信 |
|
||||||
|
| `fetch_orders.py` | 复用 Cookie 获取订单数据(命令行) |
|
||||||
|
| `fengxiang_scraper.py` | 首次登录获取 Cookie(Playwright 半自动) |
|
||||||
|
| `cookies.json` | 丰享平台的登录 Cookie |
|
||||||
|
| `order_data.json` | 订单数据样例 |
|
||||||
|
|
||||||
|
## 已逆向的 API
|
||||||
|
|
||||||
|
### 丰享平台
|
||||||
|
|
||||||
|
- **登录**: `POST https://fspass.szfx.com/api/login`
|
||||||
|
- 参数: `uname`(手机号), `upass`(密码Base64反转), `ticket`, `randstr`(腾讯验证码)
|
||||||
|
- 登录后通过 `https://fs.szfx.com/saasmerchant/setstoken` 设置 Cookie
|
||||||
|
- Cookie 保存在浏览器中,主要: `USS`, `PTOKEN`, `CPTOKEN`, `STOKEN`
|
||||||
|
|
||||||
|
- **订单列表**: `POST https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list`
|
||||||
|
- Content-Type: application/json
|
||||||
|
- Body: `{"shopId": "20434543575189", "page": 1, "pageSize": 20}`
|
||||||
|
- 需要登录 Cookie,未登录返回 `errno: 110003`
|
||||||
|
- 返回: `{errno: 0, data: {total, list: [...], count}}`
|
||||||
|
|
||||||
|
- **关键字段**:
|
||||||
|
- `shopId`: `20434543575189` (益选便利店)
|
||||||
|
- `orderId`: 订单号
|
||||||
|
- `amountPayable`: 实付金额(单位:分)
|
||||||
|
- `allPrice`: 订单金额(单位:分)
|
||||||
|
- `shopName`, `city`, `companyName`, `workplaceName`: 业务信息
|
||||||
|
- `createTime`, `payTime`, `finishTime`: 时间戳(秒)
|
||||||
|
|
||||||
|
### 本地 TTS(IndexTTS2)
|
||||||
|
|
||||||
|
- 使用本地部署的 IndexTTS2 模型,无需联网
|
||||||
|
- 模型路径: `E:\2025Code\python\IndexTT\index-tts\`
|
||||||
|
- 语音样本: `examples/voice_01.wav`
|
||||||
|
- 封装模块: `local_tts.py`
|
||||||
|
- 调用方式: `_local_speak(text)` → 合成 WAV → 本地播放
|
||||||
|
|
||||||
|
### 企业微信 Webhook
|
||||||
|
- URL: `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=644ab6d9-3b66-4166-88e9-5a8a89e3731d`
|
||||||
|
- 只能发消息,不能收消息
|
||||||
|
|
||||||
|
## order_monitor.py 核心逻辑
|
||||||
|
|
||||||
|
1. Playwright 打开浏览器,用户手动登录丰享平台
|
||||||
|
2. 登录成功后,通过浏览器页面的 `fetch()` 调用订单 API(复用浏览器会话,最自然)
|
||||||
|
3. 每次拉取最近 N 条订单,和上次最新订单号对比,找出所有新订单
|
||||||
|
4. 新订单同时推送企业微信 + 飞书 + 本地TTS播报
|
||||||
|
5. 高峰期(11-13点、17-19点)10-20秒轮询,闲时 30-60 秒
|
||||||
|
6. 每 5 分钟刷新页面防 Session 过期
|
||||||
|
7. Session 过期自动检测 → 通知企业微信 → 等待用户重新登录
|
||||||
|
|
||||||
|
## 消息格式
|
||||||
|
|
||||||
|
- 企业微信: `【丰享丰食】订单收款成功,收款24.00元`
|
||||||
|
- 本地TTS: 同上文案语音播报
|
||||||
|
|
||||||
|
## 已安装的依赖
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install playwright requests
|
||||||
|
playwright install chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 丰享登录有**腾讯验证码 TCaptcha**,无法纯接口绕过,必须用 Playwright 浏览器登录
|
||||||
|
- Windows 终端输出中文会乱码,但实际发送的内容是正确的 UTF-8
|
||||||
@@ -1,106 +1,63 @@
|
|||||||
# 丰享订单监控
|
# 丰享订单监控
|
||||||
|
|
||||||
自动监控丰享商家端(fs.szfx.com)的新订单,同时推送到**企业微信群**和**小爱音箱语音播报**。
|
自动监控丰享商家端(fs.szfx.com)新订单,同时推送到**企业微信**、**飞书群**并进行**本地TTS语音播报**。
|
||||||
|
|
||||||
## 功能特性
|
|
||||||
|
|
||||||
- **自动轮询**:高峰期(11-13点、17-19点)10-20秒/次,闲时30-60秒/次
|
|
||||||
- **双通道推送**:新订单同时推送企业微信消息 + 小爱音箱 TTS 语音播报
|
|
||||||
- **Cookie 复用**:首次登录后保存 Cookie,后续启动无需重复登录
|
|
||||||
- **夜间暂停**:21:00 ~ 07:40 自动暂停轮询,节省资源
|
|
||||||
- **Session 保活**:每5分钟自动发请求维持 Cookie 活性
|
|
||||||
- **过期自动恢复**:Cookie 过期时弹浏览器提示重新登录,并推送企业微信通知
|
|
||||||
- **开机自启**:支持 Windows 计划任务,登录系统后自动运行
|
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
### 1. 安装依赖
|
双击 `run.bat` 启动。首次运行会自动打开浏览器登录丰享平台,后续复用 Cookie 无需反复登录。
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install playwright requests miservice_fork aiohttp
|
|
||||||
playwright install chromium
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 配置小米音箱(可选)
|
|
||||||
|
|
||||||
首次使用需要获取小米 passToken:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python setup_xiaomi.py
|
|
||||||
```
|
|
||||||
|
|
||||||
浏览器打开后手动登录小米账号,Token 自动保存到 `~/.mi.token`。
|
|
||||||
|
|
||||||
### 3. 运行监控
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python order_monitor.py
|
|
||||||
```
|
|
||||||
|
|
||||||
首次运行会打开浏览器,手动完成丰享平台登录(有腾讯验证码,需人工操作)。登录成功后 Cookie 自动保存,之后重启无需再登录。
|
|
||||||
|
|
||||||
### 4. 测试推送
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python test_push.py
|
|
||||||
```
|
|
||||||
|
|
||||||
拉取最新一条订单,测试企业微信和小爱音箱是否正常。
|
|
||||||
|
|
||||||
## 文件说明
|
## 文件说明
|
||||||
|
|
||||||
| 文件 | 用途 |
|
| 文件 | 用途 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `order_monitor.py` | 主监控程序,轮询订单 + 推送 + 播报 |
|
| `order_monitor.py` | 主程序:监控订单 + 企业微信/飞书推送 + 本地TTS播报 |
|
||||||
| `test_push.py` | 测试脚本,验证企业微信和音箱通道 |
|
| `local_tts.py` | 本地 TTS 封装模块(基于 IndexTTS2) |
|
||||||
| `push_latest_order.py` | 单次获取最新订单并推送企业微信 |
|
| `run.bat` | 一键启动脚本 |
|
||||||
| `fetch_orders.py` | 命令行获取订单数据(JSON 输出) |
|
| `push_latest_order.py` | 单次获取最新订单并推送 |
|
||||||
| `fengxiang_scraper.py` | Playwright 半自动登录获取 Cookie |
|
| `fetch_orders.py` | 复用 Cookie 获取订单数据(命令行) |
|
||||||
| `setup_xiaomi.py` | 小米账号登录获取 serviceToken |
|
| `fengxiang_scraper.py` | 首次登录获取 Cookie(Playwright 半自动) |
|
||||||
| `run_monitor.bat` | Windows 启动批处理 |
|
| `cookies.json` | 丰享平台登录 Cookie(自动生成) |
|
||||||
| `setup_autostart.ps1` | Windows 开机自启配置脚本 |
|
|
||||||
|
|
||||||
## 已逆向的 API
|
## 运行环境
|
||||||
|
|
||||||
### 丰享平台
|
- Python 虚拟环境: `E:\2025Code\python\IndexTT\index-tts\.venv`
|
||||||
|
- 依赖: `playwright`、`requests`
|
||||||
|
- 浏览器: Chromium(Playwright 自动管理)
|
||||||
|
- TTS 模型: IndexTTS2(本地部署,首次加载需 1-2 分钟)
|
||||||
|
|
||||||
- **登录**:`POST https://fspass.szfx.com/api/login`
|
## 通知渠道
|
||||||
- 参数:`uname`(手机号)、`upass`(密码 Base64 反转)、`ticket`、`randstr`(腾讯验证码)
|
|
||||||
- Cookie:`USS`、`PTOKEN`、`CPTOKEN`、`STOKEN`
|
|
||||||
|
|
||||||
- **订单列表**:`POST https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list`
|
| 渠道 | 说明 |
|
||||||
- Content-Type: application/json
|
|------|------|
|
||||||
- Body: `{"shopId": "...", "page": 1, "pageSize": 20}`
|
| 企业微信 | Webhook 推送消息 |
|
||||||
- 返回:`{errno: 0, data: {total, list: [...], count}}`
|
| 飞书群 | Webhook 推送消息 |
|
||||||
|
| 本地 TTS | IndexTTS2 合成语音 + 扬声器播放 |
|
||||||
|
|
||||||
### 小米/小爱音箱
|
## 轮询策略
|
||||||
|
|
||||||
- 使用 [miservice_fork](https://github.com/nicepkg/miservice_fork) Python 库
|
| 时段 | 间隔 | 拉取数量 |
|
||||||
- TTS 播报:`MiNAService.text_to_speech(deviceId, text)`
|
|------|------|---------|
|
||||||
- Token 保存在 `~/.mi.token`
|
| 高峰期 11:00-13:00, 17:00-19:00 | 5-10秒 | 20条 |
|
||||||
|
| 闲时 | 5-10秒 | 5条 |
|
||||||
|
| 夜间 21:00-07:40 | 自动暂停 | — |
|
||||||
|
|
||||||
## 消息格式
|
## 消息格式
|
||||||
|
|
||||||
企业微信和小爱音箱使用统一格式:
|
- 推送: `【丰享丰食】订单收款成功,收款24.00元`
|
||||||
|
- TTS: 同上文案语音播报
|
||||||
|
|
||||||
> 【丰享丰食】订单收款成功,收款24.00元
|
## 已逆向 API
|
||||||
|
|
||||||
## 开机自启(Windows)
|
### 丰享平台
|
||||||
|
|
||||||
已配置计划任务 `FXOrderMonitor`,用户登录时自动运行。
|
- **登录**: `POST https://fspass.szfx.com/api/login`
|
||||||
|
- 腾讯验证码 TCaptcha,必须浏览器手动登录
|
||||||
```powershell
|
- **订单列表**: `POST https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list`
|
||||||
# 手动配置
|
- Body: `{"shopId": "20434543575189", "page": 1, "pageSize": 20}`
|
||||||
.\setup_autostart.ps1
|
- 返回: `{errno: 0, data: {total, list: [...], count}}`
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
- 丰享登录有腾讯验证码 TCaptcha,无法纯接口绕过,首次必须用浏览器登录
|
- Cookie 过期时自动弹浏览器提示重新登录,并推送通知
|
||||||
- Cookie 有效期较长,持续使用可维持;过期会自动弹窗提示重新登录
|
- 每 5 分钟自动保活防 Session 过期
|
||||||
- 小米 passToken 会过期,过期后需重新运行 `setup_xiaomi.py`
|
- 无需小米账号或小爱音箱,TTS 完全本地运行
|
||||||
- 配置文件(cookies.json、xiaomi_config.json)含敏感信息,已加入 .gitignore
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""
|
||||||
|
本地TTS语音播报模块 - 基于IndexTTS2
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import tempfile
|
||||||
|
import winsound
|
||||||
|
|
||||||
|
# IndexTTS2 路径配置
|
||||||
|
_INDEXTTS_HOME = r"E:\2025Code\python\IndexTT\index-tts"
|
||||||
|
_INDEXTTS_MODEL_DIR = os.path.join(_INDEXTTS_HOME, "checkpoints")
|
||||||
|
_INDEXTTS_CFG = os.path.join(_INDEXTTS_MODEL_DIR, "config.yaml")
|
||||||
|
_INDEXTTS_VENV_PYTHON = os.path.join(_INDEXTTS_HOME, ".venv", "Scripts", "python.exe")
|
||||||
|
|
||||||
|
# 默认语音样本
|
||||||
|
_DEFAULT_VOICE_PROMPT = os.path.join(_INDEXTTS_HOME, "examples", "voice_01.wav")
|
||||||
|
|
||||||
|
# 全局 TTS 实例(延迟初始化)
|
||||||
|
_tts_instance = None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tts():
|
||||||
|
"""延迟初始化 IndexTTS2 实例"""
|
||||||
|
global _tts_instance
|
||||||
|
if _tts_instance is None:
|
||||||
|
sys.path.insert(0, _INDEXTTS_HOME)
|
||||||
|
from indextts.infer_v2 import IndexTTS2
|
||||||
|
print("[TTS] 正在加载 IndexTTS2 模型(首次加载较慢,请耐心等待)...")
|
||||||
|
_tts_instance = IndexTTS2(
|
||||||
|
cfg_path=_INDEXTTS_CFG,
|
||||||
|
model_dir=_INDEXTTS_MODEL_DIR,
|
||||||
|
use_fp16=True,
|
||||||
|
use_cuda_kernel=False,
|
||||||
|
use_deepspeed=False
|
||||||
|
)
|
||||||
|
print("[TTS] IndexTTS2 模型加载完成")
|
||||||
|
return _tts_instance
|
||||||
|
|
||||||
|
|
||||||
|
def speak(text, voice_prompt=None):
|
||||||
|
"""使用本地 IndexTTS2 合成语音并播放
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: 要播报的文本
|
||||||
|
voice_prompt: 语音样本路径,默认使用 voice_01.wav
|
||||||
|
"""
|
||||||
|
if voice_prompt is None:
|
||||||
|
voice_prompt = _DEFAULT_VOICE_PROMPT
|
||||||
|
|
||||||
|
tts = _get_tts()
|
||||||
|
output_path = os.path.join(tempfile.gettempdir(), f"fx_tts_{int(time.time() * 1000)}.wav")
|
||||||
|
|
||||||
|
tts.infer(
|
||||||
|
spk_audio_prompt=voice_prompt,
|
||||||
|
text=text,
|
||||||
|
output_path=output_path,
|
||||||
|
verbose=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# 播放生成的音频
|
||||||
|
winsound.PlaySound(output_path, winsound.SND_FILENAME)
|
||||||
|
return output_path
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
speak("本地语音播报测试成功")
|
||||||
+27
-47
@@ -10,25 +10,20 @@ import json
|
|||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import asyncio
|
|
||||||
import threading
|
|
||||||
import requests
|
import requests
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from aiohttp import ClientSession
|
|
||||||
from miservice import MiAccount, MiNAService
|
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
|
from local_tts import speak as _local_speak
|
||||||
|
|
||||||
# ============ 配置 ============
|
# ============ 配置 ============
|
||||||
LOGIN_URL = "https://fspass.szfx.com/ucenter/userlogin?platform=saas&redirect_url=https%3A%2F%2Ffs.szfx.com%2FMMS%23%2F&return_url=https%3A%2F%2Ffs.szfx.com%2Fsaasmerchant%2Fsetstoken"
|
LOGIN_URL = "https://fspass.szfx.com/ucenter/userlogin?platform=saas&redirect_url=https%3A%2F%2Ffs.szfx.com%2FMMS%23%2F&return_url=https%3A%2F%2Ffs.szfx.com%2Fsaasmerchant%2Fsetstoken"
|
||||||
DATA_API = "https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list"
|
DATA_API = "https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list"
|
||||||
SHOP_ID = "20434543575189"
|
SHOP_ID = "20434543575189"
|
||||||
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=644ab6d9-3b66-4166-88e9-5a8a89e3731d"
|
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=644ab6d9-3b66-4166-88e9-5a8a89e3731d"
|
||||||
|
FEISHU_WEBHOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/oc_d5ef8b1abf66842c28ef57e1658dc096"
|
||||||
COOKIES_FILE = "cookies.json"
|
COOKIES_FILE = "cookies.json"
|
||||||
|
|
||||||
# 小爱音箱配置
|
# 本地 TTS 配置(使用 IndexTTS2,无需额外配置)
|
||||||
XIAOMI_USER_ID = "1136458602"
|
|
||||||
XIAOMI_TOKEN_PATH = str(Path.home() / ".mi.token")
|
|
||||||
XIAOMI_SPEAKER_DID = "3ba2c1e8-d8cb-45c5-b88a-15624e7a02f3"
|
|
||||||
|
|
||||||
# 高峰时段配置
|
# 高峰时段配置
|
||||||
PEAK_HOURS = [(11, 13), (17, 19)]
|
PEAK_HOURS = [(11, 13), (17, 19)]
|
||||||
@@ -86,6 +81,12 @@ def format_msg(order):
|
|||||||
return f"【丰享丰食】订单收款成功,收款{fmt_money(order['amountPayable'])}"
|
return f"【丰享丰食】订单收款成功,收款{fmt_money(order['amountPayable'])}"
|
||||||
|
|
||||||
|
|
||||||
|
def notify_all(text):
|
||||||
|
"""统一发送通知到企业微信 + 飞书"""
|
||||||
|
send_to_wecom(text)
|
||||||
|
send_to_feishu(text)
|
||||||
|
|
||||||
|
|
||||||
def send_to_wecom(text):
|
def send_to_wecom(text):
|
||||||
payload = {"msgtype": "text", "text": {"content": text}}
|
payload = {"msgtype": "text", "text": {"content": text}}
|
||||||
try:
|
try:
|
||||||
@@ -94,53 +95,32 @@ def send_to_wecom(text):
|
|||||||
if result.get("errcode") == 0:
|
if result.get("errcode") == 0:
|
||||||
log("企业微信推送成功")
|
log("企业微信推送成功")
|
||||||
else:
|
else:
|
||||||
log(f"推送失败: {result}")
|
log(f"企业微信推送失败: {result}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"推送异常: {e}")
|
log(f"企业微信推送异常: {e}")
|
||||||
|
|
||||||
|
|
||||||
def _run_async_in_thread(coro):
|
def send_to_feishu(text):
|
||||||
"""在独立线程中运行协程,避免与 Playwright 事件循环冲突"""
|
payload = {"msg_type": "text", "content": {"text": text}}
|
||||||
result = None
|
|
||||||
error = None
|
|
||||||
|
|
||||||
def _target():
|
|
||||||
nonlocal result, error
|
|
||||||
loop = asyncio.new_event_loop()
|
|
||||||
asyncio.set_event_loop(loop)
|
|
||||||
try:
|
try:
|
||||||
result = loop.run_until_complete(coro)
|
resp = requests.post(FEISHU_WEBHOOK_URL, json=payload, timeout=10)
|
||||||
|
result = resp.json()
|
||||||
|
if result.get("code") == 0:
|
||||||
|
log("飞书推送成功")
|
||||||
|
else:
|
||||||
|
log(f"飞书推送失败: {result}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error = e
|
log(f"飞书推送异常: {e}")
|
||||||
finally:
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
t = threading.Thread(target=_target)
|
|
||||||
t.start()
|
|
||||||
t.join(timeout=15)
|
|
||||||
if error:
|
|
||||||
raise error
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def speak(text):
|
def speak(text):
|
||||||
"""调用小爱音箱语音播报"""
|
"""使用本地 IndexTTS2 语音播报"""
|
||||||
try:
|
try:
|
||||||
async def _tts():
|
_local_speak(text)
|
||||||
async with ClientSession() as session:
|
|
||||||
account = MiAccount(session, XIAOMI_USER_ID, None, XIAOMI_TOKEN_PATH)
|
|
||||||
mina = MiNAService(account)
|
|
||||||
return await mina.text_to_speech(XIAOMI_SPEAKER_DID, text)
|
|
||||||
|
|
||||||
result = _run_async_in_thread(_tts())
|
|
||||||
if result and result.get("code") == 0:
|
|
||||||
log("语音播报成功")
|
log("语音播报成功")
|
||||||
else:
|
|
||||||
log(f"语音播报失败: {result}")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"语音播报异常: {e}")
|
log(f"语音播报异常: {e}")
|
||||||
|
|
||||||
|
|
||||||
def load_cookies():
|
def load_cookies():
|
||||||
"""加载本地 Cookie 文件"""
|
"""加载本地 Cookie 文件"""
|
||||||
cookie_path = Path(__file__).parent / COOKIES_FILE
|
cookie_path = Path(__file__).parent / COOKIES_FILE
|
||||||
@@ -227,7 +207,7 @@ def main():
|
|||||||
|
|
||||||
# 2. 没有有效 Cookie → 打开浏览器登录
|
# 2. 没有有效 Cookie → 打开浏览器登录
|
||||||
if not cookies:
|
if not cookies:
|
||||||
send_to_wecom("【监控通知】需要登录丰享平台,请查看电脑浏览器完成登录")
|
notify_all("【监控通知】需要登录丰享平台,请查看电脑浏览器完成登录")
|
||||||
cookies = browser_login()
|
cookies = browser_login()
|
||||||
if not cookies:
|
if not cookies:
|
||||||
log("登录失败,退出")
|
log("登录失败,退出")
|
||||||
@@ -259,7 +239,7 @@ def main():
|
|||||||
test = fetch_orders_via_requests(cookies, page_size=1)
|
test = fetch_orders_via_requests(cookies, page_size=1)
|
||||||
if test == "SESSION_EXPIRED":
|
if test == "SESSION_EXPIRED":
|
||||||
log("Cookie 在夜间过期,需要重新登录")
|
log("Cookie 在夜间过期,需要重新登录")
|
||||||
send_to_wecom("【监控通知】Cookie 过期,请查看电脑浏览器完成登录")
|
notify_all("【监控通知】Cookie 过期,请查看电脑浏览器完成登录")
|
||||||
cookies = browser_login()
|
cookies = browser_login()
|
||||||
if not cookies:
|
if not cookies:
|
||||||
log("登录失败,退出")
|
log("登录失败,退出")
|
||||||
@@ -285,10 +265,10 @@ def main():
|
|||||||
# Cookie 过期 → 浏览器重新登录
|
# Cookie 过期 → 浏览器重新登录
|
||||||
if orders == "SESSION_EXPIRED":
|
if orders == "SESSION_EXPIRED":
|
||||||
log("Cookie 已过期!")
|
log("Cookie 已过期!")
|
||||||
send_to_wecom("【监控异常】Cookie 已过期,请查看电脑浏览器完成登录")
|
notify_all("【监控异常】Cookie 已过期,请查看电脑浏览器完成登录")
|
||||||
cookies = browser_login()
|
cookies = browser_login()
|
||||||
if not cookies:
|
if not cookies:
|
||||||
send_to_wecom("登录超时,监控已退出。请手动重新运行。")
|
notify_all("登录超时,监控已退出。请手动重新运行。")
|
||||||
return
|
return
|
||||||
send_to_wecom("重新登录成功,监控已恢复")
|
send_to_wecom("重新登录成功,监控已恢复")
|
||||||
last_keepalive = time.time()
|
last_keepalive = time.time()
|
||||||
@@ -312,7 +292,7 @@ def main():
|
|||||||
# 从旧到新依次推送
|
# 从旧到新依次推送
|
||||||
for o in reversed(new_orders):
|
for o in reversed(new_orders):
|
||||||
msg = format_msg(o)
|
msg = format_msg(o)
|
||||||
send_to_wecom(msg)
|
notify_all(msg)
|
||||||
speak(msg)
|
speak(msg)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
last_order_id = new_orders[0]["orderId"]
|
last_order_id = new_orders[0]["orderId"]
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
title 丰享订单监控
|
||||||
|
|
||||||
|
echo ============================================
|
||||||
|
echo 丰享订单监控系统 v4
|
||||||
|
echo 推送: 企业微信 + 本地TTS语音播报
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
:: Python路径(IndexTTS虚拟环境)
|
||||||
|
set PYTHON=E:\2025Code\python\IndexTT\index-tts\.venv\Scripts\python.exe
|
||||||
|
|
||||||
|
:: 检查Python是否存在
|
||||||
|
if not exist "%PYTHON%" (
|
||||||
|
echo [错误] 未找到Python: %PYTHON%
|
||||||
|
echo 请确认IndexTTS已正确安装
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: 检查cookie文件
|
||||||
|
if not exist "cookies.json" (
|
||||||
|
echo [提示] 未检测到登录Cookie,首次运行将自动打开浏览器
|
||||||
|
echo 请在浏览器中手动完成登录(含验证码)
|
||||||
|
echo.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [启动] 正在启动订单监控...
|
||||||
|
echo.
|
||||||
|
|
||||||
|
"%PYTHON%" order_monitor.py
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo.
|
||||||
|
echo [异常] 监控程序异常退出,错误码: %errorlevel%
|
||||||
|
pause
|
||||||
|
)
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
"""
|
|
||||||
小米账号登录 - 通过浏览器获取 serviceToken
|
|
||||||
浏览器打开后请手动登录,脚本会自动检测登录成功
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
from playwright.sync_api import sync_playwright
|
|
||||||
|
|
||||||
XIAOI_CONFIG = "xiaomi_config.json"
|
|
||||||
|
|
||||||
|
|
||||||
def login_and_get_tokens():
|
|
||||||
print("启动浏览器,请登录小米账号...")
|
|
||||||
with sync_playwright() as p:
|
|
||||||
browser = p.chromium.launch(headless=False, args=["--start-maximized"])
|
|
||||||
context = browser.new_context(no_viewport=True)
|
|
||||||
page = context.new_page()
|
|
||||||
|
|
||||||
page.goto("https://account.xiaomi.com/pass/serviceLogin?sid=micoapi&_json=true&callback=https%3A%2F%2Fapi2.mina.mi.com%2Fsts")
|
|
||||||
page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
print("请在浏览器中完成登录(输入手机号、密码、验证码)")
|
|
||||||
print("等待登录完成(最长5分钟)...")
|
|
||||||
|
|
||||||
# 等待页面离开登录页(说明登录成功)
|
|
||||||
try:
|
|
||||||
page.wait_for_url(lambda url: "account.xiaomi.com/pass" not in url, timeout=300_000)
|
|
||||||
print("检测到登录成功!")
|
|
||||||
except Exception:
|
|
||||||
print("登录超时或失败")
|
|
||||||
browser.close()
|
|
||||||
return None
|
|
||||||
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# 获取 cookies
|
|
||||||
cookies = context.cookies()
|
|
||||||
cookie_dict = {c["name"]: c["value"] for c in cookies}
|
|
||||||
|
|
||||||
service_token = cookie_dict.get("serviceToken", "")
|
|
||||||
user_id = cookie_dict.get("userId", "")
|
|
||||||
|
|
||||||
print(f"获取到 {len(cookies)} 个 cookies")
|
|
||||||
print(f" userId: {user_id}")
|
|
||||||
print(f" serviceToken: {'有' if service_token else '无'}")
|
|
||||||
|
|
||||||
# 通过浏览器调用 device_list API
|
|
||||||
print("\n获取设备列表...")
|
|
||||||
result = page.evaluate("""
|
|
||||||
async () => {
|
|
||||||
const resp = await fetch('https://api2.mina.mi.com/admin/v2/device_list?master=0&requestId=app_ios_' + Math.random().toString(36).substr(2, 30));
|
|
||||||
return await resp.json();
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
|
|
||||||
devices = []
|
|
||||||
if result.get("code") == 0:
|
|
||||||
devices = result.get("data", [])
|
|
||||||
print(f"找到 {len(devices)} 个设备:")
|
|
||||||
for d in devices:
|
|
||||||
print(f" 名称: {d.get('name')} | 型号: {d.get('hardware')} | DID: {d.get('deviceID')}")
|
|
||||||
else:
|
|
||||||
print(f"获取设备失败: {result}")
|
|
||||||
|
|
||||||
# 保存配置
|
|
||||||
config = {
|
|
||||||
"userId": user_id,
|
|
||||||
"serviceToken": service_token,
|
|
||||||
"devices": devices,
|
|
||||||
"allCookies": cookie_dict,
|
|
||||||
}
|
|
||||||
|
|
||||||
with open(XIAOI_CONFIG, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
||||||
|
|
||||||
print(f"\n配置已保存到 {XIAOI_CONFIG}")
|
|
||||||
browser.close()
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
login_and_get_tokens()
|
|
||||||
Reference in New Issue
Block a user