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
All checks were successful
Build & Push Friends Image to Gitea Registry / build-and-push (push) Successful in 8s
This commit is contained in:
@@ -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=
|
||||||
|
|
||||||
|
|||||||
50
telegram.js
50
telegram.js
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user