Files
tg-spam-guard/services/bind_service.py
T
2026-07-06 19:26:00 +08:00

81 lines
2.4 KiB
Python
Raw 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.
"""账号绑定服务 — TG ↔ Harvest"""
import secrets
import logging
import httpx
import config
from db.models import UserBinding
logger = logging.getLogger("spam_guard")
# 临时绑定 token 存储(token -> user_id5分钟过期)
_pending_bindings: dict[str, dict] = {}
def generate_bind_token(tg_user_id: int, tg_username: str) -> tuple[str, str]:
"""生成绑定链接,返回 (token, url)"""
token = secrets.token_urlsafe(16)
_pending_bindings[token] = {
"tg_user_id": tg_user_id,
"tg_username": tg_username,
}
url = f"{config.HARVEST_SERVER_URL}/bind?token={token}&tg_user_id={tg_user_id}"
return token, url
async def verify_binding(token: str) -> dict:
"""验证绑定(harvest-server 回调时调用)"""
info = _pending_bindings.pop(token, None)
if not info:
return {"ok": False, "reason": "invalid_or_expired_token"}
tg_user_id = info["tg_user_id"]
tg_username = info["tg_username"]
# 尝试调用 harvest-server verify
try:
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.post(
f"{config.HARVEST_SERVER_URL}/api/verify",
json={"token": token, "tg_user_id": tg_user_id},
)
if resp.status_code == 200:
data = resp.json()
harvest_uid = data.get("uid", "")
harvest_token = data.get("token", "")
else:
harvest_uid = str(tg_user_id)
harvest_token = token
except Exception:
harvest_uid = str(tg_user_id)
harvest_token = token
# 写入绑定
binding, _ = await UserBinding.get_or_create(
tg_user_id=tg_user_id,
defaults={
"tg_username": tg_username,
"harvest_uid": harvest_uid,
"harvest_token": harvest_token,
},
)
logger.info(f"🔗 账号绑定成功: TG={tg_user_id} Harvest={harvest_uid}")
return {"ok": True, "harvest_uid": harvest_uid}
async def unbind(tg_user_id: int) -> bool:
"""解绑"""
deleted = await UserBinding.filter(tg_user_id=tg_user_id).delete()
if deleted:
logger.info(f"🔗 账号解绑: TG={tg_user_id}")
return deleted > 0
async def get_binding(tg_user_id: int) -> dict | None:
"""查询绑定"""
b = await UserBinding.filter(tg_user_id=tg_user_id).first()
if b:
return {"harvest_uid": b.harvest_uid, "bound_at": b.bound_at}
return None