mirror of
https://github.com/mii443/typcord.git
synced 2025-08-22 16:25:53 +00:00
first commit
This commit is contained in:
2004
Cargo.lock
generated
Normal file
2004
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
Cargo.toml
Normal file
33
Cargo.toml
Normal file
@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "typcord"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bollard = "0.13"
|
||||
futures-util = "0.3"
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
regex = "1"
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
flate2 = "1"
|
||||
tar = "0.4"
|
||||
memfile = "0.2"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
log = "0.4.0"
|
||||
env_logger = "0.9.0"
|
||||
|
||||
[dependencies.serenity]
|
||||
default-features = true
|
||||
features = ["builder", "cache", "client", "gateway", "model", "utils", "unstable_discord_api", "collector", "rustls_backend", "framework"]
|
||||
git = "https://github.com/serenity-rs/serenity"
|
||||
branch = "next"
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.0"
|
||||
features = ["macros", "rt-multi-thread"]
|
160
lib/theorems.typ
Normal file
160
lib/theorems.typ
Normal file
@ -0,0 +1,160 @@
|
||||
// Store theorem environment numbering
|
||||
|
||||
#let thmcounters = state("thm",
|
||||
(
|
||||
"counters": ("heading": ()),
|
||||
"latest": ()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
#let thmenv(identifier, base, base_level, fmt) = {
|
||||
|
||||
let global_numbering = numbering
|
||||
|
||||
return (
|
||||
body,
|
||||
name: none,
|
||||
numbering: "1.1",
|
||||
base: base,
|
||||
base_level: base_level
|
||||
) => {
|
||||
let number = none
|
||||
if not numbering == none {
|
||||
locate(loc => {
|
||||
thmcounters.update(thmpair => {
|
||||
let counters = thmpair.at("counters")
|
||||
// Manually update heading counter
|
||||
counters.at("heading") = counter(heading).at(loc)
|
||||
if not identifier in counters.keys() {
|
||||
counters.insert(identifier, (0, ))
|
||||
}
|
||||
|
||||
let tc = counters.at(identifier)
|
||||
if base != none {
|
||||
let bc = counters.at(base)
|
||||
|
||||
// Pad or chop the base count
|
||||
if base_level != none {
|
||||
if bc.len() < base_level {
|
||||
bc = bc + (0,) * (base_level - bc.len())
|
||||
} else if bc.len() > base_level{
|
||||
bc = bc.slice(0, base_level)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset counter if the base counter has updated
|
||||
if tc.slice(0, -1) == bc {
|
||||
counters.at(identifier) = (..bc, tc.last() + 1)
|
||||
} else {
|
||||
counters.at(identifier) = (..bc, 1)
|
||||
}
|
||||
} else {
|
||||
// If we have no base counter, just count one level
|
||||
counters.at(identifier) = (tc.last() + 1,)
|
||||
let latest = counters.at(identifier)
|
||||
}
|
||||
|
||||
let latest = counters.at(identifier)
|
||||
return (
|
||||
"counters": counters,
|
||||
"latest": latest
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
number = thmcounters.display(x => {
|
||||
return global_numbering(numbering, ..x.at("latest"))
|
||||
})
|
||||
}
|
||||
|
||||
fmt(name, number, body)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#let thmref(
|
||||
label,
|
||||
fmt: auto,
|
||||
makelink: true,
|
||||
..body
|
||||
) = {
|
||||
if fmt == auto {
|
||||
fmt = (nums, body) => {
|
||||
if body.pos().len() > 0 {
|
||||
body = body.pos().join(" ")
|
||||
return [#body #numbering("1.1", ..nums)]
|
||||
}
|
||||
return numbering("1.1", ..nums)
|
||||
}
|
||||
}
|
||||
|
||||
locate(loc => {
|
||||
let elements = query(label, loc)
|
||||
let locationreps = elements.map(x => repr(x.location().position())).join(", ")
|
||||
assert(elements.len() > 0, message: "label <" + str(label) + "> does not exist in the document: referenced at " + repr(loc.position()))
|
||||
assert(elements.len() == 1, message: "label <" + str(label) + "> occurs multiple times in the document: found at " + locationreps)
|
||||
let target = elements.first().location()
|
||||
let number = thmcounters.at(target).at("latest")
|
||||
if makelink {
|
||||
return link(target, fmt(number, body))
|
||||
}
|
||||
return fmt(number, body)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#let thmbox(
|
||||
identifier,
|
||||
head,
|
||||
fill: none,
|
||||
stroke: none,
|
||||
inset: 1.2em,
|
||||
radius: 0.3em,
|
||||
breakable: false,
|
||||
padding: (top: 0.5em, bottom: 0.5em),
|
||||
namefmt: x => [(#x)],
|
||||
titlefmt: strong,
|
||||
bodyfmt: x => x,
|
||||
separator: [#h(0.1em):#h(0.2em)],
|
||||
base: "heading",
|
||||
base_level: none,
|
||||
) = {
|
||||
let boxfmt(name, number, body) = {
|
||||
if not name == none {
|
||||
name = [ #namefmt(name)]
|
||||
} else {
|
||||
name = []
|
||||
}
|
||||
let title = head
|
||||
if not number == none {
|
||||
title += " " + number
|
||||
}
|
||||
title = titlefmt(title)
|
||||
body = bodyfmt(body)
|
||||
pad(
|
||||
..padding,
|
||||
block(
|
||||
fill: fill,
|
||||
stroke: stroke,
|
||||
inset: inset,
|
||||
width: 100%,
|
||||
radius: radius,
|
||||
breakable: breakable,
|
||||
[#title#name#separator#body]
|
||||
)
|
||||
)
|
||||
}
|
||||
return thmenv(identifier, base, base_level, boxfmt)
|
||||
}
|
||||
|
||||
|
||||
#let thmplain = thmbox.with(
|
||||
padding: (top: 0em, bottom: 0em),
|
||||
breakable: true,
|
||||
inset: (top: 0em, left: 1.2em, right: 1.2em),
|
||||
namefmt: name => emph([(#name)]),
|
||||
titlefmt: emph,
|
||||
)
|
||||
|
||||
|
104
src/event_handler.rs
Normal file
104
src/event_handler.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
|
||||
use regex::Regex;
|
||||
use serenity::builder::{CreateAttachment, CreateMessage};
|
||||
use serenity::model::prelude::Message;
|
||||
use serenity::{
|
||||
async_trait,
|
||||
prelude::{Context, EventHandler},
|
||||
};
|
||||
|
||||
pub struct Handler;
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn message(&self, context: Context, message: Message) {
|
||||
let regex =
|
||||
Regex::new("(^```typst\n(?P<code>(\n|.)+)\n```)|(?P<formula>^\\$(.)+\\$)").unwrap();
|
||||
|
||||
let capture = regex.captures(&message.content);
|
||||
|
||||
if let Some(captures) = capture {
|
||||
let code = if let Some(code) = captures.name("code") {
|
||||
"#set text(font: \"Noto Serif CJK JP\")\n".to_string() + code.as_str()
|
||||
} else {
|
||||
"#set page(fill: rgb(\"#313338\"))\n#set text(fill: rgb(\"#ffffff\"))\n#set text(font: \"Noto Serif CJK JP\")\n".to_string() + captures.name("formula").unwrap().as_str()
|
||||
};
|
||||
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let mut file = File::create(format!("tmp/{uuid}.typ")).unwrap();
|
||||
file.write_all(code.as_bytes()).unwrap();
|
||||
|
||||
let output = Command::new("typst")
|
||||
.args([
|
||||
"compile",
|
||||
"--ppi",
|
||||
"288",
|
||||
"-f",
|
||||
"png",
|
||||
&format!("tmp/{uuid}.typ"),
|
||||
&format!("tmp/{uuid}.png"),
|
||||
])
|
||||
.output();
|
||||
|
||||
if let Ok(output) = output {
|
||||
if File::open(&format!("./tmp/{uuid}.png")).is_err() {
|
||||
message
|
||||
.reply(
|
||||
&context.http,
|
||||
format!(
|
||||
"Error\n```\n{}\n```",
|
||||
String::from_utf8(output.stderr).unwrap()
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
return;
|
||||
}
|
||||
let output = Command::new("convert")
|
||||
.args([
|
||||
"convert",
|
||||
&format!("tmp/{uuid}.png"),
|
||||
"-trim",
|
||||
"+repage",
|
||||
"-bordercolor",
|
||||
"#313338",
|
||||
"-border",
|
||||
"10x10",
|
||||
&format!("tmp/{uuid}-output.png"),
|
||||
])
|
||||
.output();
|
||||
if let Ok(_) = output {
|
||||
message
|
||||
.channel_id
|
||||
.send_files(
|
||||
&context.http,
|
||||
CreateAttachment::path(format!("./tmp/{uuid}-output.png")).await,
|
||||
CreateMessage::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
} else {
|
||||
message
|
||||
.reply(&context.http, format!("Error: `{:?}`", output.err()))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
} else {
|
||||
message
|
||||
.reply(&context.http, format!("Error: `{:?}`", output.err()))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* typst compile -f png test.typ test.png
|
||||
* convert test.png -trim +repage -bordercolor White -border 10x10 output.png
|
||||
* */
|
45
src/main.rs
Normal file
45
src/main.rs
Normal file
@ -0,0 +1,45 @@
|
||||
mod event_handler;
|
||||
|
||||
use std::env;
|
||||
|
||||
use event_handler::Handler;
|
||||
use serenity::{framework::StandardFramework, http::Http, prelude::GatewayIntents, Client};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), ()> {
|
||||
env::set_var("RUST_LOG", "info");
|
||||
env_logger::init();
|
||||
|
||||
let token = "";
|
||||
|
||||
let http = Http::new(token);
|
||||
|
||||
let bot_id = match http.get_current_user().await {
|
||||
Ok(bot_id) => bot_id.id,
|
||||
Err(why) => panic!("Could not access the bot id: {why:?}"),
|
||||
};
|
||||
|
||||
let framework = StandardFramework::new();
|
||||
framework.configure(|c| {
|
||||
c.with_whitespace(true)
|
||||
.on_mention(Some(bot_id))
|
||||
.prefix("t.")
|
||||
});
|
||||
|
||||
let mut client = Client::builder(
|
||||
&token,
|
||||
GatewayIntents::MESSAGE_CONTENT
|
||||
| GatewayIntents::GUILD_MESSAGES
|
||||
| GatewayIntents::DIRECT_MESSAGES,
|
||||
)
|
||||
.framework(framework)
|
||||
.event_handler(Handler)
|
||||
.await
|
||||
.expect("Err creating client");
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {why:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user