moderatior
bendn 12 months ago
parent 2685741 · commit afeee61
-rw-r--r--Cargo.toml9
-rw-r--r--build.rs3
-rw-r--r--src/db.rs29
-rw-r--r--src/help.md4
-rw-r--r--src/main.rs361
5 files changed, 130 insertions, 276 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 1ca0eb9..5e2ce70 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,11 +21,18 @@ poise = { git = "https://github.com/serenity-rs/poise" }
anyhow = "1.0.75"
jemallocator-global = "0.3.2"
ahash = "0.8.11"
-emoji = { git = "https://github.com/apricot-conservation-project/emoji", version = "0.1.0" }
+emojib = { git = "https://github.com/Apricot-Conservation-Project/emoji", package = "emoji" }
kv = { version = "0.24.0", features = ["bincode-value"] }
sled = { version = "0.34.7", features = ["compression"] }
diff = "0.1.13"
humantime = "2.1.0"
+phf = "0.11.3"
+mindus = { version = "5.0.29", default-features = false }
+
+[build-dependencies]
+emojib = { git = "https://github.com/Apricot-Conservation-Project/emoji", features = [
+ "build",
+], package = "emoji" }
[profile.release]
strip = true
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..1411e26
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ emojib::load();
+}
diff --git a/src/db.rs b/src/db.rs
index e105728..448ea6a 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -1,8 +1,8 @@
// TODO pruning?
-use kv::*;
-use poise::serenity_prelude::*;
use std::sync::LazyLock;
+use kv::*;
+
fn cfg() -> kv::Config {
kv::Config {
path: "./db1".into(),
@@ -23,31 +23,6 @@ pub fn set(k: u64, v: (String, Vec<String>, u64)) {
pub fn get(k: u64) -> Option<(String, Vec<String>, u64)> {
BU.get(&k.into()).unwrap().map(|x| x.0)
}
-pub fn keys() -> impl Iterator<Item = MessageId> {
- BU.iter()
- .filter_map(Result::ok)
- .filter_map(|x| x.key::<u64>().ok())
- .map(|x| MessageId::new(x))
-}
-pub fn values() -> impl Iterator<Item = (String, Vec<String>, UserId)> {
- BU.iter()
- .filter_map(Result::ok)
- .filter_map(|x| x.value::<Bincode<(String, Vec<String>, u64)>>().ok())
- .map(|Bincode((y, z, α))| (y, z, UserId::new(α)))
-}
-pub fn iter() -> impl Iterator<Item = (MessageId, (String, Vec<String>, UserId))> {
- BU.iter()
- .filter_map(Result::ok)
- .filter_map(|x| {
- x.key::<u64>()
- .and_then(|y| {
- x.value::<Bincode<(String, Vec<String>, u64)>>()
- .map(|x| (y, x))
- })
- .ok()
- })
- .map(|(x, Bincode((y, z, α)))| (MessageId::new(x), (y, z, UserId::new(α))))
-}
pub fn sz() -> f32 {
DB.size_on_disk().unwrap() as f32 / (1 << 20) as f32
}
diff --git a/src/help.md b/src/help.md
index 1bbec94..60305c3 100644
--- a/src/help.md
+++ b/src/help.md
@@ -1,3 +1,3 @@
-hi i am moderatior <:icewoll:1191513368922689589>
+hi i am moderatios <:icewoll:1191513368922689589>
-i echo the audit log for all your logging pleasures
+i echo the audit log for all your logging pleasures \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index ceae5b9..339abb8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,7 @@
-#![feature(if_let_guard, let_chains, lazy_cell)]
-#![allow(confusable_idents, mixed_script_confusables)]
+#![feature(if_let_guard, let_chains, generic_const_exprs)]
use anyhow::Result;
-use emoji::named::*;
+emojib::the_crate!();
+use emoji::named::*;
use poise::{serenity_prelude::*, CreateReply};
use std::convert::identity;
use std::fs::read_to_string;
@@ -133,7 +133,8 @@ pub fn format(log: AuditLogEntry) -> Option<String> {
.reduce(|a, b| format!("{a}\n{b}"))
.unwrap_or_else(String::new);
Some(format!(
- "<@{}> {}",
+ "<t:{}:d> <@{}> {}",
+ log.id.created_at().unix_timestamp(),
log.user_id,
match log.action {
GuildUpdate => format!("guild changes\n{changes}"),
@@ -215,192 +216,6 @@ fn diff(old: &str, new: &str) -> String {
.replace('`', "\u{200b}`")
}
-async fn event_handler(c: &serenity::all::Context, e: &FullEvent) -> Result<()> {
- match e {
- FullEvent::GuildAuditLogEntryCreate { entry, .. } => {
- let Some(h) = format(entry.clone()) else {
- return Ok(());
- };
- ChannelId::new(1220060625338761286)
- .send_message(
- c,
- CreateMessage::new()
- .allowed_mentions(CreateAllowedMentions::new().empty_users().empty_roles())
- .content(h),
- )
- .await?;
- }
- FullEvent::GuildMemberAddition { new_member } => {
- ChannelId::new(944772532559568936)
- .send_message(
- c,
- CreateMessage::new().content(format!("{RIGHT} hi <@{}>", new_member.user.id)),
- )
- .await
- .unwrap();
- }
- FullEvent::GuildMemberRemoval {
- user,
- member_data_if_available,
- ..
- } => {
- ChannelId::new(944772532559568936)
- .send_message(
- c,
- CreateMessage::new().content(format!(
- "{LEFT} goodbye {} <@{}>",
- match member_data_if_available {
- Some(x) => x.nick.as_ref().unwrap_or(&user.name),
- None => &user.name,
- },
- user.id
- )),
- )
- .await
- .unwrap();
- }
- FullEvent::Message { new_message } => db::set_m(new_message.clone()),
- FullEvent::MessageUpdate {
- event:
- MessageUpdateEvent {
- id,
- channel_id,
- content: Some(content),
- author:
- Some(User {
- id: author, bot, ..
- }),
- attachments: Some(attachments),
- ..
- },
- ..
- } if !bot => {
- if let Some((oc, _, _)) = db::get(id.get()) {
- let diff = diff(&oc, content);
- ChannelId::new(1226396559185285280)
- .send_message(
- c,
- CreateMessage::new()
- .allowed_mentions(
- CreateAllowedMentions::new()
- .empty_users()
- .empty_roles(),
- )
- .content(format!(
- "<t:{}:d> <@{author}> {EDIT} their message https://discord.com/channels/925674713429184564/{channel_id}/{id}\n```diff{diff}```",
- id.created_at().unix_timestamp()
- )),
- )
- .await
- .unwrap();
- }
- db::set(
- id.get(),
- (
- content.clone(),
- attachments.iter().map(|a| a.url.clone()).collect(),
- author.get(),
- ),
- )
- }
- FullEvent::MessageDelete {
- deleted_message_id,
- channel_id,
- ..
- } => {
- let log = c
- .http()
- .get_guild(925674713429184564.into())
- .await
- .unwrap()
- .audit_logs(
- c,
- Some(audit_log::Action::Message(MessageAction::Delete)),
- None,
- None,
- Some(1),
- )
- .await?
- .entries
- .into_iter()
- .next()
- .unwrap();
-
- let ban_log = c
- .http()
- .get_guild(925674713429184564.into())
- .await
- .unwrap()
- .audit_logs(
- c,
- Some(audit_log::Action::Member(MemberAction::BanAdd)),
- None,
- None,
- Some(1),
- )
- .await?
- .entries
- .into_iter()
- .next()
- .unwrap()
- .id;
-
- let now = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap()
- .as_secs();
- let since = now.saturating_sub(log.id.created_at().unix_timestamp() as _);
- let since_ban = now.saturating_sub(ban_log.created_at().unix_timestamp() as _);
- if since_ban < 60 {
- return Ok(());
- }
- let (author, who) = (log.target_id.unwrap(), log.user_id);
- ChannelId::new(1226396559185285280)
- .send_message(
- c,
- CreateMessage::new()
- .allowed_mentions(
- CreateAllowedMentions::new()
- .empty_users()
- .empty_roles(),
- )
- .content(match db::get(deleted_message_id.get()) {
- Some((content, links, a)) => {
- if a == 1224510735959462068 { return Ok(()) }
- if author.get() != a || since > 20 {
- format!(
- "<t:{}:d> <@{a}> {CANCEL} deleted their own message (in <#{channel_id}>):```\n{content}\n```\n{}",
- deleted_message_id.created_at().unix_timestamp(),
- links
- .into_iter()
- .reduce(|a, b| format!("{a} {b}"))
- .unwrap_or_else(String::new)
- )
- } else {
- format!(
- "<t:{}:d> <@{who}> {CANCEL} deleted message by <@{author}> (in <#{channel_id}>):```\n{content}\n```\n{}",
- deleted_message_id.created_at().unix_timestamp(),
- links
- .into_iter()
- .reduce(|a, b| format!("{a} {b}"))
- .unwrap_or_else(String::new)
- )
- }
- }
- None => format!(
- "<t:{}:d> <@{who}> {CANCEL} deleted message by <@{author}> in <#{channel_id}> (content unavailable)",
- deleted_message_id.created_at().unix_timestamp()
- ),
- }),
- )
- .await
- .unwrap();
- }
- _ => (),
- }
- Ok(())
-}
-
pub struct Bot;
impl Bot {
pub async fn spawn() {
@@ -409,9 +224,117 @@ impl Bot {
std::env::var("TOKEN").unwrap_or_else(|_| read_to_string("token").expect("wher token"));
let f = poise::Framework::builder()
.options(poise::FrameworkOptions {
- commands: vec![help(), reload(), redact(), spy()],
+ commands: vec![help(), reload(), redact()],
on_error: |e| Box::pin(on_error(e)),
- event_handler: |c, e, _, _| Box::pin(event_handler(c, e)),
+ event_handler: |c, e, _, _| {
+ Box::pin(async move {
+ match e {
+ FullEvent::GuildAuditLogEntryCreate { entry, .. } => {
+ let Some(h) = format(entry.clone()) else {
+ return Ok(());
+ };
+ ChannelId::new(1220060625338761286).send_message(
+ c,
+ CreateMessage::new()
+ .allowed_mentions(CreateAllowedMentions::new().empty_users().empty_roles())
+ .content(h),
+ )
+ .await?;
+ }
+ FullEvent::GuildMemberAddition { new_member } => {
+ ChannelId::new(944772532559568936)
+ .send_message(
+ c,
+ CreateMessage::new()
+ .content(format!("{RIGHT} hi <@{}>", new_member.user.id),
+ ))
+ .await
+ .unwrap();
+ }
+ FullEvent::GuildMemberRemoval { user, member_data_if_available, .. } => {
+ ChannelId::new(944772532559568936)
+ .send_message(
+ c,
+ CreateMessage::new()
+ .content(format!("{LEFT} goodbye {} <@{}>", match member_data_if_available {
+ Some(x) => x.nick.as_ref().unwrap_or(&user.name),
+ None => &user.name,
+ }, user.id)),
+ )
+ .await
+ .unwrap();
+ },
+ FullEvent::Message { new_message } => db::set_m(new_message.clone()),
+ FullEvent::MessageUpdate { event: MessageUpdateEvent { id, channel_id, content: Some(content), author: Some(User{ id: author, bot, ..}), attachments: Some(attachments), .. }, .. } if !bot => {
+ if let Some((oc, _, _)) = db::get(id.get()) && &oc != content {
+ let diff = diff(&oc, content);
+ ChannelId::new(1226396559185285280)
+ .send_message(
+ c,
+ CreateMessage::new()
+ .allowed_mentions(
+ CreateAllowedMentions::new()
+ .empty_users()
+ .empty_roles(),
+ )
+ .add_file(CreateAttachment::bytes(diff, "diff.patch"))
+ .content(format!("<@{author}> {EDIT} their message https://discord.com/channels/925674713429184564/{channel_id}/{id}")),
+ )
+ .await
+ .unwrap();
+ }
+ db::set(id.get(), (content.clone(), attachments.iter().map(|a|a.url.clone()).collect(), author.get()))
+ },
+ FullEvent::MessageDelete {
+ deleted_message_id, channel_id, ..
+ } => {
+ let log = c.http().get_guild(925674713429184564.into()).await.unwrap()
+ .audit_logs(c, Some(audit_log::Action::Message(MessageAction::Delete)), None, None, Some(1)).await?
+ .entries.into_iter().next().unwrap();
+
+ let since = std::time::SystemTime::now().duration_since( std::time::UNIX_EPOCH).unwrap().as_secs().saturating_sub(log.id.created_at().unix_timestamp() as _);
+ let (author, who) = (log.target_id.unwrap(), log.user_id);
+ ChannelId::new(1226396559185285280)
+ .send_message(
+ c,
+ CreateMessage::new()
+ .allowed_mentions(
+ CreateAllowedMentions::new()
+ .empty_users()
+ .empty_roles(),
+ )
+ .content(match db::get(deleted_message_id.get()) {
+ Some((content, links, a)) => {
+ if a == 1224510735959462068 { return Ok(()) }
+ if author.get() != a || since > 20 {
+ format!(
+ "<@{a}> {CANCEL} deleted their own message (in <#{channel_id}>):```\n{content}\n```\n{}",
+ links
+ .into_iter()
+ .reduce(|a, b| format!("{a} {b}"))
+ .unwrap_or_else(String::new)
+ )
+ } else {
+ format!(
+ "<@{who}> {CANCEL} deleted message by <@{author}> (in <#{channel_id}>):```\n{content}\n```\n{}",
+ links
+ .into_iter()
+ .reduce(|a, b| format!("{a} {b}"))
+ .unwrap_or_else(String::new)
+ )
+ }
+ }
+ None => format!("<@{who}> {CANCEL} deleted message by <@{author}> in <#{channel_id}> (content unavailable)"),
+ }),
+ )
+ .await
+ .unwrap();
+ }
+ _ => (),
+ }
+ Ok(())
+ })
+ },
..Default::default()
})
.setup(|ctx, _ready, f| {
@@ -463,60 +386,6 @@ async fn main() {
Bot::spawn().await;
}
-#[poise::command(
- slash_command,
- context_menu_command = "Read all messages",
- rename = "spy"
-)]
-/// Collect the messages of a user.
-/// Will be dispatched to your DM's.
-/// This command may take some time.
-pub async fn spy(c: Context<'_>, #[description = "the user to spy on"] who: User) -> Result<()> {
- let u = c.author_member().await.ok_or(anyhow::anyhow!("dang"))?;
- if !(u.user.name == "bendn"
- || u.roles.contains(&RoleId::new(925676016708489227))
- || u.roles.contains(&RoleId::new(925708634896367647)))
- {
- poise::say_reply(c, "access denied. this incident will be reported").await?;
- return Ok(());
- }
- let h = c.reply("please check your dm's").await?;
- let mut n = 0u64;
- for (x, y) in db::values()
- .filter(|(_, _, x)| *x == who.id)
- .map(|(x, y, _)| (x, y))
- {
- if let Err(_) = c
- .author()
- .dm(
- c,
- CreateMessage::new().content(format!(
- "```{x}```\n{}",
- y.into_iter()
- .reduce(|a, b| format!("{a} {b}"))
- .unwrap_or_default()
- )),
- )
- .await
- && n == 0
- {
- h.edit(
- c,
- CreateReply::default().content("please open your dm's (couldnt send)"),
- )
- .await?;
- return Ok(());
- };
- n += 1;
- }
- h.edit(
- c,
- CreateReply::default().content(format!("all ({n}) sent. please check your dm's")),
- )
- .await?;
- Ok(())
-}
-
#[poise::command(slash_command)]
pub async fn reload(c: Context<'_>) -> Result<()> {
if c.author().id != OWNER {