From 5ea49ef05ff023760a3aa95a5396aa05d0fadc35 Mon Sep 17 00:00:00 2001 From: Kroonk Date: Fri, 22 May 2026 18:16:10 +0200 Subject: [PATCH] Feat: Migrate voice transcription from OpenAI Whisper to Google Gemini 1.5 Flash API --- .env.example | 2 +- telegram.js | 50 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index 29fda33..4b3e9e8 100644 --- a/.env.example +++ b/.env.example @@ -6,5 +6,5 @@ DATA_DIR=./data # Telegram Bot Integration TELEGRAM_BOT_TOKEN= TELEGRAM_ALLOWED_CHAT_IDS= -OPENAI_API_KEY= +GEMINI_API_KEY= diff --git a/telegram.js b/telegram.js index be0d10b..14776fb 100644 --- a/telegram.js +++ b/telegram.js @@ -77,34 +77,48 @@ async function downloadTelegramFile(filePath) { return await res.arrayBuffer(); } -// Transcribe OGG/Opus voice data using OpenAI Whisper API +// Transcribe OGG/Opus voice data using Google Gemini 1.5 Flash API (100% Free) async function transcribeVoice(audioBuffer) { - if (!process.env.OPENAI_API_KEY) { - throw new Error("OPENAI_API_KEY is not configured in .env."); + const geminiKey = process.env.GEMINI_API_KEY; + if (!geminiKey) { + throw new Error("GEMINI_API_KEY is not configured in .env."); } - const formData = new FormData(); - // Whisper requires the ogg/opus file to have a proper filename extension - const blob = new Blob([audioBuffer], { type: "audio/ogg" }); - formData.append("file", blob, "voice.ogg"); - formData.append("model", "whisper-1"); - formData.append("language", "de"); + const base64Audio = Buffer.from(audioBuffer).toString("base64"); + const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${geminiKey}`; - const res = await fetch("https://api.openai.com/v1/audio/transcriptions", { + const res = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, + "Content-Type": "application/json", }, - body: formData, + body: JSON.stringify({ + contents: [ + { + parts: [ + { + inlineData: { + mimeType: "audio/ogg", + data: base64Audio, + }, + }, + { + text: "Transkribiere diese Sprachnachricht wortgetreu in deutschen Text. Gib NUR die Transkription zurück, ohne Einleitung, Kommentare oder sonstige Zusätze. Falls nichts verständlich gesprochen wurde, antworte mit einem leeren Text.", + }, + ], + }, + ], + }), }); if (!res.ok) { const errorText = await res.text(); - throw new Error(`OpenAI Whisper API failed (${res.status}): ${errorText}`); + throw new Error(`Gemini API failed (${res.status}): ${errorText}`); } const data = await res.json(); - return data.text || ""; + const text = data.candidates?.[0]?.content?.parts?.[0]?.text || ""; + return text.trim(); } // Fetch available contacts from Monica to perform NLP name matching @@ -180,10 +194,10 @@ async function handleUpdate(update) { // 2. Extract text (either message text or transcribe voice note) if (voice) { isVoice = true; - if (!process.env.OPENAI_API_KEY) { + if (!process.env.GEMINI_API_KEY) { await sendTelegramMessage( chatId, - "⚠️ Sprachnachrichten deaktiviert:\nEs ist kein OPENAI_API_KEY in der .env-Konfiguration hinterlegt. Bitte sende stattdessen Textnachrichten oder füge den API-Key hinzu.", + "⚠️ Sprachnachrichten deaktiviert:\nEs ist kein GEMINI_API_KEY in der .env-Konfiguration hinterlegt. Bitte erstelle einen kostenlosen Key im Google AI Studio und füge ihn hinzu.", message.message_id ); return; @@ -204,7 +218,7 @@ async function handleUpdate(update) { // Download file binary const audioBuffer = await downloadTelegramFile(fileInfo.result.file_path); - // Transcribe via Whisper + // Transcribe via Gemini processedText = await transcribeVoice(audioBuffer); if (!processedText.trim()) { await sendTelegramMessage( @@ -262,7 +276,7 @@ async function handleUpdate(update) { let responseText = `Eintrag erfasst! ✅\n\n`; if (isVoice) { - responseText += `🎤 Transkription:\n"${escapeHtml(processedText)}"\n\n`; + responseText += `🎤 Transkription (Gemini):\n"${escapeHtml(processedText)}"\n\n`; } if (analysis.contact) {