From d75e7d52f2073e0b07d28d4f40c683477d6600cb Mon Sep 17 00:00:00 2001 From: Michael K <1747304+tsal@users.noreply.github.com> Date: Sun, 24 Sep 2023 10:03:33 -0400 Subject: [PATCH] Revert "Development Updates (#6)" This reverts commit 6372556ccedd66f563b97bfe19a26733dc1495f5. --- Cargo.lock | 86 ++++++++++++++++++++++ Cargo.toml | 4 +- src/bot.rs | 187 ------------------------------------------------ src/commands.rs | 8 +-- src/main.rs | 124 ++++++++++++++++++++++++++++++-- 5 files changed, 211 insertions(+), 198 deletions(-) delete mode 100644 src/bot.rs diff --git a/Cargo.lock b/Cargo.lock index 76733d0..f4f85f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,7 @@ version = "0.1.0" dependencies = [ "anyhow", "libsql-client", + "poise", "reqwest", "serde", "serenity", @@ -344,6 +345,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -367,6 +403,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -853,6 +900,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.4.0" @@ -1359,6 +1412,37 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "poise" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d591af1c934c29adda172665f69b837e642d4fee85598baffb95dd98110467d" +dependencies = [ + "async-trait", + "derivative", + "futures-core", + "futures-util", + "log", + "once_cell", + "parking_lot", + "poise_macros", + "regex", + "serenity", + "tokio", +] + +[[package]] +name = "poise_macros" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40270099e1527efae99fdc0609d397e76310b529d4980ad38ab14d81803ca0fa" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1828,6 +1912,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "cfg-if", + "chrono", "dashmap", "flate2", "futures", @@ -1836,6 +1921,7 @@ dependencies = [ "parking_lot", "percent-encoding", "reqwest", + "rustversion", "serde", "serde-value", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 387b6cd..6f64a5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" anyhow = "1.0.66" shuttle-serenity = "0.25.0" shuttle-runtime = "0.25.0" -serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } +serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } shuttle-secrets = "0.25.0" tokio = "1.26.0" tracing = "0.1.37" @@ -15,4 +15,4 @@ reqwest = "0.11.20" serde = "1.0.188" shuttle-turso = "0.25.0" libsql-client = "0.30.1" -#poise = "0.5.5" \ No newline at end of file +poise = "0.5.5" \ No newline at end of file diff --git a/src/bot.rs b/src/bot.rs deleted file mode 100644 index 584fca7..0000000 --- a/src/bot.rs +++ /dev/null @@ -1,187 +0,0 @@ -// Serenity imports -use serenity::client::{Context, EventHandler}; -use serenity::model::gateway::Ready; -use serenity::model::prelude::{GuildId, ChannelId, Interaction, InteractionResponseType, ScheduledEventId, RichInvite}; -use serenity::model::guild::ScheduledEvent; -use serenity::async_trait; -// use serenity::http::CacheHttp; - -// Error and logging imports -use tracing::{error, info}; -use anyhow::anyhow; - -// Service imports -use shuttle_secrets::SecretStore; -use libsql_client::Client as SqlClient; - -// Local imports -use crate::commands; - -pub struct Bot { - pub steam_api_key: String, - pub owner_guild_id: u64, - pub arma_servers: Vec, - pub turso_client: SqlClient, -} - -// TODO: make these fetched from the database -// const TEST_CHANNEL_ID: u64 = 1021230323247349902; -const EVENT_FEED_ID: u64 = 1151219763549327471; - -#[async_trait] -impl EventHandler for Bot { - async fn ready(&self, ctx: Context, ready: Ready) { - info!("{} is connected!", ready.user.name); - let guild_id = GuildId(self.owner_guild_id); - - let commands = GuildId::set_application_commands(&guild_id, &ctx.http, |commands| { - commands.create_application_command(|command| commands::ServerStatusCommand::register(command)) - }) - .await - .unwrap(); - - info!( - "Successfully registered application commands: {:#?}", - commands - ); - } - - async fn interaction_create(&self, ctx: Context, interaction: Interaction) { - if let Interaction::ApplicationCommand(command) = interaction { - let response_content = match command.data.name.as_str() { - "status" => { - // let channel_id = command.channel_id; - commands::ServerStatusCommand::run( - &command.data.options, - self.steam_api_key.to_owned(), - self.arma_servers.to_owned(), - // channel_id, - // ctx.to_owned() - ) - .await - } - command => unreachable!("Unknown command: {}", command), - }; - - let create_interaction_response = - command.create_interaction_response(&ctx.http, |response| { - response - .kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|message| message.content(response_content)) - }); - - if let Err(why) = create_interaction_response.await { - error!("Cannot respond to slash command: {:?}", why); - } - } - } - - // TODO: use real channel id - async fn guild_scheduled_event_create(&self, ctx: Context, event: ScheduledEvent) { - info!("Event created: {:?}", event); - - let channel = match ctx.cache.guild_channel(EVENT_FEED_ID) { - Some(channel) => channel, - None => { - error!("Error creating invite, could not access channel!"); - return; - }, - }; - - info!("Channel: {:?}", channel); - - let guild_event = GuildEvent::from(event); - - info!("Guild event: {:?}", guild_event); - - let event_url = guild_event.url(&ctx).await; - - info!("Event url: {}", event_url); - - let _ = channel.say(&ctx, format!("Event Posted! {}", event_url)).await; - - } -} - -async fn get_rich_invite_from_guild_event(_event: &GuildEvent, ctx: &Context) -> Result { - - let channel = match ctx.cache.guild_channel(EVENT_FEED_ID) { - Some(channel) => channel, - None => { - return Err(anyhow!("Error creating invite, could not access channel!")); - }, - }; - - let creation = channel - // 14 days in seconds - .create_invite(&ctx, |i| i.max_age(1209600 / 14).max_uses(50)).await; - - let invite = match creation { - Ok(invite) => invite, - Err(why) => { - error!("Err creating invite: {:?}", why); - return Err(anyhow!("Error creating invite: {:?}", why)); - } - }; - - Ok(invite) -} - -#[derive(Debug)] -struct GuildEvent { - // scheduled_event_id of the event - id: ScheduledEventId, - // guild_id of the guild hosting the event - // guild_id: GuildId, - // channel_id of the event (if any) - // channel_id: Option, -} - -impl GuildEvent { - fn new(id: ScheduledEventId, _guild_id: GuildId, _channel_id: Option) -> Self { - Self { - id, - // guild_id, - // channel_id, - } - } - - pub async fn url(&self, ctx: &Context) -> String { - let invite = get_rich_invite_from_guild_event(self, ctx).await.unwrap(); - format!("{}?event={}", invite.url(), self.id.0) - } -} - -impl From for GuildEvent { - fn from(event: ScheduledEvent) -> Self { - Self::new(event.id, event.guild_id, event.channel_id) - } -} - -pub fn check_environment(secret_store: SecretStore) -> Result<(String, String, u64, Vec), anyhow::Error> { - let discord_token = if let Some(token) = secret_store.get("DISCORD_TOKEN") { - token - } else { - return Err(anyhow!("'DISCORD_TOKEN' was not found")); - }; - - let steam_api_key = if let Some(steam_key) = secret_store.get("STEAM_API_KEY") { - steam_key - } else { - return Err(anyhow!("'STEAM_API_KEY' was not found")); - }; - - let owner_guild_id = if let Some(guild_id) = secret_store.get("OWNER_GUILD_ID") { - guild_id.parse().unwrap() - } else { - return Err(anyhow!("'OWNER_GUILD_ID' was not found")); - }; - - let arma_servers: Vec = if let Some(arma_servers) = secret_store.get("ARMA_SERVERS") { - arma_servers.split(',').map(|s| s.to_owned()).collect() - } else { - return Err(anyhow!("'ARMA_SERVERS' was not found")); - }; - - Ok((discord_token, steam_api_key, owner_guild_id, arma_servers.to_owned())) -} diff --git a/src/commands.rs b/src/commands.rs index bc1013f..3db89e1 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,16 +1,16 @@ -// use tracing::error; -use tracing::info; +// Error handling / trace logging +use tracing::{error, info}; // Serenity use serenity::{ builder::CreateApplicationCommand, model::prelude::application_command::CommandDataOption, - utils::MessageBuilder, + prelude::*, utils::MessageBuilder, }; // Utility use serde::Deserialize; use std::time::Duration; -// use serenity::model::prelude::ChannelId; +use serenity::model::prelude::ChannelId; use tokio::time::sleep; pub struct ServerStatusCommand; diff --git a/src/main.rs b/src/main.rs index 0141a33..94bbaf9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,93 @@ mod commands; -mod bot; + +// Error handling / trace logging +use anyhow::anyhow; +use tracing::{error, info}; // Serenity -use serenity::prelude::*; +use serenity::{ + async_trait, + model::{ + gateway::Ready, + prelude::{ + GuildId, + Interaction, + InteractionResponseType, + } + }, + prelude::*, +}; -use libsql_client::Client as SqlClient; +// Shuttle use shuttle_secrets::SecretStore; +// Turso +use libsql_client::client::Client as SqlClient; +use serenity::http::CacheHttp; +use serenity::model::guild::ScheduledEvent; + +struct Bot { + steam_api_key: String, + owner_guild_id: u64, + arma_servers: Vec, + turso_client: SqlClient, +} + +// TODO: make these fetched from the database +const TEST_CHANNEL_ID: u64 = 1021230323247349902; +const EVENT_FEED_ID: u64 = 1151219763549327471; + +#[async_trait] +impl EventHandler for Bot { + async fn ready(&self, ctx: Context, ready: Ready) { + info!("{} is connected!", ready.user.name); + let guild_id = GuildId(self.owner_guild_id); + + let commands = GuildId::set_application_commands(&guild_id, &ctx.http, |commands| { + commands.create_application_command(|command| commands::ServerStatusCommand::register(command)) + }) + .await + .unwrap(); + + info!( + "Successfully registered application commands: {:#?}", + commands + ); + } + + async fn interaction_create(&self, ctx: Context, interaction: Interaction) { + if let Interaction::ApplicationCommand(command) = interaction { + let response_content = match command.data.name.as_str() { + "status" => { + // let channel_id = command.channel_id; + commands::ServerStatusCommand::run( + &command.data.options, + self.steam_api_key.to_owned(), + self.arma_servers.to_owned(), + // channel_id, + // ctx.to_owned() + ) + .await + } + command => unreachable!("Unknown command: {}", command), + }; + + let create_interaction_response = + command.create_interaction_response(&ctx.http, |response| { + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| message.content(response_content)) + }); -use bot::{Bot, check_environment}; + if let Err(why) = create_interaction_response.await { + error!("Cannot respond to slash command: {:?}", why); + } + } + } + + async fn guild_scheduled_event_create(&self, _ctx: Context, _event: ScheduledEvent) { + todo!() + } +} #[shuttle_runtime::main] async fn att_bot( @@ -27,7 +107,6 @@ async fn att_bot( let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES - | GatewayIntents::GUILD_SCHEDULED_EVENTS | GatewayIntents::MESSAGE_CONTENT; let client = Client::builder(&discord_token, intents) @@ -44,3 +123,38 @@ async fn att_bot( } + +fn check_environment(secret_store: SecretStore) -> Result<(String, String, u64, Vec), anyhow::Error> { + let mut discord_token = String::new(); + let mut steam_api_key = String::new(); + let mut owner_guild_id: u64 = 0; + let mut arma_servers = Vec::new(); + + discord_token = if let Some(token) = secret_store.get("DISCORD_TOKEN") { + token + } else { + return Err(anyhow!("'DISCORD_TOKEN' was not found").into()); + }; + + steam_api_key = if let Some(steam_key) = secret_store.get("STEAM_API_KEY") { + steam_key + } else { + return Err(anyhow!("'STEAM_API_KEY' was not found").into()); + }; + + owner_guild_id = if let Some(guild_id) = secret_store.get("OWNER_GUILD_ID") { + guild_id.parse().unwrap() + } else { + return Err(anyhow!("'OWNER_GUILD_ID' was not found").into()); + }; + + arma_servers = if let Some(arma_servers) = secret_store.get("ARMA_SERVERS") { + arma_servers.split(',').map(|s| s.to_owned()).collect() + } else { + return Err(anyhow!("'ARMA_SERVERS' was not found").into()); + }; + + Ok((discord_token, steam_api_key, owner_guild_id, arma_servers.to_owned())) +} + +