Skip to content

feat(messages): transcribe voice and video messages - #31

Open
leemour wants to merge 1 commit into
dapi:mainfrom
leemour:feat/transcribe-voice
Open

leemour wants to merge 1 commit into
dapi:mainfrom
leemour:feat/transcribe-voice

Conversation

@leemour

@leemour leemour commented Sep 11, 2026

Copy link
Copy Markdown

What

Adds tgcli messages transcribe --chat <id> --id <msgId> — Telegram's own speech-to-text for voice and video messages. The underlying MTProto method (messages.transcribeAudio) is already in the bundled mtcute schema; the CLI just had no way to reach it.

tgcli messages transcribe --chat @username --id 123 --id 124
tgcli --json messages transcribe --chat 174942312 --id 123 --wait 90

Accepts repeated --id, honours --json, and follows the shape of messages show.

Two things that are easy to get wrong

Both were found by measuring against a live account, and both are the reason this is more than a one-line wrapper.

The finished text does not come back in the reply. When Telegram answers with pending, the refined transcription arrives later as an updateTranscribedAudio event. My first attempt polled transcribeAudio in a loop instead — that spent the rate-limit budget and returned the unrefined first-pass text (no punctuation, mangled proper nouns) while reporting itself complete. The command now subscribes to the update and waits, --wait seconds, 60 by default, and only reports pending: true if the update never arrives.

Transcription is rate-limited even on Premium. A batch of six messages reliably produced FLOOD_WAIT_13. A flood wait shorter than the caller's ceiling is slept through and retried; longer ones are rethrown. mtcute's own floodSleepThreshold does not help here — it only applies when the flood waiter middleware is enabled, which this client does not configure.

Verification

  • npx vitest run — 307 passed, 1 skipped, 19 files.
  • New tests/transcribe.test.js covers the flood-wait retry, the ceiling, the pending-to-update handoff, and the incomplete fallback.
  • Driven against a real chat: short clips transcribe cleanly; the --json and multi---id paths both work.

Limitation worth stating plainly

Transcription requires Telegram Premium. Without it Telegram grants a small weekly allowance, returned in trial_remains_num and surfaced as trialRemainingbut that branch is untested, since this was developed against a Premium account. Happy to adjust if you want different behaviour when the allowance runs out.

Unrelated, so not included here: session.json is created world-readable (644) while config.json and account.json are 600. Since that file is the account credential, it probably wants 600 too — say the word and I will open a separate PR.

Adds `tgcli messages transcribe --chat <id> --id <msgId>`, exposing the
`messages.transcribeAudio` MTProto method that mtcute already ships but the
CLI had no path to. Accepts several `--id` flags and honours `--json`.

Two behaviours that are easy to get wrong, and that this implements:

- When Telegram answers `pending`, the finished text arrives as an
  `updateTranscribedAudio` event, not in the reply. Re-calling the method to
  poll for it spends the rate-limit budget and yields the unrefined
  first-pass text, so the command subscribes and waits (`--wait`, 60s
  default) and only reports `pending` if the update never comes.
- Transcription is rate-limited even on Premium; `FLOOD_WAIT_X` shorter than
  the caller's ceiling is slept through and retried, longer ones rethrown.
  mtcute's own `floodSleepThreshold` does not apply here because the flood
  waiter middleware is not enabled on this client.

Requires Telegram Premium. The non-Premium path returns a weekly trial
allowance in `trial_remains_num`, surfaced as `trialRemaining`, but that
branch is untested — it was developed against a Premium account.

Tests cover the flood-wait retry, the ceiling, the pending/update handoff
and the incomplete fallback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant