116 lines
3.6 KiB
Python
116 lines
3.6 KiB
Python
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from loguru import logger
|
||
|
||
from src.bot.command_guard import CommandGuard
|
||
from src.bot.io_layer import BotIOLayer
|
||
from src.bot.observability import CommandTrace
|
||
from src.bot.services.deb_command_service import DebCommandService
|
||
from src.bot.settings import DEB_QUERY_COST
|
||
|
||
|
||
def _normalized_command_head(text: str | None) -> str:
|
||
raw = str(text or "")
|
||
for marker in (
|
||
"\ufeff",
|
||
"\u200b",
|
||
"\u200c",
|
||
"\u200d",
|
||
"\u200e",
|
||
"\u200f",
|
||
"\u2060",
|
||
"\u2066",
|
||
"\u2067",
|
||
"\u2068",
|
||
"\u2069",
|
||
"\u202a",
|
||
"\u202b",
|
||
"\u202c",
|
||
"\u202d",
|
||
"\u202e",
|
||
):
|
||
raw = raw.replace(marker, "")
|
||
raw = raw.strip()
|
||
if raw[:1] in {"/", "⁄", "∕", "╱", "⧸"}:
|
||
raw = "/" + raw[1:]
|
||
return raw.split(maxsplit=1)[0].lower() if raw else ""
|
||
|
||
|
||
def _is_deb_command_text(text: str | None) -> bool:
|
||
head = _normalized_command_head(text)
|
||
return head == "/deb" or head.startswith("/deb@")
|
||
|
||
|
||
class DebCommandHandler:
|
||
def __init__(
|
||
self,
|
||
bot: Any,
|
||
guard: CommandGuard,
|
||
deb_service: DebCommandService,
|
||
io_layer: BotIOLayer,
|
||
):
|
||
self.bot = bot
|
||
self.guard = guard
|
||
self.deb_service = deb_service
|
||
self.io_layer = io_layer
|
||
|
||
def register(self) -> None:
|
||
@self.bot.message_handler(
|
||
func=lambda message: _is_deb_command_text(getattr(message, "text", None)),
|
||
content_types=["text"],
|
||
)
|
||
def _deb(message):
|
||
self.handle(message)
|
||
|
||
def handle(self, message: Any) -> None:
|
||
trace = CommandTrace("/deb", message)
|
||
try:
|
||
parts = (message.text or "").split(maxsplit=1)
|
||
if len(parts) < 2:
|
||
trace.set_status("bad_request", "missing_city")
|
||
self.io_layer.send_query_message(
|
||
message,
|
||
"❌ 用法: <code>/deb ankara</code>",
|
||
parse_mode="HTML",
|
||
)
|
||
return
|
||
|
||
city_input = parts[1].strip().lower()
|
||
city_name = self.deb_service.resolve_city(city_input)
|
||
if not self.deb_service.has_history(city_name):
|
||
trace.set_status("bad_request", "history_missing")
|
||
self.io_layer.send_query_message(
|
||
message,
|
||
f"❌ 暂无 {city_name} 的历史数据。",
|
||
parse_mode="HTML",
|
||
)
|
||
return
|
||
|
||
if not self.guard.ensure_access_and_points(message, DEB_QUERY_COST, "/deb"):
|
||
trace.set_status("blocked", "guard_rejected")
|
||
return
|
||
|
||
report_result = self.deb_service.build_report(city_name, DEB_QUERY_COST)
|
||
if not report_result.ok:
|
||
trace.set_status("failed", report_result.error or "deb_report_failed")
|
||
self.io_layer.send_query_message(
|
||
message,
|
||
f"❌ 查询失败: {report_result.error}",
|
||
)
|
||
return
|
||
|
||
self.io_layer.send_query_message(
|
||
message,
|
||
str(report_result.report),
|
||
parse_mode="HTML",
|
||
)
|
||
trace.set_status("ok", city_name)
|
||
except Exception as exc:
|
||
trace.set_status("failed", "unexpected_error")
|
||
logger.exception("查询 /deb 失败")
|
||
self.io_layer.send_query_message(message, f"❌ 查询失败: {exc}")
|
||
finally:
|
||
trace.emit()
|