diff --git a/src/trudex/application/bot/admin_dialogs/tests.py b/src/trudex/application/bot/admin_dialogs/tests.py
index 0bf85e0..043e3c9 100644
--- a/src/trudex/application/bot/admin_dialogs/tests.py
+++ b/src/trudex/application/bot/admin_dialogs/tests.py
@@ -14,6 +14,7 @@ from dishka.integrations.aiogram_dialog import inject
from trudex.application.bot.admin_dialogs.states import (AdminMenuSG,
AdminTestsSG)
+from trudex.application.bot.creator_dialogs.states import CreateTestSG
from trudex.infrastructure.database.dao.group import GroupDAO
from trudex.infrastructure.database.dao.test import TestDAO
from trudex.infrastructure.database.repo.test import TestRepository
@@ -393,8 +394,8 @@ async def on_remove_expires(_callback: CallbackQuery, _button: Button, manager:
await manager.switch_to(AdminTestsSG.test_detail)
-async def on_add_test_clicked(_callback: CallbackQuery, _button: Button, _manager: DialogManager):
- await _callback.answer("Добавление теста")
+async def on_add_test_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager):
+ await manager.start(CreateTestSG.input_title, mode=StartMode.RESET_STACK)
async def on_back_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager):
diff --git a/src/trudex/application/bot/user_dialogs/main_menu.py b/src/trudex/application/bot/user_dialogs/main_menu.py
index b5b09bc..f37af0b 100644
--- a/src/trudex/application/bot/user_dialogs/main_menu.py
+++ b/src/trudex/application/bot/user_dialogs/main_menu.py
@@ -1,6 +1,9 @@
+import asyncio
+import functools
from datetime import datetime, timedelta, timezone
-from aiogram.types import CallbackQuery, Message
+from aiogram import Bot
+from aiogram.types import BufferedInputFile, CallbackQuery, Message
from aiogram_dialog import Dialog, DialogManager, Window
from aiogram_dialog.widgets.input import MessageInput
from aiogram_dialog.widgets.kbd import Button, Column, Row, ScrollingGroup, Select
@@ -14,6 +17,9 @@ from trudex.infrastructure.database.dao.group import GroupDAO
from trudex.infrastructure.database.dao.user import UserDAO
from trudex.infrastructure.database.repo.test import TestRepository
from trudex.infrastructure.database.repo.test_attempt import TestAttemptRepository
+from trudex.infrastructure.utils.config import Config
+from trudex.infrastructure.utils.qr_generator import generate_qr_bytes
+from trudex.infrastructure.utils.test_id_to_hash import encode_id
@inject
@@ -189,6 +195,44 @@ async def on_back_to_tests(_callback: CallbackQuery, _button: Button, manager: D
await manager.switch_to(UserMenuSG.available_tests)
+@inject
+async def on_share_test(
+ _callback: CallbackQuery,
+ _button: Button,
+ manager: DialogManager,
+ config: FromDishka[Config],
+ bot_inst: FromDishka[Bot],
+):
+ test_id = manager.dialog_data.get("selected_test_id")
+
+ if not test_id:
+ await _callback.answer("Ошибка: тест не найден")
+ return
+
+ test_hash = encode_id(
+ test_id,
+ config.security.encode_key,
+ config.security.encoded_string_length,
+ )
+
+ bot_info = await bot_inst.get_me()
+ bot_username = bot_info.username or "your_bot"
+ share_link = f"https://t.me/{bot_username}?start={test_hash}"
+
+ loop = asyncio.get_running_loop()
+ qr_bytes = await loop.run_in_executor(
+ None,
+ functools.partial(generate_qr_bytes, share_link),
+ )
+
+ assert _callback.message is not None
+
+ await _callback.message.answer_photo(
+ photo=BufferedInputFile(qr_bytes, filename="qr.png"),
+ caption=f"🔗 Поделиться тестом\n\n📎 Ссылка на тест:\n{share_link}\n\n💡 Отправьте эту ссылку или QR-код пользователям для прохождения теста",
+ )
+
+
@inject
async def get_test_detail(
dialog_manager: DialogManager,
@@ -349,6 +393,7 @@ user_menu_dialog = Dialog(
Format("{test_info}"),
Column(
Button(Const("▶️ Пройти тест"), id="start_test", on_click=on_start_test),
+ Button(Const("🔗 Поделиться"), id="share", on_click=on_share_test),
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_tests),
),
state=UserMenuSG.test_detail,