| Crates.io | cal-jambonz-actix |
| lib.rs | cal-jambonz-actix |
| version | 0.1.25 |
| created_at | 2025-04-17 07:48:35.209359+00 |
| updated_at | 2025-04-17 19:50:41.770829+00 |
| description | Jambonz Verbs |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1637352 |
| size | 66,377 |
A lightweight, extensible WebSocket framework built with Actix Web for building voice and media applications using the Jambonz protocol. It allows dynamic routing and state-aware handling of WebSocket messages, such as call hooks and audio recordings.
ws.jambonz.org, audio.jambonz.org)#[derive(Clone)]
pub struct AppState {
pub message: String,
}
use cal_jambonz::ws::{JambonzRequest, RecordingRequest};
let recording_handler = register_handler(|mut ctx: HandlerContext<AppState>| async move {
match ctx.request {
JambonzRequest::Recording(RecordingRequest::SessionNew(session)) => {
println!("Started recording session: {:?}", session);
}
JambonzRequest::Recording(RecordingRequest::Binary(bytes)) => {
println!("Received audio bytes: {} bytes", bytes.len());
}
JambonzRequest::Recording(RecordingRequest::Close) => {
println!("Recording session closed");
}
_ => {}
}
});
use cal_jambonz::verbs::{Verbs, Say};
use cal_jambonz::ws::{JambonzRequest, WebsocketRequest};
let hook_handler = register_handler(|mut ctx: HandlerContext<AppState>| async move {
match ctx.request {
JambonzRequest::Hook(req) => {
match req {
WebsocketRequest::SessionNew(new_session) => {
let ack = Verbs::new(&new_session.msgid)
.say(Say::new("Welcome! Please wait while we find an agent.".to_string()))
.as_ack_reply()
.json();
let _ = ctx.session.text(ack).await;
}
WebsocketRequest::SessionRedirect(redirect) => {
println!("Session redirected: {:?}", redirect);
}
WebsocketRequest::SessionReconnect(reconnect) => {
println!("Session reconnected: {:?}", reconnect);
}
WebsocketRequest::CallStatus(status) => {
println!("Call status update: {:?}", status);
}
WebsocketRequest::VerbHook(hook) => {
println!("Verb hook event: {:?}", hook);
}
WebsocketRequest::Close => {
println!("Session closed");
}
}
}
_ => {
println!("Received non-hook request on hook handler");
}
}
});
use your_crate::{JambonzRoute, JambonzRouteType, JambonzWebServer};
let server = JambonzWebServer::new(AppState {
message: "stateful message".into(),
})
.with_bind_ip("127.0.0.1")
.with_bind_port(3000)
.add_route(JambonzRoute {
path: "/hook".into(),
ws_type: JambonzRouteType::Hook,
handler: hook_handler,
})
.add_route(JambonzRoute {
path: "/recording".into(),
ws_type: JambonzRouteType::Recording,
handler: recording_handler,
});
server.start();
The core WebSocket message types are represented using the JambonzRequest enum:
pub enum JambonzRequest {
Hook(WebsocketRequest),
Recording(RecordingRequest),
}
pub enum WebsocketRequest {
SessionNew(SessionNew),
SessionRedirect(SessionRedirect),
SessionReconnect(SessionReconnect),
CallStatus(SessionCallStatus),
VerbHook(SessionVerbHook),
Close,
}
These represent various control and lifecycle events during a live call.
pub enum RecordingRequest {
SessionNew(SessionRecording),
Binary(Vec<u8>),
Close,
}
Used for handling real-time audio streams and recording metadata.
Each handler receives a HandlerContext<T> containing:
pub struct HandlerContext<T> {
pub uuid: Uuid, // Unique per-session ID
pub session: Session, // WebSocket session object
pub request: JambonzRequest, // Incoming parsed request
pub state: Data<T>, // Your app state
}
Use ctx.session.text(...) to respond directly to the WebSocket client.
actix-web, actix-ws, uuid, serde, futures, etc.PRs and issues are welcome. Whether it's bug fixes, new features, or documentation improvements — let's build together.
MIT