Перенос ботов из Telegram: что сломается, и как чинить
Bot API совместим с Telegram на ~99% — большинство ботов взлетают подменой api.telegram.org → api.telefon.chat. Но есть несколько семантических различий, которые молча ломают портированных ботов. Прежде чем переносить — пройдитесь по этому списку.
1. User.username ведёт себя как в Telegram
В Telefon каждый пользователь задаёт @username при регистрации (это обязательное поле в onboarding), поэтому from.username приходит так же стабильно, как у Telegram. Фильтры по username работают штатно:
@router.message(filters.User(username="admin"))
async def admin_only(m): ...
# Тоже работает
@dp.message(F.from_user.username.in_(["alice", "bob"]))
async def handler(m): ...
Тем не менее по спецификации Telegram username остаётся опциональным — пользователь может его сбросить. Для долгоживущих whitelist'ов и привязок (admin, биллинг, прав) безопаснее использовать **числовой ** — он уникален и стабилен.
2. Chat.username тоже может быть None
То же самое для каналов и публичных групп. Если вы фильтруете по chat.username — переходите на chat.id.
# ❌
if message.chat.username == "my_channel":
# ✅
if message.chat.id == -100123456789:
3. ID-схема совместима, но цифры свои
user.id, chat.id, message.id — все int, как в Telegram. Но числа другие — у нас своё пространство ID. Значит:
- Захардкоженные
chat_idиз Telegram не работают — нужно получить наш ID (например, черезchat.idв первом апдейте от пользователя). - Сохраняйте маппинг
external_id → ваш_user_idесли у вас своя БД пользователей.
Префикс знака сохранён: -100… для каналов/супергрупп, положительные для приватных, отрицательные не-100 для обычных групп.
4. Команды (commands) — массив, а не строка
В Telegram через multipart/form-data библиотеки иногда сериализуют commands как JSON-строку. Telefon принимает это (с автоматическим парсингом), но в старых записях БД из ранних версий могло сохраниться как строка — мы это уже починили миграцией. Если ваш бот сам пишет в setMyCommands — используйте обычный массив, как в спецификации.
5. Платежи: нет официальных провайдеров
sendInvoice / successful_payment пока stub'ы. Используйте внешние платёжные ссылки (Stripe Checkout, ЮKassa) и Mini App для приёма платежей.
6. Stars (XTR) недоступны
Цифровая валюта Telegram Stars не реализована. Подписки делайте через свой биллинг.
7. Голосовые / video_note — наш формат
Voice — audio/webm, video_note — video/webm. Telegram отдаёт audio/ogg и video/mp4. Если ваш бот качает их и обрабатывает — ставьте ffmpeg для конвертации.
8. Inline mode — без CDN-кеша Telegram
answerInlineQuery работает, но кеша картинок Telegram'а у нас нет. Подавайте URL HTTPS-картинок которые отдаются быстро (CDN), иначе будет лаг.
Чек-лист при переносе
- Уберите все
filters.User(username=...),from_user.username == "...",allowed_telegram_usernames— переведите наuser.id - Уберите захардкоженные
chat_id/user_idиз Telegram — получайте их в рантайме - Проверьте платежи — отключите/замените на внешний провайдер
- Конвертируйте
audio/ogg→audio/webmв коде если работаете с voice - Запустите бота, отправьте тестовое сообщение — посмотрите в логах что ваши хендлеры срабатывают
Помощь и обратная связь
Если бот ведёт себя странно — пишите в dev@telefon.chat с примером лога. Часто оказывается что фильтр проглотил сообщение из-за username == None.