Commit 2397c4b

benny-dou <60535774+benny-dou@users.noreply.github.com>
2026-03-08 09:33:21
feat(danmu): add fallback to query YouTube channel name by handle when no results found
1 parent 4cb855d
Changed files (3)
src/danmu/entrypoint.py
@@ -82,6 +82,7 @@ async def query_danmu(client: Client, message: Message, **kwargs):
         resp = await query_turso(match_time, user, keyword, caption, super_chats, qtype, **kwargs)
         count = resp.get("count", 0)
         paths = resp.get("paths", [])
+        user = resp.get("user", user)
     else:
         resp = {}
         paths = []
@@ -120,7 +121,7 @@ async def query_danmu(client: Client, message: Message, **kwargs):
     if DANMU.QUERY_METHOD.lower() == "turso":
         merged_paths = paths
     else:
-        dates: list[str] = flatten(engine_dates.values())  # type: ignore
+        dates: list[str] = flatten(engine_dates.values())
         merged_paths = merge_txt_files(paths, dates, user, keyword, qtype, tips)
     media = [{"document": path} for path in sorted(merged_paths)]
     # less than 200K, add instant view
src/danmu/turso.py
@@ -12,10 +12,11 @@ from danmu.utils import TURSO_KWARGS
 from database.turso import turso_exec, turso_parse_resp
 from messages.progress import modify_progress
 from others.emoji import CURRENCY
+from preview.youtube import get_youtube_channel_name_by_handle
 from utils import nowstr, number
 
 
-async def query_turso(match_time: str, user: str, keyword: str, caption: str, super_chats: defaultdict, qtype: str, **kwargs) -> dict:
+async def query_turso(match_time: str, user: str, keyword: str, caption: str, super_chats: defaultdict, qtype: str, *, parse_handle: bool = True, **kwargs) -> dict:
     """从Turso获取记录.
 
     Returns:
@@ -68,12 +69,16 @@ async def query_turso(match_time: str, user: str, keyword: str, caption: str, su
         resp = await turso_exec([{"type": "execute", "stmt": {"sql": sql}}], silent=True, retry=2, **TURSO_KWARGS)
         parsed = await parse_from_turso(turso_parse_resp(resp), user, keyword, super_chats, qtype)
         count = parsed.get("count", 0)
+        if count == 0 and user and parse_handle and (channel_name := await get_youtube_channel_name_by_handle(user)):
+            await modify_progress(text=caption + f"\n⚠️未匹配到“{user}”的{qtype}\n🔍尝试使用“{channel_name}”查询", force_update=True, **kwargs)
+            return await query_turso(match_time, channel_name, keyword, caption, super_chats, qtype, parse_handle=False, **kwargs)
+
         await modify_progress(text=caption + f"\n⏳已匹配 {count} 条{qtype}", force_update=True, **kwargs)
         texts = parsed.get("texts", "")
         save_path = f"{DOWNLOAD_DIR}/{user}-{match_time}-{keyword}-{qtype}.txt".replace("--", "-")
         async with await anyio.open_file(save_path, "w") as f:
             await f.write(texts.strip())
-    return {"paths": [save_path], "count": count}
+    return {"paths": [save_path], "count": count, "user": user}
 
 
 async def parse_from_turso(data: list[dict], user: str, keyword: str, super_chats: defaultdict, qtype: str) -> dict:
src/preview/youtube.py
@@ -184,3 +184,17 @@ async def get_youtube_channel_thumb(channel_id: str) -> str:
         return ""
     thumbnails = glom(resp, "items.0.snippet.thumbnails", default={})
     return glom(thumbnails, Coalesce("high.url", "medium.url", "default.url"), default="")
+
+
+@cache.memoize(ttl=120)
+async def get_youtube_channel_name_by_handle(handle: str) -> str:
+    """Get YouTube channel by handle."""
+    if not handle:
+        return ""
+    api = "https://www.googleapis.com/youtube/v3/channels"
+    params = {"key": TOKEN.YOUTUBE_API_KEY, "part": "snippet", "forHandle": handle}
+    resp = await hx_req(api, proxy=PROXY.GOOGLE, params=params, check_keys=["items.0.snippet"], max_retry=3)
+    if resp.get("hx_error"):
+        logger.warning(f"YouTube Channels API failed: {resp['hx_error']}")
+        return ""
+    return glom(resp, "items.0.snippet.title", default="")