mirror of
https://github.com/artemium428/428th-exchange-bot.git
synced 2026-09-15 16:56:20 +00:00
200 lines
6.1 KiB
Python
200 lines
6.1 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from aiogram import F, Router
|
|
from aiogram.enums import ChatType, ParseMode
|
|
from aiogram.filters import Command, CommandStart
|
|
from aiogram.types import CallbackQuery, Message, MessageReactionUpdated
|
|
|
|
from bot.db.repository import ClientRepository
|
|
from bot.i18n import (
|
|
format_bot_html,
|
|
format_bot_message,
|
|
language_keyboard,
|
|
normalize_language,
|
|
services_keyboard,
|
|
t,
|
|
)
|
|
from bot.services.cooldown import CooldownService
|
|
from bot.services.relay import RelayService, is_relayable_content
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = Router(name="client")
|
|
|
|
|
|
async def _answer_bot(message: Message, language: str | None, key: str) -> None:
|
|
await message.answer(
|
|
format_bot_message(language, t(language, key)),
|
|
parse_mode=ParseMode.HTML,
|
|
)
|
|
|
|
|
|
async def _answer_welcome(message: Message, language: str) -> None:
|
|
await message.answer(
|
|
format_bot_message(language, t(language, "welcome")),
|
|
reply_markup=services_keyboard(language),
|
|
parse_mode=ParseMode.HTML,
|
|
)
|
|
|
|
|
|
async def _prompt_language(message: Message) -> None:
|
|
await message.answer(
|
|
t(None, "choose_language"),
|
|
reply_markup=language_keyboard(),
|
|
parse_mode=None,
|
|
)
|
|
|
|
|
|
@router.message(CommandStart(), F.chat.type == ChatType.PRIVATE)
|
|
async def cmd_start(message: Message, relay: RelayService) -> None:
|
|
if message.from_user is None:
|
|
return
|
|
client = await relay.ensure_client(message.from_user)
|
|
if client.language is None:
|
|
await _prompt_language(message)
|
|
return
|
|
await _answer_welcome(message, client.language)
|
|
|
|
|
|
@router.message(Command("lang"), F.chat.type == ChatType.PRIVATE)
|
|
async def cmd_lang(message: Message, relay: RelayService) -> None:
|
|
if message.from_user is None:
|
|
return
|
|
await relay.ensure_client(message.from_user)
|
|
await _prompt_language(message)
|
|
|
|
|
|
@router.message(Command("del"), F.chat.type == ChatType.PRIVATE)
|
|
async def cmd_del(message: Message, relay: RelayService) -> None:
|
|
"""Reply to a relayed message with /del to remove it on both sides."""
|
|
if message.from_user is None:
|
|
return
|
|
client = await relay.ensure_client(message.from_user)
|
|
if message.reply_to_message is None:
|
|
return
|
|
try:
|
|
deleted = await relay.delete_paired_from_client(message.bot, message)
|
|
except Exception:
|
|
logger.exception("Failed to delete paired message for client %s", message.from_user.id)
|
|
return
|
|
if deleted:
|
|
try:
|
|
await message.delete()
|
|
except Exception:
|
|
pass
|
|
await _answer_bot(message, client.language, "deleted_notice")
|
|
|
|
|
|
@router.callback_query(F.data.startswith("lang:"))
|
|
async def on_language_chosen(
|
|
callback: CallbackQuery,
|
|
relay: RelayService,
|
|
repo: ClientRepository,
|
|
) -> None:
|
|
if callback.from_user is None or callback.data is None:
|
|
return
|
|
if callback.message is None or callback.message.chat.type != ChatType.PRIVATE:
|
|
await callback.answer()
|
|
return
|
|
|
|
language = normalize_language(callback.data.split(":", 1)[1])
|
|
if language is None:
|
|
await callback.answer()
|
|
return
|
|
|
|
await relay.ensure_client(callback.from_user)
|
|
await repo.set_language(callback.from_user.id, language)
|
|
await callback.answer()
|
|
|
|
body = f"{t(language, 'language_set')}\n\n{t(language, 'welcome')}"
|
|
await callback.message.edit_text(
|
|
format_bot_message(language, body),
|
|
reply_markup=services_keyboard(language),
|
|
parse_mode=ParseMode.HTML,
|
|
)
|
|
|
|
|
|
@router.callback_query(F.data == "services")
|
|
async def on_services(
|
|
callback: CallbackQuery,
|
|
relay: RelayService,
|
|
cooldown: CooldownService,
|
|
) -> None:
|
|
if callback.from_user is None or callback.message is None:
|
|
return
|
|
if callback.message.chat.type != ChatType.PRIVATE:
|
|
await callback.answer()
|
|
return
|
|
|
|
client = await relay.ensure_client(callback.from_user)
|
|
if client.language is None:
|
|
await callback.answer()
|
|
await _prompt_language(callback.message)
|
|
return
|
|
|
|
if not cooldown.try_take_services(callback.from_user.id):
|
|
await callback.answer()
|
|
return
|
|
|
|
await callback.answer()
|
|
await callback.message.answer(
|
|
format_bot_html(client.language, t(client.language, "services")),
|
|
parse_mode=ParseMode.HTML,
|
|
)
|
|
|
|
|
|
@router.message_reaction(F.chat.type == ChatType.PRIVATE)
|
|
async def client_reaction(event: MessageReactionUpdated, relay: RelayService) -> None:
|
|
try:
|
|
await relay.relay_reaction(event.bot, event)
|
|
except Exception:
|
|
logger.exception(
|
|
"Failed to relay client reaction on message %s",
|
|
event.message_id,
|
|
)
|
|
|
|
|
|
@router.edited_message(F.chat.type == ChatType.PRIVATE)
|
|
async def client_edited(message: Message, relay: RelayService) -> None:
|
|
if message.from_user is None or not is_relayable_content(message):
|
|
return
|
|
try:
|
|
await relay.relay_client_edit(message.bot, message)
|
|
except Exception:
|
|
logger.exception(
|
|
"Failed to relay client edit from %s",
|
|
message.from_user.id,
|
|
)
|
|
|
|
|
|
@router.message(F.chat.type == ChatType.PRIVATE)
|
|
async def client_message(
|
|
message: Message,
|
|
relay: RelayService,
|
|
cooldown: CooldownService,
|
|
) -> None:
|
|
if message.from_user is None or not is_relayable_content(message):
|
|
return
|
|
|
|
client = await relay.ensure_client(message.from_user)
|
|
if client.language is None:
|
|
await _prompt_language(message)
|
|
return
|
|
|
|
decision = cooldown.check(message.from_user.id, message.media_group_id)
|
|
if not decision.allowed:
|
|
await _answer_bot(message, client.language, "cooldown")
|
|
return
|
|
|
|
try:
|
|
await relay.relay_client_to_manager(message.bot, message)
|
|
except Exception:
|
|
logger.exception("Failed to relay client message from %s", message.from_user.id)
|
|
await _answer_bot(message, client.language, "send_failed")
|
|
return
|
|
|
|
cooldown.commit(message.from_user.id, message.media_group_id)
|
|
if decision.should_ack:
|
|
await _answer_bot(message, client.language, "ack")
|