smol bot
things
| -rw-r--r-- | Cargo.toml | 14 | ||||
| -rw-r--r-- | build.rs | 3 | ||||
| -rw-r--r-- | src/bot/logic.rs | 120 | ||||
| -rw-r--r-- | src/bot/mod.rs | 109 | ||||
| -rw-r--r-- | src/bot/ownership.rs | 2 | ||||
| -rw-r--r-- | src/bot/repos.rs | 16 | ||||
| -rw-r--r-- | src/bot/schematic.rs | 7 | ||||
| -rw-r--r-- | src/bot/search.rs | 5 | ||||
| -rw-r--r-- | src/expose.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 12 |
10 files changed, 189 insertions, 101 deletions
@@ -19,12 +19,9 @@ serenity = { version = "0.12", features = [ "gateway", "model", ], default-features = false } -poise = { git = "https://github.com/fgardt/poise", features = [ - "unstable", -], branch = "feat/user_apps" } +poise = { git = "https://github.com/fgardt/poise", branch = "feat/user_apps" } anyhow = "1.0.75" regex = { version = "1.8.4", features = ["std"], default-features = false } -btparse = "0.1.1" mindus = { version = "5.0.7", features = [], default-features = false } flate2 = { version = "1.0", features = [ "cloudflare_zlib", @@ -34,7 +31,7 @@ dashmap = "5.5.3" oxipng = { version = "9.0.0", default-features = false } fimg = "0.4.26" phf = { version = "0.11.2", features = ["macros"] } -emoji = { git = "https://github.com/Apricot-Conservation-Project/emoji" } +emojib = { git = "https://github.com/Apricot-Conservation-Project/emoji", package = "emoji" } rust-fuzzy-search = "0.1.1" jemallocator-global = "0.3.2" const_format = { version = "0.2.32", features = ["fmt"] } @@ -53,7 +50,13 @@ atools = "0.1.5" # edg = { path = "../edg" } httpdate = "1.0.3" pollster = "0.3.0" +btparse-stable = "0.1.2" +cpu-monitor = "0.1.1" +[build-dependencies] +emojib = { git = "https://github.com/Apricot-Conservation-Project/emoji", features = [ + "build", +], package = "emoji" } [profile.release] strip = true @@ -69,3 +72,4 @@ debug-assertions = false [patch.crates-io] serenity = { git = "https://github.com/serenity-rs/serenity" } +mindus = { git = "https://github.com/bend-n/mindus" } @@ -1,4 +1,5 @@ #![feature(let_chains)] +// use serenity::prelude::*; use std::fs; use std::io::prelude::*; use std::path::Path; @@ -32,6 +33,8 @@ fn main() -> std::io::Result<()> { fs::create_dir("html")?; } + emojib::load(); + for path in fs::read_dir("html-src")? { process(path.unwrap().path().file_name().unwrap())?; } diff --git a/src/bot/logic.rs b/src/bot/logic.rs index 10d5034..e299c08 100644 --- a/src/bot/logic.rs +++ b/src/bot/logic.rs @@ -2,13 +2,77 @@ use super::{Context, Result}; use lemu::Executor; use poise::{serenity_prelude::*, CodeBlock, KeyValueArgs}; +#[poise::command(slash_command, rename = "eval_file", install_context = "Guild|User")] +/// Execute MLOG from a file. +/// +/// Your file can run up to 52789849 instructions, and up to 50 iterations. +/// You get one large display to use. +pub async fn run_file( + ctx: Context<'_>, + #[description = "logic, txt"] mlog: Attachment, + #[description = "number of iterations (0–50)"] iterations: Option<u8>, +) -> Result<()> { + ctx.defer().await?; + let bytes = mlog.download().await?; + let Ok(code) = String::from_utf8(bytes) else { + ctx.say("this is not a mlog file!").await?; + return Ok(()); + }; + match exec(code, iterations.map_or(1, |x| x.clamp(0, 50)) as _).await { + Err(Err::Other(x)) => return Err(x), + Err(Err::Lemu(x)) => { + ctx.send( + poise::CreateReply::default() + .allowed_mentions(CreateAllowedMentions::default().empty_users().empty_roles()) + .content(format!("```ansi\n{x}\n```")), + ) + .await?; + } + Ok(x) => drop(ctx.send(x).await?), + } + ctx.say(format!("executed [{}]({})", mlog.filename, mlog.url)) + .await?; + Ok(()) +} + #[poise::command(prefix_command, track_edits, rename = "eval")] pub async fn run( ctx: Context<'_>, #[description = "number of iterations"] kv: KeyValueArgs, #[description = "Script"] block: CodeBlock, ) -> Result<()> { - let _ = ctx.channel_id().start_typing(&ctx.serenity_context().http); + match exec( + block.code, + kv.get("iters") + .map_or(1, |v| v.parse::<usize>().unwrap_or(1).clamp(1, 50)), + ) + .await + { + Err(Err::Other(x)) => return Err(x), + Err(Err::Lemu(x)) => { + ctx.send( + poise::CreateReply::default() + .allowed_mentions(CreateAllowedMentions::default().empty_users().empty_roles()) + .content(format!("```ansi\n{x}\n```")), + ) + .await?; + } + Ok(x) => drop(ctx.send(x).await?), + } + Ok(()) +} + +enum Err { + Lemu(String), + Other(anyhow::Error), +} +impl<T: Into<anyhow::Error>> From<T> for Err { + fn from(value: T) -> Self { + Self::Other(value.into()) + } +} + +async fn exec(code: String, iters: usize) -> Result<poise::CreateReply, Err> { let lemu::Output { output: Some(output), displays, @@ -16,29 +80,20 @@ pub async fn run( } = (match tokio::task::spawn_blocking(move || { Executor::with_output(vec![]) .large_display() - .limit_iterations( - kv.get("iters") - .map_or(1, |v| v.parse::<usize>().unwrap_or(1).clamp(1, 50)), - ) + .limit_iterations(iters) .limit_instructions(52789849) - .program(&block.code) + .program(&code) .map(|mut v| { v.run(); v.output() }) - .map_err(|e| format!("{}", e.diagnose(&block.code)).replace('`', "\u{200b}`")) + .map_err(|e| format!("{}", e.diagnose(&code)).replace('`', "\u{200b}`")) }) .await? { Ok(o) => o, Err(e) => { - ctx.send( - poise::CreateReply::default() - .allowed_mentions(CreateAllowedMentions::default().empty_users().empty_roles()) - .content(format!("```ansi\n{e}\n```")), - ) - .await?; - return Ok(()); + return Err(Err::Lemu(e)); } }) else { @@ -65,25 +120,20 @@ pub async fn run( None }; - ctx.send({ - let mut c = poise::CreateReply::default(); - if output.is_empty() && display.is_none() { - c = c.content("no output"); - } - if !output.is_empty() { - c = c.content(format!( - "```\n{}\n```", - String::from_utf8_lossy(&output).replace('`', "\u{200b}`") - )); - } - if let Some(display) = display { - c = c - .attachment(CreateAttachment::bytes(display, "display1.png")) - .embed(CreateEmbed::default().attachment("display1.png")); - } - c - }) - .await?; - - Ok(()) + let mut c = poise::CreateReply::default(); + if output.is_empty() && display.is_none() { + c = c.content("no output"); + } + if !output.is_empty() { + c = c.content(format!( + "```\n{}\n```", + String::from_utf8_lossy(&output).replace('`', "\u{200b}`") + )); + } + if let Some(display) = display { + c = c + .attachment(CreateAttachment::bytes(display, "display1.png")) + .embed(CreateEmbed::default().attachment("display1.png")); + } + Ok(c) } diff --git a/src/bot/mod.rs b/src/bot/mod.rs index d76718a..ff02b4c 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -5,6 +5,7 @@ mod repos; mod schematic; pub mod search; +use crate::emoji; use anyhow::Result; use dashmap::DashMap; use mindus::data::DataWrite; @@ -202,7 +203,7 @@ async fn handle_message( return Ok(()); } ControlFlow::Break((m, n, s)) => { - if THREADED.contains(&m.channel_id.get()) { + if SPECIAL.contains_key(&m.channel_id.get()) || THREADED.contains(&m.channel_id.get()) { m.channel_id .create_thread_from_message( c, @@ -224,14 +225,14 @@ async fn handle_message( (m.author.name.clone(), m.author.id.get()), ); use emoji::named::*; - if repo.id == 925674713429184564 && !cfg!(debug_assertions) { - send(c,|x| x + if repo.id == 925674713429184564 && !cfg!(debug_assertions) { + send(c,|x| x .avatar_url(new_message.author.avatar_url().unwrap_or(CAT.to_string())) .username(&who) .embed(CreateEmbed::new().color(AD) .description(format!("https://discord.com/channels/925674713429184564/{}/{} {ADD} add {} (`{:x}.msch`)", m.channel_id,m.id, emoji::mindustry::to_discord(&strip_colors(s.tags.get("name").unwrap())), new_message.id.get()))) ).await; - } + } repo.write(dir, new_message.id, s); repo.add(); repo.commit(&who, &format!("add {:x}.msch", new_message.id.get())); @@ -248,6 +249,8 @@ async fn handle_message( map::with(new_message, c).await?; Ok(()) } +static SEEN: LazyLock<Mutex<HashSet<(GuildId, u64, String, UserId)>>> = + LazyLock::new(|| Mutex::new(HashSet::new())); pub struct Bot; impl Bot { @@ -260,18 +263,37 @@ 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![logic::run(), lb(), schembrowser_instructions(), lb_no_vds(), ping(), help(), scour(), search::search(), search::file(), render(), render_file(), render_message()], + commands: vec![logic::run(), lb(), logic::run_file(), schembrowser_instructions(), lb_no_vds(), ping(), help(), scour(), search::search(), search::file(), render(), render_file(), render_message()], event_handler: |c, e, _, d| { Box::pin(async move { match e { FullEvent::Ready { .. } => { println!("bot ready"); + // pruning + // while SEEN.lock().await.len() < 5 { + // tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + // } + // let mut x = SEEN.lock().await.clone().into_iter().collect::<Vec<_>>(); + // x.sort_by_key(|(_, x, _,_)|*x); + // for (g, _, _ ,_ ) in x.iter().take_while(|(_, x, _,_)| *x <= 6).filter(|(_, _, _,x)| *x != OWNER) { + // g.leave(&c).await.unwrap(); + // }; + // for (i, member_count, name, _) in x { + // println!( + // "{name} has {member_count:?} members {i:?}" + // ); + // } + SEEN.lock().await.clear(); emojis::load(c.http()).await; hookup(c.http()).await; } + FullEvent::GuildCreate { guild , ..} => { + SEEN.lock().await.insert((guild.id, guild.member_count, guild.name.clone(), guild.owner_id)); + // let User{id,name:owner_name,..} = c.http().get_user(*owner_id).await.unwrap(); + } // :deny:, @vd FullEvent::ReactionAdd { - add_reaction: Reaction { message_id, guild_id: Some(guild_id), emoji: ReactionType::Custom { id,.. } ,channel_id,member: Some( m @ Member{ nick,user,..}),..}} + add_reaction: Reaction { message_id, guild_id: Some(guild_id), emoji: ReactionType::Custom { id,.. } ,channel_id,member: Some( m @ Member{ nick,user,..}),..}} if let Some(git) = REPOS.get(&guild_id.get()) && *id == git.deny_emoji && git.auth(m) @@ -285,7 +307,7 @@ impl Bot { git.commit(who, &format!("remove {:x}.msch", message_id.get())); git.push(); _ = m.delete_reaction(c,Some(1174262682573082644.into()), emojis::get!(MERGE)).await; - _ = m.delete_reaction(c,Some(1174262682573082644.into()), ReactionType::Custom { animated: false, id: 1192316518395039864.into(), name: Some("merge".into()) }).await.unwrap(); + m.delete_reaction(c,Some(1174262682573082644.into()), ReactionType::Custom { animated: false, id: 1192316518395039864.into(), name: Some("merge".into()) }).await.unwrap(); m.react(c,emojis::get!(DENY)).await?; // only design-it has a webhook (possibly subject to future change) if *guild_id == 925674713429184564 && !cfg!(debug_assertions) { @@ -298,28 +320,6 @@ impl Bot { } }; } - FullEvent::GuildCreate { guild ,..} => { - static SEEN: LazyLock<Mutex<HashSet<GuildId>>> = - LazyLock::new(|| Mutex::new(HashSet::new())); - if SEEN.lock().await.insert(guild.id) { - let Guild { - member_count, - name, - owner_id, - .. - } = guild; - let User{id,name:owner_name,..} = c.http().get_user(*owner_id).await.unwrap(); - c.http() - .get_user(696196765564534825.into()) - .await - .unwrap() - .dm(c.http(), CreateMessage::new().allowed_mentions(CreateAllowedMentions::default().empty_users()).content(format!( - "{name} (owned by <@{id}>({owner_name})) has {member_count:?} members" - ))) - .await - .unwrap(); - } - } FullEvent::Message { new_message } => { if new_message.content.starts_with('!') || new_message.content.starts_with(PFX) @@ -338,7 +338,6 @@ impl Bot { channel_id, .. }, ..} => { - if let Some((_, r)) = d.tracker.remove(id) { _ = r.delete(c).await; let who = author @@ -398,7 +397,6 @@ impl Bot { } }; } - if let Some((_, r)) = d.tracker.remove(deleted_message_id) { r.delete(c).await.unwrap(); } @@ -420,9 +418,28 @@ impl Bot { }) .setup(|ctx, _ready, _| { Box::pin(async move { - poise::builtins::register_globally(ctx, &[logic::run(), help(), ping(), render(), schembrowser_instructions(), render_file(), render_message()]).await?; - poise::builtins::register_in_guild(ctx, &[scour()], 1110086242177142854.into()).await?; - poise::builtins::register_in_guild(ctx, &[search::search(), lb(), lb_no_vds(), search::file()], 925674713429184564.into()).await?; + poise::builtins::register_globally( + ctx, + &[ + logic::run(), + help(), + ping(), + render(), + schembrowser_instructions(), + render_file(), + render_message(), + logic::run_file(), + ], + ) + .await?; + poise::builtins::register_in_guild(ctx, &[scour()], 1110086242177142854.into()) + .await?; + poise::builtins::register_in_guild( + ctx, + &[search::search(), lb(), lb_no_vds(), search::file()], + 925674713429184564.into(), + ) + .await?; println!("registered"); let tracker = Arc::new(DashMap::new()); let tc = Arc::clone(&tracker); @@ -439,7 +456,8 @@ impl Bot { }); Ok(Data { tracker }) }) - }).build(); + }) + .build(); ClientBuilder::new( tok, GatewayIntents::non_privileged() | GatewayIntents::MESSAGE_CONTENT, @@ -576,7 +594,7 @@ pub async fn leaderboard(c: Context<'_>, channel: Option<ChannelId>, vds: bool) let mut map = HashMap::new(); search::dir(ch.get()) .unwrap() - .map(|y| lock.map[&search::flake(y.file_name().unwrap().to_str().unwrap()).into()].1) + .map(|y| lock.map[&search::flake(y.file_name().unwrap().to_str().unwrap())].1) .filter(|x| vds || !VDS.contains(x)) .for_each(|x| *map.entry(x).or_default() += 1); poise::say_reply( @@ -595,9 +613,7 @@ pub async fn leaderboard(c: Context<'_>, channel: Option<ChannelId>, vds: bool) None => { let mut map = std::collections::HashMap::new(); search::files() - .map(|(y, _)| { - lock.map[&search::flake(y.file_name().unwrap().to_str().unwrap()).into()].1 - }) + .map(|(y, _)| lock.map[&search::flake(y.file_name().unwrap().to_str().unwrap())].1) .filter(|x| vds || !VDS.contains(x)) .for_each(|x| *map.entry(x).or_default() += 1); poise::say_reply(c, format!("## Leaderboard\n{}", process(map))) @@ -678,9 +694,9 @@ async fn on_error(error: poise::FrameworkError<'_, Data, anyhow::Error>) { } } } - let bt = error.backtrace(); + /* let bt = error.backtrace(); if bt.status() == std::backtrace::BacktraceStatus::Captured { - let parsed = btparse::deserialize(dbg!(error.backtrace())).unwrap(); + let parsed = btparse_stable::deserialize(error.backtrace()).unwrap(); let mut s = vec![]; for frame in parsed.frames { if let Some(line) = frame.line @@ -695,7 +711,7 @@ async fn on_error(error: poise::FrameworkError<'_, Data, anyhow::Error>) { } s.truncate(15); write!(msg, "trace: ```rs\n{}\n```", s.join("\n")).unwrap(); - } + } */ ctx.say(msg).await.unwrap(); } err => poise::builtins::on_error(err).await.unwrap(), @@ -786,10 +802,17 @@ pub fn png(p: fimg::Image<Vec<u8>, 3>) -> Vec<u8> { pub async fn ping(c: Context<'_>) -> Result<()> { use emoji::named::*; let m = memory_stats::memory_stats().unwrap().physical_mem as f32 / (1 << 20) as f32; + + let start = cpu_monitor::CpuInstant::now()?; + std::thread::sleep(Duration::from_millis(200)); + let end = cpu_monitor::CpuInstant::now()?; + let duration = end - start; + let util = duration.non_idle() * 100.0; + // let m = (m / 0.1) + 0.5; // let m = m.floor() * 0.1; c.reply(format!( - "pong!\n{DISCORD}{RIGHT}: {} — {HOST} mem used: {m:.1}MiB — <:time:1244901561688260742> uptime: {}", + "pong!\n{DISCORD}{RIGHT}: {} — {HOST} mem used: {m:.1}MiB - <:stopwatch:1283755550726684723> cpu utilization {util:.2}% — <:time:1244901561688260742> uptime: {}", humantime::format_duration(Duration::from_millis( Timestamp::now() .signed_duration_since(*c.created_at()) diff --git a/src/bot/ownership.rs b/src/bot/ownership.rs index 12a3fc3..100dda6 100644 --- a/src/bot/ownership.rs +++ b/src/bot/ownership.rs @@ -21,7 +21,7 @@ impl Ownership { self.flush(); } fn flush(&self) { - std::fs::write(&self.path, serde_json::to_string_pretty(&self.map).unwrap()).unwrap(); + std::fs::write(self.path, serde_json::to_string_pretty(&self.map).unwrap()).unwrap(); } pub fn get(&self, k: u64) -> &(String, u64) { self.map.get(&k).unwrap() diff --git a/src/bot/repos.rs b/src/bot/repos.rs index 33c1a92..1cdd1fd 100644 --- a/src/bot/repos.rs +++ b/src/bot/repos.rs @@ -124,7 +124,7 @@ macro_rules! decl { ]; )+ ) => { - use emoji::to_mindustry::named::*; + use crate::emoji::to_mindustry::named::*; pub const THREADED: phf::Set<u64> = phf::phf_set! { $($threaded,)+ }; pub const SPECIAL: phf::Map<u64, Ch> = phf::phf_map! { $($($ch => Ch { d: $item, labels: &[$($labels,)+], repo: $repo },)?)+ @@ -178,20 +178,20 @@ decl! { 925721957209636914u64 => "cryofluid" : [CRYOFLUID, CRYOFLUID_MIXER], 925721791475904533u64 => "graphite" : [GRAPHITE, GRAPHITE_PRESS], 925721824556359720u64 => "metaglass" : [METAGLASS, KILN], -925721863525646356u64 => "phase-fabric" : [PHASE_FABRIC, PHASE_WEAVER], +925721863525646356u64 => "phase-fabric" : [PHASEFABRIC, PHASE_WEAVER], 927036346869104693u64 => "plastanium" : [PLASTANIUM, PLASTANIUM_COMPRESSOR], 925736419983515688u64 => "pyratite" : [PYRATITE, PYRATITE_MIXER], -925736573037838397u64 => "blast-compound" : [BLAST_COMPOUND, BLAST_MIXER], +925736573037838397u64 => "blast-compound" : [BLASTCOMPOUND, BLAST_MIXER], 927793648417009676u64 => "scrap" : [DISASSEMBLER, SCRAP], 1198556531281637506u64 => "spore-press" : [OIL, SPORE_PRESS], 1200308146460180520u64 => "oil-extractor" : [OIL, OIL_EXTRACTOR], 1200301847387316317u64 => "rtg-gen" : [POWER, RTG_GENERATOR], -1200308292744921088u64 => "cultivator" : [SPORE_POD, CULTIVATOR], +1200308292744921088u64 => "cultivator" : [SPOREPOD, CULTIVATOR], 1200305956689547324u64 => "graphite-multipress" : [GRAPHITE, MULTI_PRESS], 1200306409036857384u64 => "silicon-crucible" : [SILICON, SILICON_CRUCIBLE], 1198555991667646464u64 => "coal" : [COAL, COAL_CENTRIFUGE], 925721763856404520u64 => "silicon" : [SILICON, SILICON_SMELTER], -925721930814869524u64 => "surge-alloy" : [SURGE_ALLOY, SURGE_SMELTER], +925721930814869524u64 => "surge-alloy" : [SURGEALLOY, SURGE_SMELTER], 1141034314163826879u64 => "defensive-outpost" : [""], 949529149800865862u64 => "drills" : [PRODUCTION], 925729855574794311u64 => "logic-schems" : [MICRO_PROCESSOR], @@ -203,19 +203,19 @@ decl! { 926163105694752811u64 => "thorium-reactor" : [POWER, THORIUM_REACTOR], 973234467357458463u64 => "carbide" : [CARBIDE, ""], 1198527267933007893u64 => "erekir-defensive-outpost" : [""], -973236445567410186u64 => "fissile-matter" : [FISSILE_MATTER, ""], +973236445567410186u64 => "fissile-matter" : [FISSILEMATTER, ""], 1147887958351945738u64 => "electrolyzer" : [HYDROGEN, OZONE, ""], 1202001032503365673u64 => "nitrogen" : [NITROGEN, ""], 1202001055349477426u64 => "cyanogen" : [CYANOGEN, ""], 1096157669112418454u64 => "mass-driver" : ["…", PLANET], 973234248054104115u64 => "oxide" : [OXIDE, ""], -973422874734002216u64 => "erekir-phase" : [PHASE_FABRIC, ""], +973422874734002216u64 => "erekir-phase" : [PHASEFABRIC, ""], 973369188800413787u64 => "ccc" : ["", POWER], 1218453338396430406u64 => "neoplasia-reactor": ["", POWER], 1218453292045172817u64 => "flux-reactor": ["", POWER], 1218452986788053012u64 => "pyrolisis-gen": ["", POWER], 1147722735305367572u64 => "silicon-arc" : [SILICON, ""], -974450769967341568u64 => "erekir-surge" : [SURGE_ALLOY, ""], +974450769967341568u64 => "erekir-surge" : [SURGEALLOY, ""], 973241041685737532u64 => "erekir-units" : ["[#ff9266][]"], 1158818171139133490u64 => "unit-core" : [UNITS, CORE_NUCLEUS], 1158818324210274365u64 => "unit-delivery" : [UNITS, FLARE], diff --git a/src/bot/schematic.rs b/src/bot/schematic.rs index 94493e8..96c1720 100644 --- a/src/bot/schematic.rs +++ b/src/bot/schematic.rs @@ -1,3 +1,4 @@ +use crate::emoji; use anyhow::Result; use base64::Engine; use logos::Logos; @@ -147,9 +148,9 @@ pub async fn with( labels: Option<String>, ) -> Result<ControlFlow<(Message, String, Schem), ()>> { if let Ok(Some(mut v)) = from((&m.content, &m.attachments)).await { - labels.map(|x| { + if let Some(x) = labels { v.schem.tags.insert("labels".into(), x); - }); + }; return Ok(ControlFlow::Break(send(m, c, v).await?)); } @@ -203,7 +204,7 @@ fn decode_tags(tags: &str) -> Vec<String> { #[regex(r"[^,\]\[]+", priority = 6)] String(&'s str), } - let mut lexer = Tokens::lexer(&tags); + let mut lexer = Tokens::lexer(tags); let mut t = Vec::new(); let mut next = || lexer.find_map(|x| x.ok()); assert_eq!(next().unwrap(), Tokens::Open); diff --git a/src/bot/search.rs b/src/bot/search.rs index 28d6cd3..d7b4364 100644 --- a/src/bot/search.rs +++ b/src/bot/search.rs @@ -1,5 +1,5 @@ +use crate::emoji::named::*; use anyhow::Result; -use emoji::named::*; use mindus::data::DataRead; use mindus::{Schematic, Serializable}; use poise::serenity_prelude::*; @@ -130,8 +130,7 @@ pub fn files() -> impl Iterator<Item = (PathBuf, u64)> { super::SPECIAL .keys() .filter_map(|&ch| dir(ch).map(|x| (x, ch))) - .map(|(fs, channel)| fs.map(move |f| (f, channel))) - .flatten() + .flat_map(|(fs, channel)| fs.map(move |f| (f, channel))) } pub fn load(f: &Path) -> Schematic { diff --git a/src/expose.rs b/src/expose.rs index 28d25e4..d80aef9 100644 --- a/src/expose.rs +++ b/src/expose.rs @@ -7,7 +7,7 @@ use axum::{ }; use std::{net::SocketAddr, sync::LazyLock, time::SystemTime}; -const COMPILED_AT: LazyLock<SystemTime> = LazyLock::new(|| SystemTime::now()); +const COMPILED_AT: LazyLock<SystemTime> = LazyLock::new(SystemTime::now); // LazyLock::new(|| edg::r! { || -> std::time::SystemTime { std::time::SystemTime::now() }}); static COMPILED: LazyLock<String> = LazyLock::new(|| httpdate::fmt_http_date(*COMPILED_AT)); diff --git a/src/main.rs b/src/main.rs index fed3c1d..b5999a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,12 @@ -#![feature(let_chains, iter_intersperse, if_let_guard, const_mut_refs)] +#![feature( + let_chains, + lazy_cell_consume, + iter_intersperse, + if_let_guard, + const_mut_refs, + backtrace_frames +)] +emojib::the_crate! {} use std::{net::SocketAddr, sync::OnceLock, time::Instant}; mod expose; @@ -7,7 +15,7 @@ mod bot; static START: OnceLock<Instant> = OnceLock::new(); #[tokio::main(flavor = "current_thread")] async fn main() { - START.get_or_init(|| Instant::now()); + START.get_or_init(Instant::now); expose::Server::spawn(<SocketAddr as std::str::FromStr>::from_str("0.0.0.0:2000").unwrap()) .await; bot::Bot::spawn().await; |