use std::sync::Arc;
use std::time::Duration;
use crate::caps::ChannelCaps;
use crate::id::{ChannelId, ConversationKey, UserId};
use crate::message::{InboundEvent, RenderedMessage, ReplyTarget, SentRef};
use crate::prompt::{Prompt, PromptAnswer};
use crate::webhook::WebhookHandler;
use crate::{BoxError, CancellationToken, async_trait};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ChannelError {
#[error("not supported by this platform: {0}")]
Unsupported(&'static str),
#[error("authentication failed: {0}")]
Auth(String),
#[error("rate limited")]
RateLimited {
retry_after: Option<Duration>,
},
#[error("temporary failure: {0}")]
Transient(#[source] BoxError),
#[error("{0}")]
Failed(#[source] BoxError),
}
impl ChannelError {
pub fn is_retryable(&self) -> bool {
matches!(self, Self::RateLimited { .. } | Self::Transient(_))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandInfo {
pub name: String,
pub description: String,
pub descriptions: Vec<(String, String)>,
pub params: Vec<CommandParam>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandParam {
pub name: String,
pub required: bool,
pub rest: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("the runtime is no longer accepting events")]
pub struct SinkClosed;
#[async_trait]
pub trait InboundSink: Send + Sync {
async fn deliver(&self, event: InboundEvent) -> Result<(), SinkClosed>;
async fn answer(&self, _answer: PromptAnswer) -> Result<(), SinkClosed> {
Ok(())
}
}
#[async_trait]
pub trait Channel: Send + Sync {
fn id(&self) -> &ChannelId;
fn caps(&self) -> &ChannelCaps;
async fn run(
&self,
sink: Arc<dyn InboundSink>,
shutdown: CancellationToken,
) -> Result<(), ChannelError>;
async fn send(
&self,
to: &ReplyTarget,
message: &RenderedMessage,
) -> Result<SentRef, ChannelError>;
async fn edit(&self, _sent: &SentRef, _message: &RenderedMessage) -> Result<(), ChannelError> {
Err(ChannelError::Unsupported("edit"))
}
async fn typing(&self, _to: &ReplyTarget) -> Result<(), ChannelError> {
Ok(())
}
async fn prompt(&self, _to: &ReplyTarget, _prompt: &Prompt) -> Result<SentRef, ChannelError> {
Err(ChannelError::Unsupported("prompt"))
}
async fn close_prompt(
&self,
_sent: &SentRef,
_message: &RenderedMessage,
_keep_buttons: bool,
) -> Result<(), ChannelError> {
Err(ChannelError::Unsupported("prompt"))
}
async fn open_direct(&self, _user: &UserId) -> Result<ConversationKey, ChannelError> {
Err(ChannelError::Unsupported("open_direct"))
}
async fn set_commands(&self, _commands: &[CommandInfo]) -> Result<(), ChannelError> {
Ok(())
}
fn webhook(&self) -> Option<Arc<dyn WebhookHandler>> {
None
}
}