main
  1#!/usr/bin/env python
  2# -*- coding: utf-8 -*-
  3import asyncio
  4from typing import Literal
  5from urllib.parse import urlparse
  6
  7from pyrogram.types import Message
  8
  9from config import DEVICE_NAME
 10from custom.config import CHANNEL_EMBY_SEARCH, GROUP_DEV, USER_BENNY, USER_XIAOHAO
 11from database.kv import get_cf_kv, set_cf_kv
 12from emby.account import all_accounts, emby_login
 13from emby.api import get_items_count
 14from emby.constant import ITEM_TYPE
 15from emby.search import emby_search
 16from messages.progress import modify_progress
 17from messages.utils import blockquote, delete_message, remove_prefix, startswith_prefix
 18from utils import strings_list, to_int, true
 19
 20CREDENTIAL_HEADER = "emby,enable,bot,server,user,pass,keepalive,nsfw"
 21
 22
 23async def emby_entrypoint(message: Message):
 24    """Emby相关."""
 25    cid = message.chat.id
 26    # VPS上的账号不响应 开发Group 的消息
 27    if cid == GROUP_DEV and DEVICE_NAME in ["BennyBot-JP", "BennyBot-US", "BennyBot-CN"]:
 28        return
 29
 30    if str(message.text).startswith(CREDENTIAL_HEADER):
 31        if await set_credentials(message):
 32            await delete_message(message)
 33        return
 34
 35    if not (cid == CHANNEL_EMBY_SEARCH or startswith_prefix(message.content, prefix="/emby")):
 36        return
 37    if message.content == "/emby":
 38        await count_items(message)
 39        return
 40    if message.content == "/embyget":
 41        if message.from_user.id in [USER_BENNY, USER_XIAOHAO]:
 42            await show_credentials(message, formats="text")
 43        else:
 44            await message.reply("❌权限不足", quote=True)
 45        return
 46    if message.content == "/embyset":
 47        if message.from_user.id in [USER_BENNY, USER_XIAOHAO]:
 48            await show_credentials(message, formats="csv")
 49        else:
 50            await message.reply("❌权限不足", quote=True)
 51        return
 52
 53    # search emby
 54    query = remove_prefix(message.content, prefix="/emby")
 55    if not query:
 56        return
 57    status = await message.reply(f"🔍正在搜索: {query}", quote=True)
 58    if cid == CHANNEL_EMBY_SEARCH:
 59        summary, _, telegraph_url = await emby_search(query, allow_nsfw=True)
 60    else:
 61        summary, _, telegraph_url = await emby_search(query)
 62    if not summary:
 63        await modify_progress(status, text="❌没有找到任何结果", force_update=True)
 64        return
 65    summary = f"⚡️[即时预览]({telegraph_url})\n{blockquote(summary)}" if telegraph_url else blockquote(summary)
 66    await modify_progress(status, text=summary, force_update=True)
 67
 68
 69async def count_items(message: Message):
 70    async def count(acc_name: str, config: dict, *, refresh: bool = False) -> str:
 71        credentials = await emby_login(acc_name, refresh=refresh)
 72        if not credentials:
 73            return f"🤖[{acc_name}](t.me/{config['bot']}): ❌"
 74        resp = await get_items_count(credentials)
 75        if resp.get("hx_error"):
 76            return await count(acc_name, config, refresh=True) if not refresh else f"🤖[{acc_name}](t.me/{config['bot']}): ❌"
 77        return f"🤖[{acc_name}](t.me/{config['bot']}): {ITEM_TYPE['Movie']}{resp['MovieCount']} {ITEM_TYPE['Series']}{resp['EpisodeCount']}"
 78
 79    accounts = await all_accounts()
 80    tasks = [count(acc_name, config) for acc_name, config in accounts.items()]
 81    res = await asyncio.gather(*tasks)
 82    await message.reply("\n".join(res), quote=True)
 83
 84
 85async def show_credentials(message: Message, formats: Literal["text", "csv"] = "text"):
 86    if formats == "text":
 87        accounts = await all_accounts()  # {"account_name": {"bot": "bot_handle", "server": "https://example.com", "username": "username", "password": "password"}}
 88        msg = "⚠️除非特别说明, 否则所有服务器协议均为 **HTTPS**, 端口均为 **443**\n"
 89        no_proto = ""
 90        with_proto = ""
 91        for acc_name, config in accounts.items():
 92            r18 = "🔞" if config.get("nsfw") else ""
 93            parsed = urlparse(config["server"])
 94            no_proto += f"\n{r18}`{acc_name}`】"
 95            with_proto += f"\n{r18}`{acc_name}`】"
 96            if parsed.scheme == "http":
 97                no_proto += "\n⚠️协议: **HTTP**"
 98            no_proto += f"\n服务器: `{parsed.hostname}`"
 99            with_proto += f"\n服务器: `{parsed.scheme}://{parsed.hostname}`"
100            if parsed.port is not None and parsed.port != 443:
101                no_proto += f"\n⚠️端口: `{parsed.port}`"
102                with_proto = with_proto[:-1] + f":{parsed.port}`"
103            no_proto += f"\n用户名: `{config['username']}`\n密码: `{config['password']}`\n"
104            with_proto += f"\n用户名: `{config['username']}`\n密码: `{config['password']}`\n"
105        await message.reply(msg + no_proto, quote=True)
106        await message.reply(msg + with_proto, quote=True)
107
108    elif formats == "csv":
109        kv = await get_cf_kv("emby")
110        msg = CREDENTIAL_HEADER
111        for name, c in sorted(kv.items(), key=lambda x: x[1]["enable"], reverse=True):
112            msg += f"\n{name},{c['enable']},@{c['bot']},{c['server']},{c['username']},{c['password']},{c.get('keepalive', '0')},{c.get('nsfw', '')}"
113        msg += f"\n{CREDENTIAL_HEADER}"
114        await message.reply(msg, quote=True)
115
116
117async def set_credentials(message: Message) -> bool:
118    if message.from_user.id not in [USER_BENNY, USER_XIAOHAO]:
119        return False
120    texts = message.text.split("\n")[1:]
121    credentials = {}
122    for line in texts:
123        name, enable, bot, server, user, passwd, keepalive, nsfw = line.split(",")
124        if name == strings_list(CREDENTIAL_HEADER)[0]:
125            continue
126        keepalive = to_int(keepalive)
127        credentials[name] = {
128            "enable": true(enable),
129            "bot": bot.lstrip("@").strip(),
130            "server": server.strip(),
131            "username": user.strip(),
132            "password": passwd.strip(),
133            "keepalive": keepalive,
134            "nsfw": true(nsfw),
135        }
136        if not isinstance(keepalive, int) or keepalive <= 0:
137            del credentials[name]["keepalive"]
138        if not true(nsfw):
139            del credentials[name]["nsfw"]
140    if credentials:
141        return await set_cf_kv("emby", credentials, silent=True)
142    return False