html terminal
-rw-r--r--Cargo.toml1
-rw-r--r--src/bot/mod.rs85
-rw-r--r--src/bot/schematic.rs65
3 files changed, 115 insertions, 36 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a13dacd..c6aef00 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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(()))
}