use std::sync::Arc;
use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use tokio::sync::{mpsc, RwLock};
use tokio_util::sync::CancellationToken;
use tracing::info;
use crate::config::{ChannelConfig, OutputConfig};
use crate::types::{ChatId, FormattedResponse, InboundMessage, WorkspaceHandle};
pub mod slack;
pub mod telegram;
pub mod whatsapp;
#[async_trait]
pub trait ChannelProvider: Send + Sync {
async fn start(
&self,
tx: mpsc::Sender<InboundMessage>,
self_arc: Arc<dyn ChannelProvider>,
shutdown: CancellationToken,
) -> Result<()>;
async fn send_response(&self, chat_id: &ChatId, response: FormattedResponse) -> Result<()>;
}
#[async_trait]
pub trait ChannelProviderFactory: ChannelProvider + Sized {
async fn create(
ch_config: &ChannelConfig,
workspace: Arc<RwLock<WorkspaceHandle>>,
global_output: &Arc<OutputConfig>,
) -> Result<Arc<dyn ChannelProvider>>;
}
pub async fn build(
ch_config: &ChannelConfig,
workspace_name: &str,
workspace: Arc<RwLock<WorkspaceHandle>>,
global_output: &Arc<OutputConfig>,
) -> Result<Arc<dyn ChannelProvider>> {
let provider: Arc<dyn ChannelProvider> = match ch_config.kind.as_str() {
"telegram" => telegram::TelegramProvider::create(ch_config, workspace, global_output).await,
"whatsapp" => whatsapp::WhatsAppProvider::create(ch_config, workspace, global_output).await,
"slack" => slack::SlackProvider::create(ch_config, workspace, global_output).await,
other => bail!("channel kind `{other}` is not implemented"),
}
.with_context(|| {
format!(
"workspace `{workspace_name}`: failed to build `{}` channel",
ch_config.kind
)
})?;
info!(
workspace = workspace_name,
kind = ch_config.kind,
bot_name = ch_config.bot_name.as_deref().unwrap_or("(unnamed)"),
"channel registered"
);
Ok(provider)
}