html terminal
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/bot/mod.rs | 85 | ||||
| -rw-r--r-- | src/bot/schematic.rs | 65 |
3 files changed, 115 insertions, 36 deletions
@@ -43,6 +43,7 @@ btparse = "0.1.1" mindus = { version = "4", features = [], default-features = false } oxipng = { git = "https://github.com/shssoichiro/oxipng", branch = "master", features = [], default-features = false } strip-ansi-escapes = "0.2.0" +dashmap = "5.5.1" [profile.release] strip = true diff --git a/src/bot/mod.rs b/src/bot/mod.rs index eb41fe0..81d259d 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -10,26 +10,37 @@ mod voting; use crate::webhook::Webhook; use anyhow::Result; +use dashmap::DashMap; use maps::Maps; -use poise::serenity_prelude::GuildId; +use poise::serenity_prelude::*; use serenity::http::Http; use serenity::model::channel::Message; -use serenity::prelude::*; use std::fmt::Write; use std::fs::read_to_string; +use std::ops::ControlFlow; use std::sync::{ atomic::{AtomicU8, Ordering}, Arc, OnceLock, }; +use std::time::Duration; use tokio::sync::broadcast; #[derive(Debug)] pub struct Data { + // message -> resp + tracker: Arc<DashMap<MessageId, Message>>, stdin: broadcast::Sender<String>, vote_data: voting::Votes, } +pub struct SMsg { + author: String, + content: String, + channel: ChannelId, + attachments: Vec<Attachment>, +} + static SKIPPING: OnceLock<(Arc<AtomicU8>, broadcast::Sender<String>)> = OnceLock::new(); #[macro_export] @@ -169,7 +180,7 @@ pub async fn safe(m: &Message, c: &serenity::client::Context) -> String { for id in &m.mention_roles { let mention = id.mention().to_string(); - if let Some(role) = id.to_role_cached(&c) { + if let Some(role) = id.to_role_cached(c) { result = result.replace(&mention, &["@", &role.name].concat()); } else { result = result.replace(&mention, "@deleted-role"); @@ -239,13 +250,65 @@ impl Bot { { return Ok(()); } - if schematic::with(new_message, c).await?.is_break() { + if let ControlFlow::Break(m) = schematic::with( + SMsg { + author: new_message + .author_nick(c) + .await + .unwrap_or(new_message.author.name.clone()), + attachments: new_message.attachments.clone(), + content: new_message.content.clone(), + channel: new_message.channel_id, + }, + c, + ) + .await? + { + d.tracker.insert(new_message.id, m); return Ok(()); } if CHANNEL == new_message.channel_id.0 { say(c, new_message, d).await?; } } + poise::Event::MessageUpdate { event, .. } => { + let MessageUpdateEvent { + author: Some(author), + guild_id: Some(guild_id), + content: Some(content), + attachments: Some(attachments), + .. + } = event.clone() + else { + return Ok(()); + }; + if let Some((_, r)) = d.tracker.remove(&event.id) { + r.delete(c).await.unwrap(); + if let ControlFlow::Break(m) = schematic::with( + SMsg { + author: author + .nick_in(c, guild_id) + .await + .unwrap_or(author.name.clone()), + content, + attachments, + channel: event.channel_id, + }, + c, + ) + .await? + { + d.tracker.insert(event.id, m); + } + } + } + poise::Event::MessageDelete { + deleted_message_id, .. + } => { + if let Some((_, r)) = d.tracker.remove(deleted_message_id) { + r.delete(c).await.unwrap(); + } + } _ => {} }; Ok(()) @@ -274,7 +337,21 @@ impl Bot { poise::builtins::register_globally(ctx, &framework.options().commands[18..]) .await?; println!("registered"); + let tracker = Arc::new(DashMap::new()); + let tc = Arc::clone(&tracker); + tokio::spawn(async move { + loop { + // every 10 minutes + tokio::time::sleep(Duration::from_secs(60 * 10)).await; + tc.retain(|_, v: &mut Message| { + // prune messagees older than 3 hours + Timestamp::now().unix_timestamp() - v.timestamp.unix_timestamp() + < 60 * 60 * 3 + }); + } + }); Ok(Data { + tracker, stdin, vote_data: voting::Votes::new(vec![]), }) diff --git a/src/bot/schematic.rs b/src/bot/schematic.rs index 1b93e7e..2e1856c 100644 --- a/src/bot/schematic.rs +++ b/src/bot/schematic.rs @@ -7,7 +7,7 @@ use regex::Regex; use std::sync::LazyLock; use std::{borrow::Cow, ops::ControlFlow}; -use super::{emojis, strip_colors, SUCCESS}; +use super::{emojis, strip_colors, SMsg, SUCCESS}; static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(```)?(\n)?([^`]+)(\n)?(```)?").unwrap()); @@ -35,46 +35,47 @@ async fn from_attachments(attchments: &[Attachment]) -> Result<Option<Schematic< Ok(None) } -pub async fn with(m: &Message, c: &serenity::client::Context) -> Result<ControlFlow<(), ()>> { +pub async fn with(m: SMsg, c: &serenity::client::Context) -> Result<ControlFlow<Message, ()>> { + let author = m.author; let send = |v| async move { let p = to_png(&v); - let author = m.author_nick(c).await.unwrap_or(m.author.name.clone()); - m.channel_id - .send_message(c, |m| { - m.add_file(AttachmentType::Bytes { - data: Cow::Owned(p), - filename: "image.png".to_string(), - }) - .embed(|e| { - e.attachment("image.png"); - if let Some(d) = v.tags.get("description") { - e.description(d); - } - let mut s = String::new(); - for (i, n) in v.compute_total_cost().0.iter() { - if n == 0 { - continue; + anyhow::Ok( + m.channel + .send_message(c, |m| { + m.add_file(AttachmentType::Bytes { + data: Cow::Owned(p), + filename: "image.png".to_string(), + }) + .embed(|e| { + e.attachment("image.png"); + if let Some(d) = v.tags.get("description") { + e.description(d); + } + let mut s = String::new(); + for (i, n) in v.compute_total_cost().0.iter() { + if n == 0 { + continue; + } + use std::fmt::Write; + write!(s, "{} {n} ", emojis::item(i)).unwrap(); } - use std::fmt::Write; - write!(s, "{} {n} ", emojis::item(i)).unwrap(); - } - e.field("", s, true); - e.title(strip_colors(v.tags.get("name").unwrap())) - .footer(|f| f.text(format!("requested by {author}",))) - .color(SUCCESS) + e.field("", s, true); + e.title(strip_colors(v.tags.get("name").unwrap())) + .footer(|f| f.text(format!("requested by {author}"))) + .color(SUCCESS) + }) }) - }) - .await?; - anyhow::Ok(()) + .await?, + ) }; if let Ok(Some(v)) = from_attachments(&m.attachments).await { - send(v).await?; - return Ok(ControlFlow::Break(())); + println!("sent {}", v.tags.get("name").unwrap()); + return Ok(ControlFlow::Break(send(v).await?)); } if let Ok(v) = from_msg(&m.content) { - send(v).await?; - return Ok(ControlFlow::Break(())); + println!("sent {}", v.tags.get("name").unwrap()); + return Ok(ControlFlow::Break(send(v).await?)); } Ok(ControlFlow::Continue(())) } |