2021-04-09 10:17:38 +02:00
|
|
|
import asyncio
|
|
|
|
import re
|
|
|
|
import time
|
2021-04-13 16:23:31 +02:00
|
|
|
from abc import ABC
|
2022-09-02 17:44:56 +02:00
|
|
|
from typing import Union
|
2021-04-09 10:17:38 +02:00
|
|
|
|
|
|
|
from grpc import StatusCode
|
|
|
|
from grpc.experimental.aio import AioRpcError
|
|
|
|
from library.telegram.base import RequestContext
|
2022-09-02 17:44:56 +02:00
|
|
|
from library.telegram.common import close_button
|
|
|
|
from library.telegram.utils import safe_execution
|
|
|
|
from nexus.bot.exceptions import BannedUserError
|
|
|
|
from nexus.bot.widgets.search_widget import (
|
|
|
|
InlineSearchWidget,
|
|
|
|
SearchWidget,
|
2021-04-13 11:03:44 +02:00
|
|
|
)
|
2021-04-09 10:17:38 +02:00
|
|
|
from nexus.translations import t
|
2022-09-02 17:44:56 +02:00
|
|
|
from nexus.views.telegram.base_holder import BaseHolder
|
|
|
|
from nexus.views.telegram.common import encode_deep_query
|
2021-04-09 10:17:38 +02:00
|
|
|
from telethon import (
|
2022-09-02 17:44:56 +02:00
|
|
|
Button,
|
2021-04-09 10:17:38 +02:00
|
|
|
events,
|
|
|
|
)
|
2022-09-02 17:44:56 +02:00
|
|
|
from telethon.tl.types import InlineQueryPeerTypeSameBotPM
|
2021-04-09 10:17:38 +02:00
|
|
|
|
|
|
|
from .base import (
|
|
|
|
BaseCallbackQueryHandler,
|
|
|
|
BaseHandler,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-04-13 16:23:31 +02:00
|
|
|
class BaseSearchHandler(BaseHandler, ABC):
|
2022-09-02 17:44:56 +02:00
|
|
|
async def setup_widget(
|
2021-04-09 10:17:38 +02:00
|
|
|
self,
|
|
|
|
request_context: RequestContext,
|
|
|
|
prefetch_message,
|
|
|
|
query: str,
|
|
|
|
is_shortpath_enabled: bool = False,
|
2022-09-02 17:44:56 +02:00
|
|
|
) -> tuple[str, list[Union[list[Button]], list[Button]]]:
|
2021-04-09 10:17:38 +02:00
|
|
|
session_id = self.generate_session_id()
|
|
|
|
message_id = prefetch_message.id
|
2022-09-02 17:44:56 +02:00
|
|
|
request_context.add_default_fields(
|
|
|
|
is_group_mode=request_context.is_group_mode(),
|
|
|
|
mode='search',
|
|
|
|
session_id=session_id,
|
|
|
|
)
|
2021-04-09 10:17:38 +02:00
|
|
|
start_time = time.time()
|
2022-09-02 17:44:56 +02:00
|
|
|
language = request_context.chat.language
|
|
|
|
bot_name = self.application.config['telegram']['bot_name']
|
2021-04-13 16:23:31 +02:00
|
|
|
|
2021-04-09 10:17:38 +02:00
|
|
|
try:
|
|
|
|
search_widget = await SearchWidget.create(
|
|
|
|
application=self.application,
|
|
|
|
chat=request_context.chat,
|
|
|
|
session_id=session_id,
|
|
|
|
request_id=request_context.request_id,
|
|
|
|
query=query,
|
2022-09-02 17:44:56 +02:00
|
|
|
is_group_mode=request_context.is_group_mode(),
|
2021-04-09 10:17:38 +02:00
|
|
|
)
|
|
|
|
except AioRpcError as e:
|
|
|
|
if e.code() == StatusCode.INVALID_ARGUMENT:
|
2022-09-02 17:44:56 +02:00
|
|
|
return t('INVALID_SYNTAX_ERROR', language).format(
|
|
|
|
too_difficult_picture_url=self.application.config['application'].get('too_difficult_picture_url', ''),
|
|
|
|
), [close_button()]
|
2021-04-09 10:17:38 +02:00
|
|
|
elif e.code() == StatusCode.CANCELLED:
|
2022-09-02 17:44:56 +02:00
|
|
|
return t('MAINTENANCE', language).format(
|
|
|
|
maintenance_picture_url=self.application.config['application'].get('maintenance_picture_url', ''),
|
|
|
|
), [close_button()],
|
|
|
|
request_context.error_log(e)
|
2021-04-09 10:17:38 +02:00
|
|
|
raise e
|
|
|
|
|
|
|
|
request_context.statbox(
|
2022-09-02 17:44:56 +02:00
|
|
|
action='documents_retrieved',
|
2021-04-09 10:17:38 +02:00
|
|
|
duration=time.time() - start_time,
|
2022-09-02 17:44:56 +02:00
|
|
|
query=query,
|
|
|
|
page=0,
|
|
|
|
scored_documents=len(search_widget.scored_documents),
|
2021-04-09 10:17:38 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
if len(search_widget.scored_documents) == 1 and is_shortpath_enabled:
|
2022-09-02 17:44:56 +02:00
|
|
|
holder = BaseHolder.create(search_widget.scored_documents[0].typed_document)
|
|
|
|
view = holder.view_builder(language).add_view(bot_name=bot_name).build()
|
|
|
|
buttons = holder.buttons_builder(language).add_default_layout(
|
|
|
|
bot_name=bot_name,
|
2021-04-09 10:17:38 +02:00
|
|
|
session_id=session_id,
|
2022-09-02 17:44:56 +02:00
|
|
|
position=0,
|
|
|
|
).build()
|
|
|
|
return view, buttons
|
2021-04-09 10:17:38 +02:00
|
|
|
|
2022-09-02 17:44:56 +02:00
|
|
|
return await search_widget.render(message_id=message_id)
|
2021-04-09 10:17:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
class SearchHandler(BaseSearchHandler):
|
2022-09-02 17:44:56 +02:00
|
|
|
filter = events.NewMessage(incoming=True, pattern=re.compile(r'^(/search(?:@\w+)?\s+)?(.*)', flags=re.DOTALL))
|
2021-04-09 10:17:38 +02:00
|
|
|
is_group_handler = True
|
|
|
|
should_reset_last_widget = False
|
|
|
|
is_subscription_required_for_handler = True
|
|
|
|
|
2021-05-01 11:10:45 +02:00
|
|
|
def check_search_ban_timeout(self, user_id: str):
|
|
|
|
ban_timeout = self.application.user_manager.check_search_ban_timeout(user_id=user_id)
|
2021-04-09 10:17:38 +02:00
|
|
|
if ban_timeout:
|
2021-04-13 11:03:44 +02:00
|
|
|
raise BannedUserError(ban_timeout=ban_timeout)
|
2021-05-01 11:10:45 +02:00
|
|
|
self.application.user_manager.add_search_time(user_id=user_id, search_time=time.time())
|
2021-04-09 10:17:38 +02:00
|
|
|
|
2021-04-13 11:03:44 +02:00
|
|
|
def parse_pattern(self, event: events.ChatAction):
|
2021-04-09 10:17:38 +02:00
|
|
|
search_prefix = event.pattern_match.group(1)
|
2022-09-02 17:44:56 +02:00
|
|
|
query = event.pattern_match.group(2).strip()
|
2021-04-09 10:17:38 +02:00
|
|
|
|
2022-09-02 17:44:56 +02:00
|
|
|
return search_prefix, query
|
2021-04-13 11:03:44 +02:00
|
|
|
|
|
|
|
async def handler(self, event: events.ChatAction, request_context: RequestContext):
|
2022-09-02 17:44:56 +02:00
|
|
|
language = request_context.chat.language
|
2021-04-13 11:03:44 +02:00
|
|
|
try:
|
2021-05-01 11:10:45 +02:00
|
|
|
self.check_search_ban_timeout(user_id=str(request_context.chat.chat_id))
|
2021-04-13 11:03:44 +02:00
|
|
|
except BannedUserError as e:
|
|
|
|
request_context.error_log(e)
|
2022-09-02 17:44:56 +02:00
|
|
|
async with safe_execution(error_log=request_context.error_log):
|
|
|
|
return await event.reply(t('BANNED_FOR_SECONDS', language).format(
|
|
|
|
seconds=e.ban_timeout,
|
|
|
|
reason=t('BAN_MESSAGE_TOO_MANY_REQUESTS', language),
|
|
|
|
))
|
|
|
|
search_prefix, query = self.parse_pattern(event)
|
|
|
|
|
|
|
|
if request_context.is_group_mode() and not search_prefix:
|
2021-04-09 10:17:38 +02:00
|
|
|
return
|
2022-09-02 17:44:56 +02:00
|
|
|
if request_context.is_personal_mode() and search_prefix:
|
2021-04-09 10:17:38 +02:00
|
|
|
query = event.raw_text
|
2021-04-13 11:03:44 +02:00
|
|
|
|
2021-04-09 10:17:38 +02:00
|
|
|
prefetch_message = await event.reply(
|
2022-09-02 17:44:56 +02:00
|
|
|
t("SEARCHING", language),
|
2021-04-09 10:17:38 +02:00
|
|
|
)
|
2021-04-12 19:47:21 +02:00
|
|
|
self.application.user_manager.last_widget[request_context.chat.chat_id] = prefetch_message.id
|
2021-04-09 10:17:38 +02:00
|
|
|
try:
|
2022-09-02 17:44:56 +02:00
|
|
|
text, buttons = await self.setup_widget(
|
2021-04-13 16:23:31 +02:00
|
|
|
request_context=request_context,
|
|
|
|
prefetch_message=prefetch_message,
|
2021-04-09 10:17:38 +02:00
|
|
|
query=query,
|
|
|
|
is_shortpath_enabled=True,
|
|
|
|
)
|
2022-09-02 17:44:56 +02:00
|
|
|
return await self.application.telegram_client.edit_message(
|
|
|
|
request_context.chat.chat_id,
|
|
|
|
prefetch_message.id,
|
|
|
|
text,
|
|
|
|
buttons=buttons,
|
|
|
|
link_preview=False,
|
|
|
|
)
|
2021-04-09 10:17:38 +02:00
|
|
|
except (AioRpcError, asyncio.CancelledError) as e:
|
|
|
|
await asyncio.gather(
|
|
|
|
event.delete(),
|
|
|
|
prefetch_message.delete(),
|
|
|
|
)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
2022-09-02 17:44:56 +02:00
|
|
|
class InlineSearchHandler(BaseSearchHandler):
|
|
|
|
filter = events.InlineQuery()
|
|
|
|
stop_propagation = False
|
|
|
|
|
|
|
|
async def handler(self, event, request_context: RequestContext):
|
|
|
|
if event.query.peer_type == InlineQueryPeerTypeSameBotPM():
|
|
|
|
await event.answer()
|
|
|
|
return
|
|
|
|
|
|
|
|
builder = event.builder
|
|
|
|
session_id = self.generate_session_id()
|
|
|
|
|
|
|
|
try:
|
|
|
|
if len(event.text) <= 3:
|
|
|
|
await event.answer([])
|
|
|
|
raise events.StopPropagation()
|
|
|
|
inline_search_widget = await InlineSearchWidget.create(
|
|
|
|
application=self.application,
|
|
|
|
chat=request_context.chat,
|
|
|
|
session_id=session_id,
|
|
|
|
request_id=request_context.request_id,
|
|
|
|
query=event.text,
|
|
|
|
is_group_mode=request_context.is_group_mode(),
|
|
|
|
)
|
|
|
|
items = inline_search_widget.render(builder=builder)
|
|
|
|
encoded_query = encode_deep_query(event.text)
|
|
|
|
if len(encoded_query) < 32:
|
|
|
|
await event.answer(
|
|
|
|
items,
|
|
|
|
private=True,
|
|
|
|
switch_pm=self.application.config['telegram']['bot_name'],
|
|
|
|
switch_pm_param=encoded_query,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
await event.answer(items)
|
|
|
|
except AioRpcError as e:
|
|
|
|
if e.code() == StatusCode.INVALID_ARGUMENT or e.code() == StatusCode.CANCELLED:
|
|
|
|
await event.answer([])
|
|
|
|
raise e
|
|
|
|
raise events.StopPropagation()
|
|
|
|
|
|
|
|
|
2021-04-09 10:17:38 +02:00
|
|
|
class SearchEditHandler(BaseSearchHandler):
|
2022-09-02 17:44:56 +02:00
|
|
|
filter = events.MessageEdited(incoming=True, pattern=re.compile(r'^(/search(?:@\w+)\s+)?(.*)', flags=re.DOTALL))
|
2021-04-09 10:17:38 +02:00
|
|
|
is_group_handler = True
|
|
|
|
should_reset_last_widget = False
|
|
|
|
|
2021-04-13 11:03:44 +02:00
|
|
|
def parse_pattern(self, event: events.ChatAction):
|
2021-04-09 10:17:38 +02:00
|
|
|
search_prefix = event.pattern_match.group(1)
|
2022-09-02 17:44:56 +02:00
|
|
|
query = event.pattern_match.group(2).strip()
|
|
|
|
return search_prefix, query
|
2021-04-13 11:03:44 +02:00
|
|
|
|
|
|
|
async def handler(self, event: events.ChatAction, request_context: RequestContext):
|
2022-09-02 17:44:56 +02:00
|
|
|
search_prefix, query = self.parse_pattern(event)
|
2021-04-13 11:03:44 +02:00
|
|
|
request_context.add_default_fields(mode='search_edit')
|
|
|
|
|
2022-09-02 17:44:56 +02:00
|
|
|
if request_context.is_group_mode() and not search_prefix:
|
2021-04-09 10:17:38 +02:00
|
|
|
return
|
2022-09-02 17:44:56 +02:00
|
|
|
if request_context.is_personal_mode() and search_prefix:
|
2021-04-09 10:17:38 +02:00
|
|
|
query = event.raw_text
|
2021-04-13 11:03:44 +02:00
|
|
|
|
2022-09-02 17:44:56 +02:00
|
|
|
for next_message in await self.get_last_messages_in_chat(event):
|
|
|
|
if next_message.is_reply and event.id == next_message.reply_to_msg_id:
|
|
|
|
request_context.statbox(action='resolved')
|
|
|
|
text, buttons = await self.setup_widget(
|
|
|
|
request_context=request_context,
|
|
|
|
prefetch_message=next_message,
|
|
|
|
query=query,
|
|
|
|
)
|
|
|
|
return await self.application.telegram_client.edit_message(
|
|
|
|
request_context.chat.chat_id,
|
|
|
|
next_message.id,
|
|
|
|
text,
|
|
|
|
buttons=buttons,
|
|
|
|
link_preview=False,
|
|
|
|
)
|
|
|
|
return await event.reply(
|
|
|
|
t('REPLY_MESSAGE_HAS_BEEN_DELETED', request_context.chat.language),
|
|
|
|
)
|
2021-04-09 10:17:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
class SearchPagingHandler(BaseCallbackQueryHandler):
|
|
|
|
filter = events.CallbackQuery(pattern='^/search_([A-Za-z0-9]+)_([0-9]+)_([0-9]+)$')
|
|
|
|
should_reset_last_widget = False
|
|
|
|
|
2021-04-13 16:23:31 +02:00
|
|
|
def parse_pattern(self, event: events.ChatAction):
|
2021-04-09 10:17:38 +02:00
|
|
|
session_id = event.pattern_match.group(1).decode()
|
|
|
|
message_id = int(event.pattern_match.group(2).decode())
|
|
|
|
page = int(event.pattern_match.group(3).decode())
|
|
|
|
|
2021-04-13 16:23:31 +02:00
|
|
|
return session_id, message_id, page
|
|
|
|
|
|
|
|
async def handler(self, event: events.ChatAction, request_context: RequestContext):
|
|
|
|
session_id, message_id, page = self.parse_pattern(event)
|
|
|
|
|
2021-04-09 10:17:38 +02:00
|
|
|
request_context.add_default_fields(mode='search_paging', session_id=session_id)
|
2021-04-13 11:03:44 +02:00
|
|
|
start_time = time.time()
|
2021-04-09 10:17:38 +02:00
|
|
|
|
|
|
|
message = await event.get_message()
|
|
|
|
if not message:
|
|
|
|
return await event.answer()
|
|
|
|
|
2021-04-13 11:03:44 +02:00
|
|
|
reply_message = await message.get_reply_message()
|
2022-09-02 17:44:56 +02:00
|
|
|
if not reply_message:
|
|
|
|
return await event.respond(
|
|
|
|
t('REPLY_MESSAGE_HAS_BEEN_DELETED', request_context.chat.language),
|
|
|
|
)
|
|
|
|
|
|
|
|
query = reply_message.raw_text.replace(f'@{self.application.config["telegram"]["bot_name"]}', '').strip()
|
|
|
|
|
2021-04-09 10:17:38 +02:00
|
|
|
try:
|
|
|
|
search_widget = await SearchWidget.create(
|
|
|
|
application=self.application,
|
|
|
|
chat=request_context.chat,
|
|
|
|
session_id=session_id,
|
|
|
|
request_id=request_context.request_id,
|
|
|
|
query=query,
|
|
|
|
page=page,
|
|
|
|
)
|
|
|
|
except AioRpcError as e:
|
|
|
|
if e.code() == StatusCode.INVALID_ARGUMENT or e.code() == StatusCode.CANCELLED:
|
|
|
|
request_context.error_log(e)
|
|
|
|
return await event.answer(
|
2022-09-02 17:44:56 +02:00
|
|
|
t('MAINTENANCE_WO_PIC', request_context.chat.language),
|
2021-04-09 10:17:38 +02:00
|
|
|
)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
request_context.statbox(
|
2022-09-02 17:44:56 +02:00
|
|
|
action='documents_retrieved',
|
2021-04-09 10:17:38 +02:00
|
|
|
duration=time.time() - start_time,
|
2022-09-02 17:44:56 +02:00
|
|
|
query=query,
|
|
|
|
page=page,
|
|
|
|
scored_documents=len(search_widget.scored_documents),
|
2021-04-09 10:17:38 +02:00
|
|
|
)
|
2022-09-02 17:44:56 +02:00
|
|
|
|
|
|
|
serp, buttons = await search_widget.render(message_id=message_id)
|
2021-04-09 10:17:38 +02:00
|
|
|
return await asyncio.gather(
|
|
|
|
event.answer(),
|
|
|
|
message.edit(serp, buttons=buttons, link_preview=False)
|
|
|
|
)
|