mirror of https://github.com/coddrago/Heroku
267 lines
8.2 KiB
Python
267 lines
8.2 KiB
Python
"""Inline buttons, galleries and other Telegram-Bot-API stuff"""
|
|
|
|
# ©️ Dan Gazizullin, 2021-2023
|
|
# This file is a part of Hikka Userbot
|
|
# 🌐 https://github.com/hikariatama/Hikka
|
|
# You can redistribute it and/or modify it under the terms of the GNU AGPLv3
|
|
# 🔑 https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
import asyncio
|
|
import contextlib
|
|
import logging
|
|
import time
|
|
import typing
|
|
|
|
from aiogram import Bot, Dispatcher
|
|
from aiogram.types import ParseMode
|
|
from aiogram.utils.exceptions import TerminatedByOtherGetUpdates, Unauthorized
|
|
from hikkatl.errors.rpcerrorlist import InputUserDeactivatedError, YouBlockedUserError
|
|
from hikkatl.tl.functions.contacts import UnblockRequest
|
|
from hikkatl.tl.types import Message
|
|
from hikkatl.utils import get_display_name
|
|
|
|
from .. import utils
|
|
from ..database import Database
|
|
from ..tl_cache import CustomTelegramClient
|
|
from ..translations import Translator
|
|
from .bot_pm import BotPM
|
|
from .events import Events
|
|
from .form import Form
|
|
from .gallery import Gallery
|
|
from .list import List
|
|
from .query_gallery import QueryGallery
|
|
from .token_obtainment import TokenObtainment
|
|
from .utils import Utils
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class InlineManager(
|
|
Utils,
|
|
Events,
|
|
TokenObtainment,
|
|
Form,
|
|
Gallery,
|
|
QueryGallery,
|
|
List,
|
|
BotPM,
|
|
):
|
|
"""
|
|
Inline buttons, galleries and other Telegram-Bot-API stuff
|
|
:param client: Telegram client
|
|
:param db: Database instance
|
|
:param allmodules: All modules
|
|
:type client: hikka.tl_cache.CustomTelegramClient
|
|
:type db: hikka.database.Database
|
|
:type allmodules: hikka.loader.Modules
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
client: CustomTelegramClient,
|
|
db: Database,
|
|
allmodules: "Modules", # type: ignore # noqa: F821
|
|
):
|
|
"""Initialize InlineManager to create forms"""
|
|
self._client = client
|
|
self._db = db
|
|
self._allmodules = allmodules
|
|
self.translator: Translator = allmodules.translator
|
|
|
|
self._units: typing.Dict[str, dict] = {}
|
|
self._custom_map: typing.Dict[str, callable] = {}
|
|
self.fsm: typing.Dict[str, str] = {}
|
|
self._web_auth_tokens: typing.List[str] = []
|
|
self._error_events: typing.Dict[str, asyncio.Event] = {}
|
|
|
|
self._markup_ttl = 60 * 60 * 24
|
|
self.init_complete = False
|
|
|
|
self._token = db.get("hikka.inline", "bot_token", False)
|
|
|
|
self._me: int = None
|
|
self._name: str = None
|
|
self._dp: Dispatcher = None
|
|
self._task: asyncio.Future = None
|
|
self._cleaner_task: asyncio.Future = None
|
|
self.bot: Bot = None
|
|
self.bot_id: int = None
|
|
self.bot_username: str = None
|
|
|
|
async def _cleaner(self):
|
|
"""Cleans outdated inline units"""
|
|
while True:
|
|
for unit_id, unit in self._units.copy().items():
|
|
if (unit.get("ttl") or (time.time() + self._markup_ttl)) < time.time():
|
|
del self._units[unit_id]
|
|
|
|
await asyncio.sleep(5)
|
|
|
|
async def register_manager(
|
|
self,
|
|
after_break: bool = False,
|
|
ignore_token_checks: bool = False,
|
|
):
|
|
"""
|
|
Register manager
|
|
:param after_break: Loop marker
|
|
:param ignore_token_checks: If `True`, will not check for token
|
|
:type after_break: bool
|
|
:type ignore_token_checks: bool
|
|
:return: None
|
|
:rtype: None
|
|
"""
|
|
self._me = self._client.tg_id
|
|
self._name = get_display_name(self._client.hikka_me)
|
|
|
|
if not ignore_token_checks:
|
|
is_token_asserted = await self._assert_token()
|
|
if not is_token_asserted:
|
|
self.init_complete = False
|
|
return
|
|
|
|
self.init_complete = True
|
|
|
|
self.bot = Bot(token=self._token, parse_mode=ParseMode.HTML)
|
|
Bot.set_current(self.bot)
|
|
self._bot = self.bot
|
|
self._dp = Dispatcher(self.bot)
|
|
|
|
try:
|
|
bot_me = await self.bot.get_me()
|
|
self.bot_username = bot_me.username
|
|
self.bot_id = bot_me.id
|
|
except Unauthorized:
|
|
logger.critical("Token expired, revoking...")
|
|
return await self._dp_revoke_token(False)
|
|
|
|
try:
|
|
m = await self._client.send_message(self.bot_username, "/start hikka init")
|
|
except (InputUserDeactivatedError, ValueError):
|
|
self._db.set("hikka.inline", "bot_token", None)
|
|
self._token = False
|
|
|
|
if not after_break:
|
|
return await self.register_manager(True)
|
|
|
|
self.init_complete = False
|
|
return False
|
|
except YouBlockedUserError:
|
|
await self._client(UnblockRequest(id=self.bot_username))
|
|
try:
|
|
m = await self._client.send_message(
|
|
self.bot_username, "/start hikka init"
|
|
)
|
|
except Exception:
|
|
logger.critical("Can't unblock users bot", exc_info=True)
|
|
return False
|
|
except Exception:
|
|
self.init_complete = False
|
|
logger.critical("Initialization of inline manager failed!", exc_info=True)
|
|
return False
|
|
|
|
await self._client.delete_messages(self.bot_username, m)
|
|
|
|
self._dp.register_inline_handler(
|
|
self._inline_handler,
|
|
lambda _: True,
|
|
)
|
|
|
|
self._dp.register_callback_query_handler(
|
|
self._callback_query_handler,
|
|
lambda _: True,
|
|
)
|
|
|
|
self._dp.register_chosen_inline_handler(
|
|
self._chosen_inline_handler,
|
|
lambda _: True,
|
|
)
|
|
|
|
self._dp.register_message_handler(
|
|
self._message_handler,
|
|
lambda *_: True,
|
|
content_types=["any"],
|
|
)
|
|
|
|
old = self.bot.get_updates
|
|
revoke = self._dp_revoke_token
|
|
|
|
async def new(*args, **kwargs):
|
|
nonlocal revoke, old
|
|
try:
|
|
return await old(*args, **kwargs)
|
|
except TerminatedByOtherGetUpdates:
|
|
await revoke()
|
|
except Unauthorized:
|
|
logger.critical("Got Unauthorized")
|
|
await self._stop()
|
|
|
|
self.bot.get_updates = new
|
|
|
|
self._task = asyncio.ensure_future(self._dp.start_polling())
|
|
self._cleaner_task = asyncio.ensure_future(self._cleaner())
|
|
|
|
async def _stop(self):
|
|
"""Stop the bot"""
|
|
self._task.cancel()
|
|
self._dp.stop_polling()
|
|
self._cleaner_task.cancel()
|
|
|
|
def pop_web_auth_token(self, token: str) -> bool:
|
|
"""
|
|
Check if web confirmation button was pressed
|
|
:param token: Token to check
|
|
:type token: str
|
|
:return: `True` if token was found, `False` otherwise
|
|
:rtype: bool
|
|
"""
|
|
if token not in self._web_auth_tokens:
|
|
return False
|
|
|
|
self._web_auth_tokens.remove(token)
|
|
return True
|
|
|
|
async def _invoke_unit(self, unit_id: str, message: Message) -> Message:
|
|
event = asyncio.Event()
|
|
self._error_events[unit_id] = event
|
|
|
|
q: "InlineResults" = None # type: ignore # noqa: F821
|
|
exception: Exception = None
|
|
|
|
async def result_getter():
|
|
nonlocal unit_id, q
|
|
with contextlib.suppress(Exception):
|
|
q = await self._client.inline_query(self.bot_username, unit_id)
|
|
|
|
async def event_poller():
|
|
nonlocal exception
|
|
await asyncio.wait_for(event.wait(), timeout=10)
|
|
if self._error_events.get(unit_id):
|
|
exception = self._error_events[unit_id]
|
|
|
|
result_getter_task = asyncio.ensure_future(result_getter())
|
|
event_poller_task = asyncio.ensure_future(event_poller())
|
|
|
|
_, pending = await asyncio.wait(
|
|
[result_getter_task, event_poller_task],
|
|
return_when=asyncio.FIRST_COMPLETED,
|
|
)
|
|
|
|
for task in pending:
|
|
task.cancel()
|
|
|
|
self._error_events.pop(unit_id, None)
|
|
|
|
if exception:
|
|
raise exception # skipcq: PYL-E0702
|
|
|
|
if not q:
|
|
raise Exception("No query results")
|
|
|
|
return await q[0].click(
|
|
utils.get_chat_id(message) if isinstance(message, Message) else message,
|
|
reply_to=(
|
|
message.reply_to_msg_id if isinstance(message, Message) else None
|
|
),
|
|
)
|