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