- STT: OpenAI (
whisper-1,gpt-4o-transcribe,gpt-4o-mini-transcribey servidores compatibles) y Deepgram (nova-3). - TTS: OpenAI (
gpt-4o-mini-tts,tts-1,tts-1-hd) y ElevenLabs. - Sin SDKs: HTTPS directo con
secureFetchde@botmesh/http(anti-SSRF, timeouts, límites de tamaño). install()es síncrono y nunca toca la red: los adapters se resuelven en el primer uso y un warmup no fatal registra al arrancar una configuración mala (falta la API key, proveedor desconocido).
Inicio rápido
autoTranscribe sin stt, o autoReplyWithVoice sin tts, lanza un
VoiceConfigError al instalar el plugin.
Transcribir notas de voz (autoTranscribe)
Un hook before('message') (prioridad 100, así que corre antes que la
mayoría) descarga cada nota de voz, la transcribe y deja:
ctx.transcript: el texto tal cual lo devolvió el servicio (''si no se dijo nada).ctx.body: el mismo texto sin la puntuación de los extremos (¡Hola!pasa aHola), para que los triggers por igualdad,c.ask(), la NLU y los handlers de IA “oigan” el audio.ctx.media: el audio descargado (no se vuelve a descargar).ctx.transcriptError: el error, si la transcripción falló. El mensaje se sigue enrutando (sin transcripción) y se registra un aviso.ctx.type: sigue siendo'voice', así que los triggersVOICE_NOTEsiguen disparando. Con el modo por defectoasText: 'auto'pasa a'text'solo mientras una conversación de ese usuario espera texto (c.ask(),c.waitText()), para que la nota de voz responda a la pregunta. Si la conversación espera audio (c.waitMedia(['voice'])), un menú (c.choose(), que leebody) o cualquier mensaje (c.waitAny()), la nota llega como'voice'con su transcripción.
autoTranscribe: false desactiva el hook; ctx.tools.voice.transcribe(ctx)
sigue disponible a demanda.
Con el autoresponder de IA
El autoresponder de@botmesh/ai solo contesta mensajes de
texto. Con el modo por defecto asText: 'auto', una nota de voz enviada
fuera de una conversación sigue siendo 'voice', así que la IA no la
contesta. Para que la IA conteste notas de voz, usa asText: true:
Idioma e i18n
La pista de idioma para el servicio de STT es, en este orden:autoTranscribe.language, si está definida.- El idioma que el i18n resolvió para el remitente
(
ctx.lang, código base:en-gbpasa aen), solo si viene de una señal real (preferencia guardada, provider o detector), nunca del valor por defecto a secas. Se desactiva conuseContextLanguage: false. - Si no, ninguna: el servicio detecta el idioma.
ctx.body, el core vuelve
a resolver el idioma si venía del detector o del valor por defecto, así que
una función detect del i18n ve el texto hablado. Un ctx.lang fijado por
un hook se respeta.
Responder con voz (autoReplyWithVoice)
Mientras el último mensaje entrante de un chat sea una nota de voz, el
texto saliente hacia ese chat se sintetiza y se envía como nota de voz. Es un
hook before('send'), así que cubre ctx.reply, los pasos say de los
flujos, c.say y el autoresponder de IA. Cuando el usuario vuelve a escribir
texto, el chat vuelve a texto.
Siempre se quedan en texto:
- Todo lo que envía
ctx.stream(): borradores, ediciones en vivo, trozos y el mensaje final (el payload de envío llevastream). Una respuesta en streaming debe seguir siendo un único texto editable. - Las ediciones en el sitio de
ctx.edit. - Las respuestas a un canal cuyo
capabilities.contentKindsno incluye'voice'.
ctx.tools.voice
El plugin inyecta un VoiceClient como ctx.tools.voice (tipado por
module augmentation, sin casts):
Opciones de transcripción:
language, prompt, signal, timeoutMs.
Opciones de síntesis: voice, instructions, signal, timeoutMs. Un
timeoutMs por llamada solo puede acortar limits.timeoutMs. source también
puede ser un Uint8Array.
Adapters
Todos los adapters incluidos aceptan además
timeoutMs (solo puede acortar
limits.timeoutMs), maxResponseBytes y, para servidores propios en una red
de confianza, allowHttp / allowPrivateHosts (por ejemplo un servidor
compatible con Whisper en http://127.0.0.1:8000/v1).
También puedes pasar un adapter propio (un modelo local, otro servicio):
Formatos de audio
- Entrada: las notas de voz de WhatsApp y Telegram llegan en OGG/Opus, que
Whisper,
gpt-4o-*-transcribey Deepgram aceptan directamente. Si el canal no informa el mimetype, se detecta por los primeros bytes (OGG, WAV, MP3, MP4/M4A, WebM, FLAC, AMR) y, si no, se asume OGG. - Salida en WhatsApp: una nota de voz debe ser OGG/Opus.
@botmesh/baileystranscodificakind: 'voice'a OGG/Opus por su cuenta (con el binario deffmpegque encuentre:FFMPEG_PATH, el paquete opcionalffmpeg-staticoffmpegen el PATH), así que cualquier formato vale. Para Cloud API y Telegram pide OGG/Opus al TTS: es el valor por defecto de OpenAI (format: 'opus'); en ElevenLabs usaoutputFormat: 'opus_48000_64'si tu plan lo ofrece. - Este paquete no transcodifica nada (no depende de
ffmpeg).
Límites
durationSec explícito o un archivo OGG). Para otros contenedores no se
estima.
Los límites se validan al enchufar el plugin (o al crear el VoiceClient):
timeoutMs debe estar entre 1 y 2147483647 ms (el rango de setTimeout) y los
demás topes deben ser positivos (Infinity desactiva un tope). Cualquier otro
valor lanza un VoiceConfigError que nombra el campo.
Errores
Todos extiendenVoiceError y llevan un code estable:
Los mensajes de error y los logs nunca incluyen API keys, audio ni
transcripciones. Se conserva el detalle de error del propio servicio (explica un 400 o un
401), pero antes se le quita la API key de la petición y todo lo que tenga forma de clave
(
sk-… o sk_…, aunque venga enmascarada).
Testing
fetch y resolver inyectados para probarlos sin
red. Más en el
README de @botmesh/voice.
Siguientes pasos
- Plugin de IA — contesta con IA las notas de voz transcritas
- Respuestas en streaming — por qué la salida en streaming se queda en texto
- Internacionalización (i18n) — de dónde sale
ctx.lang - Mensajes y media —
ctx.voice()y recibir media