feat: implement SQLite database management and Supabase integration for user authentication and points synchronization

This commit is contained in:
2569718930@qq.com
2026-05-19 18:07:39 +08:00
parent 0c2e22e770
commit a5c508cce8
8 changed files with 277 additions and 4 deletions
+46
View File
@@ -114,11 +114,57 @@ class BasicCommandHandler:
def handle_start_help(self, message: Any) -> None:
trace = CommandTrace("/start", message)
try:
parts = (getattr(message, "text", None) or "").split(maxsplit=1)
payload = str(parts[1] if len(parts) > 1 else "").strip()
if payload.startswith("bind_"):
token = payload[len("bind_") :].strip()
result = self._bind_from_web_token(message, token)
trace.set_status("ok" if result == "bound" else "error", result)
return
self.bot.reply_to(message, self.io_layer.build_welcome_text(), parse_mode="HTML")
trace.set_status("ok")
finally:
trace.emit()
def _bind_from_web_token(self, message: Any, token: str) -> str:
if not token:
self.bot.reply_to(message, "❌ 绑定链接无效,请回到网页重新点击一键绑定。")
return "invalid_token"
user = message.from_user
try:
payload = self.io_layer.db.consume_web_bind_token(token)
except Exception as exc:
logger.warning("web bind token consume failed user_id={}: {}", getattr(user, "id", ""), exc)
payload = None
if not isinstance(payload, dict):
self.bot.reply_to(message, "❌ 绑定链接已过期或无效,请回到网页重新点击一键绑定。")
return "invalid_or_expired_token"
supabase_user_id = str(payload.get("supabase_user_id") or "").strip()
supabase_email = str(payload.get("supabase_email") or "").strip()
if not supabase_user_id:
self.bot.reply_to(message, "❌ 绑定链接缺少网页账号信息,请重新登录后再试。")
return "missing_supabase_user_id"
self.io_layer.db.upsert_user(user.id, self.io_layer.display_name(user))
bind_result = self.io_layer.db.bind_supabase_identity(
telegram_id=user.id,
supabase_user_id=supabase_user_id,
supabase_email=supabase_email,
)
if not bool(bind_result.get("ok")):
reason = str(bind_result.get("reason") or "bind_failed")
self.bot.reply_to(message, f"❌ 绑定失败:{reason}")
return reason
self.bot.reply_to(
message,
(
"✅ 账号绑定完成。\n"
"现在可以回到网页点击“加入 Telegram 群组”,入群申请会自动审核。"
),
)
return "bound"
def handle_id(self, message: Any) -> None:
trace = CommandTrace("/id", message)
try:
+75
View File
@@ -366,6 +366,18 @@ class DBManager:
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_telegram_bind_tokens_expires ON telegram_bind_tokens(expires_at)"
)
conn.execute("""
CREATE TABLE IF NOT EXISTS web_telegram_bind_tokens (
token TEXT PRIMARY KEY,
supabase_user_id TEXT NOT NULL,
supabase_email TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL
)
""")
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_web_telegram_bind_tokens_expires ON web_telegram_bind_tokens(expires_at)"
)
conn.execute("""
CREATE TABLE IF NOT EXISTS airport_obs_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1452,6 +1464,69 @@ class DBManager:
conn.commit()
return int(row["telegram_id"])
def create_web_bind_token(
self,
supabase_user_id: str,
supabase_email: str = "",
ttl_minutes: int = 10,
) -> str:
normalized_uid = str(supabase_user_id or "").strip().lower()
if not normalized_uid:
raise ValueError("supabase_user_id is required")
token = secrets.token_urlsafe(16)
now = datetime.now()
expires_at = now + timedelta(minutes=max(1, int(ttl_minutes)))
with self._get_connection() as conn:
conn.execute(
"""
DELETE FROM web_telegram_bind_tokens
WHERE supabase_user_id = ? OR expires_at < ?
""",
(normalized_uid, now.isoformat()),
)
conn.execute(
"""
INSERT INTO web_telegram_bind_tokens (
token, supabase_user_id, supabase_email, expires_at
)
VALUES (?, ?, ?, ?)
""",
(token, normalized_uid, str(supabase_email or "").strip(), expires_at.isoformat()),
)
conn.commit()
return token
def consume_web_bind_token(self, token: str) -> Optional[Dict[str, str]]:
token = str(token or "").strip()
if not token:
return None
now = datetime.now()
with self._get_connection() as conn:
conn.row_factory = sqlite3.Row
row = conn.execute(
"""
SELECT supabase_user_id, supabase_email, expires_at
FROM web_telegram_bind_tokens
WHERE token = ?
LIMIT 1
""",
(token,),
).fetchone()
if not row:
return None
try:
expires_at = datetime.fromisoformat(row["expires_at"])
except Exception:
expires_at = now
conn.execute("DELETE FROM web_telegram_bind_tokens WHERE token = ?", (token,))
conn.commit()
if now > expires_at:
return None
return {
"supabase_user_id": str(row["supabase_user_id"] or "").strip().lower(),
"supabase_email": str(row["supabase_email"] or "").strip(),
}
def add_message_activity(
self,
telegram_id: int,