6 Commits

Author SHA1 Message Date
goyban f5b16fab84 Bump VERSION to 1.0.2 for the list-truncation fix
VERSION had been left at 1.0.0 while the registry already carried 1.0.1, so
push.sh (which defaults to the VERSION file) would have rebuilt an existing
tag. This realigns the file with what is actually published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 13:02:19 +00:00
goyban 5305b654c7 Merge pull request 'Fix list truncation' (#2) from codex/fix-list-truncation into main
Reviewed-on: #2
2026-09-01 14:57:45 +02:00
goyban 196edf1120 Merge branch 'main' into codex/fix-list-truncation 2026-09-01 14:57:30 +02:00
goyban faf4fee274 Merge pull request 'Add menu command' (#1) from snail/add-menu-command into main
Reviewed-on: #1
2026-07-27 12:02:19 +02:00
snail f14fdc20f7 Register Telegram command menu 2026-07-26 18:14:24 +02:00
snail 53aa9cfda7 Add menu command 2026-07-26 16:56:29 +02:00
3 changed files with 65 additions and 18 deletions
+6 -1
View File
@@ -54,12 +54,17 @@ The database will be saved in `./bot_db/media.db`.
**Searching inline** — in any chat type `@yourbotusername keyword` to search and send. **Searching inline** — in any chat type `@yourbotusername keyword` to search and send.
Telegram suggests these commands as you type `/`, with a short description beside each suggestion.
**Commands** **Commands**
| Command | Description | | Command | Description |
|---|---| |---|---|
| `/start` | Start the bot and show usage instructions |
| `/menu` | Show the available commands |
| `/list` | Show all saved media | | `/list` | Show all saved media |
| `/delete <id>` | Remove an entry (get ID from `/list`) | | `/edit [search]` | Select a meme and edit its keywords (admin only) |
| `/delete <id>` | Remove an entry (get ID from `/list`; admin only) |
| `/cancel` | Cancel current add operation | | `/cancel` | Cancel current add operation |
### Moving to another server ### Moving to another server
+1 -1
View File
@@ -1 +1 @@
1.0.0 1.0.2
+55 -13
View File
@@ -9,6 +9,8 @@ from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.storage.memory import MemoryStorage from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.types import ( from aiogram.types import (
BotCommand,
BotCommandScopeChat,
CallbackQuery, CallbackQuery,
InlineKeyboardButton, InlineKeyboardButton,
InlineKeyboardMarkup, InlineKeyboardMarkup,
@@ -40,6 +42,18 @@ ADMIN_USERS: set[int] = {
bot = Bot(token=BOT_TOKEN) bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(storage=MemoryStorage()) dp = Dispatcher(storage=MemoryStorage())
USER_COMMANDS = [
BotCommand(command="start", description="Start the bot"),
BotCommand(command="menu", description="Show available commands"),
BotCommand(command="list", description="Browse saved memes"),
BotCommand(command="cancel", description="Cancel current operation"),
]
ADMIN_COMMANDS = [
*USER_COMMANDS,
BotCommand(command="edit", description="Edit meme keywords"),
BotCommand(command="delete", description="Remove a meme by ID"),
]
def is_allowed(user_id: int) -> bool: def is_allowed(user_id: int) -> bool:
if user_id in ADMIN_USERS: if user_id in ADMIN_USERS:
@@ -54,6 +68,20 @@ def is_admin(user_id: int) -> bool:
return not ALLOWED_USERS or user_id in ALLOWED_USERS return not ALLOWED_USERS or user_id in ALLOWED_USERS
async def register_commands() -> None:
"""Publish Telegram's command menu with role-appropriate suggestions."""
if not ADMIN_USERS:
await bot.set_my_commands(ADMIN_COMMANDS)
return
await bot.set_my_commands(USER_COMMANDS)
for admin_id in ADMIN_USERS:
await bot.set_my_commands(
ADMIN_COMMANDS,
scope=BotCommandScopeChat(chat_id=admin_id),
)
class AddMedia(StatesGroup): class AddMedia(StatesGroup):
waiting_for_keywords = State() waiting_for_keywords = State()
@@ -62,30 +90,43 @@ class EditMedia(StatesGroup):
waiting_for_new_keywords = State() waiting_for_new_keywords = State()
def menu_text(user_id: int) -> str:
if is_admin(user_id):
return (
"Send me any <b>photo, GIF, video, voice, or sticker</b>.\n"
"I'll ask you for keywords next.\n\n"
"/list — show all saved memes\n"
"/edit [search] — edit keywords of a meme\n"
"/delete &lt;id&gt; — remove a meme by ID\n"
"/cancel — cancel current operation"
)
return (
"Use me inline: type <code>@botname keyword</code> in any chat.\n\n"
"/list — browse all saved memes"
)
# ── /start ─────────────────────────────────────────────────────────────────── # ── /start ───────────────────────────────────────────────────────────────────
@dp.message(Command("start")) @dp.message(Command("start"))
async def cmd_start(msg: Message): async def cmd_start(msg: Message):
if not is_allowed(msg.from_user.id): if not is_allowed(msg.from_user.id):
return return
if is_admin(msg.from_user.id):
await msg.answer( await msg.answer(
"Send me any <b>photo, GIF, video, voice, or sticker</b>.\n" f"{menu_text(msg.from_user.id)}\n/menu — show this list again",
"I'll ask you for keywords next.\n\n"
"/list — show all saved memes\n"
"/edit [search] — edit keywords of a meme\n"
"/delete &lt;id&gt; — remove a meme by ID\n"
"/cancel — cancel current operation",
parse_mode="HTML",
)
else:
await msg.answer(
"Use me inline: type <code>@botname keyword</code> in any chat.\n\n"
"/list — browse all saved memes",
parse_mode="HTML", parse_mode="HTML",
) )
# ── /menu ────────────────────────────────────────────────────────────────────
@dp.message(Command("menu"))
async def cmd_menu(msg: Message):
if not is_allowed(msg.from_user.id):
return
await msg.answer(menu_text(msg.from_user.id), parse_mode="HTML")
# ── /cancel ────────────────────────────────────────────────────────────────── # ── /cancel ──────────────────────────────────────────────────────────────────
@dp.message(Command("cancel")) @dp.message(Command("cancel"))
@@ -392,6 +433,7 @@ async def inline_handler(query: InlineQuery):
async def main(): async def main():
await db.init_db() await db.init_db()
await register_commands()
log.info("Bot starting…") log.info("Bot starting…")
await dp.start_polling(bot) await dp.start_polling(bot)