import re from pyrogram import Client, raw from pyrogram.errors import BadRequest, Forbidden from pyrogram.types import Chat from userbot.modules.capture.context import CaptureContext from userbot.modules.jobs.context import JobContext from userbot.modules.jobs.registry import register from userbot.modules.profiles.snapshots import save_chat DEFAULT_LIMIT = 30 _USERNAME = re.compile(r"^[a-z][a-z0-9_]{3,31}$", re.IGNORECASE) _PREFIXES = ("https://t.me/", "http://t.me/", "t.me/", "@") def _normalize(query: str) -> str: text = query.strip() for prefix in _PREFIXES: if text.lower().startswith(prefix): text = text[len(prefix) :] break return text.strip("/") _SOURCE_TYPES = (raw.types.User, raw.types.Chat, raw.types.Channel) def _source( peer: raw.base.Peer, users: dict, chats: dict ) -> raw.types.User | raw.types.Chat | raw.types.Channel | None: if isinstance(peer, raw.types.PeerUser): source = users.get(peer.user_id) elif isinstance(peer, raw.types.PeerChannel): source = chats.get(peer.channel_id) elif isinstance(peer, raw.types.PeerChat): source = chats.get(peer.chat_id) else: return None return source if isinstance(source, _SOURCE_TYPES) else None async def _save_found( client: Client, ctx: CaptureContext, peer: raw.base.Peer, users: dict, chats: dict ) -> int | None: source = _source(peer, users, chats) if source is None: return None chat = Chat._parse_chat(client, source) # noqa: SLF001 if chat is None or chat.id is None: return None await save_chat(ctx, chat) return chat.id async def _resolve(client: Client, ctx: CaptureContext, query: str) -> int | None: try: chat = await client.get_chat(query) except (BadRequest, Forbidden): return None if not isinstance(chat, Chat) or chat.id is None: return None await save_chat(ctx, chat) return chat.id async def _search( client: Client, query: str, limit: int ) -> raw.base.contacts.Found | None: try: return await client.invoke(raw.functions.contacts.Search(q=query, limit=limit)) except (BadRequest, Forbidden): return None @register("search_peers") async def search_peers(ctx: JobContext) -> None: client = ctx.client if client is None: return capture = getattr(client, "capture", None) if capture is None: return query = _normalize(ctx.job.params.get("query", "")) if not query: await ctx.report_progress({"ids": [], "done": True}) return limit = int(ctx.job.params.get("limit", DEFAULT_LIMIT)) found = await _search(client, query, limit) ids: list[int] = [] if found is not None: users = {user.id: user for user in found.users} chats = {chat.id: chat for chat in found.chats} for peer in (*found.my_results, *found.results): peer_id = await _save_found(client, capture, peer, users, chats) if peer_id is not None and peer_id not in ids: ids.append(peer_id) if _USERNAME.match(query): resolved = await _resolve(client, capture, query) if resolved is not None and resolved not in ids: ids.insert(0, resolved) await ctx.report_progress({"ids": ids, "done": True})