Passer à la navigation

Bot Telegram de transcription

Concevez un bot Telegram qui transcrit des messages audio et vidéo dans plus de 90 langues avec TypeScript, Deno et les fonctions Edge de Supabase.

Guide pratique · Suppose que vous avez suivi le guide de démarrage rapide de Speech to Text et que vous disposez d’un jeton de bot Telegram ainsi que d’un compte Supabase.

Introduction

Dans ce tutoriel, vous apprendrez à concevoir un bot Telegram qui transcrit des messages audio et vidéo dans plus de 90 langues à l’aide de TypeScript et du modèle Scribe d’ElevenLabs, via l’API Speech to Text.

Prérequis

Configuration

Enregistrer un bot Telegram

Utilisez le BotFather pour créer un bot Telegram. Exécutez la commande /newbot et suivez les instructions pour créer votre bot. À la fin, vous recevrez votre jeton de bot secret. Conservez-le en lieu sûr pour l’étape suivante.

BotFather

Créer un projet Supabase en local

Après avoir installé la CLI Supabase, exécutez la commande suivante pour créer un projet Supabase en local :

supabase init

Créer une table de base de données pour enregistrer les résultats de transcription

Créez ensuite une table de base de données pour enregistrer les résultats de transcription :

supabase migrations new init

Cette commande crée un fichier de migration dans le répertoire supabase/migrations. Ouvrez ce fichier et ajoutez le SQL suivant :

supabase/migrations/init.sql
CREATE TABLE IF NOT EXISTS transcription_logs (
id BIGSERIAL PRIMARY KEY,
file_type VARCHAR NOT NULL,
duration INTEGER NOT NULL,
chat_id BIGINT NOT NULL,
message_id BIGINT NOT NULL,
username VARCHAR,
transcript TEXT,
language_code VARCHAR,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
error TEXT
);
ALTER TABLE transcription_logs ENABLE ROW LEVEL SECURITY;

Créer une fonction Edge Supabase pour gérer les requêtes webhook Telegram

Créez ensuite une fonction Edge pour gérer les requêtes webhook Telegram :

supabase functions new scribe-bot

Si vous utilisez VS Code ou Cursor, sélectionnez y lorsque la CLI vous demande « Generate VS Code settings for Deno? [y/N] ».

Configurer les variables d’environnement

Dans le répertoire supabase/functions, créez un fichier .env et ajoutez les variables suivantes :

supabase/functions/.env
# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
ELEVENLABS_API_KEY=your_api_key
# The bot token you received from the BotFather.
TELEGRAM_BOT_TOKEN=your_bot_token
# A random secret chosen by you to secure the function.
FUNCTION_SECRET=random_secret

Dépendances

Le projet utilise quelques dépendances :

  • Le framework open source grammY pour gérer les requêtes webhook Telegram.
  • La bibliothèque @supabase/supabase-js pour interagir avec la base de données Supabase.
  • Le SDK JavaScript d’ElevenLabs pour interagir avec l’API Speech to Text.

Les fonctions Edge Supabase utilisent l’environnement d’exécution Deno. Vous n’avez donc pas besoin d’installer les dépendances, mais pouvez les importer avec le préfixe npm:.

Programmer le bot Telegram

Dans votre nouveau fichier scribe-bot/index.ts, ajoutez le code suivant :

supabase/functions/scribe-bot/index.ts
import { Bot, webhookCallback } from "https://deno.land/x/grammy@v1.34.0/mod.ts";
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";
import { ElevenLabsClient } from "npm:elevenlabs@1.50.5";
console.log(`Function "elevenlabs-scribe-bot" up and running!`);
const elevenlabs = new ElevenLabsClient({
apiKey: Deno.env.get("ELEVENLABS_API_KEY") || "",
});
const supabase = createClient(
Deno.env.get("SUPABASE_URL") || "",
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || ""
);
async function scribe({
fileURL,
fileType,
duration,
chatId,
messageId,
username,
}: {
fileURL: string;
fileType: string;
duration: number;
chatId: number;
messageId: number;
username: string;
}) {
let transcript: string | null = null;
let languageCode: string | null = null;
let errorMsg: string | null = null;
try {
const sourceFileArrayBuffer = await fetch(fileURL).then((res) => res.arrayBuffer());
const sourceBlob = new Blob([sourceFileArrayBuffer], {
type: fileType,
});
const scribeResult = await elevenlabs.speechToText.convert({
file: sourceBlob,
model_id: "scribe_v3",
tag_audio_events: false,
});
transcript = scribeResult.text;
languageCode = scribeResult.language_code;
// Reply to the user with the transcript
await bot.api.sendMessage(chatId, transcript, {
reply_parameters: { message_id: messageId },
});
} catch (error) {
errorMsg = error.message;
console.log(errorMsg);
await bot.api.sendMessage(chatId, "Sorry, there was an error. Please try again.", {
reply_parameters: { message_id: messageId },
});
}
// Write log to Supabase.
const logLine = {
file_type: fileType,
duration,
chat_id: chatId,
message_id: messageId,
username,
language_code: languageCode,
error: errorMsg,
};
console.log({ logLine });
await supabase.from("transcription_logs").insert({ ...logLine, transcript });
}
const telegramBotToken = Deno.env.get("TELEGRAM_BOT_TOKEN");
const bot = new Bot(telegramBotToken || "");
const startMessage = `Welcome to the ElevenLabs Scribe Bot\\! I can transcribe speech in 90\\+ languages with super high accuracy\\!
\nTry it out by sending or forwarding me a voice message, video, or audio file\\!
\n[Learn more about Scribe](https://elevenlabs.io/speech-to-text) or [build your own bot](https://elevenlabs.io/developers/guides/cookbooks/speech-to-text/telegram-bot)\\!
`;
bot.command("start", (ctx) => ctx.reply(startMessage.trim(), { parse_mode: "MarkdownV2" }));
bot.on([":voice", ":audio", ":video"], async (ctx) => {
try {
const file = await ctx.getFile();
const fileURL = `https://api.telegram.org/file/bot${telegramBotToken}/${file.file_path}`;
const fileMeta = ctx.message?.video ?? ctx.message?.voice ?? ctx.message?.audio;
if (!fileMeta) {
return ctx.reply("No video|audio|voice metadata found. Please try again.");
}
// Run the transcription in the background.
EdgeRuntime.waitUntil(
scribe({
fileURL,
fileType: fileMeta.mime_type!,
duration: fileMeta.duration,
chatId: ctx.chat.id,
messageId: ctx.message?.message_id!,
username: ctx.from?.username || "",
})
);
// Reply to the user immediately to let them know we received their file.
return ctx.reply("Received. Scribing...");
} catch (error) {
console.error(error);
return ctx.reply(
"Sorry, there was an error getting the file. Please try again with a smaller file!"
);
}
});
const handleUpdate = webhookCallback(bot, "std/http");
Deno.serve(async (req) => {
try {
const url = new URL(req.url);
if (url.searchParams.get("secret") !== Deno.env.get("FUNCTION_SECRET")) {
return new Response("not allowed", { status: 405 });
}
return await handleUpdate(req);
} catch (err) {
console.error(err);
}
});

Analyse détaillée du code

Quelques éléments du code méritent votre attention. Examinons-les étape par étape.

1

Gestion de la requête entrante

Pour gérer la requête entrante, utilisez le gestionnaire Deno.serve. Il vérifie que la requête contient le bon secret, puis la transmet à la fonction handleUpdate.

const handleUpdate = webhookCallback(bot, 'std/http');
Deno.serve(async (req) => {
try {
const url = new URL(req.url);
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) {
return new Response('not allowed', { status: 405 });
}
return await handleUpdate(req);
} catch (err) {
console.error(err);
}
});
2

Gérer les messages vocaux, audio et vidéo

Le framework grammY permet facilement de filtrer des types de messages spécifiques. Ici, le bot écoute les messages vocaux, audio et vidéo.

À partir du contexte de la requête, le bot extrait les métadonnées du fichier, puis utilise EdgeRuntime.waitUntil des tâches d’arrière-plan Supabase pour exécuter la transcription en arrière-plan.

Vous pouvez ainsi répondre immédiatement à l’utilisateur tout en traitant la transcription du fichier en arrière-plan.

bot.on([':voice', ':audio', ':video'], async (ctx) => {
try {
const file = await ctx.getFile();
const fileURL = `https://api.telegram.org/file/bot${telegramBotToken}/${file.file_path}`;
const fileMeta = ctx.message?.video ?? ctx.message?.voice ?? ctx.message?.audio;
if (!fileMeta) {
return ctx.reply('No video|audio|voice metadata found. Please try again.');
}
// Run the transcription in the background.
EdgeRuntime.waitUntil(
scribe({
fileURL,
fileType: fileMeta.mime_type!,
duration: fileMeta.duration,
chatId: ctx.chat.id,
messageId: ctx.message?.message_id!,
username: ctx.from?.username || '',
})
);
// Reply to the user immediately to let them know we received their file.
return ctx.reply('Received. Scribing...');
} catch (error) {
console.error(error);
return ctx.reply(
'Sorry, there was an error getting the file. Please try again with a smaller file!'
);
}
});
3

Transcription avec l'API ElevenLabs

Enfin, dans le worker d’arrière-plan, le bot utilise le SDK JavaScript d’ElevenLabs pour transcrire le fichier. Une fois la transcription terminée, il répond à l’utilisateur avec le texte transcrit et ajoute une entrée de journal dans la base de données Supabase à l’aide de supabase-js.

const elevenlabs = new ElevenLabsClient({
apiKey: Deno.env.get('ELEVENLABS_API_KEY') || '',
});
const supabase = createClient(
Deno.env.get('SUPABASE_URL') || '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') || ''
);
async function scribe({
fileURL,
fileType,
duration,
chatId,
messageId,
username,
}: {
fileURL: string;
fileType: string;
duration: number;
chatId: number;
messageId: number;
username: string;
}) {
let transcript: string | null = null;
let languageCode: string | null = null;
let errorMsg: string | null = null;
try {
const sourceFileArrayBuffer = await fetch(fileURL).then((res) => res.arrayBuffer());
const sourceBlob = new Blob([sourceFileArrayBuffer], {
type: fileType,
});
const scribeResult = await elevenlabs.speechToText.convert({
file: sourceBlob,
model_id: 'scribe_v3',
tag_audio_events: false,
});
transcript = scribeResult.text;
languageCode = scribeResult.language_code;
// Reply to the user with the transcript
await bot.api.sendMessage(chatId, transcript, {
reply_parameters: { message_id: messageId },
});
} catch (error) {
errorMsg = error.message;
console.log(errorMsg);
await bot.api.sendMessage(chatId, 'Sorry, there was an error. Please try again.', {
reply_parameters: { message_id: messageId },
});
}
// Write log to Supabase.
const logLine = {
file_type: fileType,
duration,
chat_id: chatId,
message_id: messageId,
username,
language_code: languageCode,
error: errorMsg,
};
console.log({ logLine });
await supabase.from('transcription_logs').insert({ ...logLine, transcript });
}

Déployer sur Supabase

Si ce n’est pas déjà fait, créez un compte Supabase sur database.new, puis liez le projet local à votre compte Supabase :

supabase link

Appliquer les migrations de base de données

Exécutez la commande suivante pour appliquer les migrations de base de données du répertoire supabase/migrations :

supabase db push

Accédez à l’éditeur de tables de votre Dashboard Supabase. Vous devriez voir une table transcription_logs vide.

Table vide

Enfin, exécutez la commande suivante pour déployer la fonction Edge :

supabase functions deploy --no-verify-jwt scribe-bot

Accédez à la vue des fonctions Edge de votre Dashboard Supabase. Vous devriez voir la fonction scribe-bot déployée. Notez l’URL de la fonction, car vous en aurez besoin plus tard. Elle devrait ressembler à https://<project-ref>.functions.supabase.co/scribe-bot.

Fonction Edge déployée

Configurer le webhook

Définissez l’URL webhook de votre bot sur https://<PROJECT_REFERENCE>.functions.supabase.co/telegram-bot (remplacez <...> par les valeurs correspondantes). Pour cela, envoyez simplement une requête GET à l’URL suivante, par exemple dans votre navigateur :

https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/setWebhook?url=https://<PROJECT_REFERENCE>.supabase.co/functions/v1/scribe-bot?secret=<FUNCTION_SECRET>

Notez que FUNCTION_SECRET correspond au secret défini dans votre fichier .env.

Configurer le webhook

Définir les secrets de la fonction

Maintenant que tous vos secrets sont configurés en local, exécutez la commande suivante pour les définir dans votre projet Supabase :

supabase secrets set --env-file supabase/functions/.env

Tester le bot

Vous pouvez maintenant tester le bot en lui envoyant un message vocal, un fichier audio ou une vidéo.

Tester le bot

Après avoir reçu le texte transcrit en réponse, revenez à l’éditeur de tables de votre Dashboard Supabase. Vous devriez voir une nouvelle ligne dans votre table transcription_logs.

Nouvelle ligne dans la table

Étapes suivantes