mpsc_bot/src/handler.rs

377 lines
12 KiB
Rust
Raw Permalink Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

use std::{str::FromStr, sync::atomic::Ordering};
use serde_json::Value;
use serenity::{
async_trait,
client::{Context, EventHandler},
http::{
request::{Request, RequestBuilder},
routing::RouteInfo,
},
model::{
channel::{Channel, ChannelType, Message},
id::ChannelId,
id::GuildId,
},
};
use crate::track::{err_strs::*, Active, Sent, SourceChannel, TargetChannel, Weave};
macro_rules! create_embed {
($embed:expr, $message:expr, $channel_name:expr) => {{
$embed.author(|author| {
author
.icon_url($message.author.face())
.name(format!["in #{}\n", $channel_name])
.url(format![
"https://discord.com/channels/{}/{}",
$message
.guild_id
.expect("Unsure how we got here, but we did"),
$message.channel_id
]);
author
});
if let (Some(guild_id), Some(replied_to)) = ($message.guild_id, $message.referenced_message) {
$embed
.title(format![
"@{}: {}",
replied_to.author.name,
if replied_to.content.chars().count() > 48 {
format![
"{}...",
replied_to.content.chars().take(64).collect::<String>()
]
} else {
replied_to.content.clone()
}
])
.url(format![
"https://discord.com/channels/{}/{}/{}",
guild_id, $message.channel_id, replied_to.id,
]);
}
#[allow(clippy::invisible_characters)]
if !$message.content.is_empty() {
$embed
.description(format![
"<@{}> [➤➤➤](https://discord.com/channels/{}/{}/{})\n{}",
$message.author.id,
$message
.guild_id
.expect("Unsure how we got here, but we did"),
$message.channel_id,
$message.id,
$message.timestamp.to_string()
])
.field(
"",
$message.content.chars().take(1024).collect::<String>(),
true,
);
if $message.content.chars().count() > 1024 {
#[allow(clippy::invisible_characters)]
$embed.field(
"",
$message
.content
.chars()
.skip(1024)
.take(1024)
.collect::<String>(),
true,
);
}
}
if !$message.attachments.is_empty() {
let mut s = String::new();
for attachment in $message.attachments {
s = format!["{}\n{}", s, attachment.url];
}
let mut size = 0usize;
let mut stopped_at = None;
$embed.field(
"__Attachments__",
s.lines()
.enumerate()
.map(|(n, line)| {
if stopped_at.is_none() {
if (size + line.chars().count()) > 1024 {
stopped_at = Some(n);
""
} else {
size += line.chars().count();
line
}
} else {
""
}
})
.collect::<String>(),
false,
);
if let Some(n) = stopped_at {
#[allow(clippy::invisible_characters)]
$embed.field("", s.lines().skip(n + 1).collect::<String>(), true);
}
}
$embed
}};
}
#[derive(Default)]
pub struct Handler;
#[async_trait]
impl EventHandler for Handler {
fn message<'life0, 'async_trait>(
&'life0 self,
ctx: Context,
new_message: Message,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
where
'life0: 'async_trait,
Self: 'async_trait,
{
Box::pin(async move {
let data = ctx.data.read().await;
if data
.get::<Active>()
.expect(EXPECTED_ACTIVE)
.load(Ordering::Relaxed)
{
let source = if let Some(source) = *data
.get::<SourceChannel>()
.expect(EXPECTED_SOURCE_CHANNEL)
.lock()
.await
{
source
} else {
return;
};
let weave = data
.get::<Weave>()
.expect(EXPECTED_ACTIVE)
.load(Ordering::Relaxed);
if source != new_message.channel_id {
if weave {
let request = RequestBuilder::new(RouteInfo::GetChannel {
channel_id: new_message.channel_id.into(),
});
let request = Request::new(request);
if let Ok(response) = ctx.http.request(request).await {
if let Ok(response) = response.json::<Value>().await {
if let Some(parent_id) = response.get("parent_id") {
if let Ok(parent_id) = ChannelId::from_str(&format![
"<#{}>",
parent_id.as_str().unwrap_or("")
]) {
if source != parent_id {
return;
}
} else {
return;
};
} else {
return;
};
} else {
#[cfg(debug)]
println!["Failed to deserialize response"];
return;
};
} else {
#[cfg(debug)]
println!["Failed to get channel information"];
return;
};
} else {
return;
}
}
let target = if let Some(target) = *data
.get::<TargetChannel>()
.expect(EXPECTED_SOURCE_CHANNEL)
.lock()
.await
{
target
} else {
return;
};
let channel_name =
if let Ok(Channel::Guild(gc)) = new_message.channel_id.to_channel(&ctx.http).await {
if gc.kind == ChannelType::PrivateThread {
return;
}
gc.name
} else {
return;
};
let msg = target
.send_message(&ctx.http, |m| {
m.embed(|e| {
let e = create_embed![e, new_message, channel_name];
e
});
m
})
.await
.expect("Failed to send message");
let sent = data.get::<Sent>().expect("Expected Sent in TypeMap");
(*sent.lock().await).insert((source, new_message.id), (target, msg.id));
}
})
}
fn message_delete<'life0, 'async_trait>(
&'life0 self,
ctx: Context,
event_channel_id: ChannelId,
deleted_message_id: serenity::model::id::MessageId,
_guild_id: Option<GuildId>,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
where
'life0: 'async_trait,
Self: 'async_trait,
{
Box::pin(async move {
let data = ctx.data.read().await;
let sent = data
.get::<Sent>()
.expect("Expected Sent in TypeMap")
.clone();
if let Some((channel_id, message_id)) =
(*sent.lock().await).remove(&(event_channel_id, deleted_message_id))
{
ctx.http
.delete_message(channel_id.into(), message_id.into())
.await
.expect("Failed to delete message");
};
})
}
fn message_delete_bulk<'life0, 'async_trait>(
&'life0 self,
ctx: Context,
event_channel_id: ChannelId,
multiple_deleted_messages_ids: Vec<serenity::model::id::MessageId>,
_guild_id: Option<GuildId>,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
where
'life0: 'async_trait,
Self: 'async_trait,
{
Box::pin(async move {
let data = ctx.data.read().await;
let sent = data.get::<Sent>().expect("Expected Sent in TypeMap");
for deleted_message_id in multiple_deleted_messages_ids {
if let Some((channel_id, message_id)) =
(*sent.lock().await).remove(&(event_channel_id, deleted_message_id))
{
ctx.http
.delete_message(channel_id.into(), message_id.into())
.await
.expect("Failed to delete message");
}
}
})
}
fn message_update<'life0, 'async_trait>(
&'life0 self,
ctx: Context,
_old_if_available: Option<Message>,
new: Option<Message>,
event: serenity::model::event::MessageUpdateEvent,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
where
'life0: 'async_trait,
Self: 'async_trait,
{
Box::pin(async move {
let data = ctx.data.read().await;
let sent = data
.get::<Sent>()
.expect("Expected Sent in TypeMap")
.clone();
let channel_name = if let Some(nym) = event.channel_id.name(&ctx).await {
nym
} else {
return;
};
if let Some(new) = new {
if let Some((channel_id, message_id)) =
(*sent.lock().await).get(&(event.channel_id, event.id))
{
channel_id
.edit_message(ctx.http, *message_id, |m| {
m.embed(|e| {
let e = create_embed![e, new, channel_name];
e
});
m
})
.await
.expect("Failed to delete message");
}
}
})
}
// fn ready<'life0, 'async_trait>(
// &'life0 self,
// _ctx: Context,
// _data_about_bot: serenity::model::prelude::Ready,
// ) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
// where
// 'life0: 'async_trait,
// Self: 'async_trait,
// {
// Box::pin(async move {
// let __self = self;
// let _ctx = _ctx;
// let _data_about_bot = _data_about_bot;
// let _: () = {};
// })
// }
// fn thread_create<'life0, 'async_trait>(
// &'life0 self,
// _ctx: Context,
// _thread: serenity::model::channel::GuildChannel,
// ) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
// where
// 'life0: 'async_trait,
// Self: 'async_trait,
// {
// Box::pin(async move {
// let __self = self;
// let _ctx = _ctx;
// let _thread = _thread;
// let _: () = {};
// })
// }
// fn thread_delete<'life0, 'async_trait>(
// &'life0 self,
// _ctx: Context,
// _thread: serenity::model::channel::PartialGuildChannel,
// ) -> core::pin::Pin<Box<dyn core::future::Future<Output = ()> + core::marker::Send + 'async_trait>>
// where
// 'life0: 'async_trait,
// Self: 'async_trait,
// {
// Box::pin(async move {
// let __self = self;
// let _ctx = _ctx;
// let _thread = _thread;
// let _: () = {};
// })
// }
}