from .types import InlineUnit, InlineCall
from aiogram.types import (
Message as AiogramMessage,
InlineQuery as AiogramInlineQuery,
CallbackQuery,
ChosenInlineResult,
InlineQueryResultArticle,
InputTextMessageContent,
InlineQueryResultPhoto,
InlineQueryResultGif,
InlineQueryResultVideo,
InlineQueryResultDocument,
)
import logging
from typing import List
import re
from .. import utils
from .types import InlineQuery
import functools
import inspect
from telethon.tl.types import Message
logger = logging.getLogger(__name__)
class Events(InlineUnit):
async def _message_handler(self, message: AiogramMessage) -> None:
"""Processes incoming messages"""
if message.chat.type != "private":
return
for mod in self._allmodules.modules:
if not hasattr(mod, "aiogram_watcher"):
continue
setattr(
message,
"answer",
functools.partial(
self._bot_message_answer,
message=message,
),
)
try:
await mod.aiogram_watcher(message)
except BaseException:
logger.exception("Error on running aiogram watcher!")
async def _inline_handler(self, inline_query: AiogramInlineQuery) -> None:
"""Inline query handler (forms' calls)"""
# Retrieve query from passed object
query = inline_query.query
# If we didn't get any query, return help
if not query:
await self._query_help(inline_query)
return
# First, dispatch all registered inline handlers
cmd = inline_query.query.split()[0].lower()
if (
cmd in self._allmodules.inline_handlers
and await self.check_inline_security(
self._allmodules.inline_handlers[cmd],
inline_query.from_user.id,
)
):
instance = InlineQuery(inline_query)
try:
result = await self._allmodules.inline_handlers[cmd](instance)
except BaseException:
logger.exception("Error on running inline watcher!")
return
if not result:
return
if isinstance(result, dict):
result = [result]
if not isinstance(result, list) or not all(
(
"message" in res
or "photo" in res
or "gif" in res
or "video" in res
or "file" in res
and "mime_type" in res
)
and "title" in res
for res in result
):
logger.error("Got invalid type from inline handler. Refer to docs for more info") # fmt: skip
return
inline_result = []
for res in result:
if "message" in res:
inline_result += [
InlineQueryResultArticle(
id=utils.rand(20),
title=res["title"],
description=res.get("description", None),
input_message_content=InputTextMessageContent(
res["message"],
"HTML",
disable_web_page_preview=True,
),
thumb_url=res.get("thumb", None),
thumb_width=128,
thumb_height=128,
reply_markup=self._generate_markup(
res.get("reply_markup", None)
),
)
]
elif "photo" in res:
inline_result += [
InlineQueryResultPhoto(
id=utils.rand(20),
title=res.get("title", None),
description=res.get("description", None),
caption=res.get("caption", None),
parse_mode="HTML",
thumb_url=res.get("thumb", res["photo"]),
photo_url=res["photo"],
reply_markup=self._generate_markup(
res.get("reply_markup", None)
),
)
]
elif "gif" in res:
inline_result += [
InlineQueryResultGif(
id=utils.rand(20),
title=res.get("title", None),
caption=res.get("caption", None),
parse_mode="HTML",
thumb_url=res.get("thumb", res["gif"]),
gif_url=res["gif"],
reply_markup=self._generate_markup(
res.get("reply_markup", None)
),
)
]
elif "video" in res:
inline_result += [
InlineQueryResultVideo(
id=utils.rand(20),
title=res.get("title", None),
description=res.get("description", None),
caption=res.get("caption", None),
parse_mode="HTML",
thumb_url=res.get("thumb", res["video"]),
video_url=res["video"],
mime_type="video/mp4",
reply_markup=self._generate_markup(
res.get("reply_markup", None)
),
)
]
elif "file" in res:
inline_result += [
InlineQueryResultDocument(
id=utils.rand(20),
title=res.get("title", None),
description=res.get("description", None),
caption=res.get("caption", None),
parse_mode="HTML",
thumb_url=res.get("thumb", res["file"]),
document_url=res["file"],
mime_type=res["mime_type"],
reply_markup=self._generate_markup(
res.get("reply_markup", None)
),
)
]
try:
await inline_query.answer(inline_result)
except Exception:
logger.exception(f"Exception when answering inline query with result from {cmd}") # fmt: skip
return
await self._form_inline_handler(inline_query)
await self._gallery_inline_handler(inline_query)
await self._list_inline_handler(inline_query)
async def _check_inline_sec_by_mask(
self,
*,
mask: int,
user: int,
message: Message,
) -> bool:
"""Checks if user is able to execute command with provided security mask"""
return await self._client.dispatcher.security._check(
message=message,
func=mask,
user=user,
)
async def _callback_query_handler(
self,
query: CallbackQuery,
reply_markup: List[List[dict]] = None,
) -> None:
"""Callback query handler (buttons' presses)"""
if reply_markup is None:
reply_markup = []
if re.search(r"authorize_web_(.{8})", query.data):
self._web_auth_tokens += [
re.search(r"authorize_web_(.{8})", query.data).group(1)
]
return
# First, dispatch all registered callback handlers
for func in self._allmodules.callback_handlers.values():
if await self.check_inline_security(func, query.from_user.id):
try:
await func(query)
except Exception:
logger.exception("Error on running callback watcher!")
await query.answer(
"Error occured while processing request. More info in logs",
show_alert=True,
)
continue
for form_uid, form in self._forms.copy().items():
for button in utils.array_sum(form.get("buttons", [])):
if button.get("_callback_data", None) == query.data:
if (
form.get("disable_security", False)
or (
form.get("force_me", False)
and query.from_user.id == self._me
)
or not form.get("force_me", False)
and (
await self._check_inline_sec_by_mask(
mask=form.get(
"perms_map",
lambda: self._client.dispatcher.security._default,
)(),
user=query.from_user.id,
message=form["message"],
)
if "message" in form
else False
)
):
pass
elif (
query.from_user.id
not in self._client.dispatcher.security._owner
and query.from_user.id not in form.get("always_allow", [])
):
await query.answer("You are not allowed to press this button!")
return
query.delete = functools.partial(
self._callback_query_delete,
form=form,
form_uid=form_uid,
)
query.unload = functools.partial(
self._callback_query_unload,
form_uid=form_uid,
)
query.edit = functools.partial(
self._callback_query_edit,
query=query,
form=form,
form_uid=form_uid,
)
query.form = {"id": form_uid, **form}
try:
return await button["callback"](
query,
*button.get("args", []),
**button.get("kwargs", {}),
)
except Exception:
logger.exception("Error on running callback watcher!")
await query.answer(
"Error occurred while "
"processing request. "
"More info in logs",
show_alert=True,
)
return
del self._forms[form_uid]
if query.data in self._custom_map:
if (
self._custom_map[query.data].get("disable_security", False)
or (
self._custom_map[query.data].get("force_me", False)
and query.from_user.id == self._me
)
or not self._custom_map[query.data].get("force_me", False)
and (
await self._check_inline_sec_by_mask(
mask=self._custom_map[query.data].get(
"perms_map",
lambda: self._client.dispatcher.security._default,
)(),
user=query.from_user.id,
message=self._custom_map[query.data]["message"],
)
if "message" in self._custom_map[query.data]
else False
)
):
pass
elif (
query.from_user.id not in self._client.dispatcher.security._owner
and query.from_user.id
not in self._custom_map[query.data].get("always_allow", [])
):
await query.answer("You are not allowed to press this button!")
return
await self._custom_map[query.data]["handler"](
query,
*self._custom_map[query.data].get("args", []),
**self._custom_map[query.data].get("kwargs", {}),
)
return
async def _chosen_inline_handler(
self,
chosen_inline_query: ChosenInlineResult,
) -> None:
query = chosen_inline_query.query
for form_uid, form in self._forms.copy().items():
for button in utils.array_sum(form.get("buttons", [])):
if (
"_switch_query" in button
and "input" in button
and button["_switch_query"] == query.split()[0]
and chosen_inline_query.from_user.id
in [self._me]
+ self._client.dispatcher.security._owner
+ form.get("always_allow", [])
):
query = query.split(maxsplit=1)[1] if len(query.split()) > 1 else ""
call = InlineCall()
call.delete = functools.partial(
self._callback_query_delete,
form=form,
form_uid=form_uid,
)
call.unload = functools.partial(
self._callback_query_unload,
form_uid=form_uid,
)
call.edit = functools.partial(
self._callback_query_edit,
query=chosen_inline_query,
form=form,
form_uid=form_uid,
)
try:
return await button["handler"](
call,
query,
*button.get("args", []),
**button.get("kwargs", {}),
)
except Exception:
logger.exception(
"Exception while running chosen query watcher!"
)
return
async def _query_help(self, inline_query: InlineQuery) -> None:
_help = ""
for name, fun in self._allmodules.inline_handlers.items():
# If user doesn't have enough permissions
# to run this inline command, do not show it
# in help
if not await self.check_inline_security(fun, inline_query.from_user.id):
continue
# Retrieve docs from func
try:
doc = utils.escape_html(
"\n".join(
[
line.strip()
for line in inspect.getdoc(fun).splitlines()
if not line.strip().startswith("@")
]
)
)
except AttributeError:
doc = "🦥 No docs"
_help += f"🎹 @{self.bot_username} {name}
- {doc}\n"
await inline_query.answer(
[
InlineQueryResultArticle(
id=utils.rand(20),
title="Show available inline commands",
description=f"You have {len(_help.splitlines())} available command(-s)",
input_message_content=InputTextMessageContent(
f"ℹ️ Available inline commands:\n\n{_help}",
"HTML",
disable_web_page_preview=True,
),
thumb_url="https://img.icons8.com/fluency/50/000000/info-squared.png",
thumb_width=128,
thumb_height=128,
)
],
cache_time=0,
)