Feat: Migrate voice transcription from OpenAI Whisper to Google Gemini 1.5 Flash API
All checks were successful
Build & Push Friends Image to Gitea Registry / build-and-push (push) Successful in 8s

This commit is contained in:
Kroonk
2026-05-22 18:16:10 +02:00
parent eccee059cf
commit 5ea49ef05f
2 changed files with 33 additions and 19 deletions

View File

@@ -6,5 +6,5 @@ DATA_DIR=./data
# Telegram Bot Integration # Telegram Bot Integration
TELEGRAM_BOT_TOKEN= TELEGRAM_BOT_TOKEN=
TELEGRAM_ALLOWED_CHAT_IDS= TELEGRAM_ALLOWED_CHAT_IDS=
OPENAI_API_KEY= GEMINI_API_KEY=

View File

@@ -77,34 +77,48 @@ async function downloadTelegramFile(filePath) {
return await res.arrayBuffer(); 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) { async function transcribeVoice(audioBuffer) {
if (!process.env.OPENAI_API_KEY) { const geminiKey = process.env.GEMINI_API_KEY;
throw new Error("OPENAI_API_KEY is not configured in .env."); if (!geminiKey) {
throw new Error("GEMINI_API_KEY is not configured in .env.");
} }
const formData = new FormData(); const base64Audio = Buffer.from(audioBuffer).toString("base64");
// Whisper requires the ogg/opus file to have a proper filename extension const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${geminiKey}`;
const blob = new Blob([audioBuffer], { type: "audio/ogg" });
formData.append("file", blob, "voice.ogg");
formData.append("model", "whisper-1");
formData.append("language", "de");
const res = await fetch("https://api.openai.com/v1/audio/transcriptions", { const res = await fetch(url, {
method: "POST", method: "POST",
headers: { 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) { if (!res.ok) {
const errorText = await res.text(); 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(); 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 // 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) // 2. Extract text (either message text or transcribe voice note)
if (voice) { if (voice) {
isVoice = true; isVoice = true;
if (!process.env.OPENAI_API_KEY) { if (!process.env.GEMINI_API_KEY) {
await sendTelegramMessage( await sendTelegramMessage(
chatId, chatId,
"⚠️ <b>Sprachnachrichten deaktiviert:</b>\nEs ist kein <code>OPENAI_API_KEY</code> in der <code>.env</code>-Konfiguration hinterlegt. Bitte sende stattdessen Textnachrichten oder füge den API-Key hinzu.", "⚠️ <b>Sprachnachrichten deaktiviert:</b>\nEs ist kein <code>GEMINI_API_KEY</code> in der <code>.env</code>-Konfiguration hinterlegt. Bitte erstelle einen kostenlosen Key im Google AI Studio und füge ihn hinzu.",
message.message_id message.message_id
); );
return; return;
@@ -204,7 +218,7 @@ async function handleUpdate(update) {
// Download file binary // Download file binary
const audioBuffer = await downloadTelegramFile(fileInfo.result.file_path); const audioBuffer = await downloadTelegramFile(fileInfo.result.file_path);
// Transcribe via Whisper // Transcribe via Gemini
processedText = await transcribeVoice(audioBuffer); processedText = await transcribeVoice(audioBuffer);
if (!processedText.trim()) { if (!processedText.trim()) {
await sendTelegramMessage( await sendTelegramMessage(
@@ -262,7 +276,7 @@ async function handleUpdate(update) {
let responseText = `<b>Eintrag erfasst!</b> ✅\n\n`; let responseText = `<b>Eintrag erfasst!</b> ✅\n\n`;
if (isVoice) { if (isVoice) {
responseText += `🎤 <b>Transkription:</b>\n<i>"${escapeHtml(processedText)}"</i>\n\n`; responseText += `🎤 <b>Transkription (Gemini):</b>\n<i>"${escapeHtml(processedText)}"</i>\n\n`;
} }
if (analysis.contact) { if (analysis.contact) {