Hello, dial

use telequick::TeleQuickClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {{
    let mut client = TeleQuickClient::new(
        "quic://engine.telequick.dev:9090",
        "/usr/local/lib/telequick_core_ffi.so",
    )?;

    client.dial(
        "sip:+15551234567@example.sip.livekit.cloud",  // to
        "default",                                      // trunk_id
        false,                                          // auto_barge_in
        250,                                            // barge_in_patience_ms
        None,                                           // client_id override
    ).await?;

    tokio::time::sleep(std::time::Duration::from_secs(30)).await;
    Ok(())
}}
The first RPC implicitly calls connect(), which:
  • Configures a quinn::Endpoint with system-trusted roots and ALPN h3.
  • Connects to the gateway.
  • Spawns the accept_uni loop that drives on_audio_frame / on_call_event.
  • Sends the initial EventStreamRequest.

Receiving audio and events

OnAudioFrame and OnCallEvent are Arc<dyn Fn(Vec<u8>) + Send + Sync>, so you can clone them into spawned tasks freely.
use std::sync::Arc;

client.on_audio_frame = Some(Arc::new(|payload| {{
    println!("audio: {{}} bytes", payload.len());
}}));

client.on_call_event = Some(Arc::new(|payload| {{
    // payload is the serde envelope of CallEvent — decode with the FFI
    // helpers if you need fields.
    println!("event: {{}} bytes", payload.len());
}}));
Set callbacks before the first RPC, otherwise events that arrive between connect and your assignment are silently dropped.

Pushing audio

let pcm = std::fs::read("./prompt.alaw")?;
let mut seq: u64 = 0;
for chunk in pcm.chunks(160) {{
    client.push_audio(call_sid, chunk, "PCMU", seq, false).await?;
    seq += 1;
    tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}}
client.push_audio(call_sid, &[], "PCMU", seq, true).await?;  // EOS

Hanging up

client.terminate(call_sid).await?;

Long-running services

For a service that lives for hours, hold the client across many RPC calls. The QUIC connection auto-reconnects through quinn’s keepalive — but mid-flight RPCs in the gap will return Err, so retry your dialer loop:
loop {{
    if let Err(e) = client.dial(to, trunk, false, 250, None).await {{
        eprintln!("dial failed: {{:?}}, retrying in 1s", e);
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
        continue;
    }}
    break;
}}