oimiragieo/agent-studio

check-telegram-voice

Check voice message pipeline config — verify Whisper, ElevenLabs/OpenAI TTS keys and dependencies.

First seen Jun 11, 2026

Installation

$ npx skills add oimiragieo/agent-studio --skill check-telegram-voice

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from oimiragieo/agent-studio · top by installs.

npx skills add oimiragieo/agent-studio

Browse all from oimiragieo/agent-studio

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 40
Default branch main
Open issues 501
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version3.0.0
Declared agents claude-code

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 9,643 B
  • docs SUMMARY.md 128 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Telegram Voice Pipeline

Overview

This skill handles Telegram voice messages end-to-end:

  1. Detect voice message via attachmentfileid in channel tag
  2. Download OGG/Opus file via Telegram MCP tool
  3. Transcribe with Whisper (transcribe-anything)
  4. Process transcribed text as the user query — generate a text response
  5. Generate TTS audio via ElevenLabs (fallback: OpenAI TTS) → MP3
  6. Reply with the MP3 audio file via Telegram MCP tool

When to Invoke

Skill({ skill: 'telegram-voice-pipeline' });

Invoke when:

  • A Telegram channel tag has an attachmentfileid attribute (voice or audio message)
  • The channel agent needs to respond with audio rather than text
  • Any step in the voice pipeline needs to be re-run after a failure

Detection Pattern

Telegram voice messages arrive as channel tags:

<channel source="telegram" chat_id="123456" message_id="789" user="username" ts="1234567890" attachment_file_id="BQACAgIAAxkBAAIBc2...">

Key detection logic:

  • attachmentfileid present → voice/audio message → invoke this skill
  • image_path present → photo → use image handling instead
  • Neither present → text message → normal text response

Workflow

Step 1: Download the Voice File

Call the Telegram MCP download tool with the file_id from the channel tag:

// MCP tool call (agent uses this directly)
mcp__telegram - relay__download_attachment({ file_id: '<attachment_file_id>' });
// Returns: local file path, e.g. /tmp/voice_abc123.ogg

Verify: The returned path exists and is non-empty (> 1KB for a real voice message).

Error handling: If download fails, reply with a text message: "Sorry, I couldn't download your voice message. Please try again."


Step 2: Transcribe with Whisper

Install transcribe-anything if not present:

pip install transcribe-anything

Run transcription:

transcribe-anything /tmp/voice_abc123.ogg --model medium --output_dir /tmp/tg_voice/

Read the transcript:

cat /tmp/tg_voice/voice_abc123.txt

Model selection (trade-off between speed and accuracy):

Model Speed Accuracy Use when
tiny ~2s Low Rapid prototyping only
small ~5s Medium Short messages, speed priority
medium ~12s High Default — best balance
large-v3 ~30s Best Long/complex messages

Override via env: WHISPER_MODEL=small (default: medium)

Verify: /tmp/tg_voice/<filename>.txt exists and is non-empty.

Error handling: If transcription fails or output is empty, reply: "I received your voice message but couldn't transcribe it. Could you try sending it again or type your message?"


Step 3: Process Transcription as User Message

Use the transcribed text as the user input. Generate a text response using the agent's normal response logic.

transcribed_text = contents of /tmp/tg_voice/<filename>.txt
response_text = <agent's generated response to transcribed_text>

Guard max length for TTS: responsetext[:4000] (ElevenLabs limit) or responsetext[:4096] (OpenAI TTS limit).


Step 4: Generate TTS Audio

Primary: ElevenLabs (requires ELEVENLABSAPIKEY)

import os
from elevenlabs import ElevenLabs

client = ElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"])
audio = client.text_to_speech.convert(
    text=response_text[:4000],
    voice_id="JBFqnCBsd6RMkjVDRZzb",  # George — clear, neutral voice
    model_id="eleven_turbo_v2",
    output_format="mp3_44100_128",
)
output_path = "/tmp/tg_voice_response.mp3"
with open(output_path, "wb") as f:
    for chunk in audio:
        f.write(chunk)
print(f"TTS written to {output_path}")

Override voice via env: ELEVENLABSVOICEID=<voice_id> (default: JBFqnCBsd6RMkjVDRZzb)

Fallback: OpenAI TTS (requires OPENAIAPIKEY, no ELEVENLABSAPIKEY)

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
with client.audio.speech.with_streaming_response.create(
    model="tts-1",
    voice="nova",
    input=response_text[:4096],
) as response:
    response.stream_to_file("/tmp/tg_voice_response.mp3")
print("TTS written to /tmp/tg_voice_response.mp3")

Selection logic:

if os.environ.get("ELEVENLABS_API_KEY"):
    # Use ElevenLabs
else:
    # Use OpenAI TTS fallback

Verify: /tmp/tgvoiceresponse.mp3 exists and is > 1KB.

Error handling: If TTS fails, send response_text as a plain text reply instead of audio.


Step 5: Reply with Audio File

// MCP tool call
mcp__telegram -
  relay__reply({
    chat_id: '<chat_id from channel tag>',
    text: response_text, // Also send the transcript so user can read it
    files: ['/tmp/tg_voice_response.mp3'],
  });

Note: Including text alongside the audio file gives the user both a readable transcript and the audio reply — useful for accessibility and noisy environments.

Verify: No error returned from the reply tool.


Step 6: Cleanup Temp Files

After a successful reply, clean up to avoid disk accumulation:

rm -f /tmp/tg_voice_response.mp3
rm -rf /tmp/tg_voice/

Environment Variables

Variable Required Default Purpose
ELEVENLABSAPIKEY NO (if OpenAI set) ElevenLabs TTS API key
ELEVENLABSVOICEID NO JBFqnCBsd6RMkjVDRZzb ElevenLabs voice (George)
OPENAIAPIKEY NO (if ElevenLabs set) OpenAI TTS fallback key
WHISPER_MODEL NO medium Whisper model size

At least one of ELEVENLABSAPIKEY or OPENAIAPIKEY must be set for TTS to work.


Full Pipeline Example

[Telegram] User sends 10-second voice message
   ↓
[Agent] Detects attachment_file_id in channel tag
   ↓
[MCP] download_attachment(file_id) → /tmp/voice_abc123.ogg
   ↓
[Bash] transcribe-anything /tmp/voice_abc123.ogg --model medium → "What is the weather like today?"
   ↓
[Agent] Generates response: "I don't have real-time weather data, but I can help you check..."
   ↓
[Python] ElevenLabs TTS → /tmp/tg_voice_response.mp3
   ↓
[MCP] reply(chat_id, text="I don't have...", files=["/tmp/tg_voice_response.mp3"])
   ↓
[Telegram] User receives text + audio reply
   ↓
[Bash] rm /tmp/tg_voice_response.mp3 && rm -rf /tmp/tg_voice/

Total time for 10-second voice message: ~15-25 seconds (download 1s + transcribe 12s + TTS 2s + reply 1s).


Anti-Patterns

  • Never skip the download stepattachmentfileid is not a file path, it must be resolved via the MCP tool
  • Never use shell: true for subprocess calls in transcription — use array args with shell: false
  • Never skip the text fallback — if TTS fails, always send the text response so the user gets an answer
  • Never process image attachments through this skill — photos use imagepath, not attachmentfile_id; route them differently
  • Never leave temp files on disk — clean up after every reply to prevent disk accumulation

Related Skills

  • enable-telegram — Start the channel daemon for background Telegram monitoring
  • transcription — Whisper transcription workflow (used in Step 2)
  • tts-generation — ElevenLabs and OpenAI TTS (used in Step 4)

References

Search Protocol

For code discovery and search tasks, follow this priority order:

  1. pnpm search:code "<query>" (Primary intent-based search).
  2. ripgrep (for exact keyword/regex matches).
  3. semantic/structural search via code tools if available.

Memory Protocol (MANDATORY)

Before starting:

cat .claude/context/memory/learnings.md
cat .claude/context/memory/decisions.md

After completing:

  • New pattern -> .claude/context/memory/learnings.md
  • Issue found -> .claude/context/memory/issues.md
  • Decision made -> .claude/context/memory/decisions.md

ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.