smol bot
-rw-r--r--Cargo.toml14
-rw-r--r--build.rs3
-rw-r--r--src/bot/logic.rs120
-rw-r--r--src/bot/mod.rs109
-rw-r--r--src/bot/ownership.rs2
-rw-r--r--src/bot/repos.rs16
-rw-r--r--src/bot/schematic.rs7
-rw-r--r--src/bot/search.rs5
-rw-r--r--src/expose.rs2
-rw-r--r--src/main.rs12
10 files changed, 189 insertions, 101 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 772e26b..8fae5a1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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" }
diff --git a/build.rs b/build.rs
index 88304fa..63b9d11 100644
--- a/build.rs
+++ b/build.rs
@@ -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;