use std::sync::Arc;
use tracing::info;
use crate::error::PluginError;
use crate::traits::{CancellationToken, ChannelAdapter, ChannelAdapterHost};
use super::channel::{VoiceChannel, VoiceStatus};
pub struct TalkModeController {
channel: Arc<VoiceChannel>,
cancel: CancellationToken,
}
impl TalkModeController {
pub fn new(channel: Arc<VoiceChannel>, cancel: CancellationToken) -> Self {
Self { channel, cancel }
}
pub async fn run(&self, host: Arc<dyn ChannelAdapterHost>) -> Result<(), PluginError> {
info!("Talk Mode starting");
let result = self.channel.start(host, self.cancel.clone()).await;
info!("Talk Mode ended");
result
}
pub async fn status(&self) -> VoiceStatus {
self.channel.current_status().await
}
pub fn channel(&self) -> &Arc<VoiceChannel> {
&self.channel
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use async_trait::async_trait;
use crate::message::MessagePayload;
struct StubHost;
#[async_trait]
impl ChannelAdapterHost for StubHost {
async fn deliver_inbound(
&self,
_channel: &str,
_sender_id: &str,
_chat_id: &str,
_payload: MessagePayload,
_metadata: HashMap<String, serde_json::Value>,
) -> Result<(), PluginError> {
Ok(())
}
}
#[tokio::test]
async fn talk_mode_status_starts_idle() {
let (channel, _rx) = VoiceChannel::new();
let cancel = CancellationToken::new();
let controller = TalkModeController::new(Arc::new(channel), cancel);
assert_eq!(controller.status().await, VoiceStatus::Idle);
}
#[tokio::test]
async fn talk_mode_run_and_cancel() {
let (channel, _rx) = VoiceChannel::new();
let cancel = CancellationToken::new();
let cancel_clone = cancel.clone();
let controller = Arc::new(TalkModeController::new(Arc::new(channel), cancel_clone));
let host: Arc<dyn ChannelAdapterHost> = Arc::new(StubHost);
let handle = tokio::spawn({
let controller = Arc::clone(&controller);
async move { controller.run(host).await }
});
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
cancel.cancel();
let result = handle.await.unwrap();
assert!(result.is_ok());
}
}