381 lines
12 KiB
Rust
381 lines
12 KiB
Rust
|
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 _: () = {};
|
|||
|
// })
|
|||
|
// }
|
|||
|
}
|