use anyhow::Result;
use super::{Capability, CapabilityEdit};
pub(crate) static VOICE: Capability = Capability {
id: "voice",
resource_type: "voice",
summary: "outbound phone calls (Amazon Connect, held by the platform)",
registration_key: "VoiceClient",
receives_events: true,
workers: true,
add,
remove,
};
const IMPORT_SOURCE: &str = "@forklaunch/core/http";
#[cfg(test)]
const IMPORT_NAMES: &[&str] = &["createVoiceClient", "VoiceClient"];
const IMPORT_LINE: &str = "import { createVoiceClient, VoiceClient } from \"@forklaunch/core/http\";";
const RUNTIME_BLOCK: &str = "const configInjector = createConfigInjector(SchemaValidator(), {
VoiceClient: {
lifetime: Lifetime.Scoped,
type: VoiceClient,
factory: ({ PLATFORM_GATEWAY_URL, INSTANCE_ID, INSTANCE_HMAC_KEY }) =>
createVoiceClient({
gatewayUrl: PLATFORM_GATEWAY_URL,
instanceId: INSTANCE_ID,
hmacKey: INSTANCE_HMAC_KEY
})
}
});";
const HANDLER_STUB: &str = "import type {
PlatformEvent,
VoiceCallEndedEvent,
VoiceCallStartedEvent,
VoiceRecordingReadyEvent
} from '@forklaunch/core/http';
/**
* Voice call events from the ForkLaunch platform (Amazon Connect), already
* verified as signed with this instance's key. Generated by
* `forklaunch infra add <service> voice`; fill in what the app does.
*
* Deliveries can repeat: dedupe on event.id. Throwing makes the platform
* retry. Place calls with `ci.resolve(tokens.VoiceClient).startOutboundCall(
* { to, flow, attributes })`; never put health information in attributes.
*/
export async function handle(event: PlatformEvent): Promise<void> {
switch (event.type) {
case 'voice.call.started': {
const { callId } = event.data as unknown as VoiceCallStartedEvent;
// The call is ringing. Record callId against what it is about.
void callId;
return;
}
case 'voice.call.ended': {
const { callId, durationSeconds, disconnectReason } =
event.data as unknown as VoiceCallEndedEvent;
// disconnectReason: customer, api, busy, no_answer, … Retry or follow up here.
void [callId, durationSeconds, disconnectReason];
return;
}
case 'voice.recording.ready': {
const { callId, recordingKey } =
event.data as unknown as VoiceRecordingReadyEvent;
// recordingKey is a key in this instance's object store.
void [callId, recordingKey];
return;
}
default:
return;
}
}
";
fn add(edit: &mut CapabilityEdit) -> Result<()> {
edit.inject_registration(IMPORT_LINE, IMPORT_SOURCE, None, Some(RUNTIME_BLOCK))?;
edit.ensure_platform_events("voice", HANDLER_STUB)?;
Ok(())
}
fn remove(edit: &mut CapabilityEdit) -> Result<()> {
edit.remove_registration(None, &[], &["VoiceClient"])
}
#[cfg(test)]
mod tests;