use crate::message::{Message, SendOptions};
use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PresenceStatus {
Online,
Away,
Busy,
Invisible,
Offline,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SearchQuery {
#[serde(default)]
pub text: String,
#[serde(default)]
pub channel: Option<String>,
#[serde(default)]
pub from: Option<String>,
#[serde(default)]
pub limit: Option<usize>,
#[serde(default)]
pub before_timestamp: Option<i64>,
#[serde(default)]
pub after_timestamp: Option<i64>,
}
#[async_trait]
pub trait Messenger: Send + Sync {
fn name(&self) -> &str;
fn messenger_type(&self) -> &str;
async fn initialize(&mut self) -> Result<()>;
async fn send_message(&self, recipient: &str, content: &str) -> Result<String>;
async fn send_message_with_options(&self, opts: SendOptions<'_>) -> Result<String> {
self.send_message(opts.recipient, opts.content).await
}
async fn receive_messages(&self) -> Result<Vec<Message>>;
fn is_connected(&self) -> bool;
async fn disconnect(&mut self) -> Result<()>;
async fn set_typing(&self, _channel: &str, _typing: bool) -> Result<()> {
Ok(())
}
async fn set_status(&self, _status: PresenceStatus) -> Result<()> {
Ok(())
}
async fn add_reaction(&self, _message_id: &str, _channel: &str, _emoji: &str) -> Result<()> {
Ok(())
}
async fn remove_reaction(&self, _message_id: &str, _channel: &str, _emoji: &str) -> Result<()> {
Ok(())
}
async fn get_profile_picture(&self, _user_id: &str) -> Result<Option<String>> {
Ok(None)
}
async fn set_profile_picture(&self, _url: &str) -> Result<()> {
Ok(())
}
async fn set_text_status(&self, _text: &str) -> Result<()> {
Ok(())
}
async fn search_messages(&self, _query: SearchQuery) -> Result<Vec<Message>> {
Ok(Vec::new())
}
async fn edit_message(
&self,
_message_id: &str,
_channel: &str,
_new_content: &str,
) -> Result<()> {
Ok(())
}
async fn delete_message(&self, _message_id: &str, _channel: &str) -> Result<()> {
Ok(())
}
async fn pin_message(&self, _message_id: &str, _channel: &str) -> Result<()> {
Ok(())
}
async fn unpin_message(&self, _message_id: &str, _channel: &str) -> Result<()> {
Ok(())
}
async fn get_channel_members(&self, _channel: &str) -> Result<Vec<String>> {
Ok(Vec::new())
}
}
pub struct MessengerManager {
messengers: Vec<Box<dyn Messenger>>,
}
impl MessengerManager {
pub fn new() -> Self {
Self {
messengers: Vec::new(),
}
}
#[allow(clippy::should_implement_trait)]
pub fn add(mut self, messenger: impl Messenger + 'static) -> Self {
self.messengers.push(Box::new(messenger));
self
}
pub fn add_boxed(mut self, messenger: Box<dyn Messenger>) -> Self {
self.messengers.push(messenger);
self
}
pub async fn initialize_all(&mut self) -> Result<()> {
for m in &mut self.messengers {
m.initialize().await?;
}
Ok(())
}
pub async fn disconnect_all(&mut self) -> Result<()> {
for m in &mut self.messengers {
m.disconnect().await?;
}
Ok(())
}
pub async fn receive_all(&self) -> Result<Vec<Message>> {
let mut all = Vec::new();
for m in &self.messengers {
match m.receive_messages().await {
Ok(mut msgs) => all.append(&mut msgs),
Err(e) => tracing::warn!(messenger = %m.name(), "receive error: {e}"),
}
}
Ok(all)
}
pub async fn broadcast(
&self,
recipient: impl AsRef<str>,
content: impl AsRef<str>,
) -> Vec<Result<String>> {
let mut results = Vec::new();
for m in &self.messengers {
results.push(m.send_message(recipient.as_ref(), content.as_ref()).await);
}
results
}
pub fn messengers(&self) -> &[Box<dyn Messenger>] {
&self.messengers
}
pub fn get(&self, name: impl AsRef<str>) -> Option<&dyn Messenger> {
self.messengers
.iter()
.find(|m| m.name() == name.as_ref())
.map(|b| b.as_ref())
}
}
impl Default for MessengerManager {
fn default() -> Self {
Self::new()
}
}