Preuve · code, extrait

backend/flexylead-transcribe-telephony-recording.mjs

Dépôt jeremy-flexylead/flexylead-crm · commit ccbbaa07870e0f786729003dd6f8862e4bcc8256 · dossier de production synchronisé le 14 septembre 2026 à 08 h 46

Extrait tel qu'à ce commit : lignes 171–244, sur 279 (numérotation d'origine, 30 lignes de contexte autour de chaque citation) · fichier intégral sur GitHub (dépôt privé, accès requis)

Lignes citées par l'audit : 201–207 213–214

74 lignes reproduites · ← retour à l'audit · catalogue des preuves

171    audio_count: audios.length,
172    transcript_chars: transcriptText.length,
173    transcribed_at: transcribedAt,
174  };
175}
176
177async function updateTranscriptState(context, appelRowId, transcript, additionalFields = {}) {
178  await context.call("tables", "rows_update", {
179    table: "appels",
180    id: appelRowId,
181    fields: { transcript, ...additionalFields },
182  });
183}
184
185function terminalAnalysisFields(appel, status) {
186  return {
187    ai_analysis_status: status,
188    ...(!clean(appel.reservation_id) && ["", "pending_analysis"].includes(clean(appel.ai_outcome))
189      ? { ai_outcome: status === "unclassified" ? "unclassified" : "analysis_failed" }
190      : {}),
191  };
192}
193
194async function transcribe(context, storageFileId) {
195  const signed = await context.call("storage", "files_get_url", {
196    id: Number(storageFileId),
197    ttl_seconds: 30 * 60,
198  });
199  if (!clean(signed?.url)) throw new Error(`Storage URL unavailable for file ${storageFileId}`);
200
201  const response = await context.integration("deepgram", "deepgram_listen", {
202    url: signed.url,
203    language: "fr",
204    model: "nova-3",
205    smart_format: true,
206    punctuate: true,
207    diarize: true,
208  });
209  if (response && typeof response === "object" && response.success === false) {
210    throw new Error(`Deepgram transcription failed: ${clean(response.error || response.status) || "unknown error"}`);
211  }
212  const body = response?.data ?? response ?? {};
213  const transcript = body?.results?.channels?.[0]?.alternatives?.[0]?.transcript
214    ?? body?.transcript;
215  if (typeof transcript !== "string") {
216    throw new Error("Deepgram returned an unexpected response");
217  }
218  return transcript;
219}
220
221function objectValue(value) {
222  if (value && typeof value === "object" && !Array.isArray(value)) return value;
223  if (typeof value === "string") {
224    try {
225      const parsed = JSON.parse(value);
226      return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
227    } catch {
228      return {};
229    }
230  }
231  return {};
232}
233
234function arrayValue(value) {
235  if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
236  if (typeof value === "string") {
237    try {
238      const parsed = JSON.parse(value);
239      return Array.isArray(parsed) ? parsed.filter((item) => item && typeof item === "object") : [];
240    } catch {
241      return [];
242    }
243  }
244  return [];