feat(api,userbot,frontend): search peers without chats and start tracking them
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
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})
|
||||
Reference in New Issue
Block a user