first commit

This commit is contained in:
mii
2023-09-27 14:41:21 +00:00
commit 0ee223de9b
5 changed files with 2346 additions and 0 deletions

2004
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

33
Cargo.toml Normal file
View 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
View 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
View 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
View 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(())
}