diff --git a/bot.py b/bot.py index 6f0f9e0..4c1de30 100644 --- a/bot.py +++ b/bot.py @@ -188,21 +188,52 @@ async def on_media_during_keywords(msg: Message): # ── /list ──────────────────────────────────────────────────────────────────── +LIST_PAGE_SIZE = 30 + + +async def list_page(page: int) -> tuple[str, InlineKeyboardMarkup | None]: + total = await db.count_media() + if total == 0: + return "No memes saved yet.", None + + total_pages = (total + LIST_PAGE_SIZE - 1) // LIST_PAGE_SIZE + page = max(0, min(page, total_pages - 1)) + items = await db.list_media(page, LIST_PAGE_SIZE) + lines = [f"{item['id']} [{item['media_type']}] — {item['keywords']}" for item in items] + text = f"Page {page + 1} of {total_pages}\n" + "\n".join(lines) + + buttons = [] + if page > 0: + buttons.append(InlineKeyboardButton(text="‹ Previous", callback_data=f"list:{page - 1}")) + if page < total_pages - 1: + buttons.append(InlineKeyboardButton(text="Next ›", callback_data=f"list:{page + 1}")) + keyboard = InlineKeyboardMarkup(inline_keyboard=[buttons]) if buttons else None + return text, keyboard + + @dp.message(Command("list")) async def cmd_list(msg: Message): if not is_allowed(msg.from_user.id): return - items = await db.list_media() - if not items: - await msg.answer("No memes saved yet.") + text, keyboard = await list_page(0) + await msg.answer(text, parse_mode="HTML", reply_markup=keyboard) + + +@dp.callback_query(F.data.startswith("list:")) +async def on_list_page(callback: CallbackQuery): + if not is_allowed(callback.from_user.id): + await callback.answer("Not allowed.", show_alert=True) return - lines = [] - for item in items[:30]: - lines.append(f"{item['id']} [{item['media_type']}] — {item['keywords']}") - text = "\n".join(lines) - if len(items) > 30: - text += f"\n…and {len(items) - 30} more." - await msg.answer(text, parse_mode="HTML") + + try: + page = int(callback.data.split(":", 1)[1]) + except ValueError: + await callback.answer("Invalid page.", show_alert=True) + return + + text, keyboard = await list_page(page) + await callback.message.edit_text(text, parse_mode="HTML", reply_markup=keyboard) + await callback.answer() # ── /delete ────────────────────────────────────────────────────────────────── diff --git a/db.py b/db.py index 05d3101..4d0b297 100644 --- a/db.py +++ b/db.py @@ -59,8 +59,29 @@ async def search_media(query: str) -> list[dict]: return [dict(r) for r in rows] -async def list_media() -> list[dict]: - return await search_media("") +async def list_media(page: int, page_size: int) -> list[dict]: + """Return one page of saved media, newest first.""" + offset = page * page_size + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + cursor = await db.execute( + """ + SELECT id, file_id, file_unique_id, media_type, keywords + FROM media + ORDER BY added_at DESC, id DESC + LIMIT ? OFFSET ? + """, + (page_size, offset), + ) + rows = await cursor.fetchall() + return [dict(r) for r in rows] + + +async def count_media() -> int: + async with aiosqlite.connect(DB_PATH) as db: + cursor = await db.execute("SELECT COUNT(*) FROM media") + row = await cursor.fetchone() + return row[0] async def get_media_by_id(media_id: int) -> dict | None: