Polishing the turd
This commit is contained in:
parent
baa14a47e6
commit
fcd2c3c35d
3 changed files with 142 additions and 59 deletions
|
@ -1,11 +1,12 @@
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
pub const VERSION: u16 = 0;
|
/// TODO semver
|
||||||
|
pub const VERSION: u16 = 20; // setup reminder or git hook of some kind to inc this?
|
||||||
|
|
||||||
// This variable should never change for any reason, it will be used to identify this instance of the bot
|
// This variable should never change for any reason, it will be used to identify this instance of the bot
|
||||||
//pub const BOT_INTERNAL: &str = "npobot";
|
//pub const BOT_INTERNAL: &str = "npobot";
|
||||||
/// Should be limited to lowercase alphanumeric characters with periods, dashes, and underscores only
|
/// Should be limited to lowercase alphanumeric characters with periods, dashes, and underscores only
|
||||||
/// If you want to stylize the bot name use the fancy version
|
/// If you want to stylize the bot name use `BOT_NAME_FANCY`
|
||||||
pub const BOT_NAME: &str = "nopalmo";
|
pub const BOT_NAME: &str = "nopalmo";
|
||||||
pub const BOT_NAME_FANCY: &str = "Nopalmo";
|
pub const BOT_NAME_FANCY: &str = "Nopalmo";
|
||||||
|
|
||||||
|
@ -29,32 +30,77 @@ Nopalmo command help page:
|
||||||
Description:
|
Description:
|
||||||
Lorem Ipsum Dolar Sit Amet...
|
Lorem Ipsum Dolar Sit Amet...
|
||||||
|
|
||||||
Arguments:
|
See Usage section for more information about how this help menu functions
|
||||||
nodiscord Skips connecting to discord
|
|
||||||
|
Configuration:
|
||||||
|
print_help Prints this help information
|
||||||
|
h, help, -h, --help, -help, /h, /help, /?, ?
|
||||||
|
|
||||||
|
nodiscord Shuts down the program when the first discord connection usually takes place
|
||||||
Eventually this will connect to a simulated discord instance
|
Eventually this will connect to a simulated discord instance
|
||||||
|
-N, --no-discord
|
||||||
|
|
||||||
dbuser The username to be used when connecting to a database type that requires one
|
dbuser The username to be used when connecting to a database type that requires one
|
||||||
|
-U, --db-user <string>
|
||||||
dbpass The password used like dbuser
|
dbpass The password used like dbuser
|
||||||
|
-P, --db-pass <string>
|
||||||
dbaddress The network address of the desired database
|
dbaddress The network address of the desired database
|
||||||
|
-A, --db-address <int.int.int.int>
|
||||||
dbport The port to use with dbaddress
|
dbport The port to use with dbaddress
|
||||||
|
-P, --db-port <int>
|
||||||
dbpath The path to a unix socket to substitute an address or the path to an SQLite database file
|
dbpath The path to a unix socket to substitute an address or the path to an SQLite database file
|
||||||
dbkind The kind of database we will be using
|
-F, --db-path <path>
|
||||||
|
dbkind The kind of database we will be using, one of `sqlite` `mariadb` `postgresql` `access`
|
||||||
|
-D, --db-kind <dbkind>
|
||||||
|
|
||||||
use_cli Enables a CLI management interface for the bot
|
use_cli Enables a CLI management interface for the bot
|
||||||
|
-i, --interactive
|
||||||
use_gui The GUI version of the CLI
|
use_gui The GUI version of the CLI
|
||||||
|
-g, --gui
|
||||||
|
|
||||||
lua_engine_count The number of lua execution engines to create, This defaults to the number of cores the computer has
|
lua_engine_count The number of lua execution engines to create, This defaults to the number of cores the computer has
|
||||||
This allows executing multiple lua tasks at once, including user commands and timer tasks
|
This allows executing multiple lua tasks at once, including user commands and timer tasks
|
||||||
|
-l, --lua-engine-count <int>
|
||||||
|
|
||||||
connect_remote Connect to a remote instance of the bot
|
log_level The level the logging system will use, one of `trace` `debug` `info` `warning` `error`
|
||||||
host_remote Host an instance of the bot for remote connections
|
-L, --log-level <loglevel>
|
||||||
remote_address The remote address of the bot, or the address you wish to host the bot on
|
|
||||||
remote_port The port used like remote_address
|
connect_remote Connect to a remote instance of the bot
|
||||||
remote_key The path or literal value of the key required to authenticate with the bot on a remote connection
|
-C, --remote-connection
|
||||||
remote_key_type Decides the key type for remote connections
|
host_remote Host an instance of the bot for remote connections
|
||||||
|
-H, --host-remote-connection
|
||||||
|
|
||||||
|
remote_address The remote address of the bot, or the address you wish to host the bot on
|
||||||
|
-a, --remote-address <int.int.int.int>
|
||||||
|
remote_port The port used like remote_address
|
||||||
|
-p, --remote-port <int>
|
||||||
|
remote_key The path of the key required to authenticate with the bot on a remote connection
|
||||||
|
The remote_key_type flag decides what this value is used for
|
||||||
|
-k, --remote-key <path>
|
||||||
|
remote_key_type Decides the key type for remote connections, one of `database` `password` or default `file`
|
||||||
|
-K, --remote-key-type <keytype>
|
||||||
|
|
||||||
Remote Connections:
|
Remote Connections:
|
||||||
Remote access is disabled by default and must be enabled via the `connect_remote` or `host_remote` flags
|
Remote access is disabled by default and must be enabled via the `connect_remote` or `host_remote` flags
|
||||||
A key is required for remote connections, if you just need something quick use the `password` key type, the password must be the same for both server and client
|
A key is required for remote connections, if you just need something quick use the `password` key type, the password must be the same for both server and client
|
||||||
The key can be managed by the database as well, and the key type `database` will disable the `remote_key` variable and instead load authentication information from the server
|
The key can be managed by the database as well, and the key type `database` will disable the `remote_key` variable and instead load authentication information from the server
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
./nopalmo --help
|
||||||
|
./nopalmo -H -a 0.0.0.0 -p 2022 -K file -k ./private.key
|
||||||
|
./nopalmo -C -a 127.0.0.1 -p 2022 -K file -k ./public.key
|
||||||
|
|
||||||
|
The Configuration section is layout like this:
|
||||||
|
Name Description
|
||||||
|
Commandline opts Option arguments
|
||||||
|
|
||||||
|
Each configurable value can be changed in multiple ways for ease of use by the user
|
||||||
|
The order of configuration application is like so:
|
||||||
|
1. System Environment
|
||||||
|
2. .env file (if enabled by your maintainer using the dot_env_file feature)
|
||||||
|
3. Configuration file (currently not supported)
|
||||||
|
4. Commandline options
|
||||||
|
|
||||||
|
Environment Variables:
|
||||||
|
Environment Variables may be set in addition or in place of commandline arguments
|
||||||
";
|
";
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub mod mariadb;
|
||||||
pub mod postgres;
|
pub mod postgres;
|
||||||
pub mod sqlite;
|
pub mod sqlite;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
|
||||||
pub enum DBKind {
|
pub enum DBKind {
|
||||||
Access,
|
Access,
|
||||||
SQLite,
|
SQLite,
|
||||||
|
|
127
src/main.rs
127
src/main.rs
|
@ -23,6 +23,7 @@ use tracing::debug;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing::metadata::LevelFilter;
|
use tracing::metadata::LevelFilter;
|
||||||
|
use tracing::trace;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use constants::HELP_STRING;
|
use constants::HELP_STRING;
|
||||||
|
@ -52,11 +53,27 @@ fn string_to_log_level(str: String) -> Option<LevelFilter> {
|
||||||
"info" => Some(tracing_subscriber::filter::LevelFilter::INFO),
|
"info" => Some(tracing_subscriber::filter::LevelFilter::INFO),
|
||||||
"warn" => Some(tracing_subscriber::filter::LevelFilter::WARN),
|
"warn" => Some(tracing_subscriber::filter::LevelFilter::WARN),
|
||||||
"error" => Some(tracing_subscriber::filter::LevelFilter::ERROR),
|
"error" => Some(tracing_subscriber::filter::LevelFilter::ERROR),
|
||||||
_ => None,
|
_ => {
|
||||||
|
error!("Invalid log level! {str} Choose one of `trace` `debug` `info` `warn` `error`");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn string_to_db_kind(str: String) -> Option<DBKind> {
|
||||||
|
match str.to_lowercase().trim() {
|
||||||
|
"access" => Some(DBKind::Access),
|
||||||
|
"postgresql" => Some(DBKind::PostgreSQL),
|
||||||
|
"sqlite" => Some(DBKind::SQLite),
|
||||||
|
"mariadb" => Some(DBKind::MariaDB),
|
||||||
|
_ => {
|
||||||
|
error!("Invalid database kind! {str} Choose one of `sqlite` `mariadb` `postgresql` `access`");
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### This macro has a "header" of sorts which is used to configure the macro
|
/// ### This macro has a "header" of sorts which is used to configure the macro
|
||||||
|
/// The variables will also be initialized with the system enviroment values, so try not to use unwrap or expect, use a sane default value
|
||||||
/// ---
|
/// ---
|
||||||
/// This is the first line of an example usage
|
/// This is the first line of an example usage
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -145,9 +162,10 @@ macro_rules! program_properties {
|
||||||
}
|
}
|
||||||
|
|
||||||
$(#[$fattr])* $fvis fn $fident() -> $sident {
|
$(#[$fattr])* $fvis fn $fident() -> $sident {
|
||||||
info!("Initializing with env variables.");
|
debug!("Initializing with env variables.");
|
||||||
let mut resval = $sident {
|
let mut resval = $sident {
|
||||||
$($itemident: {
|
$($itemident: {
|
||||||
|
trace!("Reading env: {}", stringify!($itemident));
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
let $vname = std::env::var(stringify!($itemident)).ok();
|
let $vname = std::env::var(stringify!($itemident)).ok();
|
||||||
$envfixer
|
$envfixer
|
||||||
|
@ -155,7 +173,7 @@ macro_rules! program_properties {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "dot_env_file")] {
|
#[cfg(feature = "dot_env_file")] {
|
||||||
info!("Grabbing .env file input.");
|
debug!("Grabbing .env file input.");
|
||||||
|
|
||||||
if let Ok(cdir) = std::env::current_dir() {
|
if let Ok(cdir) = std::env::current_dir() {
|
||||||
if let Ok(string) = std::fs::read_to_string(cdir.join(".env")) {
|
if let Ok(string) = std::fs::read_to_string(cdir.join(".env")) {
|
||||||
|
@ -185,6 +203,7 @@ macro_rules! program_properties {
|
||||||
for (lhs,rhs) in eqvec {
|
for (lhs,rhs) in eqvec {
|
||||||
match lhs.trim() {
|
match lhs.trim() {
|
||||||
$(stringify!($itemident) => {
|
$(stringify!($itemident) => {
|
||||||
|
trace!("Reading .env file: {}", stringify!($itemident));
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
let $vname = Some(rhs.to_string());
|
let $vname = Some(rhs.to_string());
|
||||||
resval.$itemident = $envfixer;
|
resval.$itemident = $envfixer;
|
||||||
|
@ -202,7 +221,7 @@ macro_rules! program_properties {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Grabbing commandline options.");
|
debug!("Grabbing commandline options.");
|
||||||
let mut args = std::env::args().peekable();
|
let mut args = std::env::args().peekable();
|
||||||
|
|
||||||
// The first arg is the path to the executable on all systems that i know of
|
// The first arg is the path to the executable on all systems that i know of
|
||||||
|
@ -217,6 +236,7 @@ macro_rules! program_properties {
|
||||||
while let Some(item) = args.next() {
|
while let Some(item) = args.next() {
|
||||||
match item.trim() {
|
match item.trim() {
|
||||||
$($($options)|* | stringify!($itemident) => {
|
$($($options)|* | stringify!($itemident) => {
|
||||||
|
trace!("Reading commandline options: {}", stringify!($itemident));
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
let $vargsname = &mut args;
|
let $vargsname = &mut args;
|
||||||
resval.$itemident = $argumentfixer;
|
resval.$itemident = $argumentfixer;
|
||||||
|
@ -247,63 +267,71 @@ program_properties! {
|
||||||
envfixer: item.is_some_and(string_to_bool),
|
envfixer: item.is_some_and(string_to_bool),
|
||||||
argumentfixer: true;
|
argumentfixer: true;
|
||||||
pub nodiscord: bool,
|
pub nodiscord: bool,
|
||||||
optnames: [],
|
optnames: ["-N", "--no-discord"],
|
||||||
envfixer: item.is_some_and(string_to_bool),
|
envfixer: item.is_some_and(string_to_bool),
|
||||||
argumentfixer: true;
|
argumentfixer: true;
|
||||||
pub dbuser : Option<String>,
|
pub dbuser : Option<String>,
|
||||||
optnames: [],
|
optnames: ["-U", "--db-user"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub dbpass : Option<String>,
|
pub dbpass : Option<String>,
|
||||||
optnames: [],
|
optnames: ["-P","--db-pass"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub dbaddress : Option<String>,
|
pub dbaddress : Option<String>,
|
||||||
optnames: [],
|
optnames: ["-A", "--db-address"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub dbport: Option<String>,
|
pub dbport: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-P", "--db-port"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub dbpath: Option<String>,
|
pub dbpath: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-F","--db-path"],
|
||||||
envfixer: item,
|
|
||||||
argumentfixer: arglist.next();
|
|
||||||
pub dbkind: Option<String>,
|
|
||||||
optnames: [],
|
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
|
pub dbkind: Option<DBKind>,
|
||||||
|
optnames: ["-D", "--db-kind"],
|
||||||
|
envfixer: item.and_then(string_to_db_kind),
|
||||||
|
argumentfixer: arglist.next().and_then(string_to_db_kind);
|
||||||
pub use_cli: bool,
|
pub use_cli: bool,
|
||||||
optnames: [],
|
optnames: ["-i", "--interactive"],
|
||||||
envfixer: item.is_some_and(string_to_bool),
|
envfixer: item.is_some_and(string_to_bool),
|
||||||
|
argumentfixer: true;
|
||||||
|
pub use_gui: bool,
|
||||||
|
optnames: ["-g", "--gui"],
|
||||||
|
envfixer: item.is_some_and(string_to_bool),
|
||||||
argumentfixer: true;
|
argumentfixer: true;
|
||||||
pub lua_engine_count: Option<String>,
|
pub lua_engine_count: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-l", "--lua-engine-count"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub log_level: Option<LevelFilter>,
|
pub log_level: Option<LevelFilter>,
|
||||||
optnames: [],
|
optnames: ["-L", "--log-level"],
|
||||||
envfixer: string_to_log_level(item.unwrap_or_default()),
|
envfixer: item.and_then(string_to_log_level),
|
||||||
argumentfixer: string_to_log_level(arglist.next().unwrap_or_default());
|
argumentfixer: arglist.next().and_then(string_to_log_level);
|
||||||
pub connect_remote: bool,
|
pub connect_remote: bool,
|
||||||
optnames: [],
|
optnames: ["-C", "--remote-connection"],
|
||||||
|
envfixer: item.is_some_and(string_to_bool),
|
||||||
|
argumentfixer: true;
|
||||||
|
pub host_remote: bool,
|
||||||
|
optnames: ["-H", "--host-remote-connection"],
|
||||||
envfixer: item.is_some_and(string_to_bool),
|
envfixer: item.is_some_and(string_to_bool),
|
||||||
argumentfixer: true;
|
argumentfixer: true;
|
||||||
pub remote_address: Option<String>,
|
pub remote_address: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-a", "--remote-address"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub remote_port: Option<String>,
|
pub remote_port: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-p", "--remote-port"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub remote_key: Option<String>,
|
pub remote_key: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-k", "--remote-key"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
pub remote_key_type: Option<String>,
|
pub remote_key_type: Option<String>,
|
||||||
optnames: [],
|
optnames: ["-K", "--remote-key-type"],
|
||||||
envfixer: item,
|
envfixer: item,
|
||||||
argumentfixer: arglist.next();
|
argumentfixer: arglist.next();
|
||||||
}
|
}
|
||||||
|
@ -328,8 +356,22 @@ pub fn shutdown_handler() {
|
||||||
fn main() {
|
fn main() {
|
||||||
// why do we need to set global default again?
|
// why do we need to set global default again?
|
||||||
|
|
||||||
let (level_filter, level_filter_reload_handle) =
|
// TODO ansi color support
|
||||||
tracing_subscriber::reload::Layer::new(if cfg!(debug_assertions) { LevelFilter::DEBUG } else { LevelFilter::INFO });
|
|
||||||
|
let (level_filter, level_filter_reload_handle) = tracing_subscriber::reload::Layer::new({
|
||||||
|
if let Ok(var) = std::env::var("log_level") {
|
||||||
|
let Some(var) = string_to_log_level(var) else {
|
||||||
|
eprintln!("CRITICAL: Could not read `log_level` system environment variable for initialization phase!");
|
||||||
|
eprintln!("CRITICAL: The value must be one of `trace` `debug` `info` `warn` `error` or it must not be present!");
|
||||||
|
exit(-1);
|
||||||
|
};
|
||||||
|
var
|
||||||
|
} else if cfg!(debug_assertions) {
|
||||||
|
LevelFilter::DEBUG
|
||||||
|
} else {
|
||||||
|
LevelFilter::INFO
|
||||||
|
}
|
||||||
|
});
|
||||||
{
|
{
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
@ -351,17 +393,23 @@ fn main() {
|
||||||
.init();
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO set logging output to a file via a reload handle like above, store logged data and flush to file when file is registered and opened for writing as to not lose any data
|
||||||
|
|
||||||
// Manually init the lazy lock here rather than later for reliability sake
|
// Manually init the lazy lock here rather than later for reliability sake
|
||||||
LazyLock::force(&PROPERTIES);
|
LazyLock::force(&PROPERTIES);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
debug!("{:?}", *PROPERTIES);
|
debug!("{:?}", *PROPERTIES);
|
||||||
|
|
||||||
if let Some(val) = PROPERTIES.log_level {
|
// TODO make this a global function
|
||||||
level_filter_reload_handle.modify(|filter| *filter = val).unwrap();
|
if let Some(nval) = PROPERTIES.log_level {
|
||||||
|
if let Some(cval) = level_filter_reload_handle.clone_current() {
|
||||||
|
if cval != nval {
|
||||||
|
trace!("Switching from log level {cval} to {nval}");
|
||||||
|
level_filter_reload_handle.modify(|filter| *filter = nval).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store non fatal errors from behind this point to be logged and handled afterwards, for example ansi failures, log file failures, or invalid properties
|
|
||||||
// It is assumed there is no failure state before this point, if something goes wrong, shamble onwards
|
|
||||||
|
|
||||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||||
#[allow(clippy::unwrap_used)] // it is impossible for this to be an error
|
#[allow(clippy::unwrap_used)] // it is impossible for this to be an error
|
||||||
SHUTDOWN_SENDER.set(shutdown_tx.clone()).unwrap();
|
SHUTDOWN_SENDER.set(shutdown_tx.clone()).unwrap();
|
||||||
|
@ -377,23 +425,12 @@ fn main() {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbkind = if let Some(arg) = &PROPERTIES.dbkind {
|
let Some(dbkind) = PROPERTIES.dbkind else {
|
||||||
info!("Loading database accessor.");
|
|
||||||
match arg.to_lowercase().trim() {
|
|
||||||
"access" => DBKind::Access,
|
|
||||||
"postgresql" => DBKind::PostgreSQL,
|
|
||||||
"sqlite" => DBKind::SQLite,
|
|
||||||
"mariadb" => DBKind::MariaDB,
|
|
||||||
_ => {
|
|
||||||
error!("Invalid database kind! Choose one of `sqlite` `mariadb` `postgresql` `access`");
|
|
||||||
exit(-1);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!("You must select a database kind with `dbkind`");
|
warn!("You must select a database kind with `dbkind`");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info!("Loading database accessor.");
|
||||||
if let Err(err) = match dbkind {
|
if let Err(err) = match dbkind {
|
||||||
DBKind::Access => databases::access::create_interface(),
|
DBKind::Access => databases::access::create_interface(),
|
||||||
DBKind::MariaDB => databases::mariadb::create_interface(),
|
DBKind::MariaDB => databases::mariadb::create_interface(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue