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