Initial commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
"""Обработчики настроек хранения и вспомогательные команды."""
|
||||
|
||||
import logging
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import CallbackQuery, Message, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.filters import Command
|
||||
|
||||
from config import settings
|
||||
from database.database import db
|
||||
from sd.sd_client import sd_client
|
||||
from utils.image_manager import cleanup_expired_images
|
||||
|
||||
router = Router()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StorageSettingsState(StatesGroup):
|
||||
"""Состояния для настройки хранения."""
|
||||
waiting_for_ttl = State()
|
||||
|
||||
|
||||
# --- Обработчик главного меню ---
|
||||
@router.callback_query(F.data == "main_menu")
|
||||
async def main_menu(callback: CallbackQuery, state: FSMContext = None):
|
||||
"""Возврат в главное меню."""
|
||||
# Очищаем состояние, если оно есть
|
||||
if state:
|
||||
await state.clear()
|
||||
|
||||
from bot.handlers_start import get_main_keyboard
|
||||
text = (
|
||||
f"👋 <b>Привет, {callback.from_user.first_name}!</b>\n\n"
|
||||
"Я бот для генерации изображений через Stable Diffusion.\n"
|
||||
"Выберите действие из меню ниже:"
|
||||
)
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
text,
|
||||
parse_mode="HTML",
|
||||
reply_markup=get_main_keyboard(callback.from_user.id),
|
||||
)
|
||||
except Exception:
|
||||
# Если edit_text не сработал (напр., сообщение с фото), отправляем новое
|
||||
await callback.message.answer(
|
||||
text,
|
||||
parse_mode="HTML",
|
||||
reply_markup=get_main_keyboard(callback.from_user.id),
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
# --- Проверка статуса SD API ---
|
||||
@router.callback_query(F.data == "check_sd_status")
|
||||
async def check_sd_status(callback: CallbackQuery):
|
||||
"""Проверка соединения с SD API."""
|
||||
status_msg = await callback.message.edit_text("🔍 Проверяю соединение с SD API...")
|
||||
|
||||
is_connected = await sd_client.check_connection()
|
||||
|
||||
if is_connected:
|
||||
current_model = await sd_client.get_current_model()
|
||||
text = (
|
||||
f"✅ <b>SD API подключено!</b>\n\n"
|
||||
f"Адрес: <code>{settings.SD_API_URL}</code>\n"
|
||||
f"Текущая модель: <code>{current_model}</code>"
|
||||
)
|
||||
else:
|
||||
text = (
|
||||
f"❌ <b>SD API недоступно!</b>\n\n"
|
||||
f"Адрес: <code>{settings.SD_API_URL}</code>\n\n"
|
||||
"Проверьте:\n"
|
||||
"1. Запущен ли Stable Diffusion WebUI\n"
|
||||
"2. Доступен ли сервер по указанному адресу\n"
|
||||
"3. Правильность настройки API (--api флаг)"
|
||||
)
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="🔄 Повторить проверку", callback_data="check_sd_status")],
|
||||
[InlineKeyboardButton(text="📋 Главное меню", callback_data="main_menu")],
|
||||
])
|
||||
|
||||
await status_msg.edit_text(text, parse_mode="HTML", reply_markup=keyboard)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
# --- Помощь ---
|
||||
@router.callback_query(F.data == "help")
|
||||
async def help_command(callback: CallbackQuery):
|
||||
"""Справка по боту."""
|
||||
text = (
|
||||
"ℹ️ <b>Справка по боту</b>\n\n"
|
||||
"<b>Генерация изображений:</b>\n"
|
||||
"• <b>txt2img</b> — создание изображения по текстовому описанию\n\n"
|
||||
"<b>Профили:</b>\n"
|
||||
"Создавайте профили с предустановленными настройками:\n"
|
||||
"• Размер изображения\n"
|
||||
"• Количество шагов\n"
|
||||
"• CFG Scale\n"
|
||||
"• Сэмплер\n"
|
||||
"• Модель и LoRA\n\n"
|
||||
"Вы можете установить профиль по умолчанию для быстрой генерации.\n\n"
|
||||
"<b>Настройки хранения:</b>\n"
|
||||
"Настройте время хранения сгенерированных изображений.\n"
|
||||
"Изображения автоматически удаляются по истечении срока.\n\n"
|
||||
"<b>Команды:</b>\n"
|
||||
"/start — Главное меню\n"
|
||||
"/menu — Показать главное меню\n"
|
||||
"/cancel — Отменить текущую операцию\n"
|
||||
"/status — Статус SD API"
|
||||
)
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="📋 Главное меню", callback_data="main_menu")],
|
||||
])
|
||||
|
||||
await callback.message.edit_text(text, parse_mode="HTML", reply_markup=keyboard)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.message(Command("status"))
|
||||
async def cmd_status(message: Message):
|
||||
"""Команда /status."""
|
||||
is_connected = await sd_client.check_connection()
|
||||
if is_connected:
|
||||
current_model = await sd_client.get_current_model()
|
||||
text = (
|
||||
f"✅ <b>SD API подключено!</b>\n\n"
|
||||
f"Адрес: <code>{settings.SD_API_URL}</code>\n"
|
||||
f"Текущая модель: <code>{current_model}</code>"
|
||||
)
|
||||
else:
|
||||
text = (
|
||||
f"❌ <b>SD API недоступно!</b>\n\n"
|
||||
f"Адрес: <code>{settings.SD_API_URL}</code>"
|
||||
)
|
||||
await message.answer(text, parse_mode="HTML")
|
||||
|
||||
|
||||
# --- Настройки хранения ---
|
||||
@router.callback_query(F.data == "storage_settings")
|
||||
async def storage_settings(callback: CallbackQuery):
|
||||
"""Меню настроек хранения."""
|
||||
user_settings = await db.get_user_settings(callback.from_user.id)
|
||||
current_ttl = user_settings.get("image_ttl_hours", settings.DEFAULT_IMAGE_TTL_HOURS)
|
||||
|
||||
text = (
|
||||
"🗃️ <b>Настройки хранения изображений</b>\n\n"
|
||||
f"Текущее время хранения: <b>{current_ttl} часов</b>\n"
|
||||
f"Максимальное время: <b>{settings.MAX_IMAGE_TTL_HOURS} часов</b>\n\n"
|
||||
"Изображения автоматически удаляются по истечении срока.\n"
|
||||
"Очистка происходит каждые {interval} минут.".format(interval=settings.CLEANUP_INTERVAL_MINUTES)
|
||||
)
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="12 часов", callback_data="set_ttl_12")],
|
||||
[InlineKeyboardButton(text="24 часа (1 день)", callback_data="set_ttl_24")],
|
||||
[InlineKeyboardButton(text="48 часов (2 дня)", callback_data="set_ttl_48")],
|
||||
[InlineKeyboardButton(text="72 часа (3 дня)", callback_data="set_ttl_72")],
|
||||
[InlineKeyboardButton(text="168 часов (7 дней)", callback_data="set_ttl_168")],
|
||||
[InlineKeyboardButton(text="✏️ Ввести своё значение", callback_data="set_ttl_custom")],
|
||||
[InlineKeyboardButton(text="⬅️ Назад", callback_data="main_menu")],
|
||||
])
|
||||
|
||||
await callback.message.edit_text(text, parse_mode="HTML", reply_markup=keyboard)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("set_ttl_"))
|
||||
async def set_ttl(callback: CallbackQuery, state: FSMContext):
|
||||
"""Установка времени хранения."""
|
||||
if callback.data == "set_ttl_custom":
|
||||
await state.set_state(StorageSettingsState.waiting_for_ttl)
|
||||
await callback.message.edit_text(
|
||||
f"⏰ <b>Введите время хранения в часах</b>\n\n"
|
||||
f"Допустимый диапазон: 1 - {settings.MAX_IMAGE_TTL_HOURS} часов.",
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await callback.answer()
|
||||
return
|
||||
|
||||
ttl_hours = int(callback.data.replace("set_ttl_", ""))
|
||||
await _save_ttl(callback, ttl_hours)
|
||||
|
||||
|
||||
@router.message(StorageSettingsState.waiting_for_ttl)
|
||||
async def save_custom_ttl(message: Message, state: FSMContext):
|
||||
"""Сохранение пользовательского значения TTL."""
|
||||
try:
|
||||
ttl_hours = int(message.text.strip())
|
||||
if ttl_hours < 1 or ttl_hours > settings.MAX_IMAGE_TTL_HOURS:
|
||||
await message.answer(
|
||||
f"Значение должно быть от 1 до {settings.MAX_IMAGE_TTL_HOURS} часов."
|
||||
)
|
||||
return
|
||||
except ValueError:
|
||||
await message.answer("Введите корректное целое число.")
|
||||
return
|
||||
|
||||
await state.clear()
|
||||
await _save_ttl(message, ttl_hours)
|
||||
|
||||
|
||||
async def _save_ttl(target, ttl_hours: int):
|
||||
"""Сохранить TTL и показать подтверждение."""
|
||||
user_id = target.from_user.id
|
||||
await db.update_user_settings(user_id, image_ttl_hours=ttl_hours)
|
||||
|
||||
text = (
|
||||
f"✅ <b>Настройки сохранены!</b>\n\n"
|
||||
f"Время хранения изображений: <b>{ttl_hours} часов</b>\n"
|
||||
f"Изображения будут автоматически удаляться по истечении этого срока."
|
||||
)
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="🗃️ Другие настройки хранения", callback_data="storage_settings")],
|
||||
[InlineKeyboardButton(text="📋 Главное меню", callback_data="main_menu")],
|
||||
])
|
||||
|
||||
if hasattr(target, 'message'):
|
||||
await target.message.edit_text(text, parse_mode="HTML", reply_markup=keyboard)
|
||||
else:
|
||||
await target.answer(text, parse_mode="HTML", reply_markup=keyboard)
|
||||
|
||||
|
||||
# --- Команда /cancel ---
|
||||
@router.message(Command("cancel"))
|
||||
async def cmd_cancel(message: Message, state: FSMContext):
|
||||
"""Отмена текущей операции."""
|
||||
current_state = await state.get_state()
|
||||
if current_state is None:
|
||||
await message.answer("Нет активных операций.")
|
||||
return
|
||||
|
||||
await state.clear()
|
||||
await message.answer("❌ Операция отменена.")
|
||||
|
||||
from bot.handlers_start import get_main_keyboard
|
||||
await message.answer("📋 Главное меню:", reply_markup=get_main_keyboard(message.from_user.id))
|
||||
|
||||
|
||||
# --- Периодическая очистка ---
|
||||
async def scheduled_cleanup():
|
||||
"""Функция для периодической очистки просроченных изображений."""
|
||||
try:
|
||||
deleted = await cleanup_expired_images()
|
||||
if deleted > 0:
|
||||
logger.info(f"Очистка: удалено {deleted} изображений")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при очистке изображений: {e}")
|
||||
Reference in New Issue
Block a user