mpsc_bot/src/handler.rs

381 lines
12 KiB
Rust
Raw Normal View History

2022-01-24 18:33:09 +00:00
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(replied_to) = $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/{}/{}/{}",
replied_to
.guild_id
.expect("Really unsure how we got here, but we're here."),
replied_to.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 _: () = {};
// })
// }
}