Files

84 lines
3.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""阈值提醒状态机:低于阈值提醒一次,恢复后重新武装。
状态(accounts.alert_armed):
- 1 = 已武装,低于阈值时可提醒
- 0 = 已提醒,等待余额恢复到阈值以上
发送成功才切换状态;Telegram 失败则保持原状态,下次检查再试。
"""
from __future__ import annotations
import logging
from datetime import datetime
from app import db
from app.config import Config
from app.telegram import send_message
logger = logging.getLogger("monitor.alert")
def _fmt(v: float | None) -> str:
return "—" if v is None else f"{v:g}"
def build_alert_message(kind: str, account: dict, platform: dict, balance: float | None) -> str:
pname = platform["name"]
aname = account["name"]
cur = platform["currency"]
th = account["threshold"]
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if kind == "below":
return (f"⚠️ 余额不足提醒\n"
f"平台:{pname}\n账号:{aname}\n"
f"当前余额:{_fmt(balance)} {cur}\n阈值:{_fmt(th)} {cur}\n"
f"时间:{now}")
if kind == "recovered":
return (f"✅ 余额已恢复\n"
f"平台:{pname}\n账号:{aname}\n"
f"当前余额:{_fmt(balance)} {cur}\n阈值:{_fmt(th)} {cur}\n"
f"时间:{now}")
if kind == "disabled":
return (f"🚫 账号已自动禁用\n"
f"平台:{pname}\n账号:{aname}\n"
f"原因:{account.get('last_error') or '认证失败'}")
return ""
def evaluate(account: dict, platform: dict, balance: float, cfg: Config) -> None:
"""拉取成功后调用:按状态机决定是否发送提醒,发送成功才更新武装状态。"""
threshold = account["threshold"]
armed = bool(account["alert_armed"])
below = balance < threshold
if below and armed:
kind = "below"
logger.info("触发提醒: %s / %s 余额 %s < 阈值 %s%s", platform["name"], account["name"], _fmt(balance), _fmt(threshold), platform["currency"])
elif not below and not armed:
kind = "recovered"
logger.info("余额恢复: %s / %s 余额 %s >= 阈值 %s%s", platform["name"], account["name"], _fmt(balance), _fmt(threshold), platform["currency"])
else:
return
message = build_alert_message(kind, account, platform, balance)
if send_message(cfg.telegram_bot_token, cfg.telegram_chat_id, message):
new_armed = 0 if kind == "below" else 1
with db.get_conn() as conn:
conn.execute("UPDATE accounts SET alert_armed = ? WHERE id = ?", (new_armed, account["id"]))
conn.execute(
"INSERT INTO alert_log (account_id, type, message) VALUES (?, ?, ?)",
(account["id"], kind, message),
)
logger.info("已发送%s提醒: account=%s", kind, account["id"])
else:
logger.warning("提醒发送失败,保持原状态稍后重试: account=%s kind=%s", account["id"], kind)
def notify_disabled(account: dict, platform: dict, cfg: Config) -> None:
"""认证失败禁用账号后通知用户(不参与状态机)。"""
message = build_alert_message("disabled", account, platform, None)
if send_message(cfg.telegram_bot_token, cfg.telegram_chat_id, message):
with db.get_conn() as conn:
conn.execute(
"INSERT INTO alert_log (account_id, type, message) VALUES (?, ?, ?)",
(account["id"], "disabled", message),
)