Rework env vars and cli flags

This commit is contained in:
Malte Tammena 2021-12-09 17:21:55 +01:00
parent 21db37b613
commit b933d995c4
11 changed files with 125 additions and 84 deletions

2
Cargo.lock generated
View file

@ -613,7 +613,7 @@ dependencies = [
[[package]]
name = "glados"
version = "0.1.2"
version = "0.2.0"
dependencies = [
"dotenv",
"influx_db_client",

View file

@ -1,6 +1,6 @@
[package]
name = "glados"
version = "0.1.2"
version = "0.2.0"
edition = "2021"
license = "MIT"

View file

@ -11,16 +11,67 @@ lazy_static! {
#[derive(Debug, StructOpt)]
pub struct Args {
#[structopt(long,
#[structopt(long, env = "DATA_COLLECTOR_ENABLE", takes_value = false)]
pub enable_data_collector: bool,
#[structopt(long = "token",
short = "t",
value_name = "TOKEN",
validator = validate_token,
env = "DISCORD_TOKEN",
env = "DISCORD_BOT_TOKEN",
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")]
pub rcon_address: String,
pub rcon_address: Option<String>,
#[structopt(
long,
@ -28,74 +79,58 @@ pub struct Args {
env = "RCON_PASSWORD",
hide_env_values = true
)]
pub rcon_password: String,
pub rcon_password: Option<String>,
#[structopt(
long,
short,
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,
long = "state",
short = "s",
value_name = "PATH",
env = "DB_PATH",
env = "STATE_PATH",
default_value = "state.yml"
)]
pub database_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,
pub state_path: PathBuf,
}
fn validate_token(raw: String) -> std::result::Result<(), String> {
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 {
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`");
}
self.owner.iter().cloned().collect()
}
pub fn guild_id(&self) -> GuildId {
self.guild_id.into()
}
pub fn admin_channel(&self) -> ChannelId {
self.admin_channel.into()
self.discord_guild_owner.iter().cloned().collect()
}
}

View file

@ -43,15 +43,17 @@ pub struct Bot {
impl Bot {
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())
.intents(*INTENTS)
.event_handler(Handler::default())
.type_map_insert::<data::Rcon>(RconHandle::init().await)
.type_map_insert::<InfluxKey>(InfluxDb::init())
.type_map_insert::<data::StateKey>(State::load().await?)
.await
.map_err(Error::CreatingDiscordClient)?;
.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::<InfluxKey>(InfluxDb::init())
}
let client = builder.await.map_err(Error::CreatingDiscordClient)?;
Ok(Self { client })
}

View file

@ -24,7 +24,7 @@ pub fn init() -> StandardFramework {
.configure(|c| {
c.with_whitespace(true)
.owners(ARGS.owners())
.prefix(&ARGS.prefix)
.prefix(&ARGS.discord_command_prefix)
.delimiters(vec![", ", ","])
})
.help(&BOT_HELP)

View file

@ -32,7 +32,7 @@ pub async fn filter(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
}
};
// 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
let mut matched = vec![];
// Filter members
@ -44,14 +44,14 @@ pub async fn filter(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
};
// Apply Filter
if filter.matches(&member).await {
matched.push(member.user.id);
matched.push(member);
};
}
// Assemble reply
let reply = matched
.iter()
.fold(String::from("Filtered members:"), |s, id| {
s + &format!(" {}", id)
.fold(String::from("Filtered members:"), |s, member| {
s + &format!(" {}", member)
});
msg.reply(ctx, reply).await?;
Ok(())
@ -110,7 +110,7 @@ impl MemberFilter {
let RawMemberFilter { add, sub } = raw_filter;
// Map role names to role ids
let roles: HashMap<_, _> = ARGS
.guild_id()
.discord_guild_id()
.roles(ctx)
.await?
.into_iter()

View file

@ -29,14 +29,16 @@ impl EventHandler for Handler {
.await
.log_warn("sanitizing state at startup");
tokio::spawn(track_server_status(ctx_clone));
if ARGS.enable_data_collector {
tokio::spawn(track_server_status(ctx_clone));
}
self.setup_done.swap(true, Ordering::Relaxed);
}
}
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;
}
if new_member.user.bot {
@ -52,7 +54,7 @@ impl EventHandler for Handler {
user: User,
_member_data: Option<Member>,
) {
if guild_id != ARGS.guild_id {
if guild_id != ARGS.discord_guild_id() {
return;
}
if user.bot {

View file

@ -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 {
ARGS.admin_channel().say(ctx.http.clone(), what).await?;
ARGS.discord_admin_channel()
.say(ctx.http.clone(), what)
.await?;
Ok(())
}
async fn catch_up_on_guild_members(ctx: &Context) -> Result {
let actual_members: HashSet<_> = ARGS
.guild_id()
.discord_guild_id()
.members(ctx.http.clone(), None, None)
.await?
.into_iter()
@ -80,7 +82,7 @@ async fn catch_up_on_guild_members(ctx: &Context) -> Result {
for new in actual_members.difference(&known_members) {
let new_member = match ARGS
.guild_id()
.discord_guild_id()
.member(ctx.http.clone(), new)
.await
.log_warn("retrieving member based on id")

View file

@ -16,8 +16,8 @@ impl TypeMapKey for InfluxKey {
impl InfluxDb {
pub fn init() -> Self {
let inner = Client::new(ARGS.influx_host.clone(), &ARGS.influx_db)
.set_authentication(&ARGS.influx_user, &ARGS.influx_password);
let inner = Client::new(ARGS.influx_host(), &ARGS.influx_db())
.set_authentication(&ARGS.influx_user(), &ARGS.influx_password());
Self { inner }
}

View file

@ -83,8 +83,8 @@ impl RconHandle {
}
async fn connect(&mut self) {
let addr = &ARGS.rcon_address;
let password = &ARGS.rcon_password;
let addr = &ARGS.rcon_address();
let password = &ARGS.rcon_password();
self.inner = rcon::Connection::builder()
.enable_minecraft_quirks(true)
.connect(addr, password)

View file

@ -37,7 +37,7 @@ pub struct MinecraftPlayer {
impl State {
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)
}
@ -94,7 +94,7 @@ impl MinecraftPlayer {
let player = MinecraftPlayer {
uuid: whois.uuid,
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()))
.await