типо да
This commit is contained in:
13
bot/handlers/commands/users/__init__.py
Normal file
13
bot/handlers/commands/users/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from aiogram import Router
|
||||
from .start_cmd import router as start_cmd_router
|
||||
from .active import router as active_cmd_router
|
||||
|
||||
# Настройка экспорта и роутера
|
||||
__all__ = ("router",)
|
||||
router: Router = Router(name=__name__)
|
||||
|
||||
# Подключение роутеров
|
||||
router.include_routers(
|
||||
start_cmd_router,
|
||||
active_cmd_router,
|
||||
)
|
||||
45
bot/handlers/commands/users/active.py
Normal file
45
bot/handlers/commands/users/active.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
|
||||
from bot.templates import msg_photo
|
||||
from bot.core.bots import BotInfo
|
||||
from configs import COMMANDS
|
||||
from database import db
|
||||
|
||||
|
||||
|
||||
# Настройки экспорта и роутера
|
||||
__all__ = ("router",)
|
||||
|
||||
|
||||
CMD: str = "active".lower()
|
||||
router: Router = Router(name=f"{CMD}_cmd_router")
|
||||
|
||||
|
||||
@router.callback_query(F.data.lower() == CMD)
|
||||
@router.message(Command(*COMMANDS[CMD], prefix=BotInfo.prefix, ignore_case=True))
|
||||
async def active_cmd(message: Message | CallbackQuery, state: FSMContext) -> None:
|
||||
"""Обработчик команды /active"""
|
||||
await state.clear()
|
||||
|
||||
# Получить статистику сообщений пользователя
|
||||
day, week, month, total = await db.get_message_stats(message.from_user.id)
|
||||
|
||||
print(f"За день: {day} сообщений")
|
||||
print(f"За неделю: {week} сообщений")
|
||||
print(f"За месяц: {month} сообщений")
|
||||
print(f"Всего: {total} сообщений")
|
||||
|
||||
# Формируем приветственное сообщение
|
||||
text: str =f"""
|
||||
За день: {day} сообщений
|
||||
За неделю: {week} сообщений
|
||||
За месяц: {month} сообщений
|
||||
Всего: {total} сообщений
|
||||
"""
|
||||
|
||||
|
||||
# Отправляем сообщение
|
||||
await msg_photo(message=message, text=text,)
|
||||
0
bot/handlers/commands/users/anketa_cmd.py
Normal file
0
bot/handlers/commands/users/anketa_cmd.py
Normal file
218
bot/handlers/commands/users/new_cmd.py
Normal file
218
bot/handlers/commands/users/new_cmd.py
Normal file
@@ -0,0 +1,218 @@
|
||||
import re
|
||||
from typing import Optional, Dict, Tuple
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiogram.utils.i18n import gettext as _
|
||||
|
||||
from bot.core.bots import BotInfo
|
||||
from bot.keyboards.inline.decision import decision_keyboard
|
||||
from bot.states.new_states import NewStates
|
||||
from bot.templates import msg
|
||||
from middleware.loggers import log
|
||||
from configs import COMMANDS, ImportantID, RpValue
|
||||
|
||||
# Глобальная мапа для хранения связей пользователь-топик
|
||||
user_topic_map: Dict[Tuple[int, str], int] = {}
|
||||
|
||||
__all__ = ("router",)
|
||||
CMD: str = "new"
|
||||
router: Router = Router(name=f"{CMD}_cmd_router")
|
||||
TOPIC_TYPE: str = "anketa"
|
||||
|
||||
TEXTS: Dict[str, Dict[str, str]] = {
|
||||
"anketa": {
|
||||
"accept": f"<b>🎉 Ваша анкета принята!</b>\n\nДобро пожаловать в проект!\n\nФлуд: {RpValue.FLUD_URL}\nРолевая: {RpValue.RP_URL}",
|
||||
"reject": "<b>❌ Ваша анкета отклонена.</b>\n\nВы можете попробовать позже."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def validate_russian_text(text: str) -> bool:
|
||||
"""Проверяет текст на соответствие русским буквам, пробелам и дефисам."""
|
||||
return bool(re.fullmatch(r"[А-Яа-яЁё\s\-]+", text))
|
||||
|
||||
|
||||
# ===================== Команда /new =====================
|
||||
@router.callback_query(F.data == CMD)
|
||||
@router.message(Command(*COMMANDS[CMD], prefix=BotInfo.prefix, ignore_case=True))
|
||||
@log(level='INFO', log_type=CMD.upper(), text=f"использовал команду /{CMD}")
|
||||
async def new_cmd(message: Message | CallbackQuery, state: FSMContext) -> None:
|
||||
"""
|
||||
Начало анкеты /new.
|
||||
Отправляет пользователю сообщение с просьбой указать желаемую роль.
|
||||
"""
|
||||
await state.clear()
|
||||
await state.set_state(NewStates.role)
|
||||
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(InlineKeyboardButton(text="Отмена↩️", callback_data='start'))
|
||||
|
||||
text: str = _(
|
||||
"Пожалуйста, отправьте желаемую роль:\n"
|
||||
"(только русские буквы, пробелы или дефисы)"
|
||||
)
|
||||
|
||||
await msg(message=message, text=text, markup=ikb)
|
||||
|
||||
|
||||
# ===================== Обработка роли =====================
|
||||
@router.message(NewStates.role)
|
||||
async def process_role(message: Message, state: FSMContext) -> None:
|
||||
"""Обрабатывает ввод роли и запрашивает сортол."""
|
||||
if not await validate_russian_text(message.text):
|
||||
await message.reply("Ошибка: роль должна содержать только русские буквы, пробелы или дефисы.")
|
||||
return
|
||||
|
||||
await state.update_data(role=message.text.strip().title())
|
||||
await state.set_state(NewStates.sorol)
|
||||
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(InlineKeyboardButton(text="Отмена↩️", callback_data='start'))
|
||||
|
||||
await message.reply(
|
||||
text="Теперь укажите желаемый сортол:\n(только русские буквы, пробелы или дефисы)",
|
||||
reply_markup=ikb.as_markup()
|
||||
)
|
||||
|
||||
|
||||
# ===================== Обработка сортола =====================
|
||||
@router.message(NewStates.sorol)
|
||||
async def process_sortol(message: Message, state: FSMContext) -> None:
|
||||
"""Обрабатывает ввод сортола и запрашивает кодовую фразу."""
|
||||
if not await validate_russian_text(message.text):
|
||||
await message.reply("Ошибка: сорол должен содержать только русские буквы, пробелы или дефисы.")
|
||||
return
|
||||
|
||||
await state.update_data(sortol=message.text.strip().title())
|
||||
await state.set_state(NewStates.code_phrase)
|
||||
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(InlineKeyboardButton(text="Отмена↩️", callback_data='start'))
|
||||
|
||||
await message.reply(
|
||||
text="Теперь введите кодовую фразу из правил:",
|
||||
reply_markup=ikb.as_markup()
|
||||
)
|
||||
|
||||
|
||||
# ===================== Обработка кодовой фразы =====================
|
||||
@router.message(NewStates.code_phrase)
|
||||
async def process_code_phrase(message: Message, state: FSMContext) -> None:
|
||||
"""Обрабатывает ввод кодовой фразы и показывает предпросмотр анкеты."""
|
||||
code_phrase = message.text.strip()
|
||||
if not code_phrase:
|
||||
await message.reply("Кодовая фраза не может быть пустой.")
|
||||
return
|
||||
|
||||
await state.update_data(code_phrase=code_phrase)
|
||||
data: Dict[str, str] = await state.get_data()
|
||||
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(
|
||||
InlineKeyboardButton(text="Отправить!", callback_data="submit_new"),
|
||||
InlineKeyboardButton(text="Отмена↩️", callback_data="start")
|
||||
)
|
||||
|
||||
text: str = (
|
||||
f"<b>Проверьте данные анкеты:</b>\n\n"
|
||||
f"• Роль: {data['role']}\n"
|
||||
f"• Сортол: {data['sortol']}\n"
|
||||
f"• Кодовая фраза: {data['code_phrase']}"
|
||||
)
|
||||
|
||||
await message.reply(text, reply_markup=ikb.as_markup())
|
||||
|
||||
|
||||
# ===================== Отправка анкеты в поддержку =====================
|
||||
@router.callback_query(F.data == "submit_new")
|
||||
async def submit_new_cmd(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
"""Отправляет анкету в топик форума поддержки и создает запись в мапе."""
|
||||
data: Dict[str, str] = await state.get_data()
|
||||
user = callback.from_user
|
||||
|
||||
# Создаем топик в форуме
|
||||
topic = await callback.bot.create_forum_topic(
|
||||
chat_id=ImportantID.SUPPORT_CHAT_ID,
|
||||
name=f"Анкета от {user.full_name}"
|
||||
)
|
||||
thread_id: int = topic.message_thread_id
|
||||
|
||||
# Сохраняем связь пользователь-топик
|
||||
user_topic_map[(user.id, TOPIC_TYPE)] = thread_id
|
||||
|
||||
# Формируем текст анкеты
|
||||
text: str = (
|
||||
f'<b><a href="tg://user?id={user.id}">Анкета</a></b>\n\n'
|
||||
f"• Роль: {data['role']}\n"
|
||||
f"• Сортол: {data['sortol']}\n"
|
||||
f"• Кодовая фраза: {data['code_phrase']}"
|
||||
)
|
||||
|
||||
# Отправляем в топик с кнопками принятия/отклонения
|
||||
await callback.bot.send_message(
|
||||
chat_id=ImportantID.SUPPORT_CHAT_ID,
|
||||
message_thread_id=thread_id,
|
||||
text=text,
|
||||
parse_mode="HTML",
|
||||
reply_markup=decision_keyboard(thread_id=thread_id, kind=TOPIC_TYPE)
|
||||
)
|
||||
|
||||
await callback.message.edit_text("✅ Ваша анкета успешно отправлена на рассмотрение!")
|
||||
await state.clear()
|
||||
|
||||
|
||||
# ===================== Обработка решения админов =====================
|
||||
@router.callback_query(F.data.regexp(r"^([a-z_]+):(accept|reject):(\d+)$"))
|
||||
async def process_decision_callback(callback: CallbackQuery) -> None:
|
||||
"""Обрабатывает решение администраторов и отправляет результат пользователю."""
|
||||
kind, action, thread_id_str = callback.data.split(":")
|
||||
thread_id = int(thread_id_str)
|
||||
|
||||
# Ищем пользователя по thread_id в мапе
|
||||
user_id = None
|
||||
for (uid, k), tid in user_topic_map.items():
|
||||
if k == kind and tid == thread_id:
|
||||
user_id = uid
|
||||
break
|
||||
|
||||
if not user_id:
|
||||
await callback.answer("Пользователь не найден.", show_alert=True)
|
||||
return
|
||||
|
||||
text_to_send: Optional[str] = TEXTS.get(kind, {}).get(action)
|
||||
if not text_to_send:
|
||||
await callback.answer("Некорректные данные.", show_alert=True)
|
||||
return
|
||||
|
||||
await callback.bot.send_message(chat_id=user_id, text=text_to_send, parse_mode="HTML")
|
||||
await callback.message.edit_reply_markup(reply_markup=None)
|
||||
await callback.answer("Ответ отправлен пользователю.")
|
||||
|
||||
|
||||
# ===================== Пересылка ответов админов пользователю =====================
|
||||
@router.message(F.is_topic_message, F.reply_to_message, ~F.from_user.is_bot)
|
||||
async def forward_reply_to_user(message: Message) -> None:
|
||||
"""Пересылает ответы администраторов из топика пользователю."""
|
||||
thread_id = message.message_thread_id
|
||||
if not thread_id:
|
||||
return
|
||||
|
||||
# Ищем пользователя по thread_id
|
||||
user_id = None
|
||||
for (uid, _), tid in user_topic_map.items():
|
||||
if tid == thread_id:
|
||||
user_id = uid
|
||||
break
|
||||
|
||||
if not user_id:
|
||||
return
|
||||
|
||||
reply_text: str = f"<b>Ответ администратора:</b>\n{message.html_text}"
|
||||
try:
|
||||
await message.bot.send_message(chat_id=user_id, text=reply_text, parse_mode="HTML")
|
||||
except Exception as e:
|
||||
await message.reply(f"⚠️ Не удалось отправить сообщение пользователю: {e}")
|
||||
51
bot/handlers/commands/users/start_cmd.py
Normal file
51
bot/handlers/commands/users/start_cmd.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiogram.utils.i18n import gettext as _
|
||||
|
||||
from bot.templates import msg_photo
|
||||
from bot.utils.interesting_facts import interesting_fact
|
||||
from bot.core.bots import BotInfo
|
||||
from configs import COMMANDS, RpValue
|
||||
|
||||
# Настройки экспорта и роутера
|
||||
__all__ = ("router",)
|
||||
CMD: str = "start".lower()
|
||||
router: Router = Router(name=f"{CMD}_cmd_router")
|
||||
|
||||
|
||||
@router.callback_query(F.data.lower() == CMD)
|
||||
@router.message(Command(*COMMANDS[CMD], prefix=BotInfo.prefix, ignore_case=True))
|
||||
async def start_cmd(message: Message | CallbackQuery, state: FSMContext) -> None:
|
||||
"""Обработчик команды /start"""
|
||||
await state.clear()
|
||||
|
||||
# Создание инлайн-клавиатуры
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(InlineKeyboardButton(text="Инфо-канал🗂", url=RpValue.INFO_URL))
|
||||
ikb.row(InlineKeyboardButton(text="Вступление🚀", callback_data='new'),
|
||||
InlineKeyboardButton(text="Анкета📖", callback_data='anketa'))
|
||||
ikb.row(InlineKeyboardButton(text="Связь с администрацией🌐", callback_data='admin'))
|
||||
|
||||
# Формируем приветственное сообщение
|
||||
text: str = _(
|
||||
"""Добро пожаловать, <a href="{url}">{name}</a>!
|
||||
|
||||
Я ваш искусственный помощник по ролевой - <b>{rp_name}</b>!
|
||||
Моя цель — помочь вам сориентироваться и сделать ваше вступление куда проще!
|
||||
Надеюсь, я смогу вам помочь! Пожалуйста, выберите нужную функцию на клавиатуре!
|
||||
|
||||
Интересный факт:
|
||||
<blockquote>{fact}</blockquote>
|
||||
"""
|
||||
).format(
|
||||
url=message.from_user.url if message.from_user else "",
|
||||
name=message.from_user.first_name if message.from_user else "пользователь",
|
||||
rp_name=RpValue.RP_NAME,
|
||||
fact=interesting_fact(),
|
||||
)
|
||||
|
||||
# Отправляем сообщение
|
||||
await msg_photo(message=message, text=text, file=f'assets/{CMD}.jpg', markup=ikb)
|
||||
Reference in New Issue
Block a user