Send a voice or a video circle. Get the text.
A small Go Telegram bot that transcribes with Deepgram.
Quick Start · Using Voicy · How It Works · Configuration · Security · Docs
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.
| Channel | Version | Meaning |
|---|---|---|
| Latest | v0.0.1-alpha.6 |
Alpha: Rich Markdown, reliable jobs, human stats, and clean release automation |
| 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.
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 --buildVoicy 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.
- Open @voicyin_bot and send
/start. - In a private chat, send a voice message or a video circle.
- Wait for the transcript.
- 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 Markdown messages up to 32,768 characters each and split into
multiple readable messages when needed. Voicy never sends transcript files.
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 -> decide -> cache or Deepgram
http server -> /healthz
Transcription path:
getFile -> bounded download
-> Deepgram prerecorded POST /v1/listen
-> persist file_id + text
-> send Rich Markdown
| Included | Excluded |
|---|---|
| Voice notes and video circles | Arbitrary audio/video documents |
Deepgram nova-3 prerecorded Listen |
Whisper or another STT |
DM implicit transcribe; group /v and /vp |
Auto-transcribe every group voice |
file_id cache |
Stored audio bytes |
/healthz |
Public metrics surface in this alpha |
| Variable | Required | Default | Description |
|---|---|---|---|
TELEGRAM_BOT_TOKEN |
yes | — | Bot token from BotFather |
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 |
- Telegram is called over HTTPS with a first-party client. Transport errors redact the bot token.
- Deepgram requests use
Authorization: Token …andmip_opt_out=true. Telegram file URLs are never sent to Deepgram. - Empty and failed transcriptions are logged with
file_idandupdate_id, not audio bytes or full transcripts. - Logs avoid bot tokens, Deepgram keys, and full Bot API URLs.
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.
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| Document | Purpose |
|---|---|
| Architecture | Service structure and core decisions |
| Telegram behavior | Commands, privacy, Rich Markdown, 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.