200 lines
5.5 KiB
Rust
200 lines
5.5 KiB
Rust
use log::info;
|
|
use serenity::{async_trait, model::prelude::*, prelude::*};
|
|
use tokio::sync::mpsc;
|
|
|
|
use crate::{
|
|
channels::ChannelReference,
|
|
message_ast::{self, format_discord},
|
|
messages::{
|
|
DeletedMessage, EditedMessage, MessageAuthor, MessageEvent, MessageReference, SentMessage,
|
|
},
|
|
};
|
|
|
|
pub use serenity::client::Context;
|
|
pub use serenity::model::id::{ChannelId, MessageId};
|
|
|
|
impl From<&Message> for MessageReference {
|
|
fn from(message: &Message) -> Self {
|
|
Self::Discord(message.channel_id.0, message.id.0)
|
|
}
|
|
}
|
|
|
|
struct DiscordHandler {
|
|
ctx_tx: mpsc::UnboundedSender<Context>,
|
|
event_tx: mpsc::UnboundedSender<MessageEvent>,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl EventHandler for DiscordHandler {
|
|
async fn ready(&self, ctx: Context, _ready: Ready) {
|
|
let _ = self.ctx_tx.send(ctx);
|
|
info!("Discord ready!");
|
|
|
|
// TODO: Scan for channels to link
|
|
}
|
|
|
|
async fn message(&self, ctx: Context, message: Message) {
|
|
if message.author.id == ctx.cache.current_user_id().await {
|
|
return;
|
|
}
|
|
|
|
if let Some(target) = message.content.strip_prefix("phoebe!link ") {
|
|
if message
|
|
.member(&ctx)
|
|
.await
|
|
.unwrap()
|
|
.roles(&ctx)
|
|
.await
|
|
.unwrap()
|
|
.iter()
|
|
.any(|r| r.name == "Phoebe")
|
|
{
|
|
let _ = self.event_tx.send(MessageEvent::AdminLinkChannels(vec![
|
|
ChannelReference::Discord(message.channel_id.0),
|
|
ChannelReference::Matrix(target.to_string()),
|
|
]));
|
|
|
|
message.reply(&ctx, "Linking with matrix.").await.unwrap();
|
|
return;
|
|
}
|
|
}
|
|
|
|
let message_ref = MessageReference::from(&message);
|
|
|
|
let content = discord_message_format::parse(&message.content);
|
|
let content = message_ast::convert_discord(&content);
|
|
|
|
let replies_to = message
|
|
.referenced_message
|
|
.as_ref()
|
|
.map(|m| MessageReference::from(m.as_ref()));
|
|
|
|
let _ = self.event_tx.send(MessageEvent::Send(SentMessage {
|
|
source: message_ref,
|
|
content,
|
|
author: MessageAuthor {
|
|
display_name: message
|
|
.author_nick(&ctx)
|
|
.await
|
|
.unwrap_or(message.author.name),
|
|
},
|
|
replies_to,
|
|
}));
|
|
}
|
|
|
|
async fn message_update(
|
|
&self,
|
|
ctx: Context,
|
|
_old_if_available: Option<Message>,
|
|
new: Option<Message>,
|
|
event: MessageUpdateEvent,
|
|
) {
|
|
if let Ok(new_message) = {
|
|
if let Some(m) = new {
|
|
Ok(m)
|
|
} else {
|
|
event.channel_id.message(&ctx, event.id).await
|
|
}
|
|
} {
|
|
let message_ref = MessageReference::from(&new_message);
|
|
|
|
let content = discord_message_format::parse(&new_message.content);
|
|
let content = message_ast::convert_discord(&content);
|
|
|
|
let _ = self.event_tx.send(MessageEvent::Edit(EditedMessage {
|
|
replacing: message_ref,
|
|
content,
|
|
author: MessageAuthor {
|
|
display_name: new_message
|
|
.author_nick(&ctx)
|
|
.await
|
|
.unwrap_or(new_message.author.name),
|
|
},
|
|
}));
|
|
}
|
|
}
|
|
|
|
async fn message_delete(
|
|
&self,
|
|
_ctx: Context,
|
|
channel_id: ChannelId,
|
|
deleted_message_id: MessageId,
|
|
_guild_id: Option<GuildId>,
|
|
) {
|
|
let message_ref = MessageReference::Discord(channel_id.0, deleted_message_id.0);
|
|
let _ = self.event_tx.send(MessageEvent::Delete(DeletedMessage {
|
|
reference: message_ref,
|
|
}));
|
|
}
|
|
}
|
|
|
|
pub async fn forward_to_discord(
|
|
discord_ctx: &Context,
|
|
channel: ChannelId,
|
|
message: &SentMessage,
|
|
reply: Option<(u64, u64)>,
|
|
) -> Option<MessageReference> {
|
|
channel
|
|
.send_message(&discord_ctx, |m| {
|
|
let b = m.content(format_discord(&message.content));
|
|
|
|
if let Some((channel_id, message_id)) = reply {
|
|
b.reference_message((ChannelId(channel_id), MessageId(message_id)))
|
|
} else {
|
|
b
|
|
}
|
|
})
|
|
.await
|
|
.as_ref()
|
|
.ok()
|
|
.map(MessageReference::from)
|
|
}
|
|
|
|
pub async fn edit_on_discord(
|
|
discord_ctx: &Context,
|
|
channel_id: ChannelId,
|
|
message_id: MessageId,
|
|
message: &EditedMessage,
|
|
) -> Option<MessageReference> {
|
|
channel_id
|
|
.edit_message(&discord_ctx, &message_id, |m| {
|
|
m.content(format_discord(&message.content))
|
|
})
|
|
.await
|
|
.as_ref()
|
|
.ok()
|
|
.map(MessageReference::from)
|
|
}
|
|
|
|
pub async fn delete_on_discord(
|
|
discord_ctx: &Context,
|
|
channel_id: ChannelId,
|
|
message_id: MessageId,
|
|
_message: &DeletedMessage,
|
|
) -> bool {
|
|
channel_id
|
|
.delete_message(&discord_ctx, &message_id)
|
|
.await
|
|
.is_ok()
|
|
}
|
|
|
|
pub async fn create_discord_client(
|
|
ctx_tx: mpsc::UnboundedSender<Context>,
|
|
message_tx: mpsc::UnboundedSender<MessageEvent>,
|
|
token: &str,
|
|
) -> Client {
|
|
let handler = DiscordHandler {
|
|
ctx_tx,
|
|
event_tx: message_tx,
|
|
};
|
|
|
|
info!("Discord logging in…");
|
|
let client = Client::builder(token)
|
|
.event_handler(handler)
|
|
.await
|
|
.expect("Failed to create discord client");
|
|
info!("Discord starting…");
|
|
|
|
client
|
|
}
|