smol bot
add new evict
| -rw-r--r-- | src/bot/logic.rs | 2 | ||||
| -rw-r--r-- | src/bot/map.rs | 30 | ||||
| -rw-r--r-- | src/bot/mod.rs | 234 | ||||
| -rw-r--r-- | src/bot/repos.rs | 123 | ||||
| -rw-r--r-- | src/bot/schematic.rs | 20 | ||||
| -rw-r--r-- | src/expose.rs | 4 |
6 files changed, 255 insertions, 158 deletions
diff --git a/src/bot/logic.rs b/src/bot/logic.rs index ad8880a..f0257c2 100644 --- a/src/bot/logic.rs +++ b/src/bot/logic.rs @@ -1,6 +1,6 @@ use super::{Context, Result}; use lemu::Executor; -use poise::{serenity_prelude::*, CodeBlock, KeyValueArgs}; +use poise::{CodeBlock, KeyValueArgs, serenity_prelude::*}; #[poise::command(slash_command, rename = "eval_file", install_context = "Guild|User")] /// Execute MLOG from a file. diff --git a/src/bot/map.rs b/src/bot/map.rs index f798627..99e6906 100644 --- a/src/bot/map.rs +++ b/src/bot/map.rs @@ -15,15 +15,13 @@ fn string((x, f): (ReadError, &str)) -> String { format!("not a map.") } ReadError::NoBlockFound(b) => { - format!("couldnt find block `{b}`. mods are not supported") + format!("couldnt find block `{b}`. mods are not supported") } ReadError::NoSuchBlock(b) => { format!("couldnt find block at index `{b}`. mods are not supported") } ReadError::Version(v) => { - format!( - "unsupported version: `{v}`. supported versions: `7, 8`.", - ) + format!("unsupported version: `{v}`. supported versions: `7, 8`.",) } ReadError::Read(r) => { format!("failed to read map. error: `{r}`. originates from `{f}`") @@ -71,11 +69,13 @@ pub async fn reply( let (a, e) = match embed(m, deser_took).await { Ok(x) => x, Err(e) => { - BENDN.send_files( - &c, - [CreateAttachment::bytes(b, "map.msav")], - CreateMessage::new().content(format!("<@696196765564534825> failure: {e}")), - ); + BENDN + .send_files( + &c, + [CreateAttachment::bytes(b, "map.msav")], + CreateMessage::new().content(format!("<@696196765564534825> failure: {e}")), + ) + .await?; return Ok(ControlFlow::Break(CreateReply::default().content( "there was a problem. i have notified bendn about this issue.", ))); @@ -213,11 +213,13 @@ pub async fn render_message(c: super::Context<'_>, m: Message) -> Result<()> { let (png, embed) = match embed(m, deser_took).await { Ok(x) => x, Err(e) => { - BENDN.send_files( - &c, - [CreateAttachment::bytes(b, "map.msav")], - CreateMessage::new().content(format!("<@696196765564534825> failure: {e}")), - ); + BENDN + .send_files( + &c, + [CreateAttachment::bytes(b, "map.msav")], + CreateMessage::new().content(format!("<@696196765564534825> failure: {e}")), + ) + .await?; c.say("there was a problem. i have notified bendn about this issue.") .await?; return Ok(()); diff --git a/src/bot/mod.rs b/src/bot/mod.rs index 24f0cc0..548b674 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -1,3 +1,5 @@ +mod data; +mod db; mod logic; mod map; pub mod ownership; @@ -5,19 +7,16 @@ pub mod repos; mod schematic; pub mod search; mod sorter; -mod db; -mod data; use charts_rs::{Series, THEME_GRAFANA}; pub use data::log; - use crate::emoji; use anyhow::Result; use dashmap::DashMap; -use mindus::data::DataWrite; use mindus::Serializable; -use poise::{serenity_prelude::*, CreateReply}; -use repos::{Repo, FORUMS, SPECIAL, THREADED}; +use mindus::data::DataWrite; +use poise::{CreateReply, serenity_prelude::*}; +use repos::{FORUMS, Repo, SPECIAL, THREADED}; use serenity::futures::StreamExt; use std::collections::{HashMap, HashSet}; use std::fmt::Write; @@ -134,12 +133,20 @@ pub async fn scour( match ty { Type::Basic(tg) => { let mut msgs = ch.messages_iter(c).boxed(); - let tags = tags(tg); while let Some(msg) = msgs.next().await { let Ok(msg) = msg else { continue; }; if let Ok(Some(mut x)) = schematic::from((&msg.content, &msg.attachments)).await { + use emoji::to_mindustry::named::*; + let tags = if tg == &["find unit factory"] { + tags(&[x.block_iter().find_map(|x| match x.1.block.name() { + "air-factory" => Some(AIR_FACTORY), + "ground-factory" => Some(GROUND_FACTORY), + "naval-factory" => Some(NAVAL_FACTORY), + _ => None, + }).unwrap_or(AIR_FACTORY)]) + } else { tags(tg) }; x.schem.tags.insert("labels".into(), tags.clone()); let who = msg.author_nick(c).await.unwrap_or(msg.author.name.clone()); ownership::get(repo) @@ -167,21 +174,35 @@ pub async fn scour( Ok(()) } -async fn del(c: &serenity::prelude::Context,& Ch{ d:dir, repo: git, ..}: &Ch, deleted_message_id: u64) { +async fn del( + c: &serenity::prelude::Context, + &Ch { + d: dir, repo: git, .. + }: &Ch, + deleted_message_id: u64, +) { use crate::emoji::named::*; - if let Ok(s) = git.schem(dir, deleted_message_id.into()){ + if let Ok(s) = git.schem(dir, deleted_message_id.into()) { let own = git.own().await.erase(deleted_message_id).unwrap(); git.remove(dir, deleted_message_id.into()); git.commit("plent", &format!("remove {deleted_message_id:x}")); git.push(); if git == &repos::DESIGN_IT && !cfg!(debug_assertions) { - send(c,|x| x - .username("plent") - .embed(CreateEmbed::new().color(RM) - .description(format!("{CANCEL} remove {} (added by {own}) (`{:x}.msch`)", emoji::mindustry::to_discord(&strip_colors(s.tags.get("name").unwrap())), deleted_message_id)) - .footer(CreateEmbedFooter::new("message was deleted.") - )) - ).await; + send(c, |x| { + x.username("plent").embed( + CreateEmbed::new() + .color(RM) + .description(format!( + "{CANCEL} remove {} (added by {own}) (`{:x}.msch`)", + emoji::mindustry::to_discord(&strip_colors( + s.tags.get("name").unwrap() + )), + deleted_message_id + )) + .footer(CreateEmbedFooter::new("message was deleted.")), + ) + }) + .await; }; } } @@ -189,9 +210,10 @@ async fn del(c: &serenity::prelude::Context,& Ch{ d:dir, repo: git, ..}: &Ch, de static HOOK: OnceLock<Webhook> = OnceLock::new(); pub async fn hookup(c: &impl AsRef<Http>) { - let v = Webhook::from_url(c, + let v = Webhook::from_url( + c, &std::env::var("WEBHOOK") - .unwrap_or_else(|_| read_to_string("webhook").expect("wher webhook")) + .unwrap_or_else(|_| read_to_string("webhook").expect("wher webhook")), ) .await .unwrap(); @@ -230,9 +252,13 @@ async fn handle_message( let (dir, l, repo) = sep(SPECIAL.get(&new_message.channel_id.get()).or(post.as_ref())); let m = Msg { author: who.clone(), - locale: new_message.author.locale.clone().unwrap_or("unknown locale".to_string()), + locale: new_message + .author + .locale + .clone() + .unwrap_or("unknown locale".to_string()), author_id: new_message.author.id.get(), - guild: new_message.guild_id.map_or(0,Into::into), + guild: new_message.guild_id.map_or(0, Into::into), avatar: new_message.author.face(), attachments: new_message.attachments.clone(), content: new_message.content.clone(), @@ -510,6 +536,7 @@ impl Bot { 925674713429184564.into(), ) .await?; + poise::builtins::register_in_guild(ctx, &[scour()], 1388427745066750045.into()).await?; println!("registered"); let tracker = Arc::new(DashMap::new()); let tc = Arc::clone(&tracker); @@ -869,10 +896,10 @@ pub fn png(p: fimg::Image<Vec<u8>, 3>) -> Vec<u8> { )] /// Pong! pub async fn ping(c: Context<'_>) -> Result<()> { - // let p = Timestamp::now() - // .signed_duration_since(*c.created_at()) - // .to_std()? - // .as_millis() as _; + // let p = Timestamp::now() + // .signed_duration_since(*c.created_at()) + // .to_std()? + // .as_millis() as _; log(&c); use emoji::named::*; let m = memory_stats::memory_stats().unwrap().physical_mem as f32 / (1 << 20) as f32; @@ -906,19 +933,13 @@ pub async fn ping(c: Context<'_>) -> Result<()> { /// Renders base64 schematic. pub async fn render(c: Context<'_>, #[description = "schematic, base64"] s: String) -> Result<()> { log(&c); - poise::send_reply(c, - match schematic::from_b64(&s) { - Ok(s) => - schematic::reply( - s, - &c.author().name, - &c.author().face(), - ) - .await?, - Err(e) => - CreateReply::default() - .content(format!("schem broken / not schem: {e}")), - }) + poise::send_reply( + c, + match schematic::from_b64(&s) { + Ok(s) => schematic::reply(s, &c.author().name, &c.author().face()).await?, + Err(e) => CreateReply::default().content(format!("schem broken / not schem: {e}")), + }, + ) .await?; Ok(()) } @@ -940,7 +961,7 @@ pub async fn render_file( match map::reply(c, &s).await? { ControlFlow::Break(x) => return Ok(drop(poise::send_reply(c, x).await?)), ControlFlow::Continue(e) if e != "not a map." => { - return Ok(drop(poise::say_reply(c, e).await?)) + return Ok(drop(poise::say_reply(c, e).await?)); } ControlFlow::Continue(_) => (), }; @@ -955,12 +976,7 @@ pub async fn render_file( }; poise::send_reply( c, - schematic::reply( - s, - &c.author().name, - &c.author().face(), - ) - .await?, + schematic::reply(s, &c.author().name, &c.author().face()).await?, ) .await?; Ok(()) @@ -968,40 +984,52 @@ pub async fn render_file( #[poise::command(slash_command)] /// Rename a schematic. -async fn rename_file(c: Context<'_>, #[description = "schematic, msch"] s: Attachment, #[description = "new name"] name:String) -> Result<()> { +async fn rename_file( + c: Context<'_>, + #[description = "schematic, msch"] s: Attachment, + #[description = "new name"] name: String, +) -> Result<()> { log(&c); - let Some(schematic::Schem{schem: mut s}) = schematic::from_attachments(std::slice::from_ref(&s)).await? else { + let Some(schematic::Schem { schem: mut s }) = + schematic::from_attachments(std::slice::from_ref(&s)).await? + else { c.reply("no schem!").await?; return Ok(()); }; s.tags.insert("name".to_string(), name); - let mut o= DataWrite::default(); + let mut o = DataWrite::default(); s.serialize(&mut o)?; - poise::send_reply(c, CreateReply::default().attachment( - CreateAttachment::bytes(o.consume(),"out.msch") - )).await?; + poise::send_reply( + c, + CreateReply::default().attachment(CreateAttachment::bytes(o.consume(), "out.msch")), + ) + .await?; Ok(()) } - #[poise::command(slash_command)] /// Rename a schematic. -async fn rename(c: Context<'_>, #[description = "schematic, base64"] s: String, #[description = "new name"] name:String) -> Result<()> { - log(&c);let Ok(schematic::Schem{schem: mut s}) = schematic::from_b64(&*s) else { +async fn rename( + c: Context<'_>, + #[description = "schematic, base64"] s: String, + #[description = "new name"] name: String, +) -> Result<()> { + log(&c); + let Ok(schematic::Schem { schem: mut s }) = schematic::from_b64(&*s) else { c.reply("no schem!").await?; return Ok(()); }; s.tags.insert("name".to_string(), name); - let mut o= DataWrite::default(); + let mut o = DataWrite::default(); s.serialize(&mut o)?; - poise::send_reply(c, CreateReply::default().attachment( - CreateAttachment::bytes(o.consume(),"out.msch") - )).await?; + poise::send_reply( + c, + CreateReply::default().attachment(CreateAttachment::bytes(o.consume(), "out.msch")), + ) + .await?; Ok(()) } - - #[poise::command( context_menu_command = "Render schematic", install_context = "User", @@ -1009,25 +1037,25 @@ async fn rename(c: Context<'_>, #[description = "schematic, base64"] s: String, )] /// Renders schematic inside a message. pub async fn render_message(c: Context<'_>, m: Message) -> Result<()> { - log(&c);poise::send_reply( - c, match schematic::from((&m.content, &m.attachments)).await { - Ok(Some(s)) => - schematic::reply( - s, - &m.author_nick(c) - .await - .unwrap_or_else(|| m.author.name.clone()), - &m.author.face(), - ) - .await?, - Err(e) => - CreateReply::default() - .content(format!("schematic error {e}")), - Ok(None) => - CreateReply::default() + log(&c); + poise::send_reply( + c, + match schematic::from((&m.content, &m.attachments)).await { + Ok(Some(s)) => { + schematic::reply( + s, + &m.author_nick(c) + .await + .unwrap_or_else(|| m.author.name.clone()), + &m.author.face(), + ) + .await? + } + Err(e) => CreateReply::default().content(format!("schematic error {e}")), + Ok(None) => CreateReply::default() .content("no schem found") - .ephemeral(true) - } + .ephemeral(true), + }, ) .await?; Ok(()) @@ -1035,12 +1063,13 @@ pub async fn render_message(c: Context<'_>, m: Message) -> Result<()> { #[poise::command( slash_command, - install_context = "Guild", + install_context = "Guild|User", interaction_context = "Guild|PrivateChannel" )] /// Instructions on adding a schematic repository to YOUR server! pub async fn schembrowser_instructions(c: Context<'_>) -> Result<()> { - log(&c);poise::send_reply( + log(&c); + poise::send_reply( c, poise::CreateReply::default() .content(include_str!("repo.md")) @@ -1050,12 +1079,7 @@ pub async fn schembrowser_instructions(c: Context<'_>) -> Result<()> { Ok(()) } - -#[poise::command( - slash_command, - install_context = "Guild", - interaction_context = "Guild|PrivateChannel" -)] +#[poise::command(slash_command)] /// Statistics #[implicit_fn::implicit_fn] pub async fn stats(c: Context<'_>) -> Result<()> { @@ -1063,25 +1087,41 @@ pub async fn stats(c: Context<'_>) -> Result<()> { let mut schem_calls = 0; let mut map_calls = 0; let mut eval_calls = 0; - for x in std::fs::read_to_string("data").unwrap().lines().map(serde_json::from_str::<serde_json::Value>).filter_map(Result::ok) { - *guilds.entry(x.get("guild").unwrap().as_u64().unwrap()).or_default() += 1; + for x in std::fs::read_to_string("data") + .unwrap() + .lines() + .map(serde_json::from_str::<serde_json::Value>) + .filter_map(Result::ok) + { + *guilds + .entry(x.get("guild").unwrap().as_u64().unwrap()) + .or_default() += 1; let x = x.get("cname").unwrap().as_str().unwrap(); - if x.contains("schematic"){ - schem_calls+=1; + if x.contains("schematic") { + schem_calls += 1; } if x.contains("map") { - map_calls +=1; + map_calls += 1; } - if x.contains("eval") { - eval_calls+=1; + if x.contains("eval") { + eval_calls += 1; } } use futures::stream; let mut x = stream::iter(guilds.into_iter().filter(_.0 != 0).filter(_.1 > 25)) - .map(async |(k,v)| {GuildId::new(k).to_partial_guild(c.http()).await.map(|x| (x.name, v)).unwrap_or(("DM".to_string(), v))}) - .buffer_unordered(16).collect::<Vec<_>>().await.into_iter() - .map(|(a,b)| Series::new(a, vec![b as f32])) + .map(async |(k, v)| { + GuildId::new(k) + .to_partial_guild(c.http()) + .await + .map(|x| (x.name, v)) + .unwrap_or(("DM".to_string(), v)) + }) + .buffer_unordered(16) + .collect::<Vec<_>>() + .await + .into_iter() + .map(|(a, b)| Series::new(a, vec![b as f32])) .collect::<Vec<_>>(); x.sort_by_key(|x| x.data[0] as u64); @@ -1095,6 +1135,6 @@ pub async fn stats(c: Context<'_>) -> Result<()> { use emoji::named::*; let x = charts_rs::svg_to_webp(&ch.svg().unwrap()).unwrap(); - poise::send_reply(c, poise::CreateReply::default().attachment(CreateAttachment::bytes(x, "chart.webp")).content(format!("{EDIT} total schematics rendered: {schem_calls}\n{MAP} total maps rendered: {map_calls}\n{WORLD_PROCESSOR} eval calls: {eval_calls}"))).await?; + poise::send_reply(c, poise::CreateReply::default().attachment(CreateAttachment::bytes(x, "chart.webp")).content(format!("{EDIT} total schematics rendered: {schem_calls}\n{MAP} total maps rendered: {map_calls}\n{WORLD_PROCESSOR} eval calls: {eval_calls}"))).await?; Ok(()) -}
\ No newline at end of file +} diff --git a/src/bot/repos.rs b/src/bot/repos.rs index 4e27c24..cb4d595 100644 --- a/src/bot/repos.rs +++ b/src/bot/repos.rs @@ -67,48 +67,56 @@ impl Repo { } pub fn remove(&self, dir: &str, x: MessageId) { - assert!(std::process::Command::new("git") - .current_dir(self.repopath()) - .arg("rm") - .arg("-q") - .arg("-f") - .arg(self.gpath(dir, x)) - .status() - .unwrap() - .success()); + assert!( + std::process::Command::new("git") + .current_dir(self.repopath()) + .arg("rm") + .arg("-q") + .arg("-f") + .arg(self.gpath(dir, x)) + .status() + .unwrap() + .success() + ); } pub fn pull(&self) { - assert!(std::process::Command::new("git") - .current_dir(self.repopath()) - .arg("pull") - .arg("-q") - .status() - .unwrap() - .success()); + assert!( + std::process::Command::new("git") + .current_dir(self.repopath()) + .arg("pull") + .arg("-q") + .status() + .unwrap() + .success() + ); } pub fn commit(&self, by: &str, msg: &str) { - assert!(std::process::Command::new("git") - .current_dir(self.repopath()) - .args(["commit", "--no-gpg-sign", "-q", "--author"]) - .arg(format!("{by} <@designit>")) - .arg("-m") - .arg(msg) - .status() - .unwrap() - .success()); + assert!( + std::process::Command::new("git") + .current_dir(self.repopath()) + .args(["commit", "--no-gpg-sign", "-q", "--author"]) + .arg(format!("{by} <@designit>")) + .arg("-m") + .arg(msg) + .status() + .unwrap() + .success() + ); } pub fn push(&self) { #[cfg(not(debug_assertions))] - assert!(std::process::Command::new("git") - .current_dir(self.repopath()) - .arg("push") - .arg("-q") - .status() - .unwrap() - .success()) + assert!( + std::process::Command::new("git") + .current_dir(self.repopath()) + .arg("push") + .arg("-q") + .status() + .unwrap() + .success() + ) } pub fn write(&self, dir: &str, x: MessageId, s: Schem) { @@ -120,13 +128,15 @@ impl Repo { } pub fn add(&self) { - assert!(std::process::Command::new("git") - .current_dir(self.repopath()) - .arg("add") - .arg(".") - .status() - .unwrap() - .success()); + assert!( + std::process::Command::new("git") + .current_dir(self.repopath()) + .arg("add") + .arg(".") + .status() + .unwrap() + .success() + ); } } @@ -197,6 +207,11 @@ repos! { chief: 705503407179431937, deny_emoji: 1192388789952319499u64, }, + EVICT => { + admins: &[person!(&1394078459940175963), person!(&1389145164299243520)], + chief: 954347786193747968, + deny_emoji: 1395478597451518085u64, + } } decl! { @@ -271,6 +286,34 @@ forum 1297463616035098654u64 => "e-units" ACP => [ 1276759410722738186u64 => "schems": ["plague"] ]; +EVICT => [ +1394340637758722048u64 => "t1" : ["find unit factory"], +1394340696227319808u64 => "t2" : [ADDITIVE_RECONSTRUCTOR], +1394340759297065252u64 => "t3" : [MULTIPLICATIVE_RECONSTRUCTOR], +1394340939685953656u64 => "t4" : [EXPONENTIAL_RECONSTRUCTOR], +1394340961550729237u64 => "t5" : [TETRATIVE_RECONSTRUCTOR], +1394418354634362910u64 => "combustion-gen" : [COMBUSTION_GENERATOR], +1394418605461999769u64 => "differential-gen" : [DIFFERENTIAL_GENERATOR], +1394418707257626795u64 => "impact-reactor" : [IMPACT_REACTOR], +1394418470430707712u64 => "steam-gen" : [STEAM_GENERATOR], +1394418672755282040u64 => "thorium-reactor" : [THORIUM_REACTOR], +1394412929587347556u64 => "coal" : [COAL], +1394081205024063529u64 => "graphite" : [GRAPHITE], +1394081280978583624u64 => "meta" : [METAGLASS], +1394081566950424577u64 => "phase" : [PHASEFABRIC], +1394081445781176493u64 => "plast" : [PLASTANIUM], +1394341640105103400u64 => "resource-mix" : [ROUTER], +1394081152611913918u64 => "silicon" : [SILICON], +1394081607991820368u64 => "surge" : [SURGEALLOY], +1394342304021741672u64 => "miners" : [BLAST_DRILL], +1394342482027745351u64 => "scrap-schematics" : [SCRAP], +1394335945074933901u64 => "cyclone" : [CYCLONE], +1394083007245320244u64 => "fuse" : [FUSE], +1394083227001557034u64 => "hail-scorch" : [HAIL, SCORCH], +1394082982733680722u64 => "scatter": [SCATTER], +1394335981691211806u64 => "swarmer": [SWARMER], +1394414493957881906u64 => "miscellaneous" : [NEOPLASM] + ]; } macro_rules! chief { @@ -279,7 +322,7 @@ macro_rules! chief { .get(&$c.channel_id().get()) .ok_or(anyhow::anyhow!("not repo"))? .repo; - if repo.chief != $c.author().id.get() { + if repo.chief != $c.author().id.get() && $c.author().id.get() != OWNER { poise::send_reply( $c, poise::CreateReply::default() diff --git a/src/bot/schematic.rs b/src/bot/schematic.rs index 9964943..736af7e 100644 --- a/src/bot/schematic.rs +++ b/src/bot/schematic.rs @@ -2,16 +2,16 @@ use crate::emoji; use anyhow::Result; use base64::Engine; use logos::Logos; -use mindus::data::schematic::R64Error; use mindus::data::DataRead; +use mindus::data::schematic::R64Error; use mindus::*; -use poise::{serenity_prelude::*, CreateReply}; +use poise::{CreateReply, serenity_prelude::*}; use regex::Regex; use std::ops::ControlFlow; use std::sync::LazyLock; use std::{fmt::Write, ops::Deref}; -use super::{strip_colors, Msg, SUCCESS}; +use super::{Msg, SUCCESS, strip_colors}; static RE: LazyLock<Regex> = LazyLock::new(|| { Regex::new(r"(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?").unwrap() @@ -153,7 +153,19 @@ pub async fn with( "guild": m.guild, "channel": m.channel.get(), }}); - if let Some(x) = labels { + if let Some(mut x) = labels { + if x.contains("find unit factory") { + use emoji::to_mindustry::named::*; + x = super::tags(&[v + .block_iter() + .find_map(|x| match x.1.block.name() { + "air-factory" => Some(AIR_FACTORY), + "ground-factory" => Some(GROUND_FACTORY), + "naval-factory" => Some(NAVAL_FACTORY), + _ => None, + }) + .unwrap_or(AIR_FACTORY)]); + } v.schem.tags.insert("labels".into(), x); }; return Ok(ControlFlow::Break(send(m, c, v).await?)); diff --git a/src/expose.rs b/src/expose.rs index 812629b..677cdba 100644 --- a/src/expose.rs +++ b/src/expose.rs @@ -1,9 +1,9 @@ use axum::{ + Router, Server as AxumServer, extract::Path, - http::{header::*, StatusCode}, + http::{StatusCode, header::*}, response::{AppendHeaders, Html}, routing::get, - Router, Server as AxumServer, }; use std::{net::SocketAddr, sync::LazyLock, time::SystemTime}; |