"""Обработчики для работы с шаблонами.""" import logging import logging import re import logging from typing import Optional, Dict, Any, cast from telegram import Update, Message, CallbackQuery from telegram.ext import ContextTypes, ConversationHandler from telegram.error import BadRequest from app.bots.editor.states import BotStates from app.bots.editor.session import get_session_store from ..keyboards import ( template_type_keyboard, get_templates_keyboard, get_preview_keyboard ) from ..utils.parsers import parse_key_value_lines from ..utils.validation import validate_template_name from app.services.users import get_or_create_user from app.services.template import list_templates as get_user_templates_list from app.services.template import create_template logger = logging.getLogger(__name__) async def start_template_creation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Начало создания шаблона.""" if not update.message: return BotStates.CONVERSATION_END message = update.message await message.reply_text( "Выберите тип шаблона:", reply_markup=template_type_keyboard() ) return BotStates.TPL_TYPE async def handle_template_type(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка выбора типа шаблона.""" if not update.callback_query: return BotStates.CONVERSATION_END query = update.callback_query await query.answer() tpl_type = query.data user_id = query.from_user.id session_store = get_session_store() session = session_store.get_or_create(user_id) session.type = tpl_type await query.message.edit_text("Введите название шаблона:") return BotStates.TPL_NAME async def handle_template_name(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка ввода имени шаблона.""" if not update.message: return BotStates.CONVERSATION_END message = update.message user_id = message.from_user.id name = message.text.strip() if not validate_template_name(name): await message.reply_text( "Некорректное имя шаблона. Используйте только буквы, цифры и знаки - _" ) return BotStates.TPL_NAME session = get_session_store().get_or_create(user_id) session.template_name = name await message.reply_text("Введите текст шаблона:") return BotStates.TPL_TEXT async def handle_template_text(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка текста шаблона.""" if not update.message: return BotStates.CONVERSATION_END message = update.message user_id = message.from_user.id text = message.text.strip() session = get_session_store().get_or_create(user_id) session.text = text await message.reply_text( "Введите клавиатуру в формате:\n" "текст кнопки = ссылка\n\n" "Или отправьте 'skip' чтобы пропустить" ) return BotStates.TPL_NEW_KB async def handle_template_keyboard(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка клавиатуры шаблона.""" if not update.message: return BotStates.CONVERSATION_END message = update.message user_id = message.from_user.id kb_text = message.text.strip() session = get_session_store().get_or_create(user_id) if kb_text != "skip": try: keyboard = parse_key_value_lines(kb_text) session.keyboard = keyboard except ValueError as e: await message.reply_text(f"Ошибка разбора клавиатуры: {e}") return BotStates.TPL_NEW_KB try: template_data = { "owner_id": user_id, "name": session.template_name, "title": session.template_name, "content": session.text, "type": session.type, "parse_mode": session.parse_mode or "HTML", "keyboard_tpl": session.keyboard } await create_template(template_data) await message.reply_text("Шаблон успешно создан") # Очищаем сессию get_session_store().drop(user_id) return BotStates.CONVERSATION_END except ValueError as e: await message.reply_text(f"Ошибка создания шаблона: {e}") return BotStates.TPL_NEW_KB except Exception as e: logger.error(f"Неожиданная ошибка при создании шаблона: {e}") await message.reply_text("Произошла непредвиденная ошибка при создании шаблона") return BotStates.TPL_NEW_KB def extract_template_vars(content: str) -> list[str]: """Извлекает переменные из текста шаблона.""" import re pattern = r'\{([^}]+)\}' return list(set(re.findall(pattern, content))) async def list_templates(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Список шаблонов.""" if not update.message or not update.message.from_user: return BotStates.CONVERSATION_END try: message = update.message tg_user = message.from_user # Получаем или создаем пользователя user = await get_or_create_user(tg_user_id=tg_user.id, username=tg_user.username) # Получаем шаблоны пользователя templates = await get_user_templates_list(owner_id=user.id) if not templates: await message.reply_text("Нет доступных шаблонов") return BotStates.CONVERSATION_END # Сохраняем шаблоны в контексте context.user_data['templates'] = {str(t.id): t for t in templates} # Создаем клавиатуру с шаблонами keyboard = get_templates_keyboard(templates) await message.reply_text( "Выберите шаблон для использования:", reply_markup=keyboard ) return BotStates.SELECT_TEMPLATE except Exception as e: logger.error(f"Ошибка загрузки шаблонов: {e}", exc_info=True) if update.message: await update.message.reply_text(f"Ошибка загрузки шаблонов: {e}") return BotStates.CONVERSATION_END async def handle_template_selection(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка выбора шаблона.""" query = update.callback_query if not query: return BotStates.CONVERSATION_END await query.answer() try: template_id = query.data.split(":")[1] template = context.user_data['templates'].get(template_id) if not template: await query.message.edit_text("Шаблон не найден") return BotStates.CONVERSATION_END # Сохраняем выбранный шаблон context.user_data['current_template'] = template # Извлекаем переменные из шаблона vars_needed = extract_template_vars(template.content) if not vars_needed: # Если переменных нет, сразу показываем предпросмотр rendered_text = template.content keyboard = get_preview_keyboard() await query.message.edit_text( f"Предпросмотр:\n\n{rendered_text}", reply_markup=keyboard, parse_mode=template.parse_mode ) return BotStates.PREVIEW_CONFIRM # Сохраняем список необходимых переменных context.user_data['vars_needed'] = vars_needed context.user_data['template_vars'] = {} # Запрашиваем первую переменную await query.message.edit_text( f"Введите значение для переменной {vars_needed[0]}:" ) return BotStates.TEMPLATE_VARS except Exception as e: logger.error(f"Ошибка при выборе шаблона: {e}", exc_info=True) await query.message.edit_text(f"Произошла ошибка: {e}") return BotStates.CONVERSATION_END async def handle_template_vars(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка ввода значений переменных шаблона.""" if not update.message or not update.message.text: return BotStates.CONVERSATION_END try: vars_needed = context.user_data.get('vars_needed', []) template_vars = context.user_data.get('template_vars', {}) template = context.user_data.get('current_template') if not vars_needed or not template: await update.message.reply_text("Ошибка: данные шаблона потеряны") return BotStates.CONVERSATION_END # Сохраняем введенное значение current_var = vars_needed[len(template_vars)] template_vars[current_var] = update.message.text context.user_data['template_vars'] = template_vars # Проверяем, все ли переменные заполнены if len(template_vars) == len(vars_needed): # Формируем предпросмотр rendered_text = template.content for var, value in template_vars.items(): rendered_text = rendered_text.replace(f"{{{var}}}", value) keyboard = get_preview_keyboard() await update.message.reply_text( f"Предпросмотр:\n\n{rendered_text}", reply_markup=keyboard, parse_mode=template.parse_mode ) return BotStates.PREVIEW_CONFIRM # Запрашиваем следующую переменную next_var = vars_needed[len(template_vars)] await update.message.reply_text( f"Введите значение для переменной {next_var}:" ) return BotStates.TEMPLATE_VARS except Exception as e: logger.error(f"Ошибка при обработке переменных: {e}", exc_info=True) await update.message.reply_text(f"Произошла ошибка: {e}") return BotStates.CONVERSATION_END async def handle_preview_confirmation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> BotStates: """Обработка подтверждения предпросмотра.""" query = update.callback_query if not query: return BotStates.CONVERSATION_END await query.answer() try: action = query.data.split(":")[1] if action == "cancel": await query.message.edit_text("Отправка отменена") return BotStates.CONVERSATION_END if action == "send": template = context.user_data.get('current_template') template_vars = context.user_data.get('template_vars', {}) if not template: await query.message.edit_text("Ошибка: данные шаблона потеряны") return BotStates.CONVERSATION_END # TODO: Добавить логику отправки в канал await query.message.edit_text( "Пост успешно отправлен\n\n" + "(Здесь будет реальная отправка в канал)" ) return BotStates.CONVERSATION_END except Exception as e: logger.error(f"Ошибка при подтверждении: {e}", exc_info=True) await query.message.edit_text(f"Произошла ошибка: {e}") return BotStates.CONVERSATION_END