feat(call, voicemail, ivr): add voicemail and IVR call flows with DTMF handling, prompt playback, recording, and dashboard management
This commit is contained in:
124
ts/config.ts
124
ts/config.ts
@@ -78,6 +78,17 @@ export interface ISipRouteAction {
|
||||
/** Also ring connected browser clients. Default false. */
|
||||
ringBrowsers?: boolean;
|
||||
|
||||
// --- Inbound actions (IVR / voicemail) ---
|
||||
|
||||
/** Route directly to a voicemail box (skip ringing devices). */
|
||||
voicemailBox?: string;
|
||||
|
||||
/** Route to an IVR menu by menu ID (skip ringing devices). */
|
||||
ivrMenuId?: string;
|
||||
|
||||
/** Override no-answer timeout (seconds) before routing to voicemail. */
|
||||
noAnswerTimeout?: number;
|
||||
|
||||
// --- Outbound actions (provider selection) ---
|
||||
|
||||
/** Provider ID to use for outbound. */
|
||||
@@ -137,12 +148,95 @@ export interface IContact {
|
||||
starred?: boolean;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Voicebox configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface IVoiceboxConfig {
|
||||
/** Unique ID — typically matches device ID or extension. */
|
||||
id: string;
|
||||
/** Whether this voicebox is active. */
|
||||
enabled: boolean;
|
||||
/** Custom TTS greeting text. */
|
||||
greetingText?: string;
|
||||
/** TTS voice ID (default 'af_bella'). */
|
||||
greetingVoice?: string;
|
||||
/** Path to uploaded WAV greeting (overrides TTS). */
|
||||
greetingWavPath?: string;
|
||||
/** Seconds to wait before routing to voicemail (default 25). */
|
||||
noAnswerTimeoutSec?: number;
|
||||
/** Maximum recording duration in seconds (default 120). */
|
||||
maxRecordingSec?: number;
|
||||
/** Maximum stored messages per box (default 50). */
|
||||
maxMessages?: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IVR configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** An action triggered by a digit press in an IVR menu. */
|
||||
export type TIvrAction =
|
||||
| { type: 'route-extension'; extensionId: string }
|
||||
| { type: 'route-voicemail'; boxId: string }
|
||||
| { type: 'submenu'; menuId: string }
|
||||
| { type: 'play-message'; promptId: string }
|
||||
| { type: 'transfer'; number: string; providerId?: string }
|
||||
| { type: 'repeat' }
|
||||
| { type: 'hangup' };
|
||||
|
||||
/** A single digit→action mapping in an IVR menu. */
|
||||
export interface IIvrMenuEntry {
|
||||
/** Digit: '0'-'9', '*', '#'. */
|
||||
digit: string;
|
||||
/** Action to take when this digit is pressed. */
|
||||
action: TIvrAction;
|
||||
}
|
||||
|
||||
/** An IVR menu with a prompt and digit mappings. */
|
||||
export interface IIvrMenu {
|
||||
/** Unique menu ID. */
|
||||
id: string;
|
||||
/** Human-readable name. */
|
||||
name: string;
|
||||
/** TTS text for the menu prompt. */
|
||||
promptText: string;
|
||||
/** TTS voice ID for the prompt. */
|
||||
promptVoice?: string;
|
||||
/** Digit→action entries. */
|
||||
entries: IIvrMenuEntry[];
|
||||
/** Seconds to wait for a digit after prompt finishes (default 5). */
|
||||
timeoutSec?: number;
|
||||
/** Maximum retries before executing timeout action (default 3). */
|
||||
maxRetries?: number;
|
||||
/** Action on timeout (no digit pressed). */
|
||||
timeoutAction: TIvrAction;
|
||||
/** Action on invalid digit. */
|
||||
invalidAction: TIvrAction;
|
||||
}
|
||||
|
||||
/** Top-level IVR configuration. */
|
||||
export interface IIvrConfig {
|
||||
/** Whether the IVR system is active. */
|
||||
enabled: boolean;
|
||||
/** IVR menu definitions. */
|
||||
menus: IIvrMenu[];
|
||||
/** The menu to start with for incoming calls. */
|
||||
entryMenuId: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// App config
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface IAppConfig {
|
||||
proxy: IProxyConfig;
|
||||
providers: IProviderConfig[];
|
||||
devices: IDeviceConfig[];
|
||||
routing: IRoutingConfig;
|
||||
contacts: IContact[];
|
||||
voiceboxes?: IVoiceboxConfig[];
|
||||
ivr?: IIvrConfig;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -201,6 +295,27 @@ export function loadConfig(): IAppConfig {
|
||||
c.starred ??= false;
|
||||
}
|
||||
|
||||
// Voicebox defaults.
|
||||
cfg.voiceboxes ??= [];
|
||||
for (const vb of cfg.voiceboxes) {
|
||||
vb.enabled ??= true;
|
||||
vb.noAnswerTimeoutSec ??= 25;
|
||||
vb.maxRecordingSec ??= 120;
|
||||
vb.maxMessages ??= 50;
|
||||
vb.greetingVoice ??= 'af_bella';
|
||||
}
|
||||
|
||||
// IVR defaults.
|
||||
if (cfg.ivr) {
|
||||
cfg.ivr.enabled ??= false;
|
||||
cfg.ivr.menus ??= [];
|
||||
for (const menu of cfg.ivr.menus) {
|
||||
menu.timeoutSec ??= 5;
|
||||
menu.maxRetries ??= 3;
|
||||
menu.entries ??= [];
|
||||
}
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@@ -251,6 +366,12 @@ export interface IInboundRouteResult {
|
||||
/** Device IDs to ring (empty = all devices). */
|
||||
deviceIds: string[];
|
||||
ringBrowsers: boolean;
|
||||
/** If set, route directly to this voicemail box (skip ringing). */
|
||||
voicemailBox?: string;
|
||||
/** If set, route to this IVR menu (skip ringing). */
|
||||
ivrMenuId?: string;
|
||||
/** Override for no-answer timeout in seconds. */
|
||||
noAnswerTimeout?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,6 +453,9 @@ export function resolveInboundRoute(
|
||||
return {
|
||||
deviceIds: route.action.targets || [],
|
||||
ringBrowsers: route.action.ringBrowsers ?? false,
|
||||
voicemailBox: route.action.voicemailBox,
|
||||
ivrMenuId: route.action.ivrMenuId,
|
||||
noAnswerTimeout: route.action.noAnswerTimeout,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user