abridged/src/bridge_discord/bridge.rs

98 lines
2.4 KiB
Rust

use std::sync::Arc;
use async_trait::async_trait;
use log::{info, debug};
use serde::Deserialize;
use serenity::{prelude::*, model::prelude::*, http::Http};
use crate::{linkmap::Linkmap, supervisor::{Task, TaskResult, Id, Sender, Receiver}};
use crate::supervisor::Message as BMessage;
use super::DiscordConfig;
#[derive(Debug, Deserialize)]
pub struct DiscordTask {
config: DiscordConfig,
links: Arc<Linkmap<ChannelId>>,
}
#[async_trait]
impl Task for DiscordTask {
async fn start(&self, id: Id, tx: Sender, mut rx: Receiver) -> TaskResult {
let handler = Handler {
links: self.links.clone(),
tx,
id,
};
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
debug!("{id}: building Discord client");
let mut client = Client::builder(&self.config.token, intents)
.event_handler(handler)
.await?;
let http = client.cache_and_http.http.clone();
let bridge_handler = async move {
loop {
let msg = rx.recv().await?;
handle_bridge_msg(msg, id, &self.links, http.as_ref()).await?;
}
};
debug!("{id}: built client, starting");
tokio::select! {
result = bridge_handler => result,
result = client.start() => result.map_err(|e| e.into()),
}
}
}
async fn handle_bridge_msg(msg: BMessage, id: Id, links: &Linkmap<ChannelId>, http: &Http) -> TaskResult {
let content = format!("<{}> {}", msg.author, msg.content);
for channel in links.get_channels(&msg.link) {
if msg.origin.0 == id && msg.origin.1 == channel.to_string() {
continue
}
debug!("{id}: bridging message from {:?}/{:?} to {channel:?}", msg.origin, msg.link);
channel.say(http, &content).await?;
}
Ok(())
}
struct Handler {
id: Id,
links: Arc<Linkmap<ChannelId>>,
tx: Sender,
}
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
if msg.author.id == ctx.cache.current_user_id() {
return
}
let Some(link) = self.links.get_link(&msg.channel_id) else { return };
let author = msg.author.nick_in(ctx.http, msg.guild_id.expect("failed to get guild ID"))
.await.unwrap_or(msg.author.name);
debug!("{}: broadcasting message from {:?} to {:?}", self.id, msg.channel_id, link);
self.tx.send(BMessage {
origin: (self.id, msg.channel_id.to_string()),
link: link.clone(),
author,
content: msg.content,
}).expect("failed to broadcast message");
}
async fn ready(&self, _: Context, _: Ready) {
info!("{}: ready", self.id);
}
}