Sync codebase from two different sources #102
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -100,6 +100,30 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bb8"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b10cf871f3ff2ce56432fddc2615ac7acc3aa22ca321f8fea800846fbb32f188"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"futures-util",
|
||||||
|
"parking_lot",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bb8-postgres"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56ac82c42eb30889b5c4ee4763a24b8c566518171ebea648cd7e3bc532c60680"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bb8",
|
||||||
|
"tokio",
|
||||||
|
"tokio-postgres",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@ -188,9 +212,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
|
checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -836,9 +860,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.6"
|
version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
|
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@ -1106,9 +1130,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.2"
|
version = "0.36.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
|
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -1569,11 +1593,13 @@ dependencies = [
|
|||||||
name = "rustbot"
|
name = "rustbot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bb8",
|
||||||
|
"bb8-postgres",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"poise",
|
"poise",
|
||||||
|
"regex",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"tempfile",
|
|
||||||
"tokenservice-client",
|
"tokenservice-client",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
@ -1659,9 +1685,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.7.0"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
@ -1768,9 +1794,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.204"
|
version = "1.0.205"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -1786,9 +1812,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.204"
|
version = "1.0.205"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2144,6 +2170,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
11
Cargo.toml
11
Cargo.toml
@ -4,16 +4,21 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bb8 = "0.8.5"
|
||||||
|
bb8-postgres = "0.8.1"
|
||||||
cargo_toml = "0.20.4"
|
cargo_toml = "0.20.4"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
sysinfo = "0.31.0"
|
regex = "1.10.6"
|
||||||
tempfile = "3.10.1"
|
sysinfo = "0.31.2"
|
||||||
tokenservice-client = { version = "0.3.3", registry = "gitea" }
|
tokenservice-client = { version = "0.3.3", registry = "gitea" }
|
||||||
tokio = { version = "1.39.2", features = [ "macros", "signal", "rt-multi-thread" ] }
|
tokio = { version = "1.39.2", features = ["macros", "signal", "rt-multi-thread"] }
|
||||||
tokio-postgres = "0.7.11"
|
tokio-postgres = "0.7.11"
|
||||||
uptime_lib = "0.3.1"
|
uptime_lib = "0.3.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
production = []
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "rustbot"
|
name = "rustbot"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
19
Dockerfile
19
Dockerfile
@ -1,23 +1,24 @@
|
|||||||
FROM rust:1.80-alpine3.19@sha256:b3ac1f65cf33390407c9b90558eb41e7a8311c47d836fca5800960f1aa2d11d5 AS chef
|
FROM rust:1.80-alpine3.20 AS chef
|
||||||
ENV RUSTFLAGS -C target-feature=-crt-static
|
ENV RUSTFLAGS="-C target-feature=-crt-static"
|
||||||
|
ARG GIT_HASH
|
||||||
|
ENV GIT_COMMIT_HASH=${GIT_HASH}
|
||||||
RUN apk add --no-cache openssl-dev musl-dev
|
RUN apk add --no-cache openssl-dev musl-dev
|
||||||
RUN cargo install cargo-chef
|
RUN cargo install cargo-chef
|
||||||
WORKDIR /usr/src/rustbot
|
WORKDIR /builder
|
||||||
|
|
||||||
FROM chef AS planner
|
FROM chef AS planner
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN mkdir -p .cargo && \
|
|
||||||
printf '[registries.gitea]\nindex = "sparse+https://git.toast-server.net/api/packages/toast/cargo/"\ntoken = "Bearer %s"\n' "$CARGO_TOKEN" >> .cargo/config.toml
|
|
||||||
RUN cargo chef prepare
|
RUN cargo chef prepare
|
||||||
|
|
||||||
FROM chef AS builder
|
FROM chef AS builder
|
||||||
COPY --from=planner /usr/src/rustbot/recipe.json recipe.json
|
COPY --from=planner /builder/recipe.json recipe.json
|
||||||
RUN cargo chef cook --release
|
RUN cargo chef cook --release
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN cargo build -r
|
RUN cargo build --offline -rF production
|
||||||
|
|
||||||
FROM alpine:3.20@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5
|
FROM alpine:3.20
|
||||||
|
LABEL org.opencontainers.image.source="https://git.toast-server.net/toast/Rustbot"
|
||||||
RUN apk add --no-cache libgcc
|
RUN apk add --no-cache libgcc
|
||||||
WORKDIR /rustbot
|
WORKDIR /rustbot
|
||||||
COPY --from=builder /usr/src/rustbot/target/release/rustbot .
|
COPY --from=builder /builder/target/release/rustbot .
|
||||||
CMD ./rustbot
|
CMD ./rustbot
|
||||||
|
2
run.sh
2
run.sh
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
export $(grep -v '^#' .env | xargs) && cargo run
|
clear && cargo run
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
|
pub mod midi;
|
||||||
pub mod ping;
|
pub mod ping;
|
||||||
pub mod eval;
|
|
||||||
pub mod uptime;
|
|
||||||
pub mod sample;
|
pub mod sample;
|
||||||
|
pub mod uptime;
|
@ -1,59 +0,0 @@
|
|||||||
use crate::Error;
|
|
||||||
|
|
||||||
use poise::serenity_prelude::UserId;
|
|
||||||
use std::{
|
|
||||||
io::Write,
|
|
||||||
process::Command,
|
|
||||||
os::unix::fs::PermissionsExt
|
|
||||||
};
|
|
||||||
|
|
||||||
const WHITELISTED_USERS: &[UserId] = &[
|
|
||||||
UserId::new(190407856527376384)
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Evaluate a piece of code
|
|
||||||
#[poise::command(slash_command)]
|
|
||||||
pub async fn eval(
|
|
||||||
ctx: poise::Context<'_, (), Error>,
|
|
||||||
#[description = "The Rust code to evaluate"] code: String
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if !WHITELISTED_USERS.contains(&ctx.author().id) {
|
|
||||||
ctx.reply("Whitelisted users can only use this command!").await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temp directory
|
|
||||||
let dir = tempfile::tempdir()?;
|
|
||||||
let file_path = dir.path().join("temp.rs");
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut file = std::fs::File::create(&file_path)?;
|
|
||||||
writeln!(file, "fn main() {{ {} }}", code)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile
|
|
||||||
let compiled_path = dir.path().join("temp");
|
|
||||||
println!("Compiling {} -> {}", file_path.display(), compiled_path.display());
|
|
||||||
let output = Command::new("rustc").arg(&file_path).arg("-o").arg(&compiled_path).output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
ctx.reply(format!("Compilation failed:\n```{}```", String::from_utf8_lossy(&output.stderr))).await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update binary's permissions before execution stage
|
|
||||||
let permissions = std::fs::Permissions::from_mode(0o755);
|
|
||||||
let compiled_path = dir.path().join("temp");
|
|
||||||
std::fs::set_permissions(&compiled_path, permissions)?;
|
|
||||||
|
|
||||||
// If success, run it.
|
|
||||||
let output = Command::new(compiled_path).output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
ctx.reply(format!("Execution failed:\n```{}```", String::from_utf8_lossy(&output.stderr))).await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.reply(format!("Output:\n```{}```", String::from_utf8_lossy(&output.stdout))).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
101
src/commands/midi.rs
Normal file
101
src/commands/midi.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use crate::{
|
||||||
|
Error,
|
||||||
|
internals::utils::{
|
||||||
|
mention_dev,
|
||||||
|
format_bytes
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use std::{
|
||||||
|
os::unix::fs::MetadataExt,
|
||||||
|
fs::{
|
||||||
|
write,
|
||||||
|
remove_file,
|
||||||
|
metadata
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use poise::{
|
||||||
|
CreateReply,
|
||||||
|
serenity_prelude::CreateAttachment
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Convert MIDI file to WAV
|
||||||
|
#[poise::command(context_menu_command = "MIDI -> WAV")]
|
||||||
|
pub async fn midi_to_wav(
|
||||||
|
ctx: poise::Context<'_, (), Error>,
|
||||||
|
#[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let re = Regex::new(r"(?i)\.mid$").unwrap();
|
||||||
|
|
||||||
|
if !message.embeds.is_empty() || message.attachments.is_empty() || !re.is_match(&message.attachments[0].filename) {
|
||||||
|
ctx.reply("That ain't a MIDI file! What are you even doing??").await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.defer().await?;
|
||||||
|
|
||||||
|
let bytes = match message.attachments[0].download().await {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(y) => {
|
||||||
|
ctx.send(CreateReply::default()
|
||||||
|
.content(format!(
|
||||||
|
"Download failed, ask {} to check console for more information!",
|
||||||
|
mention_dev(ctx).unwrap_or_default()
|
||||||
|
))
|
||||||
|
)
|
||||||
|
.await.unwrap();
|
||||||
|
|
||||||
|
return Err(Error::from(format!("Failed to download the file: {}", y)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let midi_path = &message.attachments[0].filename;
|
||||||
|
write(midi_path, bytes)?;
|
||||||
|
|
||||||
|
let wav_path = re.replace(&midi_path, ".wav");
|
||||||
|
|
||||||
|
let sf2_path = "/tmp/FluidR3_GM.sf2";
|
||||||
|
write(sf2_path, include_bytes!("../internals/assets/FluidR3_GM.sf2"))?;
|
||||||
|
|
||||||
|
let output = std::process::Command::new("fluidsynth")
|
||||||
|
.args(&["-ni", sf2_path, midi_path, "-F", &wav_path])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
// Just to add an info to console to tell what the bot is doing when MIDI file is downloaded.
|
||||||
|
println!("Discord[{}:{}]: Processing MIDI file: \"{}\"", ctx.guild().unwrap().name, ctx.command().qualified_name, midi_path);
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(_) => {
|
||||||
|
let reply = ctx.send(CreateReply::default()
|
||||||
|
.attachment(CreateAttachment::path(&*wav_path).await.unwrap())
|
||||||
|
).await;
|
||||||
|
|
||||||
|
if reply.is_err() {
|
||||||
|
println!(
|
||||||
|
"Discord[{}:{}]: Processed file couldn't be uploaded back to Discord channel due to upload limit",
|
||||||
|
ctx.guild().unwrap().name, ctx.command().qualified_name
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.send(CreateReply::default()
|
||||||
|
.content(format!(
|
||||||
|
"Couldn't upload the processed file (`{}`, `{}`) due to upload limit",
|
||||||
|
&*wav_path, format_bytes(metadata(&*wav_path).unwrap().size())
|
||||||
|
))
|
||||||
|
).await.unwrap();
|
||||||
|
} else if reply.is_ok() {
|
||||||
|
remove_file(midi_path)?;
|
||||||
|
remove_file(&*wav_path)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(y) => {
|
||||||
|
ctx.send(CreateReply::default()
|
||||||
|
.content("Command didn't execute successfully, check console for more information!")
|
||||||
|
).await.unwrap();
|
||||||
|
|
||||||
|
return Err(Error::from(format!("Midi conversion failed: {}", y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,20 +1,52 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
|
GIT_COMMIT_HASH,
|
||||||
internals::utils::{
|
internals::utils::{
|
||||||
|
BOT_VERSION,
|
||||||
format_duration,
|
format_duration,
|
||||||
concat_message,
|
concat_message
|
||||||
BOT_VERSION
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
use uptime_lib::get;
|
use uptime_lib::get;
|
||||||
use std::time::{
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
path::Path,
|
||||||
|
time::{
|
||||||
Duration,
|
Duration,
|
||||||
SystemTime,
|
SystemTime,
|
||||||
UNIX_EPOCH
|
UNIX_EPOCH
|
||||||
|
},
|
||||||
|
io::{
|
||||||
|
BufRead,
|
||||||
|
BufReader
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn get_os_info() -> String {
|
||||||
|
let path = Path::new("/etc/os-release");
|
||||||
|
let mut name = "BoringOS".to_string();
|
||||||
|
let mut version = "v0.0".to_string();
|
||||||
|
|
||||||
|
if let Ok(file) = File::open(&path) {
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
for line in reader.lines() {
|
||||||
|
if let Ok(line) = line {
|
||||||
|
if line.starts_with("NAME=") {
|
||||||
|
name = line.split('=').nth(1).unwrap_or_default().trim_matches('"').to_string();
|
||||||
|
} else if line.starts_with("VERSION=") {
|
||||||
|
version = line.split('=').nth(1).unwrap_or_default().trim_matches('"').to_string();
|
||||||
|
} else if line.starts_with("VERSION_ID=") {
|
||||||
|
version = line.split('=').nth(1).unwrap_or_default().trim_matches('"').to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format!("{} {}", name, version)
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve host and bot uptimes
|
/// Retrieve host and bot uptimes
|
||||||
#[poise::command(slash_command)]
|
#[poise::command(slash_command)]
|
||||||
pub async fn uptime(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
pub async fn uptime(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||||
@ -25,6 +57,9 @@ pub async fn uptime(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
|||||||
// Fetch system's uptime
|
// Fetch system's uptime
|
||||||
let sys_uptime = get().unwrap().as_secs();
|
let sys_uptime = get().unwrap().as_secs();
|
||||||
|
|
||||||
|
// Fetch system's processor
|
||||||
|
let cpu = sys.cpus();
|
||||||
|
|
||||||
// Fetch bot's process uptime
|
// Fetch bot's process uptime
|
||||||
let curr_pid = sysinfo::get_current_pid().unwrap();
|
let curr_pid = sysinfo::get_current_pid().unwrap();
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
@ -35,9 +70,11 @@ pub async fn uptime(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let stat_msg = vec![
|
let stat_msg = vec![
|
||||||
format!("**{} {}**", _bot.name, BOT_VERSION.as_str()),
|
format!("**{} {}** `{}`", _bot.name, BOT_VERSION.as_str(), GIT_COMMIT_HASH),
|
||||||
format!(">>> System: `{}`", format_duration(sys_uptime)),
|
format!(">>> System: `{}`", format_duration(sys_uptime)),
|
||||||
format!("Process: `{}`", format_duration(proc_uptime))
|
format!("Process: `{}`", format_duration(proc_uptime)),
|
||||||
|
format!("CPU: `{}`", format!("{}", cpu[0].brand())),
|
||||||
|
format!("OS: `{}`", get_os_info())
|
||||||
];
|
];
|
||||||
ctx.reply(concat_message(stat_msg)).await?;
|
ctx.reply(concat_message(stat_msg)).await?;
|
||||||
|
|
||||||
|
@ -1,25 +1,45 @@
|
|||||||
use crate::internals;
|
use crate::internals::utils::token_path;
|
||||||
|
|
||||||
use poise::serenity_prelude::prelude::TypeMapKey;
|
use bb8::{Pool, PooledConnection};
|
||||||
use tokio_postgres::{Client, NoTls, Error};
|
use bb8_postgres::PostgresConnectionManager;
|
||||||
|
use tokio::time::{
|
||||||
|
Duration,
|
||||||
|
sleep
|
||||||
|
};
|
||||||
|
use tokio_postgres::{
|
||||||
|
Client,
|
||||||
|
NoTls,
|
||||||
|
Error,
|
||||||
|
config::Config
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
ops::Deref,
|
||||||
|
str::FromStr,
|
||||||
|
sync::{
|
||||||
|
Mutex,
|
||||||
|
LazyLock
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static DATABASE: LazyLock<Mutex<Option<DatabaseController>>> = LazyLock::new(|| Mutex::new(None));
|
||||||
|
|
||||||
pub struct DatabaseController {
|
pub struct DatabaseController {
|
||||||
pub client: Client
|
pub pool: Pool<PostgresConnectionManager<NoTls>>
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeMapKey for DatabaseController {
|
|
||||||
type Value = DatabaseController;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseController {
|
impl DatabaseController {
|
||||||
pub async fn new() -> Result<DatabaseController, Error> {
|
pub async fn new() -> Result<(), Error> {
|
||||||
let (client, connection) = tokio_postgres::connect(&internals::utils::token_path().await.postgres_uri, NoTls).await?;
|
let manager = PostgresConnectionManager::new(Config::from_str(token_path().await.postgres_uri.as_str())?, NoTls);
|
||||||
|
let pool = bb8::Pool::builder().build(manager).await?;
|
||||||
|
let err_name = "Postgres[Error]";
|
||||||
|
|
||||||
|
let pool_clone = pool.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = connection.await {
|
loop {
|
||||||
eprintln!("Connection error: {}", e);
|
match Self::attempt_connect(&pool_clone).await {
|
||||||
}
|
Ok(conn) => {
|
||||||
});
|
println!("Postgres[Info]: Successfully connected");
|
||||||
|
let client: &Client = conn.deref();
|
||||||
|
|
||||||
// Sample model
|
// Sample model
|
||||||
client.batch_execute("
|
client.batch_execute("
|
||||||
@ -29,8 +49,36 @@ impl DatabaseController {
|
|||||||
int_val BIGINT NOT NULL,
|
int_val BIGINT NOT NULL,
|
||||||
boolean_val BOOLEAN NOT NULL
|
boolean_val BOOLEAN NOT NULL
|
||||||
);
|
);
|
||||||
").await?;
|
").await.unwrap();
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}: {}", err_name, e);
|
||||||
|
sleep(Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(DatabaseController { client })
|
let controller = Self { pool };
|
||||||
|
*DATABASE.lock().unwrap() = Some(controller);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn attempt_connect<'a>(pool: &'a bb8::Pool<PostgresConnectionManager<NoTls>>) -> Result<PooledConnection<'a, PostgresConnectionManager<NoTls>>, bb8::RunError<Error>> {
|
||||||
|
let mut backoff = 1;
|
||||||
|
loop {
|
||||||
|
match pool.get().await {
|
||||||
|
Ok(conn) => return Ok(conn),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Postgres[ConnError]: {}, retrying in {} seconds", e, backoff);
|
||||||
|
sleep(Duration::from_secs(backoff)).await;
|
||||||
|
if backoff < 64 {
|
||||||
|
backoff *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
pub mod utils;
|
pub mod config;
|
||||||
|
pub mod tasks;
|
||||||
pub mod tsclient;
|
pub mod tsclient;
|
||||||
|
pub mod utils;
|
BIN
src/internals/assets/FluidR3_GM.sf2
Executable file
BIN
src/internals/assets/FluidR3_GM.sf2
Executable file
Binary file not shown.
62
src/internals/config.rs
Normal file
62
src/internals/config.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
pub struct ConfigMeta {
|
||||||
|
pub embed_color: i32,
|
||||||
|
pub ready_notify: u64,
|
||||||
|
pub rss_channel: u64,
|
||||||
|
pub rustbot_logs: u64,
|
||||||
|
pub deploy_commands: bool,
|
||||||
|
pub developers: Vec<u64>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "production")]
|
||||||
|
pub static BINARY_PROPERTIES: LazyLock<ConfigMeta> = LazyLock::new(|| ConfigMeta::new());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "production"))]
|
||||||
|
pub static BINARY_PROPERTIES: LazyLock<ConfigMeta> = LazyLock::new(||
|
||||||
|
ConfigMeta::new()
|
||||||
|
.embed_color(0xf1d63c)
|
||||||
|
.ready_notify(865673694184996888)
|
||||||
|
.rss_channel(865673694184996888)
|
||||||
|
.deploy_commands(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
impl ConfigMeta {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
embed_color: 0x5a99c7,
|
||||||
|
ready_notify: 865673694184996888,
|
||||||
|
rss_channel: 865673694184996888,
|
||||||
|
rustbot_logs: 1268493237912604672,
|
||||||
|
deploy_commands: false,
|
||||||
|
developers: vec![
|
||||||
|
190407856527376384 // toast.ts
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalable functions below;
|
||||||
|
#[cfg(not(feature = "production"))]
|
||||||
|
fn embed_color(mut self, color: i32) -> Self {
|
||||||
|
self.embed_color = color;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "production"))]
|
||||||
|
fn ready_notify(mut self, channel_id: u64) -> Self {
|
||||||
|
self.ready_notify = channel_id;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "production"))]
|
||||||
|
fn rss_channel(mut self, channel_id: u64) -> Self {
|
||||||
|
self.rss_channel = channel_id;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "production"))]
|
||||||
|
fn deploy_commands(mut self, deploy: bool) -> Self {
|
||||||
|
self.deploy_commands = deploy;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
9
src/internals/tasks.rs
Normal file
9
src/internals/tasks.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
pub mod sample;
|
||||||
|
|
||||||
|
fn task_info(name: &str, message: &str) {
|
||||||
|
println!("{}", format!("TaskScheduler[{}]: {}", name, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task_err(name: &str, message: &str) {
|
||||||
|
eprintln!("{}", format!("TaskScheduler[{}:Error]: {}", name, message));
|
||||||
|
}
|
43
src/internals/tasks/sample.rs
Normal file
43
src/internals/tasks/sample.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use crate::Error;
|
||||||
|
use super::{
|
||||||
|
super::config::BINARY_PROPERTIES,
|
||||||
|
task_info,
|
||||||
|
task_err
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use poise::serenity_prelude::{
|
||||||
|
Context,
|
||||||
|
ChannelId,
|
||||||
|
CreateMessage
|
||||||
|
};
|
||||||
|
use tokio::time::{
|
||||||
|
Duration,
|
||||||
|
interval
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn sample(ctx: Arc<Context>) -> Result<(), Error> {
|
||||||
|
let task_name = "SampleTask";
|
||||||
|
let mut interval = interval(Duration::from_secs(10));
|
||||||
|
task_info(&task_name, "Task loaded!");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
task_info(&task_name, "Task running!");
|
||||||
|
|
||||||
|
if BINARY_PROPERTIES.rss_channel == 0 {
|
||||||
|
task_err(&task_name, "RSS channel ID is not set!");
|
||||||
|
ChannelId::new(BINARY_PROPERTIES.rustbot_logs).send_message(
|
||||||
|
&ctx.http,
|
||||||
|
CreateMessage::new().content("RSS channel ID is not set!")
|
||||||
|
).await.unwrap();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelId::new(BINARY_PROPERTIES.rss_channel).send_message(
|
||||||
|
&ctx.http,
|
||||||
|
CreateMessage::new().content("This is a sample message executed by a task!")
|
||||||
|
).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,36 @@
|
|||||||
use tokenservice_client::{TokenService, TokenServiceApi};
|
use std::sync::LazyLock;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use tokenservice_client::{
|
||||||
|
TokenService,
|
||||||
|
TokenServiceApi
|
||||||
|
};
|
||||||
|
|
||||||
pub struct TSClient {
|
static TS_GLOBAL_CACHE: LazyLock<RwLock<Option<TokenServiceApi>>> = LazyLock::new(|| RwLock::new(None));
|
||||||
client: TokenService
|
|
||||||
}
|
pub struct TSClient(TokenService);
|
||||||
|
|
||||||
impl TSClient {
|
impl TSClient {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
let service = if args.len() > 1 { args[1].as_str() } else { "pgbot" };
|
let service = if args.len() > 1 { &args[1] } else { "pgbot" };
|
||||||
TSClient {
|
Self(TokenService::new(service))
|
||||||
client: TokenService::new(service)
|
}
|
||||||
|
|
||||||
|
pub async fn get(&self) -> Result<TokenServiceApi, crate::Error> {
|
||||||
|
{
|
||||||
|
let cache = TS_GLOBAL_CACHE.read().await;
|
||||||
|
if let Some(ref api) = *cache {
|
||||||
|
return Ok(api.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get(&self) -> Result<TokenServiceApi, Box<dyn std::error::Error>> {
|
|
||||||
let api = self.client.connect().await.unwrap();
|
match self.0.connect().await {
|
||||||
|
Ok(api) => {
|
||||||
|
let mut cache = TS_GLOBAL_CACHE.write().await;
|
||||||
|
*cache = Some(api.clone());
|
||||||
Ok(api)
|
Ok(api)
|
||||||
}
|
}
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use once_cell::sync::Lazy;
|
use poise::serenity_prelude::UserId;
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tokenservice_client::TokenServiceApi;
|
use tokenservice_client::TokenServiceApi;
|
||||||
|
use super::tsclient::TSClient;
|
||||||
|
|
||||||
pub static EMBED_COLOR: i32 = 0xf1d63c;
|
pub static BOT_VERSION: LazyLock<String> = LazyLock::new(|| {
|
||||||
|
let cargo_version = cargo_toml::Manifest::from_str(&include_str!("../../Cargo.toml"))
|
||||||
pub static BOT_VERSION: Lazy<String> = Lazy::new(|| {
|
|
||||||
let cargo_version = cargo_toml::Manifest::from_path("./Cargo.toml")
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.package
|
.package
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -13,15 +14,35 @@ pub static BOT_VERSION: Lazy<String> = Lazy::new(|| {
|
|||||||
format!("v{}", cargo_version)
|
format!("v{}", cargo_version)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static TSCLIENT: LazyLock<Mutex<TSClient>> = LazyLock::new(|| Mutex::new(TSClient::new()));
|
||||||
|
|
||||||
pub async fn token_path() -> TokenServiceApi {
|
pub async fn token_path() -> TokenServiceApi {
|
||||||
let client = super::tsclient::TSClient::new().get().await.unwrap();
|
TSCLIENT.lock().await.get().await.unwrap()
|
||||||
client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn concat_message(messages: Vec<String>) -> String {
|
pub fn concat_message(messages: Vec<String>) -> String {
|
||||||
messages.join("\n")
|
messages.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mention_dev(ctx: poise::Context<'_, (), crate::Error>) -> Option<String> {
|
||||||
|
let devs = super::config::BINARY_PROPERTIES.developers.clone();
|
||||||
|
let app_owners = ctx.framework().options().owners.clone();
|
||||||
|
|
||||||
|
let mut mentions = Vec::new();
|
||||||
|
|
||||||
|
for dev in devs {
|
||||||
|
if app_owners.contains(&UserId::new(dev)) {
|
||||||
|
mentions.push(format!("<@{}>", dev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mentions.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(mentions.join(", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn format_duration(secs: u64) -> String {
|
pub fn format_duration(secs: u64) -> String {
|
||||||
let days = secs / 86400;
|
let days = secs / 86400;
|
||||||
let hours = (secs % 86400) / 3600;
|
let hours = (secs % 86400) / 3600;
|
||||||
@ -43,15 +64,23 @@ pub fn format_duration(secs: u64) -> String {
|
|||||||
formatted_string
|
formatted_string
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pub fn format_memory(bytes: u64) -> String {
|
pub fn format_bytes(bytes: u64) -> String {
|
||||||
let kb = 1024;
|
let units = ["B", "KB", "MB", "GB", "TB", "PB"];
|
||||||
let mb = 1024 * 1024;
|
let mut value = bytes as f64;
|
||||||
let gb = 1024 * 1024 * 1024;
|
let mut unit = units[0];
|
||||||
|
|
||||||
match bytes {
|
for &u in &units[1..] {
|
||||||
b if b >= gb => format!("{:.0} GB", (b as f64 / (1024.0 * 1024.0 * 1024.0)).ceil()),
|
if value < 1024.0 {
|
||||||
b if b >= mb => format!("{:.0} MB", (b as f64 / (1024.0 * 1024.0)).ceil()),
|
break;
|
||||||
b if b >= kb => format!("{:.0} KB", (b as f64 / 1024.0).ceil()),
|
|
||||||
_ => format!("{:.0} B", bytes),
|
|
||||||
}
|
}
|
||||||
} */
|
|
||||||
|
value /= 1024.0;
|
||||||
|
unit = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit == "B" {
|
||||||
|
format!("{}{}", value, unit)
|
||||||
|
} else {
|
||||||
|
format!("{:.2}{}", value, unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
176
src/main.rs
176
src/main.rs
@ -2,10 +2,30 @@ mod commands;
|
|||||||
mod controllers;
|
mod controllers;
|
||||||
mod models;
|
mod models;
|
||||||
mod internals;
|
mod internals;
|
||||||
|
// https://cdn.toast-server.net/RustFSHiearchy.png
|
||||||
|
// Using the new filesystem hierarchy
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
internals::{
|
||||||
|
utils::{
|
||||||
|
BOT_VERSION,
|
||||||
|
token_path,
|
||||||
|
mention_dev
|
||||||
|
},
|
||||||
|
config::BINARY_PROPERTIES
|
||||||
|
},
|
||||||
|
controllers::database::DatabaseController
|
||||||
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env::var,
|
thread::current,
|
||||||
error
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{
|
||||||
|
AtomicBool,
|
||||||
|
Ordering
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
use poise::serenity_prelude::{
|
use poise::serenity_prelude::{
|
||||||
builder::{
|
builder::{
|
||||||
@ -13,45 +33,131 @@ use poise::serenity_prelude::{
|
|||||||
CreateEmbed,
|
CreateEmbed,
|
||||||
CreateEmbedAuthor
|
CreateEmbedAuthor
|
||||||
},
|
},
|
||||||
Context,
|
|
||||||
Ready,
|
Ready,
|
||||||
|
Context,
|
||||||
|
FullEvent,
|
||||||
ClientBuilder,
|
ClientBuilder,
|
||||||
ChannelId,
|
ChannelId,
|
||||||
Command,
|
Command,
|
||||||
GatewayIntents
|
GatewayIntents
|
||||||
};
|
};
|
||||||
|
|
||||||
type Error = Box<dyn error::Error + Send + Sync>;
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
static TASK_RUNNING: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
static BOT_READY_NOTIFY: u64 = 865673694184996888;
|
#[cfg(feature = "production")]
|
||||||
|
pub static GIT_COMMIT_HASH: &str = env!("GIT_COMMIT_HASH");
|
||||||
|
#[cfg(not(feature = "production"))]
|
||||||
|
pub static GIT_COMMIT_HASH: &str = "devel";
|
||||||
|
|
||||||
async fn on_ready(
|
async fn on_ready(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
ready: &Ready,
|
ready: &Ready,
|
||||||
framework: &poise::Framework<(), Error>
|
framework: &poise::Framework<(), Error>
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
println!("Connected to API as {}", ready.user.name);
|
#[cfg(not(feature = "production"))]
|
||||||
|
{
|
||||||
|
println!("Event[Ready][Notice]: Detected a non-production environment!");
|
||||||
|
let gateway = ctx.http.get_bot_gateway().await?;
|
||||||
|
let session = gateway.session_start_limit;
|
||||||
|
println!("Event[Ready][Notice]: Session limit: {}/{}", session.remaining, session.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Event[Ready]: Build version: {} ({})", BOT_VERSION.to_string(), GIT_COMMIT_HASH);
|
||||||
|
println!("Event[Ready]: Connected to API as {}", ready.user.name);
|
||||||
|
|
||||||
let message = CreateMessage::new();
|
let message = CreateMessage::new();
|
||||||
let ready_embed = CreateEmbed::new()
|
let ready_embed = CreateEmbed::new()
|
||||||
.color(internals::utils::EMBED_COLOR)
|
.color(BINARY_PROPERTIES.embed_color)
|
||||||
.thumbnail(ready.user.avatar_url().unwrap_or_default())
|
.thumbnail(ready.user.avatar_url().unwrap_or_default())
|
||||||
.author(CreateEmbedAuthor::new(format!("{} is ready!", ready.user.name)).clone());
|
.author(CreateEmbedAuthor::new(format!("{} is ready!", ready.user.name)));
|
||||||
|
|
||||||
ChannelId::new(BOT_READY_NOTIFY).send_message(&ctx.http, message.add_embed(ready_embed)).await?;
|
ChannelId::new(BINARY_PROPERTIES.ready_notify).send_message(&ctx.http, message.add_embed(ready_embed)).await?;
|
||||||
|
|
||||||
let register_commands = var("REGISTER_CMDS").unwrap_or_else(|_| String::from("true")).parse::<bool>().unwrap_or(true);
|
if BINARY_PROPERTIES.deploy_commands {
|
||||||
|
|
||||||
if register_commands {
|
|
||||||
let builder = poise::builtins::create_application_commands(&framework.options().commands);
|
let builder = poise::builtins::create_application_commands(&framework.options().commands);
|
||||||
let commands = Command::set_global_commands(&ctx.http, builder).await;
|
let commands = Command::set_global_commands(&ctx.http, builder).await;
|
||||||
|
let mut commands_deployed = std::collections::HashSet::new();
|
||||||
|
|
||||||
match commands {
|
match commands {
|
||||||
Ok(cmdmap) => for command in cmdmap.iter() {
|
Ok(cmdmap) => for command in cmdmap.iter() {
|
||||||
println!("Registered command globally: {}", command.name);
|
commands_deployed.insert(command.name.clone());
|
||||||
},
|
},
|
||||||
Err(why) => println!("Error registering commands: {:?}", why)
|
Err(y) => eprintln!("Error registering commands: {:?}", y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if commands_deployed.len() > 0 {
|
||||||
|
println!("Event[Ready]: Deployed the commands globally:\n- {}", commands_deployed.into_iter().collect::<Vec<_>>().join("\n- "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn event_processor(
|
||||||
|
ctx: &Context,
|
||||||
|
event: &FullEvent,
|
||||||
|
framework: poise::FrameworkContext<'_, (), Error>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match event {
|
||||||
|
FullEvent::Ratelimit { data } => {
|
||||||
|
println!("Event[Ratelimit]: {:#?}", data);
|
||||||
|
}
|
||||||
|
FullEvent::Message { new_message } => {
|
||||||
|
if new_message.author.bot || !new_message.guild_id.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_message.content.to_lowercase().starts_with("deploy") && new_message.author.id == BINARY_PROPERTIES.developers[0] {
|
||||||
|
let builder = poise::builtins::create_application_commands(&framework.options().commands);
|
||||||
|
let commands = Command::set_global_commands(&ctx.http, builder).await;
|
||||||
|
let mut commands_deployed = std::collections::HashSet::new();
|
||||||
|
|
||||||
|
match commands {
|
||||||
|
Ok(cmdmap) => for command in cmdmap.iter() {
|
||||||
|
commands_deployed.insert(command.name.clone());
|
||||||
|
},
|
||||||
|
Err(y) => {
|
||||||
|
eprintln!("Error registering commands: {:?}", y);
|
||||||
|
new_message.reply(&ctx.http, "Deployment failed, check console for more details!").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if commands_deployed.len() > 0 {
|
||||||
|
new_message.reply(&ctx.http, format!(
|
||||||
|
"Deployed the commands globally:\n- {}",
|
||||||
|
commands_deployed.into_iter().collect::<Vec<_>>().join("\n- ")
|
||||||
|
)).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FullEvent::Ready { .. } => {
|
||||||
|
let thread_id = format!("{:?}", current().id());
|
||||||
|
let thread_num: String = thread_id.chars().filter(|c| c.is_digit(10)).collect();
|
||||||
|
println!("Event[Ready]: Task Scheduler operating on thread {}", thread_num);
|
||||||
|
|
||||||
|
let ctx = Arc::new(ctx.clone());
|
||||||
|
|
||||||
|
if !TASK_RUNNING.load(Ordering::SeqCst) {
|
||||||
|
TASK_RUNNING.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
match internals::tasks::sample::sample(ctx).await {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(y) => {
|
||||||
|
eprintln!("TaskScheduler[Main:Sample:Error]: Task execution failed: {}", y);
|
||||||
|
if let Some(source) = y.source() {
|
||||||
|
eprintln!("TaskScheduler[Main:Sample:Error]: Task execution failed caused by: {:#?}", source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TASK_RUNNING.store(false, Ordering::SeqCst);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("TaskScheduler[Main:Notice]: Another thread is already running, ignoring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -59,47 +165,59 @@ async fn on_ready(
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let db = controllers::database::DatabaseController::new().await.expect("Failed to connect to database");
|
DatabaseController::new().await.expect("Error initializing database");
|
||||||
|
|
||||||
let framework = poise::Framework::builder()
|
let framework = poise::Framework::builder()
|
||||||
.options(poise::FrameworkOptions {
|
.options(poise::FrameworkOptions {
|
||||||
commands: vec![
|
commands: vec![
|
||||||
commands::ping::ping(),
|
commands::ping::ping(),
|
||||||
commands::eval::eval(),
|
commands::sample::sample(),
|
||||||
commands::uptime::uptime(),
|
commands::midi::midi_to_wav(),
|
||||||
commands::sample::sample()
|
commands::uptime::uptime()
|
||||||
],
|
],
|
||||||
pre_command: |ctx| Box::pin(async move {
|
pre_command: |ctx| Box::pin(async move {
|
||||||
let get_guild_name = match ctx.guild() {
|
let get_guild_name = match ctx.guild() {
|
||||||
Some(guild) => guild.name.clone(),
|
Some(guild) => guild.name.clone(),
|
||||||
None => String::from("DM")
|
None => String::from("Direct Message")
|
||||||
};
|
};
|
||||||
println!("[{}] {} ran /{}", get_guild_name, ctx.author().name, ctx.command().qualified_name)
|
println!("Discord[{}]: {} ran /{}", get_guild_name, ctx.author().name, ctx.command().qualified_name);
|
||||||
}),
|
}),
|
||||||
on_error: |error| Box::pin(async move {
|
on_error: |error| Box::pin(async move {
|
||||||
match error {
|
match error {
|
||||||
poise::FrameworkError::Command { error, ctx, .. } => {
|
poise::FrameworkError::Command { error, ctx, .. } => {
|
||||||
println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error);
|
println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error);
|
||||||
}
|
ctx.reply(format!(
|
||||||
other => println!("PoiseOtherError: {:?}", other)
|
"Encountered an error during command execution, ask {} to check console for more details!",
|
||||||
|
mention_dev(ctx).unwrap_or_default()
|
||||||
|
)).await.expect("Error sending message");
|
||||||
|
},
|
||||||
|
poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error),
|
||||||
|
poise::FrameworkError::Setup { error, .. } => println!("PoiseSetupError: {}", error),
|
||||||
|
poise::FrameworkError::UnknownInteraction { interaction, .. } => println!(
|
||||||
|
"PoiseUnknownInteractionError: {} tried to execute an unknown interaction ({})",
|
||||||
|
interaction.user.name,
|
||||||
|
interaction.data.name
|
||||||
|
),
|
||||||
|
other => println!("PoiseOtherError: {}", other)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
initialize_owners: true,
|
initialize_owners: true,
|
||||||
|
event_handler: |ctx, event, framework, _| Box::pin(event_processor(ctx, event, framework)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework)))
|
.setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let mut client = ClientBuilder::new(internals::utils::token_path().await.main, GatewayIntents::GUILDS)
|
let mut client = ClientBuilder::new(
|
||||||
|
token_path().await.main,
|
||||||
|
GatewayIntents::GUILDS
|
||||||
|
| GatewayIntents::MESSAGE_CONTENT
|
||||||
|
| GatewayIntents::DIRECT_MESSAGES
|
||||||
|
)
|
||||||
.framework(framework)
|
.framework(framework)
|
||||||
.await.expect("Error creating client");
|
.await.expect("Error creating client");
|
||||||
|
|
||||||
{
|
|
||||||
let mut data = client.data.write().await;
|
|
||||||
data.insert::<controllers::database::DatabaseController>(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(why) = client.start().await {
|
if let Err(why) = client.start().await {
|
||||||
println!("Client error: {:?}", why);
|
println!("Error starting client: {:#?}", why);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::controllers::database::DatabaseController;
|
use crate::controllers::database::DATABASE;
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub struct SampleData {
|
pub struct SampleData {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
@ -9,7 +11,13 @@ pub struct SampleData {
|
|||||||
|
|
||||||
impl SampleData {
|
impl SampleData {
|
||||||
pub async fn list_data(id: u64) -> Result<Vec<Self>, tokio_postgres::Error> {
|
pub async fn list_data(id: u64) -> Result<Vec<Self>, tokio_postgres::Error> {
|
||||||
let client = DatabaseController::new().await?.client;
|
let pool = {
|
||||||
|
let db = DATABASE.lock().unwrap();
|
||||||
|
let controller = db.as_ref().unwrap();
|
||||||
|
controller.pool.clone()
|
||||||
|
};
|
||||||
|
let conn = pool.get().await.unwrap();
|
||||||
|
let client = conn.deref();
|
||||||
let rows = client.query("
|
let rows = client.query("
|
||||||
SELECT * FROM sample
|
SELECT * FROM sample
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
@ -33,7 +41,13 @@ impl SampleData {
|
|||||||
int: i64,
|
int: i64,
|
||||||
boolean: bool
|
boolean: bool
|
||||||
) -> Result<(), tokio_postgres::Error> {
|
) -> Result<(), tokio_postgres::Error> {
|
||||||
let client = DatabaseController::new().await?.client;
|
let pool = {
|
||||||
|
let db = DATABASE.lock().unwrap();
|
||||||
|
let controller = db.as_ref().unwrap();
|
||||||
|
controller.pool.clone()
|
||||||
|
};
|
||||||
|
let conn = pool.get().await.unwrap();
|
||||||
|
let client = conn.deref();
|
||||||
client.execute("
|
client.execute("
|
||||||
INSERT INTO sample (text_val, int_val, boolean_val)
|
INSERT INTO sample (text_val, int_val, boolean_val)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
@ -48,7 +62,13 @@ impl SampleData {
|
|||||||
int: i64,
|
int: i64,
|
||||||
boolean: bool
|
boolean: bool
|
||||||
) -> Result<(), tokio_postgres::Error> {
|
) -> Result<(), tokio_postgres::Error> {
|
||||||
let client = DatabaseController::new().await?.client;
|
let pool = {
|
||||||
|
let db = DATABASE.lock().unwrap();
|
||||||
|
let controller = db.as_ref().unwrap();
|
||||||
|
controller.pool.clone()
|
||||||
|
};
|
||||||
|
let conn = pool.get().await.unwrap();
|
||||||
|
let client = conn.deref();
|
||||||
client.execute("
|
client.execute("
|
||||||
UPDATE sample
|
UPDATE sample
|
||||||
SET text_val = $1, int_val = $2, boolean_val = $3
|
SET text_val = $1, int_val = $2, boolean_val = $3
|
||||||
@ -59,7 +79,13 @@ impl SampleData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_data(id: u64) -> Result<(), tokio_postgres::Error> {
|
pub async fn delete_data(id: u64) -> Result<(), tokio_postgres::Error> {
|
||||||
let client = DatabaseController::new().await?.client;
|
let pool = {
|
||||||
|
let db = DATABASE.lock().unwrap();
|
||||||
|
let controller = db.as_ref().unwrap();
|
||||||
|
controller.pool.clone()
|
||||||
|
};
|
||||||
|
let conn = pool.get().await.unwrap();
|
||||||
|
let client = conn.deref();
|
||||||
client.execute("
|
client.execute("
|
||||||
DELETE FROM sample
|
DELETE FROM sample
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
|
Loading…
Reference in New Issue
Block a user