Rework env vars and cli flags
This commit is contained in:
parent
21db37b613
commit
b933d995c4
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -613,7 +613,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glados"
|
name = "glados"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"influx_db_client",
|
"influx_db_client",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "glados"
|
name = "glados"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
|
151
src/conf.rs
151
src/conf.rs
|
@ -11,16 +11,67 @@ lazy_static! {
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[structopt(long,
|
#[structopt(long, env = "DATA_COLLECTOR_ENABLE", takes_value = false)]
|
||||||
|
pub enable_data_collector: bool,
|
||||||
|
|
||||||
|
#[structopt(long = "token",
|
||||||
short = "t",
|
short = "t",
|
||||||
value_name = "TOKEN",
|
value_name = "TOKEN",
|
||||||
validator = validate_token,
|
validator = validate_token,
|
||||||
env = "DISCORD_TOKEN",
|
env = "DISCORD_BOT_TOKEN",
|
||||||
hide_env_values = true)]
|
hide_env_values = true)]
|
||||||
pub discord_token: String,
|
pub discord_bot_token: String,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
long = "guild-id",
|
||||||
|
short = "g",
|
||||||
|
value_name = "ID",
|
||||||
|
env = "DISCORD_GUILD_ID"
|
||||||
|
)]
|
||||||
|
pub discord_guild_id: u64,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
long = "owner",
|
||||||
|
short = "o",
|
||||||
|
value_name = "ID",
|
||||||
|
env = "DISCORD_GUILD_OWNERS",
|
||||||
|
use_delimiter = true
|
||||||
|
)]
|
||||||
|
pub discord_guild_owner: Vec<UserId>,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
long = "admin-channel",
|
||||||
|
value_name = "ID",
|
||||||
|
env = "DISCORD_GUILD_ADMIN_CHANNEL"
|
||||||
|
)]
|
||||||
|
pub discord_admin_channel: u64,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
long = "prefix",
|
||||||
|
short = "p",
|
||||||
|
value_name = "PREFIX",
|
||||||
|
env = "DISCORD_PREFIX",
|
||||||
|
default_value = "~"
|
||||||
|
)]
|
||||||
|
pub discord_command_prefix: String,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "URL", env = "INFLUX_HOST")]
|
||||||
|
pub influx_host: Option<Url>,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "DB", env = "INFLUX_DB")]
|
||||||
|
pub influx_db: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "USER", env = "INFLUX_USER")]
|
||||||
|
pub influx_user: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "PW", env = "INFLUX_PW", hide_env_values = true)]
|
||||||
|
pub influx_password: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "NAME", env = "MC_CAM_GROUP")]
|
||||||
|
pub mc_cam_group: Option<String>,
|
||||||
|
|
||||||
#[structopt(long, value_name = "URL", env = "RCON_ADDRESS")]
|
#[structopt(long, value_name = "URL", env = "RCON_ADDRESS")]
|
||||||
pub rcon_address: String,
|
pub rcon_address: Option<String>,
|
||||||
|
|
||||||
#[structopt(
|
#[structopt(
|
||||||
long,
|
long,
|
||||||
|
@ -28,74 +79,58 @@ pub struct Args {
|
||||||
env = "RCON_PASSWORD",
|
env = "RCON_PASSWORD",
|
||||||
hide_env_values = true
|
hide_env_values = true
|
||||||
)]
|
)]
|
||||||
pub rcon_password: String,
|
pub rcon_password: Option<String>,
|
||||||
|
|
||||||
#[structopt(
|
#[structopt(
|
||||||
long,
|
long = "state",
|
||||||
short,
|
short = "s",
|
||||||
value_name = "ID",
|
|
||||||
env = "DISCORD_OWNERS",
|
|
||||||
use_delimiter = true
|
|
||||||
)]
|
|
||||||
pub owner: Vec<UserId>,
|
|
||||||
|
|
||||||
#[structopt(
|
|
||||||
long,
|
|
||||||
short,
|
|
||||||
value_name = "PREFIX",
|
|
||||||
env = "DISCORD_PREFIX",
|
|
||||||
default_value = "~"
|
|
||||||
)]
|
|
||||||
pub prefix: String,
|
|
||||||
|
|
||||||
#[structopt(
|
|
||||||
long = "db",
|
|
||||||
short,
|
|
||||||
value_name = "PATH",
|
value_name = "PATH",
|
||||||
env = "DB_PATH",
|
env = "STATE_PATH",
|
||||||
default_value = "state.yml"
|
default_value = "state.yml"
|
||||||
)]
|
)]
|
||||||
pub database_path: PathBuf,
|
pub state_path: PathBuf,
|
||||||
|
|
||||||
#[structopt(long, short, value_name = "ID", env = "GUILD_ID")]
|
|
||||||
pub guild_id: u64,
|
|
||||||
|
|
||||||
#[structopt(long, value_name = "ID", env = "GUILD_ADMIN_CHANNEL")]
|
|
||||||
pub admin_channel: u64,
|
|
||||||
|
|
||||||
#[structopt(long, value_name = "URL", env = "INFLUX_HOST")]
|
|
||||||
pub influx_host: Url,
|
|
||||||
|
|
||||||
#[structopt(long, value_name = "DB", env = "INFLUX_DB")]
|
|
||||||
pub influx_db: String,
|
|
||||||
|
|
||||||
#[structopt(long, value_name = "USER", env = "INFLUX_USER")]
|
|
||||||
pub influx_user: String,
|
|
||||||
|
|
||||||
#[structopt(long, value_name = "PW", env = "INFLUX_PW", hide_env_values = true)]
|
|
||||||
pub influx_password: String,
|
|
||||||
|
|
||||||
#[structopt(long, value_name = "NAME", env = "MC_CAM_GROUP")]
|
|
||||||
pub cam_group_name: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_token(raw: String) -> std::result::Result<(), String> {
|
fn validate_token(raw: String) -> std::result::Result<(), String> {
|
||||||
serenity::client::validate_token(raw).map_err(|_| "Invalid discord token. Maybe a typo?".into())
|
serenity::client::validate_token(raw).map_err(|_| "Invalid discord token. Maybe a typo?".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_arg_method {
|
||||||
|
($arg:ident -> $ty:ty) => {
|
||||||
|
impl Args {
|
||||||
|
pub fn $arg(&self) -> $ty {
|
||||||
|
self.$arg.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($arg:ident -> $ty:ty: when $enable:ident) => {
|
||||||
|
impl Args {
|
||||||
|
pub fn $arg(&self) -> $ty {
|
||||||
|
assert!(
|
||||||
|
self.$enable,
|
||||||
|
stringify!(BUG: $arg used despite bot being disabled)
|
||||||
|
);
|
||||||
|
self.$arg.clone().unwrap().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_arg_method!(discord_guild_id -> GuildId);
|
||||||
|
impl_arg_method!(discord_admin_channel -> ChannelId);
|
||||||
|
impl_arg_method!(influx_host -> Url: when enable_data_collector);
|
||||||
|
impl_arg_method!(influx_db -> String: when enable_data_collector);
|
||||||
|
impl_arg_method!(influx_user -> String: when enable_data_collector);
|
||||||
|
impl_arg_method!(influx_password -> String: when enable_data_collector);
|
||||||
|
impl_arg_method!(rcon_address -> String: when enable_data_collector);
|
||||||
|
impl_arg_method!(rcon_password -> String: when enable_data_collector);
|
||||||
|
impl_arg_method!(mc_cam_group -> String: when enable_data_collector);
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub fn owners(&self) -> HashSet<UserId> {
|
pub fn owners(&self) -> HashSet<UserId> {
|
||||||
if self.owner.is_empty() {
|
if self.discord_guild_owner.is_empty() {
|
||||||
tracing::warn!("You should probably specify at least one `--owner`");
|
tracing::warn!("You should probably specify at least one `--owner`");
|
||||||
}
|
}
|
||||||
self.owner.iter().cloned().collect()
|
self.discord_guild_owner.iter().cloned().collect()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn guild_id(&self) -> GuildId {
|
|
||||||
self.guild_id.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn admin_channel(&self) -> ChannelId {
|
|
||||||
self.admin_channel.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,15 +43,17 @@ pub struct Bot {
|
||||||
|
|
||||||
impl Bot {
|
impl Bot {
|
||||||
pub async fn init() -> Result<Self> {
|
pub async fn init() -> Result<Self> {
|
||||||
let client = Client::builder(&ARGS.discord_token)
|
let mut builder = Client::builder(&ARGS.discord_bot_token)
|
||||||
.framework(framework::init())
|
.framework(framework::init())
|
||||||
.intents(*INTENTS)
|
.intents(*INTENTS)
|
||||||
.event_handler(Handler::default())
|
.event_handler(Handler::default())
|
||||||
|
.type_map_insert::<data::StateKey>(State::load().await?);
|
||||||
|
if ARGS.enable_data_collector {
|
||||||
|
builder = builder
|
||||||
.type_map_insert::<data::Rcon>(RconHandle::init().await)
|
.type_map_insert::<data::Rcon>(RconHandle::init().await)
|
||||||
.type_map_insert::<InfluxKey>(InfluxDb::init())
|
.type_map_insert::<InfluxKey>(InfluxDb::init())
|
||||||
.type_map_insert::<data::StateKey>(State::load().await?)
|
}
|
||||||
.await
|
let client = builder.await.map_err(Error::CreatingDiscordClient)?;
|
||||||
.map_err(Error::CreatingDiscordClient)?;
|
|
||||||
Ok(Self { client })
|
Ok(Self { client })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub fn init() -> StandardFramework {
|
||||||
.configure(|c| {
|
.configure(|c| {
|
||||||
c.with_whitespace(true)
|
c.with_whitespace(true)
|
||||||
.owners(ARGS.owners())
|
.owners(ARGS.owners())
|
||||||
.prefix(&ARGS.prefix)
|
.prefix(&ARGS.discord_command_prefix)
|
||||||
.delimiters(vec![", ", ","])
|
.delimiters(vec![", ", ","])
|
||||||
})
|
})
|
||||||
.help(&BOT_HELP)
|
.help(&BOT_HELP)
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub async fn filter(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Fetch guild members
|
// Fetch guild members
|
||||||
let mut members = ARGS.guild_id().members_iter(ctx).boxed();
|
let mut members = ARGS.discord_guild_id().members_iter(ctx).boxed();
|
||||||
// Member that match our filter
|
// Member that match our filter
|
||||||
let mut matched = vec![];
|
let mut matched = vec![];
|
||||||
// Filter members
|
// Filter members
|
||||||
|
@ -44,14 +44,14 @@ pub async fn filter(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
};
|
};
|
||||||
// Apply Filter
|
// Apply Filter
|
||||||
if filter.matches(&member).await {
|
if filter.matches(&member).await {
|
||||||
matched.push(member.user.id);
|
matched.push(member);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Assemble reply
|
// Assemble reply
|
||||||
let reply = matched
|
let reply = matched
|
||||||
.iter()
|
.iter()
|
||||||
.fold(String::from("Filtered members:"), |s, id| {
|
.fold(String::from("Filtered members:"), |s, member| {
|
||||||
s + &format!(" {}", id)
|
s + &format!(" {}", member)
|
||||||
});
|
});
|
||||||
msg.reply(ctx, reply).await?;
|
msg.reply(ctx, reply).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -110,7 +110,7 @@ impl MemberFilter {
|
||||||
let RawMemberFilter { add, sub } = raw_filter;
|
let RawMemberFilter { add, sub } = raw_filter;
|
||||||
// Map role names to role ids
|
// Map role names to role ids
|
||||||
let roles: HashMap<_, _> = ARGS
|
let roles: HashMap<_, _> = ARGS
|
||||||
.guild_id()
|
.discord_guild_id()
|
||||||
.roles(ctx)
|
.roles(ctx)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -29,14 +29,16 @@ impl EventHandler for Handler {
|
||||||
.await
|
.await
|
||||||
.log_warn("sanitizing state at startup");
|
.log_warn("sanitizing state at startup");
|
||||||
|
|
||||||
|
if ARGS.enable_data_collector {
|
||||||
tokio::spawn(track_server_status(ctx_clone));
|
tokio::spawn(track_server_status(ctx_clone));
|
||||||
|
}
|
||||||
|
|
||||||
self.setup_done.swap(true, Ordering::Relaxed);
|
self.setup_done.swap(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn guild_member_addition(&self, ctx: Context, guild_id: GuildId, new_member: Member) {
|
async fn guild_member_addition(&self, ctx: Context, guild_id: GuildId, new_member: Member) {
|
||||||
if guild_id != ARGS.guild_id {
|
if guild_id != ARGS.discord_guild_id() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if new_member.user.bot {
|
if new_member.user.bot {
|
||||||
|
@ -52,7 +54,7 @@ impl EventHandler for Handler {
|
||||||
user: User,
|
user: User,
|
||||||
_member_data: Option<Member>,
|
_member_data: Option<Member>,
|
||||||
) {
|
) {
|
||||||
if guild_id != ARGS.guild_id {
|
if guild_id != ARGS.discord_guild_id() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if user.bot {
|
if user.bot {
|
||||||
|
|
|
@ -47,13 +47,15 @@ pub async fn workflow_member_removal(ctx: &Context, user: User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn admin_channel_say(ctx: &Context, what: impl fmt::Display) -> Result {
|
async fn admin_channel_say(ctx: &Context, what: impl fmt::Display) -> Result {
|
||||||
ARGS.admin_channel().say(ctx.http.clone(), what).await?;
|
ARGS.discord_admin_channel()
|
||||||
|
.say(ctx.http.clone(), what)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn catch_up_on_guild_members(ctx: &Context) -> Result {
|
async fn catch_up_on_guild_members(ctx: &Context) -> Result {
|
||||||
let actual_members: HashSet<_> = ARGS
|
let actual_members: HashSet<_> = ARGS
|
||||||
.guild_id()
|
.discord_guild_id()
|
||||||
.members(ctx.http.clone(), None, None)
|
.members(ctx.http.clone(), None, None)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -80,7 +82,7 @@ async fn catch_up_on_guild_members(ctx: &Context) -> Result {
|
||||||
|
|
||||||
for new in actual_members.difference(&known_members) {
|
for new in actual_members.difference(&known_members) {
|
||||||
let new_member = match ARGS
|
let new_member = match ARGS
|
||||||
.guild_id()
|
.discord_guild_id()
|
||||||
.member(ctx.http.clone(), new)
|
.member(ctx.http.clone(), new)
|
||||||
.await
|
.await
|
||||||
.log_warn("retrieving member based on id")
|
.log_warn("retrieving member based on id")
|
||||||
|
|
|
@ -16,8 +16,8 @@ impl TypeMapKey for InfluxKey {
|
||||||
|
|
||||||
impl InfluxDb {
|
impl InfluxDb {
|
||||||
pub fn init() -> Self {
|
pub fn init() -> Self {
|
||||||
let inner = Client::new(ARGS.influx_host.clone(), &ARGS.influx_db)
|
let inner = Client::new(ARGS.influx_host(), &ARGS.influx_db())
|
||||||
.set_authentication(&ARGS.influx_user, &ARGS.influx_password);
|
.set_authentication(&ARGS.influx_user(), &ARGS.influx_password());
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ impl RconHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connect(&mut self) {
|
async fn connect(&mut self) {
|
||||||
let addr = &ARGS.rcon_address;
|
let addr = &ARGS.rcon_address();
|
||||||
let password = &ARGS.rcon_password;
|
let password = &ARGS.rcon_password();
|
||||||
self.inner = rcon::Connection::builder()
|
self.inner = rcon::Connection::builder()
|
||||||
.enable_minecraft_quirks(true)
|
.enable_minecraft_quirks(true)
|
||||||
.connect(addr, password)
|
.connect(addr, password)
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub struct MinecraftPlayer {
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub async fn load() -> Result<FileDatabase<Self, Yaml>> {
|
pub async fn load() -> Result<FileDatabase<Self, Yaml>> {
|
||||||
let db = FileDatabase::load_from_path_or_default(&ARGS.database_path)?;
|
let db = FileDatabase::load_from_path_or_default(&ARGS.state_path)?;
|
||||||
Ok(db)
|
Ok(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ impl MinecraftPlayer {
|
||||||
let player = MinecraftPlayer {
|
let player = MinecraftPlayer {
|
||||||
uuid: whois.uuid,
|
uuid: whois.uuid,
|
||||||
ign: whois.nick,
|
ign: whois.nick,
|
||||||
is_cam_acc: parent_group == ARGS.cam_group_name,
|
is_cam_acc: parent_group == ARGS.mc_cam_group(),
|
||||||
};
|
};
|
||||||
State::write(ctx, |state| state.known_players.insert(player.clone()))
|
State::write(ctx, |state| state.known_players.insert(player.clone()))
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Reference in a new issue