Skip to content

Repository files navigation

Voicy

Send a voice or a video circle. Get the text.
A small Go Telegram bot that transcribes with Deepgram.

latest version current version go version license telegram bot

Quick Start · Using Voicy · How It Works · Configuration · Security · Docs


The Problem

Voice notes and video circles are fast to send and slow to read later. Pasting them into a separate transcriber breaks the chat, and a bot that talks on every group voice becomes noise.

Voicy keeps the first alpha deliberately narrow:

Need Voicy approach
One action Send a voice or circle, get the text back
Quiet groups Transcribes only on /v or /vp as a reply
Private ask /vp is ephemeral and visible only to the requester
Cheap repeats Caches by Telegram file_id; audio stays on Telegram
Familiar stack Go, first-party Bot API HTTP, shared Asterfield core

Result: one small service that turns speech already in Telegram into readable text without becoming a group chatterbot.


Status

Channel Version Meaning
Latest v0.0.1-beta.1 Beta: 16 interface languages, audio files and video, streamed media
Stable Not yet. This line is pre-release until v0.0.1

The bot is live for limited testing as @voicyin_bot. Alpha means the Deepgram and Telegram paths work, but the public contract is still allowed to change.


Quick Start

You need Docker, a Telegram bot token from BotFather, and a Deepgram API key.

# 1. Copy local configuration
cp .env.example .env

# 2. Fill TELEGRAM_BOT_TOKEN and DEEPGRAM_API_KEY
$EDITOR .env

# 3. Start Voicy and PostgreSQL
docker compose up --build

Voicy runs startup migrations from migrations/ and records completed versions in schema_migrations. Keep AUTO_MIGRATE=true for local development.

The bundled docker compose is for local development only. It runs a local PostgreSQL and seeds a minimal shared core schema (deploy/core-init.sql) so migrations that reference core.person / core.chat boot cleanly. In the shared production deployment Voicy instead connects to the existing core-postgres; use the production notes in docs/releases.md.


Using Voicy

  1. Open @voicyin_bot and send /start.
  2. In a private chat, send a voice message or a video circle.
  3. Wait for the transcript.
  4. In a group, reply to a voice or circle with /v (everyone) or /vp (only you).

/start in a group is an ephemeral Bot API 10.2 command. Its prompt is visible only to the user who invoked it. Bare voices in a group are ignored.

A file that Voicy has already transcribed is served from cache. The audio file stays on Telegram; Voicy stores the file_id and the text. Long transcripts use Bot API rich messages of up to 32,768 characters each and split into multiple readable messages when needed. Voicy never sends transcript files.


How It Works

Voicy is one Go service with PostgreSQL as its only durable store. Domain tables — transcript cache, jobs, user stats, poll offset — live in a voicy schema. Telegram identity and presence are delegated to a shared core schema (core.person, core.chat), which Voicy upserts via core.touch before any dependent write. In production that schema lives in the shared core-postgres database; local docker compose seeds a minimal core schema so development boots the same way.

telegram poller  -> worker pool -> decide -> cache or Deepgram
http server      -> /healthz

One batch of updates is fanned out across MAX_CONCURRENT_JOBS workers, so a long recording no longer holds up everyone else. Updates from the same person stay on one worker, in order.

Transcription path:

getFile -> bounded download
  -> Deepgram prerecorded POST /v1/listen
  -> persist file_id + text
  -> send rich HTML message

MVP Scope

Included Excluded
Voice notes, circles, audio files, video Files with no sound track
Deepgram nova-3 prerecorded Listen Whisper or another STT
DM implicit for speech; /v for files and video Auto-transcribe every group voice
file_id cache Stored audio bytes
/healthz Public metrics surface in this alpha

Configuration

Variable Required Default Description
TELEGRAM_BOT_TOKEN yes Bot token from BotFather
TELEGRAM_API_BASE no https://api.telegram.org Bot API server; a self-hosted one lifts the 20 MB media ceiling
BOT_API_FILES_DIR with a self-hosted server That server's media directory as Voicy sees it (its --files-dir, or its --dir on a server without one). It runs with --local, so it hands over files by absolute path and serves none over HTTP; mount only <dir>/<token>, and create that directory owned by uid 101 first
TELEGRAM_READY_WAIT no 30s How long startup waits for a self-hosted server that is still booting
DEEPGRAM_API_KEY yes Deepgram project API key
DATABASE_URL yes PostgreSQL URL for voicy_core; the service enforces search_path=voicy
HTTP_ADDR no :8080 HTTP listen address
MIGRATIONS_DIR no ./migrations Migration directory
AUTO_MIGRATE no true Run migrations on startup
LOG_LEVEL no info debug, info, warn, or error
TRANSCRIPT_RETENTION no 2160h Retain terminal jobs and cached transcripts since last use
MAX_MEDIA_BYTES no 20971520 Maximum Telegram media download size
MAX_MEDIA_DURATION no 1h Maximum voice or video-circle duration
JOB_STALE_AFTER no 30m Age at which an unfinished job is failed and reported as stuck
FFMPEG_PATH no ffmpeg Audio extraction binary; empty disables extraction
EXTRACT_ABOVE_BYTES no 20971520 Extract an audio track from non-video media above this size
MEDIA_TMP_DIR no system temp Where media is staged while it is transcribed
MAX_CONCURRENT_JOBS no 4 Updates handled at once; one user is always served in order
STATS_CACHE_TTL no 5m How long a statistics snapshot is served before refreshing
STATS_TIMEZONE no Europe/Kyiv Zone the peak-hour statistic is bucketed in

Security

  • Telegram is called over HTTPS with a first-party client. Transport errors redact the bot token.
  • Deepgram requests use Authorization: Token … and mip_opt_out=true. Telegram file URLs are never sent to Deepgram.
  • Empty and failed transcriptions are logged with file_id and update_id, not audio bytes or full transcripts.
  • Logs avoid bot tokens, Deepgram keys, and full Bot API URLs.

Deployment

Local Compose is not the production source. Production runs from /opt/stacks/voicy on the shared core_net and the voicy_core role. See docs/releases.md.

/healthz reports database status, Telegram initialization, polling freshness, stuck jobs, job counts, and build metadata without exposing secrets. /metrics serves process counters in Prometheus text format: polling failures, cache hits and misses, Deepgram retries, delivery errors, and job failures by stage.


Testing

docker run --rm -v "$PWD":/src -w /src golang:1.26.6-alpine go test ./...
docker run --rm -v "$PWD":/src -w /src golang:1.26.6-alpine go vet ./...
cp .env.example .env
docker compose config

Docs

Document Purpose
Architecture Service structure and core decisions
Telegram behavior Commands, privacy, rich messages, cache
Versioning Pre-release and stable version line
Release process Changelog and GitHub Release rules

Releases · Changelog · Apache-2.0 · NOTICE

Voicy is open source software by Asterfield.
Copyright 2026 Asterfield.

About

Private-by-default Telegram voice-to-text with Deepgram, Rich Markdown, and PostgreSQL.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages