Initial commit: Fengxiang order monitor with WeChat & Xiaomi speaker push
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# Sensitive data - NEVER commit
|
||||||
|
CLAUDE.md
|
||||||
|
cookies.json
|
||||||
|
xiaomi_raw_cookies.json
|
||||||
|
xiaomi_config.json
|
||||||
|
order_data.json
|
||||||
|
*.mi.token
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
Thumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Claude
|
||||||
|
.claude/
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# 丰享订单监控
|
||||||
|
|
||||||
|
自动监控丰享商家端(fs.szfx.com)的新订单,同时推送到**企业微信群**和**小爱音箱语音播报**。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- **自动轮询**:高峰期(11-13点、17-19点)10-20秒/次,闲时30-60秒/次
|
||||||
|
- **双通道推送**:新订单同时推送企业微信消息 + 小爱音箱 TTS 语音播报
|
||||||
|
- **Cookie 复用**:首次登录后保存 Cookie,后续启动无需重复登录
|
||||||
|
- **夜间暂停**:21:00 ~ 07:40 自动暂停轮询,节省资源
|
||||||
|
- **Session 保活**:每5分钟自动发请求维持 Cookie 活性
|
||||||
|
- **过期自动恢复**:Cookie 过期时弹浏览器提示重新登录,并推送企业微信通知
|
||||||
|
- **开机自启**:支持 Windows 计划任务,登录系统后自动运行
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
|
||||||
|
```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` | 主监控程序,轮询订单 + 推送 + 播报 |
|
||||||
|
| `test_push.py` | 测试脚本,验证企业微信和音箱通道 |
|
||||||
|
| `push_latest_order.py` | 单次获取最新订单并推送企业微信 |
|
||||||
|
| `fetch_orders.py` | 命令行获取订单数据(JSON 输出) |
|
||||||
|
| `fengxiang_scraper.py` | Playwright 半自动登录获取 Cookie |
|
||||||
|
| `setup_xiaomi.py` | 小米账号登录获取 serviceToken |
|
||||||
|
| `run_monitor.bat` | Windows 启动批处理 |
|
||||||
|
| `setup_autostart.ps1` | Windows 开机自启配置脚本 |
|
||||||
|
|
||||||
|
## 已逆向的 API
|
||||||
|
|
||||||
|
### 丰享平台
|
||||||
|
|
||||||
|
- **登录**:`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}`
|
||||||
|
- 返回:`{errno: 0, data: {total, list: [...], count}}`
|
||||||
|
|
||||||
|
### 小米/小爱音箱
|
||||||
|
|
||||||
|
- 使用 [miservice_fork](https://github.com/nicepkg/miservice_fork) Python 库
|
||||||
|
- TTS 播报:`MiNAService.text_to_speech(deviceId, text)`
|
||||||
|
- Token 保存在 `~/.mi.token`
|
||||||
|
|
||||||
|
## 消息格式
|
||||||
|
|
||||||
|
企业微信和小爱音箱使用统一格式:
|
||||||
|
|
||||||
|
> 【丰享丰食】订单收款成功,收款24.00元
|
||||||
|
|
||||||
|
## 开机自启(Windows)
|
||||||
|
|
||||||
|
已配置计划任务 `FXOrderMonitor`,用户登录时自动运行。
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 手动配置
|
||||||
|
.\setup_autostart.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 丰享登录有腾讯验证码 TCaptcha,无法纯接口绕过,首次必须用浏览器登录
|
||||||
|
- Cookie 有效期较长,持续使用可维持;过期会自动弹窗提示重新登录
|
||||||
|
- 小米 passToken 会过期,过期后需重新运行 `setup_xiaomi.py`
|
||||||
|
- 配置文件(cookies.json、xiaomi_config.json)含敏感信息,已加入 .gitignore
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
"""
|
||||||
|
丰享商家端数据抓取脚本
|
||||||
|
使用 Playwright 半自动登录 + requests 调用数据接口
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
# ============ 配置 ============
|
||||||
|
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"
|
||||||
|
MMS_BASE = "https://fs.szfx.com/MMS"
|
||||||
|
SHOP_ID = "20434543575189"
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
|
||||||
|
def login_and_get_cookies():
|
||||||
|
"""用 Playwright 打开登录页,等待用户手动登录,然后提取 Cookie"""
|
||||||
|
print("[1/3] 启动浏览器,请手动完成登录(包括验证码)...")
|
||||||
|
|
||||||
|
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(LOGIN_URL)
|
||||||
|
|
||||||
|
# 等待用户登录成功(检测页面跳转到 MMS 首页)
|
||||||
|
print("[2/3] 等待登录完成...(请在浏览器中输入账号密码并完成验证码)")
|
||||||
|
try:
|
||||||
|
page.wait_for_url("**/MMS**", timeout=300_000)
|
||||||
|
print(" 登录成功!正在提取 Cookie...")
|
||||||
|
except Exception:
|
||||||
|
print(" 等待超时(5分钟),请重新运行脚本。")
|
||||||
|
browser.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 确保访问过目标页面(触发必要的 Cookie 设置)
|
||||||
|
page.goto(MMS_BASE)
|
||||||
|
page.wait_for_load_state("networkidle")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# 提取所有 Cookie
|
||||||
|
playwright_cookies = context.cookies()
|
||||||
|
cookies = {c["name"]: c["value"] for c in playwright_cookies}
|
||||||
|
|
||||||
|
print(f" 获取到 {len(cookies)} 个 Cookie")
|
||||||
|
browser.close()
|
||||||
|
return cookies
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_order_list(cookies, page=1, page_size=20):
|
||||||
|
"""调用快捷买单订单列表接口"""
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
|
"Referer": "https://fs.szfx.com/MMS",
|
||||||
|
"Origin": "https://fs.szfx.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"shopId": SHOP_ID,
|
||||||
|
"page": page,
|
||||||
|
"pageSize": page_size,
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"[3/3] 正在请求订单数据(第{page}页)...")
|
||||||
|
resp = requests.post(DATA_API, json=payload, headers=headers, cookies=cookies, timeout=30)
|
||||||
|
data = resp.json()
|
||||||
|
|
||||||
|
if data.get("errno") == 0:
|
||||||
|
print(" 请求成功!")
|
||||||
|
return data.get("data")
|
||||||
|
else:
|
||||||
|
print(f" 请求失败: errno={data.get('errno')}, errmsg={data.get('errmsg')}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_data(data, filename="order_data.json"):
|
||||||
|
"""保存数据到文件"""
|
||||||
|
with open(filename, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
print(f" 数据已保存到 {filename}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 第一步:登录获取 Cookie
|
||||||
|
cookies = login_and_get_cookies()
|
||||||
|
if not cookies:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 保存 Cookie 以便下次复用
|
||||||
|
with open("cookies.json", "w", encoding="utf-8") as f:
|
||||||
|
json.dump(cookies, f, ensure_ascii=False, indent=2)
|
||||||
|
print(" Cookie 已保存到 cookies.json(下次可直接使用)\n")
|
||||||
|
|
||||||
|
# 第二步:获取订单数据
|
||||||
|
result = fetch_order_list(cookies)
|
||||||
|
if result:
|
||||||
|
save_data(result)
|
||||||
|
# 打印摘要
|
||||||
|
if isinstance(result, dict):
|
||||||
|
print(f"\n 数据摘要:")
|
||||||
|
for k, v in result.items():
|
||||||
|
if not isinstance(v, (list, dict)):
|
||||||
|
print(f" {k}: {v}")
|
||||||
|
elif isinstance(v, list):
|
||||||
|
print(f" {k}: 共 {len(v)} 条记录")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
"""
|
||||||
|
丰享商家端数据抓取 - Cookie 复用模式
|
||||||
|
已有 cookies.json 时直接调用接口,Cookie 过期再运行主脚本重新登录
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
|
||||||
|
DATA_API = "https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list"
|
||||||
|
SHOP_ID = "20434543575189"
|
||||||
|
|
||||||
|
|
||||||
|
def load_cookies():
|
||||||
|
try:
|
||||||
|
with open("cookies.json", "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("未找到 cookies.json,请先运行 fengxiang_scraper.py 完成登录")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_order_list(cookies, page=1, page_size=20):
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
|
"Referer": "https://fs.szfx.com/MMS",
|
||||||
|
"Origin": "https://fs.szfx.com",
|
||||||
|
}
|
||||||
|
payload = {"shopId": SHOP_ID, "page": page, "pageSize": page_size}
|
||||||
|
resp = requests.post(DATA_API, json=payload, headers=headers, cookies=cookies, timeout=30)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cookies = load_cookies()
|
||||||
|
print(f"已加载 {len(cookies)} 个 Cookie")
|
||||||
|
|
||||||
|
page = int(sys.argv[1]) if len(sys.argv) > 1 else 1
|
||||||
|
result = fetch_order_list(cookies, page=page)
|
||||||
|
|
||||||
|
if result.get("errno") == 0:
|
||||||
|
data = result.get("data")
|
||||||
|
print(json.dumps(data, ensure_ascii=False, indent=2))
|
||||||
|
elif result.get("errno") == 110003:
|
||||||
|
print("Cookie 已过期,请重新运行 fengxiang_scraper.py 完成登录")
|
||||||
|
else:
|
||||||
|
print(f"请求失败: {result}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
"""
|
||||||
|
丰享订单监控 v4
|
||||||
|
- 优先使用 Cookie 文件请求 API,无需每次都打开浏览器
|
||||||
|
- Cookie 过期时自动打开浏览器提示重新登录
|
||||||
|
- 晚上 21:00 到早上 7:40 自动暂停
|
||||||
|
- 高峰期缩短间隔,闲时拉长间隔
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
from aiohttp import ClientSession
|
||||||
|
from miservice import MiAccount, MiNAService
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
# ============ 配置 ============
|
||||||
|
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"
|
||||||
|
SHOP_ID = "20434543575189"
|
||||||
|
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=644ab6d9-3b66-4166-88e9-5a8a89e3731d"
|
||||||
|
COOKIES_FILE = "cookies.json"
|
||||||
|
|
||||||
|
# 小爱音箱配置
|
||||||
|
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_INTERVAL_MIN = 10
|
||||||
|
PEAK_INTERVAL_MAX = 20
|
||||||
|
PEAK_PAGE_SIZE = 20
|
||||||
|
|
||||||
|
IDLE_INTERVAL_MIN = 30
|
||||||
|
IDLE_INTERVAL_MAX = 60
|
||||||
|
IDLE_PAGE_SIZE = 5
|
||||||
|
|
||||||
|
# 夜间暂停:21:00 ~ 07:40 不轮询
|
||||||
|
NIGHT_START_H = 21
|
||||||
|
NIGHT_END_H = 7
|
||||||
|
NIGHT_END_M = 40
|
||||||
|
|
||||||
|
KEEPALIVE_INTERVAL = 300 # 每5分钟保活一次
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_money(cents):
|
||||||
|
return f"{cents / 100:.2f}元" if cents else "0.00元"
|
||||||
|
|
||||||
|
|
||||||
|
def log(msg):
|
||||||
|
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
print(f"[{now}] {msg}")
|
||||||
|
|
||||||
|
|
||||||
|
def is_peak_hour():
|
||||||
|
hour = datetime.datetime.now().hour
|
||||||
|
return any(start <= hour < end for start, end in PEAK_HOURS)
|
||||||
|
|
||||||
|
|
||||||
|
def is_night_time():
|
||||||
|
"""是否在夜间暂停时段 (21:00 ~ 07:40)"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
minutes = now.hour * 60 + now.minute
|
||||||
|
return minutes >= NIGHT_START_H * 60 or minutes < NIGHT_END_H * 60 + NIGHT_END_M
|
||||||
|
|
||||||
|
|
||||||
|
def seconds_until_morning():
|
||||||
|
"""距离明天早上 07:40 还有多少秒"""
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
target = now.replace(hour=NIGHT_END_H, minute=NIGHT_END_M, second=0, microsecond=0)
|
||||||
|
if now.hour >= NIGHT_START_H:
|
||||||
|
target += datetime.timedelta(days=1)
|
||||||
|
return max(1, int((target - now).total_seconds()))
|
||||||
|
|
||||||
|
|
||||||
|
def get_poll_config():
|
||||||
|
if is_peak_hour():
|
||||||
|
return PEAK_INTERVAL_MIN, PEAK_INTERVAL_MAX, PEAK_PAGE_SIZE
|
||||||
|
return IDLE_INTERVAL_MIN, IDLE_INTERVAL_MAX, IDLE_PAGE_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
def format_msg(order):
|
||||||
|
return f"【丰享丰食】订单收款成功,收款{fmt_money(order['amountPayable'])}"
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_wecom(text):
|
||||||
|
payload = {"msgtype": "text", "text": {"content": text}}
|
||||||
|
try:
|
||||||
|
resp = requests.post(WEBHOOK_URL, json=payload, timeout=10)
|
||||||
|
result = resp.json()
|
||||||
|
if result.get("errcode") == 0:
|
||||||
|
log("企业微信推送成功")
|
||||||
|
else:
|
||||||
|
log(f"推送失败: {result}")
|
||||||
|
except Exception as e:
|
||||||
|
log(f"推送异常: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def _run_async_in_thread(coro):
|
||||||
|
"""在独立线程中运行协程,避免与 Playwright 事件循环冲突"""
|
||||||
|
result = None
|
||||||
|
error = None
|
||||||
|
|
||||||
|
def _target():
|
||||||
|
nonlocal result, error
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
try:
|
||||||
|
result = loop.run_until_complete(coro)
|
||||||
|
except Exception as e:
|
||||||
|
error = e
|
||||||
|
finally:
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
t = threading.Thread(target=_target)
|
||||||
|
t.start()
|
||||||
|
t.join(timeout=15)
|
||||||
|
if error:
|
||||||
|
raise error
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def speak(text):
|
||||||
|
"""调用小爱音箱语音播报"""
|
||||||
|
try:
|
||||||
|
async def _tts():
|
||||||
|
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("语音播报成功")
|
||||||
|
else:
|
||||||
|
log(f"语音播报失败: {result}")
|
||||||
|
except Exception as e:
|
||||||
|
log(f"语音播报异常: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def load_cookies():
|
||||||
|
"""加载本地 Cookie 文件"""
|
||||||
|
cookie_path = Path(__file__).parent / COOKIES_FILE
|
||||||
|
if cookie_path.exists():
|
||||||
|
with open(cookie_path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_cookies(cookiejar):
|
||||||
|
"""把浏览器 Cookie 列表保存为 dict 到文件"""
|
||||||
|
cookie_dict = {c["name"]: c["value"] for c in cookiejar}
|
||||||
|
cookie_path = Path(__file__).parent / COOKIES_FILE
|
||||||
|
with open(cookie_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(cookie_dict, f, ensure_ascii=False, indent=2)
|
||||||
|
log("Cookie 已保存到 cookies.json")
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_orders_via_requests(cookies, page_size=5):
|
||||||
|
"""通过 requests + Cookie 拉取订单列表,返回 (订单列表 / SESSION_EXPIRED / None)"""
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
|
"Referer": "https://fs.szfx.com/MMS",
|
||||||
|
"Origin": "https://fs.szfx.com",
|
||||||
|
}
|
||||||
|
payload = {"shopId": SHOP_ID, "page": 1, "pageSize": page_size}
|
||||||
|
try:
|
||||||
|
resp = requests.post(DATA_API, json=payload, headers=headers, cookies=cookies, timeout=30)
|
||||||
|
data = resp.json()
|
||||||
|
if data.get("errno") == 0:
|
||||||
|
return data["data"]["list"]
|
||||||
|
elif data.get("errno") in (110003, 10009):
|
||||||
|
return "SESSION_EXPIRED"
|
||||||
|
else:
|
||||||
|
log(f"API 异常: errno={data.get('errno')}, errmsg={data.get('errmsg')}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
log(f"请求异常: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def browser_login():
|
||||||
|
"""打开浏览器让用户手动登录,返回 Cookie dict"""
|
||||||
|
log("启动浏览器,请手动登录...")
|
||||||
|
pw = sync_playwright().start()
|
||||||
|
browser = pw.chromium.launch(headless=False, args=["--start-maximized"])
|
||||||
|
context = browser.new_context(no_viewport=True)
|
||||||
|
page = context.new_page()
|
||||||
|
page.goto(LOGIN_URL)
|
||||||
|
|
||||||
|
log("等待登录完成(10分钟超时)...")
|
||||||
|
try:
|
||||||
|
page.wait_for_url("**/MMS**", timeout=600_000)
|
||||||
|
log("登录成功!提取 Cookie...")
|
||||||
|
except Exception:
|
||||||
|
log("登录超时")
|
||||||
|
browser.close()
|
||||||
|
pw.stop()
|
||||||
|
return None
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
cookiejar = context.cookies()
|
||||||
|
save_cookies(cookiejar)
|
||||||
|
|
||||||
|
browser.close()
|
||||||
|
pw.stop()
|
||||||
|
log("浏览器已关闭")
|
||||||
|
return {c["name"]: c["value"] for c in cookiejar}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 1. 尝试加载已有 Cookie
|
||||||
|
cookies = load_cookies()
|
||||||
|
|
||||||
|
if cookies:
|
||||||
|
log("检测到已有 Cookie,验证有效性...")
|
||||||
|
test_result = fetch_orders_via_requests(cookies, page_size=1)
|
||||||
|
if test_result == "SESSION_EXPIRED" or test_result is None:
|
||||||
|
log("Cookie 已过期,需要重新登录")
|
||||||
|
cookies = None
|
||||||
|
else:
|
||||||
|
log("Cookie 有效,无需打开浏览器")
|
||||||
|
|
||||||
|
# 2. 没有有效 Cookie → 打开浏览器登录
|
||||||
|
if not cookies:
|
||||||
|
send_to_wecom("【监控通知】需要登录丰享平台,请查看电脑浏览器完成登录")
|
||||||
|
cookies = browser_login()
|
||||||
|
if not cookies:
|
||||||
|
log("登录失败,退出")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3. 获取初始最新订单号
|
||||||
|
log("获取初始订单...")
|
||||||
|
_, _, page_size = get_poll_config()
|
||||||
|
last_order_id = None
|
||||||
|
orders = fetch_orders_via_requests(cookies, page_size)
|
||||||
|
if orders and orders != "SESSION_EXPIRED" and len(orders) > 0:
|
||||||
|
last_order_id = orders[0]["orderId"]
|
||||||
|
log(f"当前最新订单号: {last_order_id}")
|
||||||
|
|
||||||
|
last_keepalive = time.time()
|
||||||
|
mode = "高峰期" if is_peak_hour() else ("夜间暂停" if is_night_time() else "闲时")
|
||||||
|
log(f"开始监控(当前模式: {mode})")
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# 夜间暂停:到点就睡,天亮再起
|
||||||
|
if is_night_time():
|
||||||
|
sleep_secs = seconds_until_morning()
|
||||||
|
until = datetime.datetime.now() + datetime.timedelta(seconds=sleep_secs)
|
||||||
|
log(f"夜间模式,暂停监控至 {until.strftime('%H:%M:%S')}(约 {sleep_secs // 60} 分钟)")
|
||||||
|
time.sleep(sleep_secs)
|
||||||
|
log("恢复监控...")
|
||||||
|
# 验证 Cookie 是否还活着
|
||||||
|
test = fetch_orders_via_requests(cookies, page_size=1)
|
||||||
|
if test == "SESSION_EXPIRED":
|
||||||
|
log("Cookie 在夜间过期,需要重新登录")
|
||||||
|
send_to_wecom("【监控通知】Cookie 过期,请查看电脑浏览器完成登录")
|
||||||
|
cookies = browser_login()
|
||||||
|
if not cookies:
|
||||||
|
log("登录失败,退出")
|
||||||
|
return
|
||||||
|
last_keepalive = time.time()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 轮询
|
||||||
|
interval_min, interval_max, page_size = get_poll_config()
|
||||||
|
wait = random.randint(interval_min, interval_max)
|
||||||
|
mode = "高峰期" if is_peak_hour() else "闲时"
|
||||||
|
log(f"[{mode}] 等待 {wait} 秒,拉取 {page_size} 条...")
|
||||||
|
time.sleep(wait)
|
||||||
|
|
||||||
|
# 定期保活(一次 API 请求即可维持 Cookie)
|
||||||
|
if time.time() - last_keepalive > KEEPALIVE_INTERVAL:
|
||||||
|
fetch_orders_via_requests(cookies, page_size=1)
|
||||||
|
last_keepalive = time.time()
|
||||||
|
|
||||||
|
# 拉取订单
|
||||||
|
orders = fetch_orders_via_requests(cookies, page_size)
|
||||||
|
|
||||||
|
# Cookie 过期 → 浏览器重新登录
|
||||||
|
if orders == "SESSION_EXPIRED":
|
||||||
|
log("Cookie 已过期!")
|
||||||
|
send_to_wecom("【监控异常】Cookie 已过期,请查看电脑浏览器完成登录")
|
||||||
|
cookies = browser_login()
|
||||||
|
if not cookies:
|
||||||
|
send_to_wecom("登录超时,监控已退出。请手动重新运行。")
|
||||||
|
return
|
||||||
|
send_to_wecom("重新登录成功,监控已恢复")
|
||||||
|
last_keepalive = time.time()
|
||||||
|
orders = fetch_orders_via_requests(cookies, page_size)
|
||||||
|
if orders and orders != "SESSION_EXPIRED" and len(orders) > 0:
|
||||||
|
last_order_id = orders[0]["orderId"]
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not orders or orders == "SESSION_EXPIRED":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 找出所有新订单(orderId > last_order_id)
|
||||||
|
new_orders = []
|
||||||
|
for o in orders:
|
||||||
|
if o["orderId"] == last_order_id:
|
||||||
|
break
|
||||||
|
new_orders.append(o)
|
||||||
|
|
||||||
|
if new_orders:
|
||||||
|
log(f"发现 {len(new_orders)} 条新订单!")
|
||||||
|
# 从旧到新依次推送
|
||||||
|
for o in reversed(new_orders):
|
||||||
|
msg = format_msg(o)
|
||||||
|
send_to_wecom(msg)
|
||||||
|
speak(msg)
|
||||||
|
time.sleep(0.5)
|
||||||
|
last_order_id = new_orders[0]["orderId"]
|
||||||
|
else:
|
||||||
|
log(f"暂无新订单(最新: {last_order_id})")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
log("监控已停止")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
"""获取最新一条订单并推送到企业微信群"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
import requests
|
||||||
|
|
||||||
|
DATA_API = "https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list"
|
||||||
|
SHOP_ID = "20434543575189"
|
||||||
|
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=644ab6d9-3b66-4166-88e9-5a8a89e3731d"
|
||||||
|
|
||||||
|
STATUS_MAP = {1: "已完成", 3: "已退款", 4: "退款中", 5: "已关闭"}
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_ts(ts):
|
||||||
|
return datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") if ts else "-"
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_money(cents):
|
||||||
|
return f"{cents / 100:.2f}元" if cents else "0.00元"
|
||||||
|
|
||||||
|
|
||||||
|
def load_cookies():
|
||||||
|
with open("cookies.json", "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_latest_order():
|
||||||
|
cookies = load_cookies()
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
|
"Referer": "https://fs.szfx.com/MMS",
|
||||||
|
"Origin": "https://fs.szfx.com",
|
||||||
|
}
|
||||||
|
payload = {"shopId": SHOP_ID, "page": 1, "pageSize": 1}
|
||||||
|
resp = requests.post(DATA_API, json=payload, headers=headers, cookies=cookies, timeout=30)
|
||||||
|
data = resp.json()
|
||||||
|
if data.get("errno") != 0:
|
||||||
|
print(f"请求失败: {data}")
|
||||||
|
return None
|
||||||
|
return data["data"]["list"][0]
|
||||||
|
|
||||||
|
|
||||||
|
def format_order(order):
|
||||||
|
status = STATUS_MAP.get(order["status"], str(order["status"]))
|
||||||
|
return (
|
||||||
|
f"【新订单通知】\n"
|
||||||
|
f"订单号:{order['orderId']}\n"
|
||||||
|
f"店铺:{order['shopName']}\n"
|
||||||
|
f"城市:{order['city']}\n"
|
||||||
|
f"大区:{order['largeAreaName']}\n"
|
||||||
|
f"企业:{order['companyName']}\n"
|
||||||
|
f"用餐地点:{order['workplaceName']}\n"
|
||||||
|
f"下单时间:{fmt_ts(order['createTime'])}\n"
|
||||||
|
f"支付时间:{fmt_ts(order['payTime'])}\n"
|
||||||
|
f"完成时间:{fmt_ts(order['finishTime'])}\n"
|
||||||
|
f"订单金额:{fmt_money(order['allPrice'])}\n"
|
||||||
|
f"实付金额:{fmt_money(order['amountPayable'])}\n"
|
||||||
|
f"支付方式:{order['payTypeName']}\n"
|
||||||
|
f"订单来源:{order['orderSourceName']}\n"
|
||||||
|
f"状态:{status}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_wecom(text):
|
||||||
|
payload = {
|
||||||
|
"msgtype": "text",
|
||||||
|
"text": {"content": text}
|
||||||
|
}
|
||||||
|
resp = requests.post(WEBHOOK_URL, json=payload, timeout=10)
|
||||||
|
result = resp.json()
|
||||||
|
if result.get("errcode") == 0:
|
||||||
|
print("推送成功!")
|
||||||
|
else:
|
||||||
|
print(f"推送失败: {result}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
order = fetch_latest_order()
|
||||||
|
if not order:
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = format_order(order)
|
||||||
|
print(msg)
|
||||||
|
print("\n--- 正在推送到企业微信群 ---\n")
|
||||||
|
send_to_wecom(msg)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
title Fengxiang Order Monitor
|
||||||
|
cd /d d:\coding\fengxiang
|
||||||
|
echo =========================================
|
||||||
|
echo Fengxiang Order Monitor v3
|
||||||
|
echo %date% %time%
|
||||||
|
echo =========================================
|
||||||
|
echo.
|
||||||
|
python order_monitor.py
|
||||||
|
echo.
|
||||||
|
echo Monitor stopped. Press any key to close...
|
||||||
|
pause >nul
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 设置丰享订单监控开机自启
|
||||||
|
# 右键此文件 → "使用 PowerShell 运行",或在管理员 PowerShell 中执行
|
||||||
|
|
||||||
|
$taskName = "丰享订单监控"
|
||||||
|
$scriptPath = "d:\coding\fengxiang\run_monitor.bat"
|
||||||
|
$taskExists = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if ($taskExists) {
|
||||||
|
Write-Host "检测到已有任务,正在更新..."
|
||||||
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建任务:用户登录时自动运行
|
||||||
|
$action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c `"$scriptPath`""
|
||||||
|
$trigger = New-ScheduledTaskTrigger -AtLogOn
|
||||||
|
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Highest
|
||||||
|
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew
|
||||||
|
|
||||||
|
Register-ScheduledTask -TaskName $taskName `
|
||||||
|
-Action $action `
|
||||||
|
-Trigger $trigger `
|
||||||
|
-Principal $principal `
|
||||||
|
-Settings $settings `
|
||||||
|
-Description "丰享订单监控 - 开机自动启动,监控新订单并推送到企业微信和小爱音箱"
|
||||||
|
|
||||||
|
Write-Host "✓ 开机自启设置成功!"
|
||||||
|
Write-Host " 任务名称: $taskName"
|
||||||
|
Write-Host " 下次登录 Windows 时将自动运行"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "立即测试运行?运行以下命令:"
|
||||||
|
Write-Host " Start-ScheduledTask -TaskName '$taskName'"
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
小米账号登录 - 通过浏览器获取 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()
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
"""
|
||||||
|
测试脚本:验证企业微信推送 + 小爱音箱播报
|
||||||
|
- 拉取最新订单,推送到企业微信和小爱音箱
|
||||||
|
- 显示 API 返回结果,方便排查问题
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
|
from aiohttp import ClientSession
|
||||||
|
from miservice import MiAccount, MiNAService
|
||||||
|
|
||||||
|
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=644ab6d9-3b66-4166-88e9-5a8a89e3731d"
|
||||||
|
DATA_API = "https://fs.szfx.com/saasmerchant/pcweb/order/quickpayorder/list"
|
||||||
|
SHOP_ID = "20434543575189"
|
||||||
|
|
||||||
|
XIAOMI_USER_ID = "1136458602"
|
||||||
|
XIAOMI_TOKEN_PATH = str(Path.home() / ".mi.token")
|
||||||
|
XIAOMI_SPEAKER_DID = "3ba2c1e8-d8cb-45c5-b88a-15624e7a02f3"
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_money(cents):
|
||||||
|
return f"{cents / 100:.2f}元" if cents else "0.00元"
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_ts(ts):
|
||||||
|
import datetime
|
||||||
|
return datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") if ts else "-"
|
||||||
|
|
||||||
|
|
||||||
|
def load_cookies():
|
||||||
|
with open("cookies.json", "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_latest_order():
|
||||||
|
"""使用 cookies.json 拉取最新订单"""
|
||||||
|
cookies = load_cookies()
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
|
"Referer": "https://fs.szfx.com/MMS",
|
||||||
|
"Origin": "https://fs.szfx.com",
|
||||||
|
}
|
||||||
|
payload = {"shopId": SHOP_ID, "page": 1, "pageSize": 1}
|
||||||
|
resp = requests.post(DATA_API, json=payload, headers=headers, cookies=cookies, timeout=30)
|
||||||
|
data = resp.json()
|
||||||
|
if data.get("errno") != 0:
|
||||||
|
print(f"[FAIL] API error: errno={data.get('errno')}, errmsg={data.get('errmsg')}")
|
||||||
|
return None
|
||||||
|
return data["data"]["list"][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_wecom(msg):
|
||||||
|
"""发送到企业微信"""
|
||||||
|
print("\n--- Test: WeChat Work ---")
|
||||||
|
payload = {"msgtype": "text", "text": {"content": msg}}
|
||||||
|
try:
|
||||||
|
resp = requests.post(WEBHOOK_URL, json=payload, timeout=10)
|
||||||
|
result = resp.json()
|
||||||
|
print(f" Response: {json.dumps(result, ensure_ascii=False)}")
|
||||||
|
if result.get("errcode") == 0:
|
||||||
|
print(" [OK] WeChat push success")
|
||||||
|
else:
|
||||||
|
print(" [FAIL] WeChat push failed")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [FAIL] Exception: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
async def _tts(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)
|
||||||
|
|
||||||
|
|
||||||
|
def test_speaker(msg):
|
||||||
|
"""发送到小爱音箱"""
|
||||||
|
print("\n--- Test: Xiaomi Speaker ---")
|
||||||
|
print(f" Text: {msg}")
|
||||||
|
try:
|
||||||
|
result = asyncio.run(_tts(msg))
|
||||||
|
print(f" Response: {json.dumps(result, ensure_ascii=False)}")
|
||||||
|
if result and result.get("code") == 0:
|
||||||
|
print(" [OK] Speaker TTS success")
|
||||||
|
else:
|
||||||
|
print(" [FAIL] Speaker TTS failed - token may be expired, run setup_xiaomi.py")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [FAIL] Exception: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=" * 50)
|
||||||
|
print(" Fengxiang Order Monitor - Test Script")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# 1. Fetch latest order
|
||||||
|
print("\n1. Fetch latest order...")
|
||||||
|
order = fetch_latest_order()
|
||||||
|
if not order:
|
||||||
|
print("[FAIL] Cannot fetch order, check cookies.json")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f" orderId: {order['orderId']}")
|
||||||
|
print(f" shopName: {order['shopName']}")
|
||||||
|
print(f" allPrice: {fmt_money(order['allPrice'])}")
|
||||||
|
print(f" amountPayable: {fmt_money(order['amountPayable'])}")
|
||||||
|
print(f" payTime: {fmt_ts(order['payTime'])}")
|
||||||
|
|
||||||
|
# 2. Format message
|
||||||
|
msg = f"【丰享丰食】订单收款成功,收款{fmt_money(order['amountPayable'])}"
|
||||||
|
print(f"\n2. Message to send:\n {msg}")
|
||||||
|
|
||||||
|
# 3. Test WeChat and Speaker
|
||||||
|
test_wecom(msg)
|
||||||
|
test_speaker(msg)
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print(" Test complete. Check:")
|
||||||
|
print(" - WeChat: open the enterprise WeChat group")
|
||||||
|
print(" - Speaker: the L15A speaker in the shop")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user