Documentation Index
Fetch the complete documentation index at: https://mintlify.com/karilaa-dev/tt-bot/llms.txt
Use this file to discover all available pages before exploring further.
TT-Bot includes admin commands for bot management, user communication, and broadcasting announcements.
Admin levels
The bot has two admin permission levels configured via environment variables:
Primary admins
ADMIN_IDS=[123456789, 987654321]
Primary admins can:
- Access admin menu (
/admin)
- Create and send broadcasts
- View and edit announcement messages
Secondary admins
SECOND_IDS=[111222333, 444555666]
Secondary admins can:
- Send direct messages to users (
/msg)
- Export user list (
/export)
- View error details in error messages
Admin menu commands
Opens the admin keyboard with broadcast controls:
# From handlers/advert.py:54
@advert_router.message(Command("admin"), IsAdmin(), F.chat.type == "private")
async def send_admin(message: Message):
await message.answer("🤖You opened admin menu", reply_markup=admin_keyboard)
Admin keyboard buttons:
- 👁🗨 Check message - Preview current announcement
- ✏ Edit message - Create/update announcement
- 📢 Send message - Broadcast to all users
- 🔽 Hide keyboard - Minimize keyboard
# From handlers/advert.py:21
admin_keyboard = ReplyKeyboardBuilder()
admin_keyboard.button(text="👁🗨Check message")
admin_keyboard.button(text="✏Edit message")
admin_keyboard.button(text="📢Send message")
admin_keyboard.button(text="🔽Hide keyboard")
admin_keyboard.adjust(2, 1, 1)
👁🗨 Check message - Preview announcement
Shows a preview of the current announcement message:
# From handlers/advert.py:59
@advert_router.message(F.text == "👁🗨Check message", IsAdmin())
async def adb_check(message: Message):
if advert_message is not None:
await advert_message.send_copy(message.from_user.id)
else:
await message.answer("⚠️You have not created a message yet")
✏ Edit message - Create announcement
Enters edit mode to create or update the announcement:
# From handlers/advert.py:99
@advert_router.message(F.text == "✏Edit message", IsAdmin())
async def adv_change(message: Message, state: FSMContext):
await message.answer("📝Write new message", reply_markup=back_keyboard)
await state.set_state(AdminMenu.add)
@advert_router.message(AdminMenu.add)
async def notify_text(message: Message, state: FSMContext):
global advert_message
advert_message = copy(message)
await message.answer("✅Message added", reply_markup=admin_keyboard)
await state.clear()
Supports:
- Text messages
- Images with captions
- Videos with captions
- Formatted text (HTML/Markdown)
- Buttons and inline keyboards
📢 Send message - Broadcast to all users
Sends the announcement to all users in the database:
# From handlers/advert.py:67
@advert_router.message(F.text == "📢Send message", IsAdmin())
async def adv_go(message: Message):
if advert_message is not None:
msg = await message.answer("<code>Announcement started</code>")
users = await get_user_ids()
num = 0
blocked = 0
errors = 0
for user_id in users:
try:
await advert_message.send_copy(user_id)
num += 1
except TelegramForbiddenError:
blocked += 1
logging.debug(f"User {user_id} blocked the bot")
except TelegramBadRequest as e:
errors += 1
logging.debug(f"Failed to send to {user_id}: {e}")
except Exception as e:
errors += 1
logging.warning(f"Unexpected error sending to {user_id}: {e}")
await sleep(0.04)
await msg.delete()
await message.answer(
f"✅Message received by <b>{num}</b> users\n"
f"🚫Blocked: <b>{blocked}</b>\n"
f"❌Errors: <b>{errors}</b>"
)
Features:
- Rate limiting (0.04s between sends = 25 messages/second)
- Error tracking (blocked users, failed sends)
- Comprehensive statistics after completion
- Continues on individual failures
🔽 Hide keyboard - Minimize keyboard
# From handlers/advert.py:46
@advert_router.message(F.text == "🔽Hide keyboard")
@advert_router.message(Command("hide"))
async def send_clear_keyboard(message: Message):
await message.answer(
"🔽You successfully hide the keyboard", reply_markup=ReplyKeyboardRemove()
)
Secondary admin commands
/msg - Send direct message to user
Send a message to a specific user/chat ID:
# From handlers/admin.py:15
@admin_router.message(
Command("msg", "tell", "say", "send"), F.chat.type == "private", IsSecondAdmin()
)
async def send_hi(message: Message):
text = message.text.split(" ", 2)
try:
await bot.send_message(chat_id=text[1], text=text[2])
await message.answer("Message sent")
except TelegramForbiddenError:
await message.answer("User has blocked the bot")
except TelegramBadRequest as e:
await message.answer(f"Bad request: {e}")
except Exception as e:
logging.error(f"Failed to send message: {e}")
await message.answer("ops")
Usage:
/msg 123456789 Hello from admin!
/tell -100987654321 Group announcement
Aliases: /msg, /tell, /say, /send
/export - Export user list
Exports all user IDs to a text file:
# From handlers/admin.py:32
@admin_router.message(Command("export"), F.chat.type == "private", IsSecondAdmin())
async def export_users(message: Message):
users_file = await get_users_file()
await message.answer_document(users_file, caption="User list")
The file contains one user ID per line, suitable for external analysis or migration.
Admin filters
Commands are protected by custom filters:
IsAdmin filter
# From misc/utils.py (referenced in handlers)
class IsAdmin(Filter):
async def __call__(self, message: Message) -> bool:
return message.from_user.id in admin_ids
Checks if user is in ADMIN_IDS list.
IsSecondAdmin filter
class IsSecondAdmin(Filter):
async def __call__(self, message: Message) -> bool:
return message.from_user.id in second_ids
Checks if user is in SECOND_IDS list.
FSM states for announcements
The broadcast feature uses Finite State Machine for multi-step flows:
# From handlers/advert.py:34
class AdminMenu(StatesGroup):
menu = State()
add = State()
State flow:
- Admin clicks ”✏ Edit message”
- Bot enters
AdminMenu.add state
- Admin sends message content
- Bot stores message and returns to menu
- State cleared automatically
Cancel/Back commands
# From handlers/advert.py:39
@advert_router.message(F.text == "↩Return")
@advert_router.message(Command("stop", "cancel", "back"))
async def cancel(message: Message, state: FSMContext):
await message.answer("↩You have returned", reply_markup=admin_keyboard)
await state.clear()
Aliases: /stop, /cancel, /back, or “↩Return” button
Error visibility for admins
Secondary admins see detailed error messages:
# From handlers/get_video.py:304
if message.chat.id in second_ids:
await message.reply("<code>{0}</code>".format(error_text))
Regular users only see generic error messages, while admins see full stack traces.
Configuration
Environment variables
# Primary admins (can broadcast)
ADMIN_IDS=[123456789]
# Secondary admins (can send direct messages and export users)
SECOND_IDS=[123456789, 987654321]
Loading configuration
# From data/config.py
admin_ids = _parse_json_list("ADMIN_IDS")
second_ids = _parse_json_list("SECOND_IDS")
IDs are parsed from JSON arrays in environment variables.
Best practices
Broadcast guidelines
- Preview first: Always use ”👁🗨 Check message” before broadcasting
- Rate limiting: Built-in 0.04s delay prevents Telegram rate limits
- Error tolerance: Broadcast continues even if individual sends fail
- Statistics: Review success/blocked/error counts after completion
Direct messaging
- Use for: Responding to specific user issues
- Avoid for: Mass messages (use broadcast instead)
- Handle errors: User might have blocked the bot
User export
- Privacy: Exported data contains only user IDs, no personal info
- Use cases: Analytics, migration, backup
- Format: Plain text, one ID per line
Security considerations
- Commands only work in private chats (except keyboard buttons)
- Admin IDs must be configured before deployment
- No runtime admin promotion (must restart bot)
- Filters prevent unauthorized access
- Error messages sanitized for non-admins