Commit 08800c5
Changed files (7)
src
src/messages/preprocess.py
@@ -52,7 +52,6 @@ async def preprocess_media(media: list[dict]) -> list[dict]:
"""
num_before = len(media)
logger.trace(f"{num_before} media info before preprocess: {media}")
-
# Step-1: Photos
done_photos = []
for data in media:
@@ -61,7 +60,7 @@ async def preprocess_media(media: list[dict]) -> list[dict]:
continue
if photo_path := data.get("photo"):
valid_photos = [validate_img(photo) for photo in split_long_img(photo_path) if validate_img(photo)]
- done_photos.extend({"photo": valid_photo} for valid_photo in valid_photos)
+ done_photos.extend({"photo": valid_photo, "has_spoiler": data.get("has_spoiler", False)} for valid_photo in valid_photos)
# Step-2: Videos
done_videos = []
@@ -84,7 +83,16 @@ async def preprocess_media(media: list[dict]) -> list[dict]:
for vpath, tpath in zip(valid_videos, thumbs, strict=True):
video_info = await parse_media_info(vpath)
thumb = valid_thumb if (valid_thumb := validate_img(tpath)) else None
- done_videos.append({"video": vpath.as_posix(), "width": video_info["width"], "height": video_info["height"], "duration": video_info["duration"], "thumb": thumb})
+ done_videos.append(
+ {
+ "video": vpath.as_posix(),
+ "width": video_info["width"],
+ "height": video_info["height"],
+ "duration": video_info["duration"],
+ "thumb": thumb,
+ "has_spoiler": data.get("has_spoiler", False),
+ }
+ )
# Step-3: Audios
done_audios = []
for data in done_videos:
src/messages/sender.py
@@ -171,8 +171,8 @@ async def send_single_media(
caption = warp_comments(caption)
message = None
try:
- if photo := media.get("photo"):
- message = await client.send_photo(chat_id=target_chat, photo=photo, caption=caption, show_caption_above_media=caption_above, reply_parameters=reply_parameters)
+ if media.get("photo"):
+ message = await client.send_photo(chat_id=target_chat, **media, caption=caption, show_caption_above_media=caption_above, reply_parameters=reply_parameters)
elif video := media.get("video"):
message = await client.send_video(
chat_id=target_chat,
src/messages/utils.py
@@ -90,6 +90,15 @@ def equal_prefix(text: str, prefix: str | list[str], ignore_prefix: str | list[s
return False
+def remove_prefix(s: str, prefix: str) -> str:
+ """Remove the prefix from the string."""
+ if not prefix:
+ return s
+ if s.lower().startswith(prefix.lower()):
+ return s[len(prefix) :].lstrip()
+ return s
+
+
def get_reply_to(msg_id: int, reply_msg_id: int | str) -> ReplyParameters:
if str(reply_msg_id) == "0":
reply_to = msg_id
@@ -141,20 +150,9 @@ async def smart_split(text: str, chars_per_string: int = TEXT_LENGTH, mode: Pars
def next_sentence(strings: str) -> str:
# ruff: noqa: RUF001
- if "\n" in strings:
- return strings.split("\n")[0] + "\n"
- if " " in strings:
- return strings.split(" ")[0] + " "
- if ". " in strings:
- return strings.split(". ")[0] + ". "
- if "。" in strings:
- return strings.split("。")[0] + "。"
- if ";" in strings:
- return strings.split(";")[0] + ";"
- if "!" in strings:
- return strings.split("!")[0] + "!"
- if "?" in strings:
- return strings.split("?")[0] + "?"
+ sentence_enders = r"[。!? \.\)\n\t)]"
+ if matched := re.search(sentence_enders, strings):
+ return strings[: matched.end()]
return strings
# for some reason, we may need to prepend `BLOCKQUOTE_EXPANDABLE_DELIM` or append `BLOCKQUOTE_EXPANDABLE_END_DELIM`
src/others/tmdb.py
@@ -0,0 +1,313 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+from typing import Literal
+
+from glom import Coalesce, glom
+from pyrogram.client import Client
+from pyrogram.types import Message
+
+from config import CAPTION_LENGTH, PREFIX, PROXY, TEXT_LENGTH, TOKEN
+from messages.parser import parse_msg
+from messages.sender import send2tg
+from messages.utils import blockquote, count_without_entities, equal_prefix, remove_prefix, set_reaction, smart_split, startswith_prefix
+from networking import download_file, download_first_success_urls, download_media, hx_req
+from publish import publish_telegraph
+from utils import seconds_to_hms
+
+HELP = f"""
+🎬**查询影视信息**
+使用说明:
+1. `{PREFIX.TMDB}` + 关键词: 查询影视作品简介及ID
+2. `{PREFIX.TMDB}` + @演员名: 查询演员简介及ID
+3. `{PREFIX.TMDB}` + ID: 根据ID查询详细信息
+ID有三种前缀:
+ - M: 电影
+ - T: 电视剧
+ - P: 演员
+
+⚠️注意: `@演员名` 和 `关键词` 不可组合使用
+
+示例:
+ - `{PREFIX.TMDB} 泰坦尼克`: 查找“泰坦尼克”相关作品
+ - `{PREFIX.TMDB} @莱昂纳多`: 查找演员“莱昂纳多”
+ - `{PREFIX.TMDB} M597`: 查询ID为597的电影详情
+ - `{PREFIX.TMDB} P6193`: 查询ID为6193的演员详情
+"""
+HEADERS = {"accept": "application/json", "Authorization": f"Bearer {TOKEN.TMDB}"}
+
+
+async def search_tmdb(client: Client, message: Message, *, include_adult: bool = True, **kwargs) -> None:
+ """TMDB command handler."""
+ if not startswith_prefix(message.content, prefix=PREFIX.TMDB):
+ return
+ # send docs if without reply
+ if equal_prefix(message.text, prefix=PREFIX.TMDB) and not message.reply_to_message:
+ await send2tg(client, message, texts=HELP, **kwargs)
+ return
+ info = parse_msg(message, silent=True)
+ # reply a message with /audio or /voice
+ if message.reply_to_message:
+ message = message.reply_to_message
+ info = parse_msg(message, use_cache=False, silent=True) # parse again
+ await set_reaction(client, message, reaction="👌")
+
+ query = remove_prefix(info["text"], prefix=PREFIX.TMDB)
+ if query.startswith(("M", "T", "P")) and query[1:].isdigit(): # check if the query is a TMDB ID
+ resp = await get_details(query)
+ elif query.startswith("@"):
+ resp = await search_people(query[1:], include_adult=include_adult)
+ else:
+ resp = await search_keyword(query, include_adult=include_adult)
+ await send2tg(client, message, **resp, **kwargs)
+ await set_reaction(client, message, reaction="")
+
+
+async def search_keyword(query: str, tmdb_lang: Literal["en-US", "zh-CN"] = "zh-CN", *, include_adult: bool = True) -> dict:
+ """Search movie & TV by keyword.
+
+ query: 泰坦
+
+ Returns: {"texts": str}
+ """
+ params = {"query": query, "include_adult": str(include_adult).lower(), "language": tmdb_lang, "page": 1}
+ url = "https://api.themoviedb.org/3/search/multi"
+ resp = await hx_req(url, headers=HEADERS, params=params, proxy=PROXY.TMDB, check_kv={"page": 1}, check_keys=["results"])
+ if resp.get("hx_error"):
+ return {"texts": resp["hx_error"]}
+ results = [x for x in resp["results"] if x.get("media_type") in ["movie", "tv"]] # only movie & TV
+ final_msg = ""
+ for item in results:
+ this_msg = ""
+ type_initial = item["media_type"][0].upper() # M: movie, T: TV
+ original_title = glom(item, Coalesce("original_title", "original_name"), default="")
+ if title := glom(item, Coalesce("title", "original_title", "name", "original_name"), default=""):
+ this_msg += f"`{type_initial}{item['id']}`:[《{title}》](https://www.themoviedb.org/{item['media_type']}/{item['id']})"
+
+ if date := glom(item, Coalesce("release_date", "first_air_date"), default=""):
+ this_msg += f"({date[:4]})"
+
+ if overview := item.get("overview"):
+ if original_title and original_title != title: # title: 中文名, original_title: 英文名
+ overview = f"《{original_title}》: {overview}"
+ this_msg += f"\n{blockquote(overview)}\n"
+ if await count_without_entities(final_msg + this_msg) > TEXT_LENGTH:
+ break
+ final_msg += f"\n{this_msg.strip()}"
+ if not final_msg:
+ return {"texts": "❌未找到相关作品"}
+
+ return {"texts": final_msg}
+
+
+async def search_people(query: str, tmdb_lang: Literal["en-US", "zh-CN"] = "zh-CN", *, include_adult: bool = True) -> dict:
+ """Search people by keyword.
+
+ query: 莱昂纳多
+
+ Returns: {"texts": str}
+ """
+ params = {"query": query, "include_adult": str(include_adult).lower(), "language": tmdb_lang, "page": 1}
+ api = "https://api.themoviedb.org/3/search/person"
+ resp = await hx_req(api, headers=HEADERS, params=params, proxy=PROXY.TMDB, check_kv={"page": 1}, check_keys=["results"])
+ if resp.get("hx_error"):
+ return {"texts": resp["hx_error"]}
+ final_msg = ""
+ for item in resp["results"]:
+ this_msg = ""
+ name = glom(item, Coalesce("name", "original_name"), default="")
+ url = f"https://www.themoviedb.org/person/{item['id']}"
+ this_msg += f"\n`P{item['id']}`{gender_emoji(item.get('gender'))}: [{name}]({url})"
+ if item.get("original_name") and item["original_name"] != name:
+ this_msg += f"({item['original_name']})"
+ if item.get("adult"):
+ this_msg += "🔞"
+ if item.get("known_for"):
+ this_msg += f"\n代表作: {', '.join([glom(x, Coalesce('title', 'name'), default='') for x in item['known_for']])}\n"
+ if await count_without_entities(final_msg + this_msg) > TEXT_LENGTH:
+ break
+ final_msg += f"\n\n{this_msg.strip()}"
+ if not final_msg:
+ return {"texts": "❌未找到相关演员"}
+
+ return {"texts": final_msg}
+
+
+async def get_details(query: str, tmdb_lang: Literal["en-US", "zh-CN"] = "zh-CN") -> dict:
+ """Get Movie & TV details by tmdb id.
+
+ query: M597, T34691, P6193
+ """
+ if query.startswith("P"):
+ return await get_people_details(int(query[1:]), tmdb_lang)
+ tmdb_id = query[1:]
+ texts = ""
+ if not query.startswith(("M", "T")):
+ return {"texts": "❌未找到作品详情"}
+
+ media_type = "movie" if query[0].upper() == "M" else "tv" # M: movie, T: tv
+ url = f"https://api.themoviedb.org/3/{media_type}/{tmdb_id}"
+ params = {"append_to_response": "credits,images", "language": tmdb_lang, "include_image_language": "en,cn,zh"}
+ resp = await hx_req(url, headers=HEADERS, params=params, proxy=PROXY.TMDB, check_kv={"id": tmdb_id}, silent=True)
+ if resp.get("hx_error"):
+ return {"texts": resp["hx_error"]}
+ if title := glom(resp, Coalesce("title", "original_title", "name", "original_name"), default=""):
+ texts += f"标题:《{title}》\n"
+
+ original_title = glom(resp, Coalesce("original_title", "original_name"), default="")
+ if original_title and (original_title != title):
+ texts += f"原名:《{original_title}》\n"
+
+ if subtitle := resp.get("tagline"):
+ texts += f"副标题: {subtitle}\n"
+
+ if genres := glom(resp, "genres.*.name", default=[]):
+ genres_with_tag = [f"#{genre}" for genre in genres]
+ texts += f"类型: {', '.join(genres_with_tag)}\n"
+
+ if date := glom(resp, Coalesce("release_date", "first_air_date"), default=""):
+ texts += f"日期: {date}\n"
+
+ if duration := resp.get("runtime", 0):
+ texts += f"时长: {seconds_to_hms(duration * 60)}\n"
+ if country := resp.get("origin_country", []):
+ texts += f"地区: {'、'.join(country)}\n"
+
+ if rate := resp.get("vote_average", 0):
+ texts += f"评分: {rate}\n"
+
+ if company := glom(resp, "production_companies.*.name", default=[]):
+ texts += f"制片: {'、'.join(company)}\n"
+
+ if imdb_id := resp.get("imdb_id"):
+ texts += f"链接: [TMDB](https://www.themoviedb.org/{media_type}/{resp['id']}), [IMDB](https://www.imdb.com/title/{imdb_id})\n"
+ else:
+ texts += f"链接: [TMDB](https://www.themoviedb.org/{media_type}/{resp['id']})\n"
+ if overview := resp.get("overview"):
+ texts += f"简介: {overview}\n"
+
+ # prefer English Poster
+ media = []
+ img_urls = []
+ posters = defaultdict(list)
+ for poster in glom(resp, "images.posters", default=[]):
+ posters[poster.get("iso_639_1", "unknown")].append(poster)
+ if posters.get("en"):
+ img_urls = [x.get("file_path", "") for x in sorted(posters["en"], key=lambda x: x.get("height", 0), reverse=True)]
+ if posters.get("cn"):
+ img_urls = [x.get("file_path", "") for x in sorted(posters["en"], key=lambda x: x.get("height", 0), reverse=True)]
+ if posters.get("zh"):
+ img_urls = [x.get("file_path", "") for x in sorted(posters["en"], key=lambda x: x.get("height", 0), reverse=True)]
+ img_urls = [f"https://image.tmdb.org/t/p/original{url}" for url in img_urls]
+ # add fallback posters
+ if resp.get("poster_path"):
+ img_urls.append(f"https://image.tmdb.org/t/p/original{resp['poster_path']}")
+ if resp.get("backdrop_path"):
+ img_urls.append(f"https://image.tmdb.org/t/p/original{resp['backdrop_path']}")
+ if img_path := await download_first_success_urls(img_urls, proxy=PROXY.TMDB):
+ media.append({"photo": img_path, "has_spoiler": resp.get("adult", False)})
+
+ # process casts
+ for idx, cast in enumerate(glom(resp, "credits.cast", default=[])):
+ if idx == 0:
+ texts += "\n演员表:\n"
+ emoji = gender_emoji(cast.get("gender"))
+ name = glom(cast, Coalesce("name", "original_name"), default="")
+ if character := cast.get("character"):
+ texts += f"{emoji}[{name}](https://www.themoviedb.org/person/{cast['id']}): {character}\n"
+ else:
+ texts += f"{emoji}[{name}](https://www.themoviedb.org/person/{cast['id']})\n"
+
+ # limit to single message
+ texts = (await smart_split(texts, CAPTION_LENGTH))[0] if media else (await smart_split(texts, TEXT_LENGTH))[0]
+ return {"texts": texts, "media": media}
+
+
+async def get_people_details(people_id: int, tmdb_lang: Literal["en-US", "zh-CN"] = "zh-CN") -> dict:
+ url = f"https://api.themoviedb.org/3/person/{people_id}"
+ params = {"append_to_response": "external_ids,combined_credits,images", "language": tmdb_lang}
+ resp = await hx_req(url, headers=HEADERS, params=params, proxy=PROXY.TMDB, check_kv={"id": people_id}, silent=True)
+
+ if resp.get("hx_error"):
+ return {"texts": resp["hx_error"]}
+ texts = gender_emoji(resp.get("gender", ""))
+ if name := glom(resp, Coalesce("name", "original_name"), default=""):
+ texts += f"{name}"
+ if birth := resp.get("birthday"):
+ texts += f"\n生日: {birth}"
+ if death := resp.get("deathday"):
+ texts += f"\n逝世: {death}"
+ if location := resp.get("place_of_birth"):
+ texts += f"\n出生地: {location}"
+
+ # external links
+ texts += f"\n链接: [TMDB](https://www.themoviedb.org/person/{people_id})"
+ if external_ids := resp.get("external_ids", {}):
+ if imdb_id := external_ids.get("imdb_id"):
+ texts += f", [IMDB](https://www.imdb.com/name/{imdb_id})"
+ if facebook_id := external_ids.get("facebook_id"):
+ texts += f", [FB](https://www.facebook.com/{facebook_id})"
+ if instagram_id := external_ids.get("instagram_id"):
+ texts += f", [Ins](https://www.instagram.com/{instagram_id})"
+ if twitter_id := external_ids.get("twitter_id"):
+ texts += f", [X](https://www.twitter.com/{twitter_id})"
+ if youtube_id := external_ids.get("youtube_id"):
+ texts += f", [油管](https://www.youtube.com/{youtube_id})"
+
+ productions_for_caption = []
+ productions_for_html = ""
+ # process casts
+ casts = [x for x in glom(resp, "combined_credits.cast", default=[]) if x.get("media_type") in ["movie", "tv"]]
+ casts = sorted(casts, key=lambda x: glom(x, Coalesce("release_date", "first_air_date"), default=""), reverse=True)
+ for item in casts:
+ type_initial = item["media_type"][0].upper() # M: movie, T: tv
+ title = glom(item, Coalesce("title", "original_title", "name", "original_name"), default="")
+ full_date = glom(item, Coalesce("release_date", "first_air_date"), default="")
+ date = full_date[:4] if full_date else ""
+ if date:
+ productions_for_caption.append(f"`{type_initial}{item['id']}`: {title} ({date})\n")
+ productions_for_html += f'<br>{type_initial}{item["id"]}: <a href="https://www.themoviedb.org/{item["media_type"]}/{item["id"]}">{title}</a> ({full_date})'
+ else:
+ productions_for_caption.append(f"`{type_initial}{item['id']}`: {title}\n")
+ productions_for_html += f'<br>{type_initial}{item["id"]}: <a href="https://www.themoviedb.org/{item["media_type"]}/{item["id"]}">{title}</a>'
+ if overview := item.get("overview"):
+ productions_for_html += f"<br>简介: {overview}"
+
+ # process images
+ media = []
+ if images := glom(resp, "images.profiles", default=[]):
+ images = sorted(images, key=lambda x: x.get("width", 0), reverse=True)[:10] # kepp 10 images
+ media = [{"photo": download_file(f"https://image.tmdb.org/t/p/original{img.get('file_path')}", proxy=PROXY.TMDB)} for img in images]
+ media = await download_media(media)
+ # html = "\n".join([f"<p>{s}</p>" for s in productions_for_html.split("\n")])
+ telegraph_url = await publish_telegraph(title=name, html=productions_for_html.strip("<br>"), author=name, url=f"https://www.themoviedb.org/person/{people_id}")
+
+ description = f"简介: {resp['biography']}" if resp.get("biography") else ""
+ max_length = CAPTION_LENGTH if media else TEXT_LENGTH
+
+ if await count_without_entities(f"{texts}\n{description}") > max_length - 10: # long desc
+ if telegraph_url:
+ texts += f"\n[查看作品列表]({telegraph_url})"
+ texts = (await smart_split(f"{texts}\n{description}", max_length))[0]
+ else: # short desc
+ texts += f"\n{description}"
+ texts += f"\n[作品列表]({telegraph_url}):"
+ productions = "".join(productions_for_caption)
+ texts = (await smart_split(f"{texts}\n{productions}", max_length))[0]
+
+ return {"texts": texts, "media": media}
+
+
+def gender_emoji(gender: str | int) -> str:
+ """Gender emoji.
+
+ 0: Not set / not specified
+ 1: Female
+ 2: Male
+ 3: Non-binary
+ """
+ if str(gender) == "1":
+ return "🚺"
+ if str(gender) == "2":
+ return "🚹"
+ return ""
src/config.py
@@ -72,6 +72,7 @@ class ENABLE: # see fine-grained permission in `src/permission.py`
TTS = os.getenv("ENABLE_TTS", "1").lower() in ["1", "y", "yes", "t", "true", "on"]
CONVERT_CHINESE = os.getenv("ENABLE_CONVERT_CHINESE", "1").lower() in ["1", "y", "yes", "t", "true", "on"]
QUOTLY = os.getenv("ENABLE_QUOTLY", "1").lower() in ["1", "y", "yes", "t", "true", "on"]
+ TMDB = os.getenv("ENABLE_TMDB", "1").lower() in ["1", "y", "yes", "t", "true", "on"]
class PREFIX:
@@ -99,6 +100,7 @@ class PREFIX:
CONVERT_TO_TC = os.getenv("PREFIX_CONVERT_TO_TC", "/tc, /tw").lower()
CONVERT_TO_SC = os.getenv("PREFIX_CONVERT_TO_SC", "/sc, /cn").lower()
QUOTLY = os.getenv("PREFIX_QUOTLY", "/quote").lower()
+ TMDB = os.getenv("PREFIX_TMDB", "/tmdb").lower()
class API:
@@ -160,6 +162,7 @@ class TOKEN:
SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID", "")
SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET", "")
V2EX = os.getenv("V2EX_TOKEN", "")
+ TMDB = os.getenv("TMDB_TOKEN", "")
class PROXY: # format: socks5://127.0.0.1:7890
@@ -184,6 +187,7 @@ class PROXY: # format: socks5://127.0.0.1:7890
WEIBO = os.getenv("WEIBO_PROXY", None)
REDDIT = os.getenv("REDDIT_PROXY", None)
V2EX = os.getenv("V2EX_PROXY", None)
+ TMDB = os.getenv("TMDB_PROXY", None)
GITHUB = os.getenv("GITHUB_PROXY", None)
YTDLP = os.getenv("YTDLP_PROXY", None) # general proxy for ytdlp
YTDLP_FALLBACK = os.getenv("YTDLP_PROXY_FALLBACK", None) # fallback proxy for ytdlp
src/handler.py
@@ -26,6 +26,7 @@ from others.favorite import save_favorite, send_favorite
from others.raw_img_file import convert_raw_img_file
from others.search_google import search_google
from others.search_ytb import search_youtube
+from others.tmdb import search_tmdb
from permission import check_service
from preview.bilibili import preview_bilibili
from preview.douyin import preview_douyin
@@ -70,6 +71,7 @@ async def handle_utilities(
convert_chinese: bool = True,
wget: bool = True,
ytb: bool = True,
+ tmdb: bool = True,
raw_img: bool = True,
show_progress: bool = True,
detail_progress: bool = False,
@@ -100,6 +102,7 @@ async def handle_utilities(
ocr (bool, optional): Enable OCR. Defaults to True.
price (bool, optional): Enable Asset price. Defaults to True.
summary (bool, optional): Enable AI summary. Defaults to True.
+ tmdb (bool, optional): Enable TMDB query. Defaults to True.
raw_img (bool, optional): Enable convert raw image. Defaults to False.
show_progress (bool, optional): Show a progress message on Telegram. Defaults to True.
detail_progress (bool, optional): Show detailed progress (Only if show_proress is set to True). Defaults to False.
@@ -140,6 +143,8 @@ async def handle_utilities(
await chinese_conversion(client, message, **kwargs) # /sc
if quotly:
await quote_message(client, message, **kwargs) # /quote
+ if tmdb:
+ await search_tmdb(client, message, **kwargs) # /tmdb
if raw_img:
await convert_raw_img_file(client, message, **kwargs)
@@ -210,6 +215,7 @@ async def handle_social_media(
PREFIX.WGET,
PREFIX.FAYAN,
PREFIX.TTS,
+ PREFIX.TMDB,
PREFIX.CONVERT_TO_SC,
PREFIX.CONVERT_TO_TC,
FAVORITE.SAVE_PREFIX,
@@ -407,13 +413,15 @@ def get_social_media_help(chat_id: int | str, ctype: str, prefix: str):
if permission["ocr"]:
msg += f"\n🔤**图片转文字**: `{PREFIX.OCR}` + 图片消息"
if permission["price"]:
- msg += f"\n💵**查询价格**: `{PREFIX.PRICE}` + Symbol"
+ msg += f"\n💵**查询价格**: `{PREFIX.PRICE}` + symbol"
if permission["subtitle"]:
msg += f"\n📃**提取字幕**: `{PREFIX.SUBTITLE}` + B站或油管链接"
if permission["history"]:
msg += f"\n🗣**查询聊天记录**: 发送 `{PREFIX.HISTORY}` 查看详细教程"
if permission["wget"]:
msg += f"\n⏬**下载文件**: `{PREFIX.WGET}` + URL"
+ if permission["tmdb"]:
+ msg += f"\n🎬**查询影视信息**: `{PREFIX.TMDB}` + 关键词"
if permission["ytb"]:
msg += f"\n🔍**搜索YouTube**: `{PREFIX.SEARCH_YOUTUBE}` + 关键词"
if permission["google"]:
src/permission.py
@@ -145,6 +145,7 @@ def check_service(cid: int | str, ctype: str) -> dict:
"favorite": True,
"convert_chinese": True,
"quotly": True,
+ "tmdb": True,
} | global_permissions()
if ctype == "PRIVATE":
@@ -210,6 +211,8 @@ def check_service(cid: int | str, ctype: str) -> dict:
permission["convert_chinese"] = False
if not ENABLE.QUOTLY:
permission["quotly"] = False
+ if not ENABLE.TMDB:
+ permission["tmdb"] = False
"""
Set for specific chat