feat(telegram): ingest and render inbound voice messages
This commit is contained in:
@@ -51,6 +51,69 @@ function normalizeString(value: unknown) {
|
||||
return normalized || null;
|
||||
}
|
||||
|
||||
function normalizeNumber(value: unknown) {
|
||||
if (typeof value === "number" && Number.isFinite(value)) return value;
|
||||
if (typeof value === "string") {
|
||||
const parsed = Number(value);
|
||||
if (Number.isFinite(parsed)) return parsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
type TelegramMediaInfo = {
|
||||
kind: "voice" | "audio" | "video_note" | null;
|
||||
fileId: string | null;
|
||||
durationSec: number | null;
|
||||
mimeType: string | null;
|
||||
title: string | null;
|
||||
};
|
||||
|
||||
function pickTelegramMedia(message: JsonObject): TelegramMediaInfo {
|
||||
const voice = asObject(message.voice);
|
||||
if (Object.keys(voice).length > 0) {
|
||||
return {
|
||||
kind: "voice",
|
||||
fileId: normalizeString(voice.file_id),
|
||||
durationSec: normalizeNumber(voice.duration),
|
||||
mimeType: normalizeString(voice.mime_type),
|
||||
title: "Voice message",
|
||||
};
|
||||
}
|
||||
|
||||
const audio = asObject(message.audio);
|
||||
if (Object.keys(audio).length > 0) {
|
||||
const performer = normalizeString(audio.performer);
|
||||
const title = normalizeString(audio.title) ?? normalizeString(audio.file_name);
|
||||
const combinedTitle = performer && title ? `${performer} - ${title}` : title ?? performer;
|
||||
return {
|
||||
kind: "audio",
|
||||
fileId: normalizeString(audio.file_id),
|
||||
durationSec: normalizeNumber(audio.duration),
|
||||
mimeType: normalizeString(audio.mime_type),
|
||||
title: combinedTitle,
|
||||
};
|
||||
}
|
||||
|
||||
const videoNote = asObject(message.video_note);
|
||||
if (Object.keys(videoNote).length > 0) {
|
||||
return {
|
||||
kind: "video_note",
|
||||
fileId: normalizeString(videoNote.file_id),
|
||||
durationSec: normalizeNumber(videoNote.duration),
|
||||
mimeType: null,
|
||||
title: "Video note",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: null,
|
||||
fileId: null,
|
||||
durationSec: null,
|
||||
mimeType: null,
|
||||
title: null,
|
||||
};
|
||||
}
|
||||
|
||||
function detectDirection(message: JsonObject, chat: JsonObject, from: JsonObject): "IN" | "OUT" {
|
||||
if (typeof message.outgoing === "boolean") return message.outgoing ? "OUT" : "IN";
|
||||
if (typeof message.is_outgoing === "boolean") return message.is_outgoing ? "OUT" : "IN";
|
||||
@@ -113,7 +176,19 @@ export function parseTelegramBusinessUpdate(raw: unknown): OmniInboundEnvelopeV1
|
||||
? String(fallbackContactSource.id)
|
||||
: null;
|
||||
|
||||
const text = cropText(message.text) ?? cropText(message.caption);
|
||||
const media = pickTelegramMedia(message);
|
||||
const text =
|
||||
cropText(message.text) ??
|
||||
cropText(message.caption) ??
|
||||
(media.kind === "voice"
|
||||
? "[voice message]"
|
||||
: media.kind === "video_note"
|
||||
? "[video note]"
|
||||
: media.kind === "audio"
|
||||
? media.title
|
||||
? `[audio] ${media.title}`
|
||||
: "[audio]"
|
||||
: null);
|
||||
|
||||
const businessConnectionId =
|
||||
message.business_connection_id != null
|
||||
@@ -148,6 +223,11 @@ export function parseTelegramBusinessUpdate(raw: unknown): OmniInboundEnvelopeV1
|
||||
contactExternalId,
|
||||
text,
|
||||
businessConnectionId,
|
||||
mediaKind: media.kind,
|
||||
mediaFileId: media.fileId,
|
||||
mediaDurationSec: media.durationSec,
|
||||
mediaMimeType: media.mimeType,
|
||||
mediaTitle: media.title,
|
||||
updateId: updateId != null ? String(updateId) : null,
|
||||
chatTitle: typeof chat.title === "string" ? chat.title : null,
|
||||
chatUsername: normalizeString(chat.username),
|
||||
|
||||
Reference in New Issue
Block a user