abridged/src/bridge_discord/bridge.rs

110 lines
2.8 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?;
self.handle_bridge_msg(msg, id, http.as_ref()).await?;
}
};
debug!("{id}: entering event loop");
tokio::select! {
result = bridge_handler => result,
result = client.start() => result.map_err(|e| e.into()),
}
}
}
impl DiscordTask {
async fn handle_bridge_msg(&self, msg: BMessage, id: Id, http: &Http) -> TaskResult {
let content = format!("<{}> {}", msg.author, msg.content);
for channel in self.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);
if let Some(hook) = self.config.webhooks.get(channel) {
let hook = http.get_webhook(*hook.as_u64()).await?;
hook.execute(http, false, |hook| {
hook.username(&msg.author)
.content(&msg.content)
.avatar_url(msg.avatar.as_ref().map(|s| s.as_str()).unwrap_or(""))
}).await?;
} else {
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.bot {
return
}
let avatar = msg.author.avatar_url();
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,
avatar,
}).expect("failed to broadcast message");
}
async fn ready(&self, _: Context, _: Ready) {
info!("{}: ready", self.id);
}
}