> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Telegram-bot voor transcriptie

> **Note**
>
> **Handleiding** · Er wordt van uitgegaan dat je de [Speech to Text quickstart](/docs/nl/eleven-api/guides/cookbooks/speech-to-text) hebt voltooid en een Telegram-bottoken en
> Supabase-account hebt.

## Inleiding

In deze tutorial leer je hoe je een Telegram-bot bouwt die audio- en videoberichten in meer dan 90 talen transcribeert met TypeScript en het ElevenLabs Scribe-model via de speech-to-text-API.

## Vereisten

* Een ElevenLabs-account met een [API-sleutel](https://elevenlabs.io/app/settings/api-keys).
* Een [Supabase](https://supabase.com)-account (je kunt een gratis account aanmaken via [database.new](https://database.new)).
* De [Supabase CLI](https://supabase.com/docs/guides/local-development), geïnstalleerd op je computer.
* De [Deno-runtime](https://docs.deno.com/runtime/getting_started/installation/), geïnstalleerd op je computer, en eventueel [ingesteld in je favoriete IDE](https://docs.deno.com/runtime/getting_started/setup_your_environment).
* Een [Telegram](https://telegram.org)-account.

## Instellen

### Een Telegram-bot registreren

Gebruik [BotFather](https://t.me/BotFather) om een nieuwe Telegram-bot te maken. Voer de opdracht `/newbot` uit en volg de instructies om een nieuwe bot te maken. Aan het einde ontvang je je geheime bottoken. Bewaar deze veilig voor de volgende stap.

![BotFather](/docs/_fern-img/28aa856bb6ace1b49b82076a18f1e281a8a4f37bbb6cfc59c22d644564377248.webp)

### Lokaal een Supabase-project maken

Voer na het installeren van de [Supabase CLI](https://supabase.com/docs/guides/local-development) de volgende opdracht uit om lokaal een nieuw Supabase-project te maken:

```bash
supabase init
```

### Een databasetabel maken om de transcriptieresultaten te loggen

Maak vervolgens een nieuwe databasetabel om de transcriptieresultaten te loggen:

```bash
supabase migrations new init
```

Hiermee wordt een nieuw migratiebestand gemaakt in de directory `supabase/migrations`. Open het bestand en voeg de volgende SQL toe:

**`supabase/migrations/init.sql`**

```sql 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;
```

### Een Supabase Edge Function maken om Telegram-webhookrequests af te handelen

Maak vervolgens een nieuwe Edge Function om Telegram-webhookrequests af te handelen:

```bash
supabase functions new scribe-bot
```

Als je VS Code of Cursor gebruikt, selecteer je `y` wanneer de CLI vraagt: "Generate VS Code settings for Deno? \[y/N]"!

### De omgevingsvariabelen instellen

Maak in de directory `supabase/functions` een nieuw `.env`-bestand en voeg de volgende variabelen toe:

**`supabase/functions/.env`**

```env 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
```

### Afhankelijkheden

Het project gebruikt enkele afhankelijkheden:

* Het opensource [grammY Framework](https://grammy.dev/) om de Telegram-webhookrequests af te handelen.
* De bibliotheek [@supabase/supabase-js](https://supabase.com/docs/reference/javascript) om met de Supabase-database te werken.
* De ElevenLabs [JavaScript SDK](/docs/nl/eleven-api/quickstart) om met de speech-to-text-API te werken.

Omdat Supabase Edge Function de [Deno-runtime](https://deno.land/) gebruikt, hoef je de afhankelijkheden niet te installeren. Je kunt ze via het voorvoegsel `npm:` [importeren](https://docs.deno.com/examples/npm/).

## De Telegram-bot programmeren

Voeg de volgende code toe aan je nieuw gemaakte bestand `scribe-bot/index.ts`:

**`supabase/functions/scribe-bot/index.ts`**

```ts 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_v2",
      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);
  }
});
```

### Toelichting op de code

Er zijn een paar zaken in de code die het vermelden waard zijn. We lopen ze stap voor stap door.

#### De inkomende request afhandelen

Gebruik de handler `Deno.serve` om de inkomende request af te handelen. De handler controleert of de request het juiste geheim bevat en geeft de request vervolgens door aan de functie `handleUpdate`.

```ts {1,6,10}
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);
  }
});
```

#### Spraak-, audio- en videoberichten afhandelen

Het grammY-framework biedt een handige manier om op specifieke berichttypen te [filteren](https://grammy.dev/guide/filter-queries#combining-multiple-queries). In dit geval luistert de bot naar spraak-, audio- en videoberichten.

Met de requestcontext haalt de bot de bestandsmetadata op en gebruikt vervolgens [Supabase Background Tasks](https://supabase.com/docs/guides/functions/background-tasks) `EdgeRuntime.waitUntil` om de transcriptie op de achtergrond uit te voeren.

Zo kun je de gebruiker direct een reactie geven en de transcriptie van het bestand op de achtergrond afhandelen.

```ts {1,3,12,24}
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!'
    );
  }
});
```

#### Transcriptie met de ElevenLabs API

Ten slotte gebruikt de bot in de achtergrondworker de ElevenLabs JavaScript SDK om het bestand te transcriberen. Zodra de transcriptie is voltooid, antwoordt de bot de gebruiker met het transcript en schrijft deze een logvermelding naar de Supabase-database met [supabase-js](https://supabase.com/docs/reference/javascript).

```ts {29-38,43-46,54-65}
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_v2',
      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 });
}
```

## Implementeren in Supabase

Maak, als je dat nog niet hebt gedaan, een nieuw Supabase-account aan op [database.new](https://database.new) en koppel het lokale project aan je Supabase-account:

```bash
supabase link
```

### De databasemigraties toepassen

Voer de volgende opdracht uit om de databasemigraties uit de directory `supabase/migrations` toe te passen:

```bash
supabase db push
```

Ga naar de [tabeleditor](https://supabase.com/dashboard/project/_/editor) in je Supabase-dashboard. Daar zou je een lege tabel `transcription_logs` moeten zien.

![Lege tabel](/docs/_fern-img/c1493deafcd6c53712dcb4fa7a44b3253b571ade0bb2e9c2b4fdb090088b0695.webp)

Voer ten slotte de volgende opdracht uit om de Edge Function te implementeren:

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

Ga naar de [weergave Edge Functions](https://supabase.com/dashboard/project/_/functions) in je Supabase-dashboard. Daar zou je de geïmplementeerde functie `scribe-bot` moeten zien. Noteer de functie-URL, want je hebt deze later nodig. Deze ziet er ongeveer zo uit: `https://<project-ref>.functions.supabase.co/scribe-bot`.

![Edge Function geïmplementeerd](/docs/_fern-img/d12141a51a09563d625b379201de844800e0c7dcfa9e6c7f21484cbca7ff37cf.webp)

### De webhook instellen

Stel de webhook-URL van je bot in op `https://<PROJECT_REFERENCE>.functions.supabase.co/telegram-bot` (vervang `<...>` door de juiste waarden). Voer hiervoor bijvoorbeeld in je browser een GET-request uit naar de volgende URL:

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

`FUNCTION_SECRET` is het geheim dat je in je `.env`-bestand hebt ingesteld.

![Webhook instellen](/docs/_fern-img/ac72044bd3df138da3d8a0df09e8413e8a9fd8a5c3156a525d6b13664854482f.webp)

### De functiegeheimen instellen

Nu je alle geheimen lokaal hebt ingesteld, kun je de volgende opdracht uitvoeren om de geheimen in je Supabase-project in te stellen:

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

## De bot testen

Ten slotte kun je de bot testen door er een spraakbericht, audiobestand of videobestand naartoe te sturen.

![De bot testen](/docs/_fern-img/a42c784e6324f15575e0299bafe02a253a2bdc57f2ad5844f83e5b64297780e7.webp)

Nadat je het transcript als antwoord hebt gezien, ga je terug naar de tabeleditor in het Supabase-dashboard. Daar zou je een nieuwe rij in de tabel `transcription_logs` moeten zien.

![Nieuwe rij in tabel](/docs/_fern-img/25c008858276b7cd4af6f8e72473891176bb9e2ce62e7002af353a2dac205e06.webp)

## Volgende stappen

#### [API-referentie](/docs/nl/api-reference/speech-to-text)

Volledige Speech to Text API-referentie en parameters.

#### [Twilio-integratie](/docs/nl/eleven-api/guides/how-to/text-to-speech/twilio)

Integreer ElevenLabs TTS met Twilio voor spraaktoepassingen via de telefoon.