Files
remnawave-bedolaga-telegram…/app/handlers/admin/faq.py

1066 lines
28 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import html
import logging
from datetime import datetime
from aiogram import Dispatcher, F, types
from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession
from app.database.models import User
from app.localization.texts import get_texts
from app.services.faq_service import FaqService
from app.states import AdminStates
from app.utils.decorators import admin_required, error_handler
from app.utils.validators import get_html_help_text, validate_html_tags
logger = logging.getLogger(__name__)
def _format_timestamp(value: datetime | None) -> str:
if not value:
return ""
try:
return value.strftime("%d.%m.%Y %H:%M")
except Exception:
return ""
async def _build_overview(
db_user: User,
db: AsyncSession,
):
texts = get_texts(db_user.language)
normalized_language = FaqService.normalize_language(db_user.language)
setting = await FaqService.get_setting(
db,
db_user.language,
fallback=False,
)
pages = await FaqService.get_pages(
db,
db_user.language,
include_inactive=True,
fallback=False,
)
total_pages = len(pages)
active_pages = sum(1 for page in pages if page.is_active)
description = texts.t(
"ADMIN_FAQ_DESCRIPTION",
"FAQ отображается в разделе «Инфо».",
)
if setting and not setting.is_enabled:
status_text = texts.t(
"ADMIN_FAQ_STATUS_DISABLED",
"⚠️ Показ FAQ выключен.",
)
elif active_pages:
status_text = texts.t(
"ADMIN_FAQ_STATUS_ENABLED",
"✅ FAQ включён. Активных страниц: {count}.",
).format(count=active_pages)
elif total_pages:
status_text = texts.t(
"ADMIN_FAQ_STATUS_ENABLED_EMPTY",
"⚠️ FAQ включён, но нет активных страниц.",
)
else:
status_text = texts.t(
"ADMIN_FAQ_STATUS_EMPTY",
"⚠️ FAQ ещё не настроен.",
)
pages_overview = texts.t(
"ADMIN_FAQ_PAGES_EMPTY",
"Страницы ещё не созданы.",
)
if pages:
rows: list[str] = []
for index, page in enumerate(pages, start=1):
title = (page.title or "").strip()
if not title:
title = texts.t("FAQ_PAGE_UNTITLED", "Без названия")
if len(title) > 60:
title = f"{title[:57]}..."
status_label = texts.t(
"ADMIN_FAQ_PAGE_STATUS_ACTIVE",
"✅ Активна",
)
if not page.is_active:
status_label = texts.t(
"ADMIN_FAQ_PAGE_STATUS_INACTIVE",
"🚫 Выключена",
)
updated = _format_timestamp(getattr(page, "updated_at", None))
updated_block = f" ({updated})" if updated else ""
rows.append(
f"{index}. {html.escape(title)}{status_label}{updated_block}"
)
pages_list_header = texts.t(
"ADMIN_FAQ_PAGES_OVERVIEW",
"<b>Список страниц:</b>\n{items}",
)
pages_overview = pages_list_header.format(items="\n".join(rows))
language_block = texts.t(
"ADMIN_FAQ_LANGUAGE",
"Язык: <code>{lang}</code>",
).format(lang=normalized_language)
stats_block = texts.t(
"ADMIN_FAQ_PAGE_STATS",
"Всего страниц: {total}",
).format(total=total_pages)
header = texts.t("ADMIN_FAQ_HEADER", "❓ <b>FAQ</b>")
actions_prompt = texts.t(
"ADMIN_FAQ_ACTION_PROMPT",
"Выберите действие:",
)
message_parts = [
header,
description,
language_block,
status_text,
stats_block,
pages_overview,
actions_prompt,
]
overview_text = "\n\n".join(part for part in message_parts if part)
buttons: list[list[types.InlineKeyboardButton]] = []
buttons.append([
types.InlineKeyboardButton(
text=texts.t(
"ADMIN_FAQ_ADD_PAGE_BUTTON",
" Добавить страницу",
),
callback_data="admin_faq_create",
)
])
for page in pages[:25]:
title = (page.title or "").strip()
if not title:
title = texts.t("FAQ_PAGE_UNTITLED", "Без названия")
if len(title) > 40:
title = f"{title[:37]}..."
buttons.append([
types.InlineKeyboardButton(
text=f"{page.display_order}. {title}",
callback_data=f"admin_faq_page:{page.id}",
)
])
toggle_text = texts.t(
"ADMIN_FAQ_ENABLE_BUTTON",
"✅ Включить показ",
)
if setting and setting.is_enabled:
toggle_text = texts.t(
"ADMIN_FAQ_DISABLE_BUTTON",
"🚫 Отключить показ",
)
buttons.append([
types.InlineKeyboardButton(
text=toggle_text,
callback_data="admin_faq_toggle",
)
])
buttons.append([
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_HTML_HELP", " HTML помощь"),
callback_data="admin_faq_help",
)
])
buttons.append([
types.InlineKeyboardButton(
text=texts.BACK,
callback_data="admin_submenu_settings",
)
])
return overview_text, types.InlineKeyboardMarkup(inline_keyboard=buttons)
@admin_required
@error_handler
async def show_faq_management(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession,
):
overview_text, markup = await _build_overview(db_user, db)
await callback.message.edit_text(
overview_text,
reply_markup=markup,
)
await callback.answer()
@admin_required
@error_handler
async def toggle_faq(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession,
):
texts = get_texts(db_user.language)
setting = await FaqService.toggle_enabled(db, db_user.language)
if setting.is_enabled:
alert_text = texts.t(
"ADMIN_FAQ_ENABLED_ALERT",
"✅ FAQ включён.",
)
else:
alert_text = texts.t(
"ADMIN_FAQ_DISABLED_ALERT",
"🚫 FAQ отключён.",
)
overview_text, markup = await _build_overview(db_user, db)
await callback.message.edit_text(
overview_text,
reply_markup=markup,
)
await callback.answer(alert_text, show_alert=True)
@admin_required
@error_handler
async def start_create_faq_page(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
await state.set_state(AdminStates.creating_faq_title)
await state.update_data(faq_language=db_user.language)
await callback.message.edit_text(
texts.t(
"ADMIN_FAQ_ENTER_TITLE",
"Введите заголовок для новой страницы FAQ:",
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text=texts.t(
"ADMIN_FAQ_CANCEL_BUTTON",
"⬅️ Отмена",
),
callback_data="admin_faq_cancel",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def cancel_faq_creation(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
await state.clear()
await show_faq_management(callback, db_user, db)
@admin_required
@error_handler
async def process_new_faq_title(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
title = (message.text or "").strip()
if not title:
await message.answer(
texts.t(
"ADMIN_FAQ_TITLE_EMPTY",
"❌ Заголовок не может быть пустым.",
)
)
return
if len(title) > 255:
await message.answer(
texts.t(
"ADMIN_FAQ_TITLE_TOO_LONG",
"❌ Заголовок слишком длинный. Максимум 255 символов.",
)
)
return
await state.update_data(faq_title=title)
await state.set_state(AdminStates.creating_faq_content)
await message.answer(
texts.t(
"ADMIN_FAQ_ENTER_CONTENT",
"Отправьте содержимое страницы FAQ. Допускается HTML.",
)
)
@admin_required
@error_handler
async def process_new_faq_content(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
content = message.text or ""
if len(content) > 6000:
await message.answer(
texts.t(
"ADMIN_FAQ_CONTENT_TOO_LONG",
"❌ Текст слишком длинный. Максимум 6000 символов.",
)
)
return
if not content.strip():
await message.answer(
texts.t(
"ADMIN_FAQ_CONTENT_EMPTY",
"❌ Текст не может быть пустым.",
)
)
return
is_valid, error_message = validate_html_tags(content)
if not is_valid:
await message.answer(
texts.t(
"ADMIN_FAQ_HTML_ERROR",
"❌ Ошибка в HTML: {error}",
).format(error=error_message)
)
return
data = await state.get_data()
title = data.get("faq_title") or texts.t("FAQ_PAGE_UNTITLED", "Без названия")
language = data.get("faq_language", db_user.language)
await FaqService.create_page(
db,
language=language,
title=title,
content=content,
)
logger.info(
"Админ %s создал страницу FAQ (%d символов)",
db_user.telegram_id,
len(content),
)
await state.clear()
success_text = texts.t(
"ADMIN_FAQ_PAGE_CREATED",
"✅ Страница FAQ создана.",
)
reply_markup = types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text=texts.t(
"ADMIN_FAQ_BACK_TO_LIST",
"⬅️ К настройкам FAQ",
),
callback_data="admin_faq",
)
]
]
)
await message.answer(success_text, reply_markup=reply_markup)
@admin_required
@error_handler
async def show_faq_page_details(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession,
):
texts = get_texts(db_user.language)
raw_id = (callback.data or "").split(":", 1)[-1]
try:
page_id = int(raw_id)
except ValueError:
await callback.answer()
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await callback.answer(
texts.t(
"ADMIN_FAQ_PAGE_NOT_FOUND",
"⚠️ Страница не найдена.",
),
show_alert=True,
)
return
header = texts.t("ADMIN_FAQ_PAGE_HEADER", "📄 <b>Страница FAQ</b>")
title = (page.title or "").strip() or texts.t("FAQ_PAGE_UNTITLED", "Без названия")
status_label = texts.t(
"ADMIN_FAQ_PAGE_STATUS_ACTIVE",
"✅ Активна",
)
if not page.is_active:
status_label = texts.t(
"ADMIN_FAQ_PAGE_STATUS_INACTIVE",
"🚫 Выключена",
)
updated_at = _format_timestamp(getattr(page, "updated_at", None))
updated_block = ""
if updated_at:
updated_block = texts.t(
"ADMIN_FAQ_PAGE_UPDATED",
"Обновлено: {timestamp}",
).format(timestamp=updated_at)
preview = (page.content or "").strip()
preview_text = texts.t(
"ADMIN_FAQ_PAGE_PREVIEW_EMPTY",
"Текст ещё не задан.",
)
if preview:
preview_trimmed = preview[:400]
if len(preview) > 400:
preview_trimmed += "..."
preview_text = (
texts.t("ADMIN_FAQ_PAGE_PREVIEW", "<b>Превью:</b>\n{content}")
.format(content=html.escape(preview_trimmed))
)
message_parts = [
header,
texts.t(
"ADMIN_FAQ_PAGE_TITLE",
"<b>Заголовок:</b> {title}",
).format(title=html.escape(title)),
texts.t(
"ADMIN_FAQ_PAGE_STATUS",
"Статус: {status}",
).format(status=status_label),
preview_text,
updated_block,
]
message_text = "\n\n".join(part for part in message_parts if part)
buttons: list[list[types.InlineKeyboardButton]] = []
buttons.append([
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_EDIT_TITLE_BUTTON", "✏️ Изменить заголовок"),
callback_data=f"admin_faq_edit_title:{page.id}",
)
])
buttons.append([
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_EDIT_CONTENT_BUTTON", "📝 Изменить текст"),
callback_data=f"admin_faq_edit_content:{page.id}",
)
])
toggle_text = texts.t("ADMIN_FAQ_PAGE_ENABLE_BUTTON", "✅ Включить страницу")
if page.is_active:
toggle_text = texts.t(
"ADMIN_FAQ_PAGE_DISABLE_BUTTON",
"🚫 Выключить страницу",
)
buttons.append([
types.InlineKeyboardButton(
text=toggle_text,
callback_data=f"admin_faq_toggle_page:{page.id}",
)
])
buttons.append([
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_PAGE_MOVE_UP", "⬆️ Выше"),
callback_data=f"admin_faq_move:{page.id}:up",
),
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_PAGE_MOVE_DOWN", "⬇️ Ниже"),
callback_data=f"admin_faq_move:{page.id}:down",
),
])
buttons.append([
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_PAGE_DELETE_BUTTON", "🗑️ Удалить"),
callback_data=f"admin_faq_delete:{page.id}",
)
])
buttons.append([
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_BACK_TO_LIST", "⬅️ К настройкам FAQ"),
callback_data="admin_faq",
)
])
await callback.message.edit_text(
message_text,
reply_markup=types.InlineKeyboardMarkup(inline_keyboard=buttons),
)
await callback.answer()
@admin_required
@error_handler
async def start_edit_faq_title(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
raw_id = (callback.data or "").split(":", 1)[-1]
try:
page_id = int(raw_id)
except ValueError:
await callback.answer()
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await callback.answer(
texts.t(
"ADMIN_FAQ_PAGE_NOT_FOUND",
"⚠️ Страница не найдена.",
),
show_alert=True,
)
return
await state.set_state(AdminStates.editing_faq_title)
await state.update_data(faq_page_id=page.id)
await callback.message.edit_text(
texts.t(
"ADMIN_FAQ_EDIT_TITLE_PROMPT",
"Введите новый заголовок для страницы:",
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text=texts.t(
"ADMIN_FAQ_CANCEL_BUTTON",
"⬅️ Отмена",
),
callback_data=f"admin_faq_page:{page.id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_faq_title(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
title = (message.text or "").strip()
if not title:
await message.answer(
texts.t(
"ADMIN_FAQ_TITLE_EMPTY",
"❌ Заголовок не может быть пустым.",
)
)
return
if len(title) > 255:
await message.answer(
texts.t(
"ADMIN_FAQ_TITLE_TOO_LONG",
"❌ Заголовок слишком длинный. Максимум 255 символов.",
)
)
return
data = await state.get_data()
page_id = data.get("faq_page_id")
if not page_id:
await state.clear()
await message.answer(texts.t("ADMIN_FAQ_UNEXPECTED_STATE", "⚠️ Состояние сброшено."))
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await message.answer(
texts.t("ADMIN_FAQ_PAGE_NOT_FOUND", "⚠️ Страница не найдена."),
)
await state.clear()
return
await FaqService.update_page(db, page, title=title)
await state.clear()
await message.answer(
texts.t("ADMIN_FAQ_TITLE_UPDATED", "✅ Заголовок обновлён."),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_BACK_TO_LIST", "⬅️ К настройкам FAQ"),
callback_data="admin_faq",
)]]
),
)
@admin_required
@error_handler
async def start_edit_faq_content(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
raw_id = (callback.data or "").split(":", 1)[-1]
try:
page_id = int(raw_id)
except ValueError:
await callback.answer()
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await callback.answer(
texts.t("ADMIN_FAQ_PAGE_NOT_FOUND", "⚠️ Страница не найдена."),
show_alert=True,
)
return
await state.set_state(AdminStates.editing_faq_content)
await state.update_data(faq_page_id=page.id)
await callback.message.edit_text(
texts.t(
"ADMIN_FAQ_EDIT_CONTENT_PROMPT",
"Отправьте новый текст для страницы FAQ.",
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text=texts.t(
"ADMIN_FAQ_CANCEL_BUTTON",
"⬅️ Отмена",
),
callback_data=f"admin_faq_page:{page.id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_faq_content(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
content = message.text or ""
if len(content) > 6000:
await message.answer(
texts.t(
"ADMIN_FAQ_CONTENT_TOO_LONG",
"❌ Текст слишком длинный. Максимум 6000 символов.",
)
)
return
if not content.strip():
await message.answer(
texts.t(
"ADMIN_FAQ_CONTENT_EMPTY",
"❌ Текст не может быть пустым.",
)
)
return
is_valid, error_message = validate_html_tags(content)
if not is_valid:
await message.answer(
texts.t(
"ADMIN_FAQ_HTML_ERROR",
"❌ Ошибка в HTML: {error}",
).format(error=error_message)
)
return
data = await state.get_data()
page_id = data.get("faq_page_id")
if not page_id:
await state.clear()
await message.answer(texts.t("ADMIN_FAQ_UNEXPECTED_STATE", "⚠️ Состояние сброшено."))
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await message.answer(
texts.t("ADMIN_FAQ_PAGE_NOT_FOUND", "⚠️ Страница не найдена."),
)
await state.clear()
return
await FaqService.update_page(db, page, content=content)
await state.clear()
await message.answer(
texts.t("ADMIN_FAQ_CONTENT_UPDATED", "✅ Текст страницы обновлён."),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_BACK_TO_LIST", "⬅️ К настройкам FAQ"),
callback_data="admin_faq",
)]]
),
)
@admin_required
@error_handler
async def toggle_faq_page(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession,
):
texts = get_texts(db_user.language)
parts = (callback.data or "").split(":")
try:
page_id = int(parts[1])
except (ValueError, IndexError):
await callback.answer()
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await callback.answer(
texts.t("ADMIN_FAQ_PAGE_NOT_FOUND", "⚠️ Страница не найдена."),
show_alert=True,
)
return
updated_page = await FaqService.update_page(db, page, is_active=not page.is_active)
alert_text = texts.t(
"ADMIN_FAQ_PAGE_ENABLED_ALERT",
"✅ Страница включена.",
)
if not updated_page.is_active:
alert_text = texts.t(
"ADMIN_FAQ_PAGE_DISABLED_ALERT",
"🚫 Страница выключена.",
)
await callback.answer(alert_text, show_alert=True)
await show_faq_page_details(callback, db_user, db)
@admin_required
@error_handler
async def delete_faq_page(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession,
):
texts = get_texts(db_user.language)
parts = (callback.data or "").split(":")
try:
page_id = int(parts[1])
except (ValueError, IndexError):
await callback.answer()
return
page = await FaqService.get_page(
db,
page_id,
db_user.language,
fallback=False,
include_inactive=True,
)
if not page:
await callback.answer(
texts.t("ADMIN_FAQ_PAGE_NOT_FOUND", "⚠️ Страница не найдена."),
show_alert=True,
)
return
await FaqService.delete_page(db, page.id)
remaining_pages = await FaqService.get_pages(
db,
db_user.language,
include_inactive=True,
fallback=False,
)
if remaining_pages:
remaining_sorted = sorted(
remaining_pages,
key=lambda item: (item.display_order, item.id),
)
await FaqService.reorder_pages(db, db_user.language, remaining_sorted)
await callback.answer(
texts.t("ADMIN_FAQ_PAGE_DELETED", "🗑️ Страница удалена."),
show_alert=True,
)
await show_faq_management(callback, db_user, db)
@admin_required
@error_handler
async def move_faq_page(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession,
):
texts = get_texts(db_user.language)
parts = (callback.data or "").split(":")
try:
page_id = int(parts[1])
direction = parts[2]
except (ValueError, IndexError):
await callback.answer()
return
pages = await FaqService.get_pages(
db,
db_user.language,
include_inactive=True,
fallback=False,
)
if not pages:
await callback.answer()
return
pages_sorted = sorted(pages, key=lambda item: (item.display_order, item.id))
index = next((i for i, page in enumerate(pages_sorted) if page.id == page_id), None)
if index is None:
await callback.answer()
return
if direction == "up" and index > 0:
pages_sorted[index - 1], pages_sorted[index] = (
pages_sorted[index],
pages_sorted[index - 1],
)
elif direction == "down" and index < len(pages_sorted) - 1:
pages_sorted[index + 1], pages_sorted[index] = (
pages_sorted[index],
pages_sorted[index + 1],
)
else:
await callback.answer()
return
await FaqService.reorder_pages(db, db_user.language, pages_sorted)
await callback.answer(
texts.t("ADMIN_FAQ_PAGE_REORDERED", "✅ Порядок обновлён."),
show_alert=True,
)
await show_faq_page_details(callback, db_user, db)
@admin_required
@error_handler
async def show_faq_html_help(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
texts = get_texts(db_user.language)
help_text = get_html_help_text()
buttons = [[
types.InlineKeyboardButton(
text=texts.t("ADMIN_FAQ_BACK_TO_LIST", "⬅️ К настройкам FAQ"),
callback_data="admin_faq",
)
]]
await callback.message.edit_text(
help_text,
reply_markup=types.InlineKeyboardMarkup(inline_keyboard=buttons),
)
await callback.answer()
def register_handlers(dp: Dispatcher) -> None:
dp.callback_query.register(
show_faq_management,
F.data == "admin_faq",
)
dp.callback_query.register(
toggle_faq,
F.data == "admin_faq_toggle",
)
dp.callback_query.register(
start_create_faq_page,
F.data == "admin_faq_create",
)
dp.callback_query.register(
cancel_faq_creation,
F.data == "admin_faq_cancel",
)
dp.callback_query.register(
show_faq_page_details,
F.data.startswith("admin_faq_page:"),
)
dp.callback_query.register(
start_edit_faq_title,
F.data.startswith("admin_faq_edit_title:"),
)
dp.callback_query.register(
start_edit_faq_content,
F.data.startswith("admin_faq_edit_content:"),
)
dp.callback_query.register(
toggle_faq_page,
F.data.startswith("admin_faq_toggle_page:"),
)
dp.callback_query.register(
delete_faq_page,
F.data.startswith("admin_faq_delete:"),
)
dp.callback_query.register(
move_faq_page,
F.data.startswith("admin_faq_move:"),
)
dp.callback_query.register(
show_faq_html_help,
F.data == "admin_faq_help",
)
dp.message.register(
process_new_faq_title,
AdminStates.creating_faq_title,
)
dp.message.register(
process_new_faq_content,
AdminStates.creating_faq_content,
)
dp.message.register(
process_edit_faq_title,
AdminStates.editing_faq_title,
)
dp.message.register(
process_edit_faq_content,
AdminStates.editing_faq_content,
)