1use axum::extract::State;
2use axum::http::{HeaderMap, StatusCode};
3use axum::response::IntoResponse;
4use axum::Json;
5use serde::{Deserialize, Serialize};
6
7use crate::registry::CallRegistry;
8use crate::AppState;
9
10use super::outbound::check_auth;
11
12#[derive(Debug, Deserialize)]
13pub struct InjectRequest {
14 pub call_sid: String,
16 pub text: String,
18}
19
20#[derive(Debug, Serialize)]
21struct InjectResponse {
22 status: String,
23}
24
25#[derive(Debug, Serialize)]
26struct ErrorResponse {
27 error: String,
28}
29
30pub async fn handle_inject(
38 State(state): State<AppState>,
39 headers: HeaderMap,
40 Json(req): Json<InjectRequest>,
41) -> impl IntoResponse {
42 if let Err(resp) = check_auth(&headers, &state.config.api.token) {
43 return resp;
44 }
45
46 tracing::info!(call_sid = %req.call_sid, text_len = req.text.len(), "Inject requested");
47
48 let entry = state.call_registry.get(&req.call_sid).await;
50 let Some(entry) = entry else {
51 tracing::warn!(call_sid = %req.call_sid, "No active call found for inject");
52 return (
53 StatusCode::NOT_FOUND,
54 Json(ErrorResponse {
55 error: format!("No active call with sid {}", req.call_sid),
56 }),
57 )
58 .into_response();
59 };
60
61 let tts_mulaw = match state.tts.synthesize(&req.text).await {
63 Ok(data) => data,
64 Err(e) => {
65 tracing::error!(call_sid = %req.call_sid, "TTS failed for inject: {e}");
66 return (
67 StatusCode::INTERNAL_SERVER_ERROR,
68 Json(ErrorResponse {
69 error: format!("TTS synthesis failed: {e}"),
70 }),
71 )
72 .into_response();
73 }
74 };
75
76 entry.set_speaking(true);
78
79 if let Err(e) = CallRegistry::send_audio(&entry, &tts_mulaw).await {
81 tracing::error!(call_sid = %req.call_sid, "Failed to inject audio: {e}");
82 entry.set_speaking(false);
83 return (
84 StatusCode::INTERNAL_SERVER_ERROR,
85 Json(ErrorResponse {
86 error: format!("Failed to send audio: {e}"),
87 }),
88 )
89 .into_response();
90 }
91
92 tracing::info!(
93 call_sid = %req.call_sid,
94 tts_bytes = tts_mulaw.len(),
95 "Audio injected successfully"
96 );
97
98 (
99 StatusCode::OK,
100 Json(InjectResponse {
101 status: "injected".to_string(),
102 }),
103 )
104 .into_response()
105}