make the bot.
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
# Telegram Bot Configuration
|
# Telegram Bot Configuration
|
||||||
# Copy this file to .env and fill in your actual bot token.
|
# Copy this file to .env and fill in your actual bot token.
|
||||||
BOT_TOKEN=123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ
|
BOT_TOKEN=123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ
|
||||||
|
GROUP_CHAT_ID=-100123456789
|
||||||
|
ADMIN_IDS=123456789,987654321
|
||||||
|
|||||||
@@ -174,3 +174,6 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
bot.db
|
||||||
|
|||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.system.id="pyproject.toml" type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="uv (Frankenstein-bot)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/frankenstein-bot.iml" filepath="$PROJECT_DIR$/.idea/frankenstein-bot.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,2 +1,68 @@
|
|||||||
# Anom-Bot
|
# Frankenstein Bot
|
||||||
|
|
||||||
|
A Telegram bot designed for secure message forwarding with captcha verification.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Captcha Verification**: Users must solve an image-based captcha before their messages are forwarded.
|
||||||
|
- **Support for All Media**: Forwarding works for text, photos, videos, documents, audio, and voice messages.
|
||||||
|
- **Message Redirection**: Verified messages are automatically redirected to a configured group chat.
|
||||||
|
- **Anonymized Forwarding**: Messages are forwarded with a custom prefix `Forwarding, ID [MSG_ID]:`, maintaining privacy while allowing for message tracking.
|
||||||
|
- **Locale Support**: Built-in support for multiple languages (defaulting to English).
|
||||||
|
- **Admin Panel**: Authorized admins can access usage statistics and message logs.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. **Clone the repository**:
|
||||||
|
```bash
|
||||||
|
git clone <repository_url>
|
||||||
|
cd Frankenstein-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Install dependencies**:
|
||||||
|
This project uses `uv` for dependency management.
|
||||||
|
```bash
|
||||||
|
uv sync
|
||||||
|
```
|
||||||
|
Or using pip:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure environment variables**:
|
||||||
|
Copy `.env.dist` to `.env` and fill in your details:
|
||||||
|
```bash
|
||||||
|
cp .env.dist .env
|
||||||
|
```
|
||||||
|
Edit `.env`:
|
||||||
|
- `BOT_TOKEN`: Your Telegram Bot token from @BotFather.
|
||||||
|
- `GROUP_CHAT_ID`: The ID of the group where messages should be redirected.
|
||||||
|
- `ADMIN_IDS`: A comma-separated list of Telegram User IDs who can access admin commands.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### For Users
|
||||||
|
1. Start the bot with `/start`.
|
||||||
|
2. Send any message or media.
|
||||||
|
3. Solve the image captcha by typing the characters shown.
|
||||||
|
4. Once solved, your message is redirected to the destination group.
|
||||||
|
|
||||||
|
### For Admins
|
||||||
|
- `/stats`: View total user count.
|
||||||
|
- `/logs`: See a list of the 10 most recent redirected messages.
|
||||||
|
- `/view <ID>`: View detailed information about a specific message by its ID.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- `/start`: Initialize the bot and receive a welcome message.
|
||||||
|
- `/id`: Retrieve the ID of the current chat (useful for configuration).
|
||||||
|
|
||||||
|
## Adding Locales
|
||||||
|
|
||||||
|
The bot supports multiple languages. To add a new locale:
|
||||||
|
|
||||||
|
1. Create a new JSON file in the `locales/` directory named after the language code (e.g., `fr.json` for French).
|
||||||
|
2. Copy the keys from `locales/en.json` and provide the translations for your language.
|
||||||
|
3. The bot will automatically load the new locale on startup.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ class Settings(BaseSettings):
|
|||||||
Application settings loaded from environment variables or a .env file.
|
Application settings loaded from environment variables or a .env file.
|
||||||
"""
|
"""
|
||||||
bot_token: SecretStr
|
bot_token: SecretStr
|
||||||
|
group_chat_id: int
|
||||||
|
admin_ids: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def admins(self) -> list[int]:
|
||||||
|
return [int(x.strip()) for x in self.admin_ids.split(",") if x.strip().isdigit()]
|
||||||
|
|
||||||
# Tell Pydantic to read from a .env file if it exists, ignoring extra fields
|
# Tell Pydantic to read from a .env file if it exists, ignoring extra fields
|
||||||
model_config = SettingsConfigDict(
|
model_config = SettingsConfigDict(
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
from aiogram import Router
|
from aiogram import Router
|
||||||
from . import user, admin
|
from . import user, admin, get_chat_id
|
||||||
|
|
||||||
|
|
||||||
def get_handlers_router() -> Router:
|
def get_handlers_router() -> Router:
|
||||||
"""
|
"""
|
||||||
Combines all feature routers (user, admin, etc.) into one main router.
|
Combines all feature routers (user, admin, etc.) into one main router.
|
||||||
"""
|
"""
|
||||||
router = Router()
|
router = Router()
|
||||||
router.include_router(user.router)
|
|
||||||
router.include_router(admin.router)
|
router.include_router(admin.router)
|
||||||
|
router.include_router(get_chat_id.router)
|
||||||
|
router.include_router(user.router)
|
||||||
return router
|
return router
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
from aiogram import Router
|
|
||||||
from aiogram.filters import Command
|
|
||||||
from aiogram.types import Message
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(Command("admin"))
|
|
||||||
async def cmd_admin(message: Message):
|
|
||||||
"""
|
|
||||||
Handles /admin command (placeholder).
|
|
||||||
In production, you'd restrict this using a custom filter or admin ID list.
|
|
||||||
"""
|
|
||||||
await message.answer("Welcome to the Admin panel. Currently, no admin tasks are configured.")
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from aiogram import Router
|
||||||
|
from aiogram.filters import Command
|
||||||
|
from aiogram.types import Message
|
||||||
|
from bot.utils.i18n import _
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.message(Command("id"))
|
||||||
|
async def cmd_id(message: Message, locale: str):
|
||||||
|
await message.answer(_("id_command", locale=locale, id=message.chat.id))
|
||||||
+92
-48
@@ -1,56 +1,100 @@
|
|||||||
from aiogram import F, Router
|
from aiogram import Router, F, Bot
|
||||||
from aiogram.filters import Command, CommandStart
|
from aiogram.filters import CommandStart
|
||||||
from aiogram.types import CallbackQuery, Message
|
from aiogram.types import Message, BufferedInputFile
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
|
|
||||||
from bot.keyboards.inline import get_links_keyboard
|
from bot.config import settings
|
||||||
from bot.keyboards.reply import get_main_menu_keyboard
|
from bot.utils.captcha import generate_captcha
|
||||||
|
from bot.utils.db import db
|
||||||
|
from bot.utils.i18n import _
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
class CaptchaStates(StatesGroup):
|
||||||
|
waiting_for_captcha = State()
|
||||||
|
|
||||||
@router.message(CommandStart())
|
@router.message(CommandStart())
|
||||||
async def cmd_start(message: Message):
|
async def cmd_start(message: Message, locale: str):
|
||||||
"""
|
db.log_user(message.from_user)
|
||||||
Handles /start command. Greets user and sends the main reply menu.
|
await message.answer(_("welcome", locale=locale))
|
||||||
"""
|
|
||||||
await message.answer(
|
@router.message(CaptchaStates.waiting_for_captcha)
|
||||||
f"Hello, {message.from_user.full_name}! Welcome to Frankenstein Bot.\n"
|
async def process_captcha(message: Message, state: FSMContext, bot: Bot, locale: str):
|
||||||
f"This bot is bootstrapped with a modular and clean structure.",
|
data = await state.get_data()
|
||||||
reply_markup=get_main_menu_keyboard(),
|
correct_captcha = data.get("captcha_text")
|
||||||
|
original_msg_id = data.get("original_msg_id")
|
||||||
|
|
||||||
|
if message.text and message.text.upper() == correct_captcha:
|
||||||
|
await state.clear()
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_msg_id = data.get("db_msg_id", "N/A")
|
||||||
|
prefix = f"Forwarding, ID [{db_msg_id}]:"
|
||||||
|
target_chat_id = settings.group_chat_id
|
||||||
|
|
||||||
|
if data.get('has_media'):
|
||||||
|
new_caption = prefix
|
||||||
|
if data.get('original_caption'):
|
||||||
|
new_caption += f"\n{data.get('original_caption')}"
|
||||||
|
|
||||||
|
await bot.copy_message(
|
||||||
|
chat_id=target_chat_id,
|
||||||
|
from_chat_id=message.chat.id,
|
||||||
|
message_id=original_msg_id,
|
||||||
|
caption=new_caption
|
||||||
|
)
|
||||||
|
elif data.get('is_text'):
|
||||||
|
await bot.send_message(
|
||||||
|
chat_id=target_chat_id,
|
||||||
|
text=f"{prefix}\n{data.get('original_text')}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Other types (stickers, etc.)
|
||||||
|
await bot.copy_message(
|
||||||
|
chat_id=target_chat_id,
|
||||||
|
from_chat_id=message.chat.id,
|
||||||
|
message_id=original_msg_id
|
||||||
|
)
|
||||||
|
await bot.send_message(chat_id=target_chat_id, text=prefix)
|
||||||
|
|
||||||
|
await message.answer(_("captcha_solved", locale=locale))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await message.answer(_("captcha_error", locale=locale))
|
||||||
|
print(f"Error forwarding: {e}")
|
||||||
|
else:
|
||||||
|
# Generate new captcha
|
||||||
|
captcha_text, captcha_img = generate_captcha()
|
||||||
|
await state.update_data(captcha_text=captcha_text)
|
||||||
|
await message.answer_photo(
|
||||||
|
BufferedInputFile(captcha_img, filename="captcha.png"),
|
||||||
|
caption=_("incorrect_captcha", locale=locale)
|
||||||
|
)
|
||||||
|
|
||||||
|
@router.message(F.chat.type == "private")
|
||||||
|
async def handle_any_message(message: Message, state: FSMContext, locale: str):
|
||||||
|
# Log everything
|
||||||
|
db_msg_id = db.log_message(message)
|
||||||
|
|
||||||
|
# Generate captcha
|
||||||
|
captcha_text, captcha_img = generate_captcha()
|
||||||
|
|
||||||
|
has_media = bool(message.photo or message.video or message.document or message.audio or message.voice)
|
||||||
|
is_text = bool(message.text)
|
||||||
|
|
||||||
|
await state.set_state(CaptchaStates.waiting_for_captcha)
|
||||||
|
await state.update_data(
|
||||||
|
captcha_text=captcha_text,
|
||||||
|
db_msg_id=db_msg_id,
|
||||||
|
original_msg_id=message.message_id,
|
||||||
|
original_text=message.text,
|
||||||
|
original_caption=message.caption,
|
||||||
|
has_media=has_media,
|
||||||
|
is_text=is_text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await message.answer_photo(
|
||||||
@router.message(Command("help"))
|
BufferedInputFile(captcha_img, filename="captcha.png"),
|
||||||
@router.message(F.text == "Help ❓")
|
caption=_("solve_captcha", locale=locale)
|
||||||
async def cmd_help(message: Message):
|
|
||||||
"""
|
|
||||||
Handles /help command or 'Help ❓' reply keyboard button click.
|
|
||||||
"""
|
|
||||||
await message.answer(
|
|
||||||
"Here is what I can do:\n"
|
|
||||||
"/start - Start the bot\n"
|
|
||||||
"/help - Get help information\n"
|
|
||||||
"/about - Learn more about the bot",
|
|
||||||
reply_markup=get_links_keyboard(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.message(Command("about"))
|
|
||||||
@router.message(F.text == "About ℹ️")
|
|
||||||
async def cmd_about(message: Message):
|
|
||||||
"""
|
|
||||||
Handles /about command or 'About ℹ️' reply keyboard button click.
|
|
||||||
"""
|
|
||||||
await message.answer(
|
|
||||||
"Frankenstein Bot is a high-performance Python Telegram bot built on aiogram v3, "
|
|
||||||
"designed to work seamlessly with systemd process management."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data == "support_contact")
|
|
||||||
async def process_support_callback(callback: CallbackQuery):
|
|
||||||
"""
|
|
||||||
Processes the inline keyboard callback query 'support_contact'.
|
|
||||||
"""
|
|
||||||
await callback.message.answer("Support: Open an issue or contact our administrator.")
|
|
||||||
await callback.answer()
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
|
||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
|
||||||
|
|
||||||
|
|
||||||
def get_links_keyboard() -> InlineKeyboardMarkup:
|
|
||||||
"""
|
|
||||||
Creates an inline keyboard markup with external links and callback query button.
|
|
||||||
"""
|
|
||||||
builder = InlineKeyboardBuilder()
|
|
||||||
builder.add(InlineKeyboardButton(text="Google", url="https://google.com"))
|
|
||||||
builder.add(InlineKeyboardButton(text="Contact Support 💬", callback_data="support_contact"))
|
|
||||||
builder.adjust(1)
|
|
||||||
return builder.as_markup()
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from aiogram.types import KeyboardButton, ReplyKeyboardMarkup
|
|
||||||
from aiogram.utils.keyboard import ReplyKeyboardBuilder
|
|
||||||
|
|
||||||
|
|
||||||
def get_main_menu_keyboard() -> ReplyKeyboardMarkup:
|
|
||||||
"""
|
|
||||||
Creates the main menu reply keyboard markup.
|
|
||||||
"""
|
|
||||||
builder = ReplyKeyboardBuilder()
|
|
||||||
builder.add(KeyboardButton(text="Help ❓"))
|
|
||||||
builder.add(KeyboardButton(text="About ℹ️"))
|
|
||||||
builder.adjust(2)
|
|
||||||
return builder.as_markup(resize_keyboard=True)
|
|
||||||
@@ -24,3 +24,19 @@ class LoggingMiddleware(BaseMiddleware):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error handling event %s: %s", event.__class__.__name__, e, exc_info=True)
|
logger.error("Error handling event %s: %s", event.__class__.__name__, e, exc_info=True)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
class I18nMiddleware(BaseMiddleware):
|
||||||
|
async def __call__(
|
||||||
|
self,
|
||||||
|
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
||||||
|
event: TelegramObject,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
) -> Any:
|
||||||
|
from_user = data.get("event_from_user")
|
||||||
|
if from_user and from_user.language_code:
|
||||||
|
data["locale"] = from_user.language_code
|
||||||
|
else:
|
||||||
|
data["locale"] = "en"
|
||||||
|
|
||||||
|
return await handler(event, data)
|
||||||
|
|||||||
+1
-4
@@ -7,9 +7,6 @@ async def set_bot_commands(bot: Bot):
|
|||||||
Sets default commands in the Telegram client's command menu.
|
Sets default commands in the Telegram client's command menu.
|
||||||
"""
|
"""
|
||||||
commands = [
|
commands = [
|
||||||
BotCommand(command="start", description="Start Frankenstein Bot"),
|
BotCommand(command="start", description="Start the bot"),
|
||||||
BotCommand(command="help", description="Show help information"),
|
|
||||||
BotCommand(command="about", description="About the bot"),
|
|
||||||
BotCommand(command="admin", description="Admin panel placeholder"),
|
|
||||||
]
|
]
|
||||||
await bot.set_my_commands(commands, scope=BotCommandScopeDefault())
|
await bot.set_my_commands(commands, scope=BotCommandScopeDefault())
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
|
from io import BytesIO
|
||||||
|
from captcha.image import ImageCaptcha
|
||||||
|
|
||||||
|
def generate_captcha():
|
||||||
|
"""Generates a random captcha and returns (text, image_bytes)"""
|
||||||
|
captcha_text = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))
|
||||||
|
image = ImageCaptcha(width=280, height=90)
|
||||||
|
data = image.generate(captcha_text)
|
||||||
|
return captcha_text, data.getvalue()
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
class I18n:
|
||||||
|
def __init__(self, default_locale="en"):
|
||||||
|
self.default_locale = default_locale
|
||||||
|
self.translations = {}
|
||||||
|
self.load_translations()
|
||||||
|
|
||||||
|
def load_translations(self):
|
||||||
|
locales_dir = "locales"
|
||||||
|
if not os.path.exists(locales_dir):
|
||||||
|
return
|
||||||
|
|
||||||
|
for filename in os.listdir(locales_dir):
|
||||||
|
if filename.endswith(".json"):
|
||||||
|
locale = filename[:-5]
|
||||||
|
with open(os.path.join(locales_dir, filename), "r", encoding="utf-8") as f:
|
||||||
|
self.translations[locale] = json.load(f)
|
||||||
|
|
||||||
|
def get(self, key, locale=None, **kwargs):
|
||||||
|
if locale is None:
|
||||||
|
locale = self.default_locale
|
||||||
|
text = self.translations.get(locale, {}).get(key, self.translations.get(self.default_locale, {}).get(key, key))
|
||||||
|
try:
|
||||||
|
return text.format(**kwargs)
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
return text
|
||||||
|
|
||||||
|
i18n = I18n()
|
||||||
|
_ = i18n.get
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Welcome! Send me any message (text, image, video, etc.) and I will redirect it after you solve a simple captcha.",
|
||||||
|
"solve_captcha": "Please type the letters/numbers you see on the image to verify you're human.",
|
||||||
|
"redirecting": "Redirecting your message...",
|
||||||
|
"captcha_solved": "Captcha solved! Your message has been redirected.",
|
||||||
|
"captcha_error": "Captcha solved, but there was an error redirecting your message.",
|
||||||
|
"incorrect_captcha": "Incorrect captcha. Please try again with this new image:",
|
||||||
|
"stats_title": "Bot Stats:",
|
||||||
|
"total_users": "Total Users: {count}",
|
||||||
|
"no_logs": "No logs found.",
|
||||||
|
"recent_messages": "Recent messages:",
|
||||||
|
"msg_not_found": "Message with ID {id} not found.",
|
||||||
|
"msg_details": "Message ID: {id}\nUser: {full_name} (@{username}) [ID: {user_id}]\nType: {content_type}\nTimestamp: {timestamp}\n\nContent:\n{text}",
|
||||||
|
"id_command": "Current Chat ID: <code>{id}</code>"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Вітаю! Надішліть мені будь-яке повідомлення (текст, зображення, відео тощо), і я перенаправлю його після того, як ви пройдете просту капчу.",
|
||||||
|
"solve_captcha": "Будь ласка, введіть літери/цифри, які ви бачите на зображенні, щоб підтвердити, що ви людина.",
|
||||||
|
"redirecting": "Перенаправляю ваше повідомлення...",
|
||||||
|
"captcha_solved": "Капчу пройдено! Ваше повідомлення було перенаправлено.",
|
||||||
|
"captcha_error": "Капчу пройдено, але сталася помилка під час перенаправлення вашого повідомлення.",
|
||||||
|
"incorrect_captcha": "Неправильна капча. Будь ласка, спробуйте ще раз із цим новим зображенням:",
|
||||||
|
"stats_title": "Статистика бота:",
|
||||||
|
"total_users": "Усього користувачів: {count}",
|
||||||
|
"no_logs": "Логів не знайдено.",
|
||||||
|
"recent_messages": "Останні повідомлення:",
|
||||||
|
"msg_not_found": "Повідомлення з ID {id} не знайдено.",
|
||||||
|
"msg_details": "ID повідомлення: {id}\nКористувач: {full_name} (@{username}) [ID: {user_id}]\nТип: {content_type}\nЧас: {timestamp}\n\nВміст:\n{text}",
|
||||||
|
"id_command": "Поточний ID чату: <code>{id}</code>"
|
||||||
|
}
|
||||||
@@ -8,7 +8,8 @@ from aiogram.enums import ParseMode
|
|||||||
|
|
||||||
from bot.config import settings
|
from bot.config import settings
|
||||||
from bot.handlers import get_handlers_router
|
from bot.handlers import get_handlers_router
|
||||||
from bot.middlewares import LoggingMiddleware
|
from bot.middlewares import LoggingMiddleware, I18nMiddleware
|
||||||
|
from bot.utils.db import db
|
||||||
from bot.ui_commands import set_bot_commands
|
from bot.ui_commands import set_bot_commands
|
||||||
|
|
||||||
# Setup logging
|
# Setup logging
|
||||||
@@ -36,6 +37,7 @@ async def main():
|
|||||||
|
|
||||||
# Register middlewares (e.g. LoggingMiddleware)
|
# Register middlewares (e.g. LoggingMiddleware)
|
||||||
dp.update.outer_middleware(LoggingMiddleware())
|
dp.update.outer_middleware(LoggingMiddleware())
|
||||||
|
dp.update.outer_middleware(I18nMiddleware())
|
||||||
|
|
||||||
# Register routers (handlers)
|
# Register routers (handlers)
|
||||||
dp.include_router(get_handlers_router())
|
dp.include_router(get_handlers_router())
|
||||||
|
|||||||
Reference in New Issue
Block a user