tg_client WebSocket Docs: Cloud Media Uploads

← До головних розділів Domain Core Unified Domain Services

Що це за flow

Це окремий сценарій для відправки медіа через Telegram без локального upload з фронта на бекенд. Клієнт спочатку отримує presigned PUT URL, заливає файл напряму в storage, а потім викликає send_message або send_comment з media.key.

  1. WS: create_tg_media_upload_url
  2. HTTP: PUT binary у upload_url
  3. WS: send_message або send_comment з media
  4. TDLib піднімає updateFileGenerationStart, бекенд підтягує файл із storage і завершує generation

Усередині TDLib використовується inputFileGenerated + cloud_fetch_v1, а не текстове посилання на файл.

Підтримувані типи

media.typeTDLib типПримітка
documentinputMessageDocumentБудь-який файл, PDF, ZIP, DOCX, а також фото/відео "без стискання".
photoinputMessagePhotoЗ підтримкою show_caption_above_media, has_spoiler, self_destruct_type.
videoinputMessageVideoЗ підтримкою thumbnail, cover, supports_streaming, has_spoiler.
audioinputMessageAudioMusic/audio track з title, performer, album_cover_thumbnail.
voice_noteinputMessageVoiceNoteNative voice message. Для коректного UI бажано .ogg + Opus.
video_noteinputMessageVideoNoteNative "відеокружечок". Обов'язково передати length.

Для photo і video прапори high_quality, without_compression, disable_compression, send_as_document, as_document, preserve_quality перемикають відправку в document.

Крок 1. Отримати upload URL

{
  "action": "create_tg_media_upload_url",
  "userbot_id": 1,
  "filename": "clip.mp4",
  "content_type": "video/mp4",
  "chat_id": "123",
  "media_type": "video"
}

Відповідь:

{
  "type": "create_tg_media_upload_url",
  "result": {
    "upload_url": "https://storage.example/...",
    "key": "tg/outgoing/userbot_1/chat_123/video/fixedid.mp4",
    "expires_in": 600,
    "source": "outgoing/userbot_1/chat_123/video",
    "content_type": "video/mp4"
  }
}

key треба зберегти. Саме він передається в send_message або send_comment. upload_url одноразовий/тимчасовий і в Telegram не відправляється.

Крок 2. Завантажити файл у storage

HTTP-запит до upload_url:

PUT {upload_url}
Content-Type: video/mp4
Body: binary file

Postman

  1. Створи звичайний HTTP request
  2. Method: PUT
  3. URL: встав upload_url
  4. Body → binary
  5. Header Content-Type має збігатися з result.content_type

Важливо

  • Не використовуй form-data
  • Не додавай Authorization
  • Успішна відповідь зазвичай 200 або 204

Крок 3. Відправити повідомлення в Telegram

Базовий текстовий upload/send flow:

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "text": "caption",
  "media": {
    "type": "video",
    "key": "tg/outgoing/userbot_1/chat_123/video/fixedid.mp4",
    "file_name": "clip.mp4",
    "expected_size": 1234567
  }
}

text стає caption для media. Якщо потрібно, можна замість нього передати media.caption і media.caption_entities.

Для альбому передайте media_items масивом. Top-level text і entities будуть застосовані до першого елемента як caption fallback.

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "text": "Album caption",
  "entities": [
    {
      "offset": 0,
      "length": 5,
      "type": { "@type": "textEntityTypeBold" }
    }
  ],
  "media_items": [
    {
      "type": "photo",
      "key": "tg/outgoing/userbot_1/chat_123/photo/one.jpg",
      "file_name": "one.jpg"
    },
    {
      "type": "video",
      "key": "tg/outgoing/userbot_1/chat_123/video/two.mp4",
      "file_name": "two.mp4",
      "supports_streaming": true
    }
  ],
  "reply_to": {
    "replyToMsgId": "555"
  },
  "protect_content": true
}

Альбом підтримує 2-10 елементів. TDLib групує лише photo, video, document і audio; для document і audio усі елементи мають бути того самого типу. reply_markup для альбомів не підтримується.

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "media_items": [
    {
      "type": "forward",
      "from_chat_id": "-100555000111",
      "message_id": "9001",
      "without_sender": true,
      "caption": "Copied forward",
      "caption_entities": [
        {
          "offset": 0,
          "length": 6,
          "type": { "@type": "textEntityTypeItalic" }
        }
      ],
      "show_caption_above_media": true
    },
    {
      "type": "forward",
      "from_chat_id": "-100555000111",
      "message_id": "9002"
    }
  ]
}

Для comments flow використовуйте send_comment і передайте comments_meta або пару discussion_chat_id + message_thread_id.

{
  "action": "send_comment",
  "userbot_id": 1,
  "comments_meta": {
    "discussion_chat_id": "-100999000777",
    "message_thread_id": "9001"
  },
  "text": "caption",
  "entities": [
    {
      "offset": 0,
      "length": 7,
      "type": { "@type": "textEntityTypeBold" }
    }
  ],
  "media": {
    "type": "document",
    "key": "tg/outgoing/userbot_1/chat_123/document/fixedid.pdf",
    "file_name": "report.pdf",
    "expected_size": 123456
  },
  "protect_content": true
}

Загальні top-level поля для send_message / send_comment

Усі поля нижче однаково підтримуються і для send_message, і для send_comment. Для comments відрізняються лише target-поля: discussion_chat_id / comments_meta / message_thread_id.

ПолеОбов'язковістьЩо робить
userbot_idrequiredID userbot listener-а.
chat_idrequired для send_messageЧат, куди відправляємо.
discussion_chat_idoptionalComments chat для send_comment. Може прийти явно або через comments_meta.
textoptionalCaption для media або звичайний текст, якщо media нема.
entitiesoptionalText entities для text.
message_thread_idoptionalВідправка в topic/forum thread.
comments_metaoptionalShortcut для send_comment: discussion_chat_id, message_thread_id, опц. comment_count.
reply_to, reply_to_message_idoptionalReply на повідомлення. Для reply в інший чат передай reply_to як об'єкт з chat_id/message_id або replyToChatId/replyToMsgId.
reply_markupoptionalTDLib reply markup payload. Для media_items/sendMessageAlbum не підтримується.
optionsoptionalСирий messageSendOptions.
disable_notification, protect_content, from_backgroundoptionalAlias-и для messageSendOptions.
allow_paid_broadcast, paid_message_star_countoptionalПлатні опції відправки, якщо TDLib/чат їх підтримує.
update_order_of_installed_sticker_setsoptionalПрокидається в messageSendOptions як у звичайному sendMessage.
schedule_date, send_when_onlineoptionalПланована відправка.
effect_id, message_effect_idoptionalЕфект повідомлення, якщо TDLib/чат це підтримує.
sending_id, only_previewoptionalДодаткові прапори/ідентифікатори з messageSendOptions.
direct_messages_chat_topic_id, suggested_post_infooptionalСпецифічні поля TDLib для direct messages / suggested posts.
send_large_photosoptionalShortcut прапор, який бекенд кладе в options для відправки великих фото.
mediaoptionalОб'єкт cloud media payload. Також можна передати масив як alias для альбому, але рекомендований формат для нього - media_items.
media_itemsoptionalМасив із 2-10 елементів для sendMessageAlbum. Елемент може бути cloud media payload або {"type":"forward","from_chat_id":"...","message_id":"..."}.
media_typeoptionalМожна передати окремо, якщо в media.type або елементах media_items[].type його нема.

Загальні поля media

ПолеДля яких типівПримітка
typealldocument, photo, video, audio, voice_note, video_note
keyallCloud key із create_tg_media_upload_url. required
file_name / filenameallІм'я файлу, яке піде в original_path.
expected_size / sizeallОчікуваний розмір для TDLib generation.
caption, caption_entitiesdocument/photo/video/audio/voice_noteCaption у самому media payload.
self_destruct_typephoto/video/voice_note/video_noteTTL/self-destruct timer.
thumbnaildocument/photo/video/video_noteОкремий cloud object для preview.

Параметри по типах

document

ПолеТипЩо робить
thumbnailobjectPreview для документа.
disable_content_type_detectionboolВимикає авто-детекцію MIME типу в Telegram.

photo

ПолеТипЩо робить
width, heightintРозміри фото.
added_sticker_file_idsarrayСтікери, прикріплені до фото.
show_caption_above_mediaboolПідпис над фото.
has_spoilerboolСпойлер-ефект.

video

ПолеТипЩо робить
thumbnailobjectPreview thumbnail.
coverobjectCover image для відео.
durationintТривалість у секундах.
width, heightintРозміри відео.
start_timestampintЗ якого місця стартує прев'ю/відтворення.
supports_streamingboolStreaming-friendly video.
show_caption_above_mediaboolПідпис над відео.
has_spoilerboolСпойлер-ефект.

audio

ПолеТипЩо робить
durationintТривалість.
titlestringНазва треку.
performerstringВиконавець.
album_cover_thumbnailobjectОбкладинка.

voice_note

ПолеТипЩо робить
durationintТривалість.
waveformbase64 / list / bytesХвиля голосового. Необов'язково.
captionstringПідпис для voice note.

Для native вигляду Telegram очікує голосове у форматі OGG/Opus. Якщо дати, наприклад, mp3 або m4a, результат може поводитися як звичайний audio file.

video_note

ПолеТипЩо робить
durationintТривалість.
lengthintДіаметр/розмір відеокружечка. required
thumbnailobjectPreview thumbnail.

Для video_note бажано відправляти квадратне mp4, інакше Telegram може обрізати/відобразити не так, як очікується.

Приклади payload-ів

Document

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "text": "PDF",
  "media": {
    "type": "document",
    "key": "tg/outgoing/userbot_1/chat_123/document/report.pdf",
    "file_name": "report.pdf",
    "expected_size": 456789,
    "disable_content_type_detection": false
  }
}

Photo

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "media": {
    "type": "photo",
    "key": "tg/outgoing/userbot_1/chat_123/photo/image.jpg",
    "file_name": "image.jpg",
    "width": 1200,
    "height": 900,
    "show_caption_above_media": true,
    "has_spoiler": false,
    "caption": "Фото"
  }
}

Video

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "text": "ось відео",
  "media": {
    "type": "video",
    "key": "tg/outgoing/userbot_1/chat_123/video/clip.mp4",
    "file_name": "clip.mp4",
    "expected_size": 1234567,
    "duration": 17,
    "width": 1920,
    "height": 1080,
    "supports_streaming": true,
    "show_caption_above_media": true,
    "has_spoiler": true
  }
}

Audio

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "media": {
    "type": "audio",
    "key": "tg/outgoing/userbot_1/chat_123/audio/song.mp3",
    "file_name": "song.mp3",
    "duration": 198,
    "title": "Song title",
    "performer": "Artist"
  }
}

Voice Note

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "media": {
    "type": "voice_note",
    "key": "tg/outgoing/userbot_1/chat_123/voice_note/voice.ogg",
    "file_name": "voice.ogg",
    "duration": 8
  }
}

Video Note

{
  "action": "send_message",
  "userbot_id": 1,
  "chat_id": "123",
  "media": {
    "type": "video_note",
    "key": "tg/outgoing/userbot_1/chat_123/video_note/circle.mp4",
    "file_name": "circle.mp4",
    "duration": 12,
    "length": 384
  }
}

Що відбувається всередині

  1. send_message / send_comment будує inputFileGenerated з conversion=cloud_fetch_v1:...
  2. TDLib запускає updateFileGenerationStart
  3. Бекенд дістає з conversion cloud key
  4. Генерує свіжий presigned GET URL по key
  5. Качає файл у destination_path або через writeGeneratedFilePart
  6. Завершує generation через finishFileGeneration

Це зроблено спеціально для того, щоб не передавати в Telegram тимчасовий GET URL, який може протухнути до моменту реальної відправки.

Типові помилки

СимптомПричинаЩо перевірити
create_tg_media_upload_url_errorНемає userbot_id або битий payloadПеревірити обов'язкові поля запиту.
403 SignatureDoesNotMatch на PUTContent-Type не збігся з підписомВикористовувати result.content_type із відповіді на створення upload URL.
send_message_error / send_comment_error: media.key is requiredНе переданий cloud keyПеревірити media.key.
send_message_error / send_comment_error: Unsupported media.typeНепідтримуваний типВикористовувати лише задокументовані значення.
send_comment_error: message_thread_id is requiredНе переданий thread commentsПередати comments_meta.message_thread_id або top-level message_thread_id.
Voice відправився як файл/audioНевірний формат голосовогоДля native voice використовувати .ogg/Opus.
Video note виглядає не як кружечокНемає length або невідповідний media formatПередати length і квадратне mp4.
Upload пройшов, але TDLib не відправив файлОб'єкт відсутній за key або listener не обробив generationПеревірити storage object, listener logs і updateFileGenerationStart.

Швидка перевірка в Postman

  1. WS connect до /ws/chats/
  2. Надіслати {"action":"open_client","userbot_id":1}
  3. Надіслати create_tg_media_upload_url
  4. Зробити HTTP PUT binary у upload_url
  5. Надіслати send_message або send_comment з media.key
  6. Очікувати send_message/send_comment, updateNewMessage, updateMessageSendSucceeded