This commit is contained in:
xds
2026-02-02 16:15:17 +03:00
commit e6aad48e72
21 changed files with 631 additions and 0 deletions

0
routers/__init__.py Normal file
View File

83
routers/auth_router.py Normal file
View File

@@ -0,0 +1,83 @@
from aiogram import Router, F, Bot
from aiogram.types import CallbackQuery, Message
from repos.user_repo import UsersRepo, UserStatus
from keyboards import get_admin_decision_kb
router = Router()
# Чтобы IDE не ругалась, переменные инициализируются в main,
# но здесь мы ожидаем, что они будут переданы или доступны через DI (workflow_data)
# В этом примере я буду доставать их из data, переданной диспетчером, или глобально (для простоты примера - глобально в рамках архитектуры aiogram лучше через middleware DI).
# Для простоты доступа предположим, что repo и admin_id прокинуты.
@router.callback_query(F.data == "req_access")
async def user_request_access(callback: CallbackQuery, repo: UsersRepo, bot: Bot, admin_id: int):
"""Пользователь нажал 'Запросить доступ'"""
user = callback.from_user
# Двойная проверка на 24 часа (на случай если пользователь не обновлял сообщение)
if not await repo.can_request_access(user.id):
await callback.answer("⏳ Вы уже отправляли запрос недавно. Повторите через сутки.", show_alert=True)
return
# 1. Записываем в БД статус PENDING
await repo.create_or_update_request(user)
# 2. Уведомляем пользователя
await callback.message.edit_text("✅ Заявка отправлена администратору. Ожидайте решения.")
# 3. Отправляем уведомление Админу
# Формируем красивый текст
info = (
f"🔔 <b>Новый запрос доступа!</b>\n"
f"👤 Имя: {user.full_name}\n"
f"🆔 ID: <code>{user.id}</code>\n"
f"🔗 Username: @{user.username if user.username else 'нет'}"
)
await bot.send_message(
chat_id=admin_id,
text=info,
reply_markup=get_admin_decision_kb(user.id)
)
await callback.answer()
@router.callback_query(F.data.startswith("access_"))
async def admin_decision(callback: CallbackQuery, repo: UsersRepo, bot: Bot):
"""Админ нажал Разрешить или Запретить"""
action, user_id_str = callback.data.split("_")[1], callback.data.split("_")[2]
target_user_id = int(user_id_str)
if action == "allow":
# Обновляем БД
await repo.set_status(target_user_id, UserStatus.ALLOWED)
# Ответ админу
await callback.message.edit_text(f"✅ Доступ для {target_user_id} <b>РАЗРЕШЕН</b>.")
# Уведомление пользователю
try:
await bot.send_message(target_user_id, "🎉 <b>Вам предоставлен доступ к боту!</b>\nНажмите /start")
except:
await callback.message.answer(
f"⚠️ Не удалось уведомить пользователя {target_user_id} (он заблокировал бота?)")
elif action == "deny":
# Обновляем БД (статус DENIED, дата запроса остается старой - то есть через сутки он сможет снова попросить)
# Если хотите, чтобы при отказе таймер 24ч сбрасывался на "сейчас", нужно обновить last_request_date.
# В текущей реализации repo, мы просто меняем статус. Таймер тикает от момента подачи заявки пользователем.
await repo.set_status(target_user_id, UserStatus.DENIED)
await callback.message.edit_text(f"🚫 Доступ для {target_user_id} <b>ЗАПРЕЩЕН</b>.")
try:
await bot.send_message(target_user_id, "🚫 Администратор отклонил ваш запрос доступа.")
except:
pass
await callback.answer()

48
routers/char_router.py Normal file
View File

@@ -0,0 +1,48 @@
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import *
from aiogram import Router, F
from models.Character import Character
from repos.dao import DAO
router = Router()
class States(StatesGroup):
char_wait_name = State()
char_wait_bio = State()
@router.message(F.document, Command("add_char"))
async def add_char(message: Message, state: FSMContext, dao: DAO):
await state.set_data({"photo": bot.download(file=message.document.file_id)})
await state.set_state(States.char_wait_name)
await message.answer("Кайф, теперь напиши ее имя")
@router.callback_query(States.char_wait_name)
async def new_char_name(message: Message, state: FSMContext, dao: DAO):
await state.set_data({"name": message.text})
await state.set_state(States.char_wait_bio)
await message.answer("А теперь напиши био. Хоть чуть чуть.")
@router.callback_query(States.char_wait_bio)
async def new_char_bio(message: Message, state: FSMContext, dao: DAO):
data = await state.get_data()
photo = data["photo"]
name = data["name"]
char = Character(id=None, name=name, character_image=photo, character_bio=message.text)
await dao.chars.add_character(char)
await message.answer_photo(photo=BufferedInputFile(char.character_image, "img.png"), caption="Персонаж создан!\n"
f"Имя:{char.name}\n"
f"Био: {char.character_bio}\n"
)
@router.message(Command("add_char"))
async def add_char_cmd(message: Message):
await message.answer(
"Добавление персонажа производится через отправку документа-фото исходного изображения персонажа.")

55
routers/gen_router.py Normal file
View File

@@ -0,0 +1,55 @@
import asyncio
from aiogram import Router, Bot, F
from aiogram.enums import ParseMode
from aiogram.filters import *
from aiogram.types import *
from adapters.google_adapter import GoogleAdapter
router = Router()
@router.message(Command("image"))
async def cmd_image_gen(message: Message, command: CommandObject, gemini: GoogleAdapter, bot: Bot):
# ... ваш код ...
# Обратите внимание: gemini теперь прилетает аргументом, так как мы сделали dp["gemini"]
prompt = command.args
if not prompt:
await message.answer("⚠️ Напиши промпт.")
return
wait_msg = await message.answer("🎨 Генерирую...")
# Получение байтов фото (логика та же)
image_bytes = None
if message.photo:
file_io = await bot.download(message.photo[-1].file_id)
image_bytes = file_io.getvalue()
elif message.reply_to_message and message.reply_to_message.photo:
file_io = await bot.download(message.reply_to_message.photo[-1].file_id)
image_bytes = file_io.getvalue()
result = await asyncio.to_thread(
gemini.generate, prompt=prompt, image_bytes=image_bytes, generate_image=True
)
await wait_msg.delete()
if result.get("images"):
for img in result["images"]:
await message.answer_document(BufferedInputFile(img.read(), "img.png"))
elif result.get("text"):
await message.answer(result["text"])
else:
await message.answer(f"Ошибка: {result.get('error', 'Unknown')}")
@router.message(F.text)
async def handle_text(message: Message, gemini: GoogleAdapter, bot: Bot):
await bot.send_chat_action(message.chat.id, "typing")
result = await asyncio.to_thread(gemini.generate, prompt=message.text)
if result.get("text"):
await message.answer(result["text"], parse_mode=ParseMode.MARKDOWN)
else:
await message.answer("Ошибка генерации")