Initial Commit

This commit is contained in:
Hay1tsme 2023-08-27 02:15:02 -04:00
commit 3172a07b38
9 changed files with 2018 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/target
.env
*.yaml
!example_config.yaml
/data
*.log

1786
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "whisper_wolf_3"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serenity = { version = "0.11", features = ["builder", "cache", "collector", "client", "utils", "rustls_backend", "model"], default-features = false }
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] }
poise = "0.5"
chrono = "0.4"
once_cell = "1.18"
serde = "1.0"
serde_yaml = "0.9"
diesel = { version = "2.1", features = ["sqlite"] }

6
example_config.yaml Normal file
View File

@ -0,0 +1,6 @@
token: ""
log_dir: "logs"
hash_ids: true
channel: 0
admins:
- 0

5
src/commands/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod notify;
pub struct Data {}
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;

11
src/commands/notify.rs Normal file
View File

@ -0,0 +1,11 @@
use super::{Error, Context};
#[poise::command(slash_command, prefix_command, dm_only)]
pub async fn notify(
ctx: Context<'_>,
#[description = "Turn notifications on or off"] toggle: bool,
) -> Result<(), Error> {
let response = format!("Notifications are now {}", toggle);
ctx.say(response).await?;
Ok(())
}

49
src/config.rs Normal file
View File

@ -0,0 +1,49 @@
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use serde_yaml::{self};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CoreConfig {
/// Discord bot token.
pub token: String,
/// Folder to store program logs.
pub log_dir: String,
/// If user IDs should be hased in the database, disables @mentioning.
pub hash_ids: bool,
/// Channel where messages should be posted.
pub channel: u64,
/// List of user IDs of users who have admin perms with the bot.
pub admins: Vec<i64>,
}
impl Default for CoreConfig {
fn default() -> Self {
Self {
token: Default::default(),
log_dir: "logs".to_string(),
hash_ids: true,
channel: 0,
admins: vec![0]
}
}
}
pub static CORE_CFG: OnceCell<CoreConfig> = OnceCell::new();
pub fn load_core_cfg(file: &String) {
let f_ = std::fs::File::open(format!("{}", file));
if f_.is_err() {
CORE_CFG.set(CoreConfig::default()).expect("Could not set default values");
println!("!! Using default config values becuase {} does not exist or could not be read !!", file);
return;
}
let r = CORE_CFG.set(serde_yaml::from_reader(f_.unwrap()).expect("Could not read values."));
if r.is_err() {
CORE_CFG.set(CoreConfig::default()).expect("Could not set default values");
println!("!! Using default config values becuase {} does not exist or could not be read !!", file)
}
}

104
src/logging.rs Normal file
View File

@ -0,0 +1,104 @@
use once_cell::sync::OnceCell;
use std::sync::atomic::*;
use std::fs::{OpenOptions, File, create_dir_all};
use std::io::Write;
#[derive(Debug, PartialEq, PartialOrd)]
pub enum LogLevel {
Trace = 5,
Debug = 10,
Info = 20,
Warn = 30,
Error = 40,
Critical = 50,
Fatal = 100,
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
LogLevel::Trace => write!(f, "T"),
LogLevel::Debug => write!(f, "D"),
LogLevel::Info => write!(f, "I"),
LogLevel::Warn => write!(f, "W"),
LogLevel::Error => write!(f, "E"),
LogLevel::Critical => write!(f, "C"),
LogLevel::Fatal => write!(f, "F"),
}
}
}
impl std::str::FromStr for LogLevel {
type Err = ();
fn from_str(input: &str) -> Result<LogLevel, Self::Err> {
match input {
"trace" => Ok(LogLevel::Trace),
"debug" => Ok(LogLevel::Debug),
"info" => Ok(LogLevel::Info),
"warn" => Ok(LogLevel::Warn),
"warning" => Ok(LogLevel::Warn),
"error" => Ok(LogLevel::Error),
"critical" => Ok(LogLevel::Critical),
"fatal" => Ok(LogLevel::Fatal),
_ => Err(()),
}
}
}
static LEVEL: OnceCell<LogLevel> = OnceCell::new();
static LOG_FILE: OnceCell<File> = OnceCell::new();
static HAS_INITED: AtomicBool = AtomicBool::new(false);
static FILE_IS_GOOD: AtomicBool = AtomicBool::new(false);
pub fn init_logger(log_folder: &str, level: LogLevel) {
if HAS_INITED.load(Ordering::Relaxed) {
return;
}
LEVEL.set(level).unwrap();
HAS_INITED.store(true, Ordering::Relaxed);
let log_file = format!("{}/apollo.log", log_folder);
let dir_create_result: Result<(), std::io::Error> = create_dir_all(log_folder);
if dir_create_result.is_err() {
log(LogLevel::Error, "logging", "init", format!("Error creating log folder {}", log_folder).as_str());
return;
}
let f_ = OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(log_file);
if f_.is_ok() {
LOG_FILE.set(f_.unwrap()).unwrap();
FILE_IS_GOOD.store(true, Ordering::Relaxed);
} else {
log(LogLevel::Error, "logging", "init", "Error getting logfile");
}
}
pub fn log(level: LogLevel, module: &str, submodule: &str, message: &str) {
if !HAS_INITED.load(Ordering::Relaxed) {
println!("Logging before logger initialized!");
return;
}
let timestamp: String = chrono::offset::Local::now().format("%F %X%.3f").to_string();
let logstr: String = format!(
"[{}]:{}:{}:{}:{}",
timestamp, level, module, submodule, message
);
if level >= *LEVEL.get().unwrap() {
println!("{logstr}");
if FILE_IS_GOOD.load(Ordering::Relaxed) {
let mut f = LOG_FILE.get().unwrap();
let _ = writeln!(f, "{}", logstr);
}
}
}

35
src/main.rs Normal file
View File

@ -0,0 +1,35 @@
use poise::serenity_prelude::{self as serenity};
mod config;
use config::{load_core_cfg, CORE_CFG};
mod logging;
use logging::{LogLevel, init_logger, log};
mod commands;
use commands::{notify::*, Data};
#[tokio::main]
async fn main() {
load_core_cfg(&"config.yaml".to_string());
let cfg = CORE_CFG.get().unwrap();
init_logger(&cfg.log_dir, LogLevel::Info);
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
commands: vec![notify()],
..Default::default()
})
.token(&cfg.token)
.intents(serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::DIRECT_MESSAGES | serenity::GatewayIntents::GUILD_MESSAGES)
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Data {})
})
});
log(LogLevel::Info, "boot", "main", "WhisperWolf3 boot");
framework.run().await.unwrap();
}