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