This commit is contained in:
admin
2025-08-10 22:22:38 +07:00
parent 0b3b957c0a
commit 6073b4b3c9
52 changed files with 1981 additions and 810 deletions

View File

@@ -0,0 +1,209 @@
from math import ceil
from typing import Final
from aiogram import Router, F
from aiogram.fsm.context import FSMContext
from aiogram.types import (
Message, CallbackQuery,
InlineKeyboardButton, InlineKeyboardMarkup,
SwitchInlineQueryChosenChat, CopyTextButton
)
from aiogram.exceptions import TelegramBadRequest
from aiogram.utils.markdown import hide_link
from bot.core import storage
from bot.utils import pagination_btn
router: Router = Router(name="posts_manager_router")
PAGE_SIZE: Final[int] = 5
async def send_posts_list(
message: Message = None,
callback_query: CallbackQuery = None,
page: int = 0
) -> None:
"""Отправляет список постов пользователя с пагинацией."""
user_id = message.from_user.id if message else callback_query.from_user.id
posts = storage.load_user_posts(user_id)
if not posts:
msg = "Нет сохранённых постов."
if message:
await message.answer(msg)
else:
await callback_query.answer(msg, show_alert=True)
return
post_ids = list(posts.keys())
total = len(post_ids)
pages = ceil(total / PAGE_SIZE)
page = max(0, min(page, pages - 1))
start = page * PAGE_SIZE
end = start + PAGE_SIZE
current_ids = post_ids[start:end]
rows: list[list[InlineKeyboardButton]] = []
for pid in current_ids:
post = posts[pid]
priv = "🔒" if post.get("private") else "🔓"
btn = InlineKeyboardButton(
text=f"{priv} Пост {pid}",
callback_data=f"view_post_{pid}"
)
rows.append([btn])
# Пагинация
nav_buttons = pagination_btn(
action="open_post_list",
page=page,
total_posts=total,
bt_page=PAGE_SIZE
)
if nav_buttons:
rows.append(nav_buttons)
# Кнопка закрытия
rows.append([InlineKeyboardButton(text="Закрыть❌", callback_data="cancel_list")])
keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
header: str = "Список ваших постов:"
try:
if callback_query:
await callback_query.message.edit_text(header, reply_markup=keyboard)
else:
await message.answer(header, reply_markup=keyboard)
except TelegramBadRequest:
if callback_query:
await callback_query.message.delete()
await callback_query.message.answer(header, reply_markup=keyboard)
else:
await message.answer(header, reply_markup=keyboard)
# --- Хендлеры списка ---
@router.message(F.text.lower() == "посмотреть список📋")
async def cmd_list(message: Message, state: FSMContext):
# Сбрасываем состояние перед показом списка
await state.clear()
await send_posts_list(message=message)
@router.callback_query(F.data == "open_post_list")
async def cb_open_list(cq: CallbackQuery, state: FSMContext):
await state.clear()
await send_posts_list(callback_query=cq)
await cq.answer()
@router.callback_query(F.data.startswith("open_post_list_page_"))
async def cb_paginate(cq: CallbackQuery, state: FSMContext):
try:
page = int(cq.data.rsplit("_", 1)[-1])
except ValueError:
await cq.answer("Некорректная страница", show_alert=True)
return
await state.clear()
await send_posts_list(callback_query=cq, page=page)
await cq.answer()
@router.callback_query(F.data == "cancel_list")
async def cb_cancel(cq: CallbackQuery):
await cq.message.delete()
await cq.answer()
@router.callback_query(F.data.startswith("view_post_"))
async def view_post_callback(cq: CallbackQuery):
"""Просмотр отдельного поста"""
pid = cq.data.replace("view_post_", "")
uid = cq.from_user.id
posts = storage.load_user_posts(uid)
if pid not in posts:
await cq.answer("Пост не найден", show_alert=True)
return
post = posts[pid]
text = post.get("text", "")
img = post.get("image", "")
if img.startswith("http"):
text = f"{hide_link(img)}{text}"
rows: list[list[InlineKeyboardButton]] = []
for row in post.get("buttons", []):
btns: list[InlineKeyboardButton] = []
for b in row:
if "copy_text" in b:
btns.append(
InlineKeyboardButton(
text=b["text"],
copy_text=CopyTextButton(text=b["copy_text"])
)
)
elif "switch_inline_query" in b:
btns.append(
InlineKeyboardButton(
text=b["text"],
switch_inline_query=b["switch_inline_query"]
)
)
elif "switch_inline_query_current_chat" in b:
btns.append(
InlineKeyboardButton(
text=b["text"],
switch_inline_query_current_chat=b["switch_inline_query_current_chat"]
)
)
elif "switch_inline_query_chosen_chat" in b:
raw = b["switch_inline_query_chosen_chat"]
cfg = raw if isinstance(raw, dict) else {
"query": raw,
"allow_user_chats": True
}
btns.append(
InlineKeyboardButton(
text=b["text"],
switch_inline_query_chosen_chat=SwitchInlineQueryChosenChat(**cfg)
)
)
elif "url" in b:
url = b["url"]
if url.lower().endswith("void"):
btns.append(
InlineKeyboardButton(text=b["text"], callback_data="void")
)
else:
btns.append(
InlineKeyboardButton(text=b["text"], url=url)
)
elif "callback_data" in b:
btns.append(
InlineKeyboardButton(text=b["text"], callback_data=b["callback_data"])
)
if btns:
rows.append(btns)
# Удалить / назад
rows.append([
InlineKeyboardButton(text="Удалить❌", callback_data=f"delete_post_{pid}"),
InlineKeyboardButton(text="Назад◀️", callback_data="open_post_list")
])
rows.append(
[InlineKeyboardButton(text="Отправить↪️", switch_inline_query=f"{pid}")])
keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
await cq.message.answer(text=text, reply_markup=keyboard)
await cq.message.delete()
await cq.answer()
@router.callback_query(lambda c: c.data and c.data.startswith("delete_post_"))
async def delete_post_callback(cq: CallbackQuery, state: FSMContext):
"""Удаление поста."""
pid = cq.data.replace("delete_post_", "")
uid = cq.from_user.id
if storage.delete_user_post(uid, pid):
await cq.answer(f"Пост {pid} удалён")
await state.clear()
await send_posts_list(callback_query=cq)
else:
await cq.answer(text="Не удалось удалить пост", show_alert=True)