From 2e86b07fa23a5366306fca38b203e59608462c17 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 14:20:00 +0200 Subject: [PATCH 01/36] feat: remove vmng --- vmng/Cargo.toml | 12 -------- vmng/src/main.rs | 74 ------------------------------------------------ 2 files changed, 86 deletions(-) delete mode 100644 vmng/Cargo.toml delete mode 100644 vmng/src/main.rs diff --git a/vmng/Cargo.toml b/vmng/Cargo.toml deleted file mode 100644 index e899390..0000000 --- a/vmng/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "vmng" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dotenv = "0.15.0" -reqwest = { version = "0.11.20", features = ["blocking", "json"] } -serde = { version = "1.0.188", features = ["serde_derive"] } -serde_derive = "1.0.188" diff --git a/vmng/src/main.rs b/vmng/src/main.rs deleted file mode 100644 index d280e83..0000000 --- a/vmng/src/main.rs +++ /dev/null @@ -1,74 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::env; - -#[derive(Serialize, Deserialize, Debug)] -struct Application { - approximate_guild_count: u64, -} - -fn main() { - let _ = match dotenv::dotenv() { - Ok(_) => println!("Loaded .env file"), - Err(_) => { - println!("No .env file found, using env vars"); - } - }; - - let url = "https://discord.com/api/applications/@me"; - let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); - - let client = reqwest::blocking::Client::new(); - - let response = client - .get(url) - .header("Authorization", format!("Bot {}", token)) - .send() - .unwrap(); - - let application: Application = response.json().unwrap(); - println!( - "Approximate guild count: {}", - application.approximate_guild_count - ); - - let shard_count = application.approximate_guild_count / 1000 + 1; - println!("Shard count: {}", shard_count); - - // 25% of the shards are used for preview - let preview_shard_count = shard_count / 4; - let prod_shard_count = shard_count - preview_shard_count; - println!("Preview shard count: {}", preview_shard_count); - println!("Production shard count: {}", prod_shard_count); - - // stop and remove old containers - std::process::Command::new("docker") - .args(&["stop", "xpbot-v8"]) - .status() - .expect("failed to execute process"); - - std::process::Command::new("docker") - .args(&["rm", "xpbot-v8"]) - .status() - .expect("failed to execute process"); - - std::process::Command::new("docker") - .args(&["stop", "xpbot-v8-preview"]) - .status() - .expect("failed to execute process"); - - std::process::Command::new("docker") - .args(&["rm", "xpbot-v8-preview"]) - .status() - .expect("failed to execute process"); - - // deploy ghcr packages raeys-v8 and raeys-v8-preview with docker - std::process::Command::new("docker") - .args(&["run", "-d", "--env .env", "--name xpbot-v8", "ghcr.io/xp-bot/raeys-v8:latest"]) - .status() - .expect("failed to execute process"); - - std::process::Command::new("docker") - .args(&["run", "-d", "--env .env", "--name xpbot-v8-preview", "ghcr.io/xp-bot/raeys-v8-preview:latest"]) - .status() - .expect("failed to execute process"); -} From 856f1d590f0720ce6d95f9c8a5cb336dbf217996 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 14:38:00 +0200 Subject: [PATCH 02/36] Update Cargo.toml --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 7a8102a..cad80e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "xp-bot", From 4456ec9ba9277dc1992a705bb65e506bde1bf067 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 15:05:23 +0200 Subject: [PATCH 03/36] fix: docker cd --- Cargo.toml | 1 - Dockerfile | 2 +- xp-bot/src/main.rs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cad80e9..7a8102a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [workspace] -resolver = "2" members = [ "xp-bot", diff --git a/Dockerfile b/Dockerfile index 11c9d7e..eb29a0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,4 +6,4 @@ COPY . . RUN cargo install --path xp-bot EXPOSE 80 -CMD ["cargo", "run", "--release"] \ No newline at end of file +CMD ["cargo", "run", "-p xp-bot", "--release"] \ No newline at end of file diff --git a/xp-bot/src/main.rs b/xp-bot/src/main.rs index ab0294c..f7d40d6 100644 --- a/xp-bot/src/main.rs +++ b/xp-bot/src/main.rs @@ -16,6 +16,7 @@ async fn main() { info!("No .env file found, using env vars"); } }; + env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); // client initialization From 652f460dbaa489f3092b358075e85e05f478fdb5 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 15:24:06 +0200 Subject: [PATCH 04/36] Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index eb29a0a..a7fc457 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:latest +FROM rust:1.71 WORKDIR /usr/src COPY . . @@ -6,4 +6,4 @@ COPY . . RUN cargo install --path xp-bot EXPOSE 80 -CMD ["cargo", "run", "-p xp-bot", "--release"] \ No newline at end of file +CMD ["cargo", "run", "--release"] \ No newline at end of file From 5a6436d2be34bacb2c42463b9eea1963b4f3f485 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 15:31:32 +0200 Subject: [PATCH 05/36] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index eb29a0a..11c9d7e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,4 +6,4 @@ COPY . . RUN cargo install --path xp-bot EXPOSE 80 -CMD ["cargo", "run", "-p xp-bot", "--release"] \ No newline at end of file +CMD ["cargo", "run", "--release"] \ No newline at end of file From 37d425476130045d391d58597965428fe7cc2b0d Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 16:07:14 +0200 Subject: [PATCH 06/36] Update Dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index a7fc457..978712d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,4 +6,5 @@ COPY . . RUN cargo install --path xp-bot EXPOSE 80 +RUN cd xp-bot CMD ["cargo", "run", "--release"] \ No newline at end of file From 21272908dbf80a42eba13be749d257a92e06cc03 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 1 Sep 2023 16:39:10 +0200 Subject: [PATCH 07/36] fix: docker deployment --- Dockerfile | 3 +-- xp-bot/src/main.rs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 978712d..11c9d7e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.71 +FROM rust:latest WORKDIR /usr/src COPY . . @@ -6,5 +6,4 @@ COPY . . RUN cargo install --path xp-bot EXPOSE 80 -RUN cd xp-bot CMD ["cargo", "run", "--release"] \ No newline at end of file diff --git a/xp-bot/src/main.rs b/xp-bot/src/main.rs index f7d40d6..29a0198 100644 --- a/xp-bot/src/main.rs +++ b/xp-bot/src/main.rs @@ -11,12 +11,12 @@ mod utils; #[tokio::main] async fn main() { let _ = match dotenv::dotenv() { - Ok(_) => info!("Loaded .env file"), + Ok(_) => println!("Loaded .env file"), Err(_) => { - info!("No .env file found, using env vars"); + println!("No .env file found, using env vars"); } }; - + env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); // client initialization From ae409498ca736be6d5e3955bda0fc8fbe64b5687 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Sun, 3 Sep 2023 18:59:00 +0200 Subject: [PATCH 08/36] feat: party command (#44) --- xp-bot/src/commands/games/mod.rs | 3 +- xp-bot/src/commands/games/party.rs | 230 +++++++++++++++++++++++++++++ xp-bot/src/commands/mod.rs | 1 + xp-bot/src/events/handler.rs | 4 + xp-bot/src/utils/topgg.rs | 2 +- xp-bot/src/utils/utils.rs | 17 +++ 6 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 xp-bot/src/commands/games/party.rs diff --git a/xp-bot/src/commands/games/mod.rs b/xp-bot/src/commands/games/mod.rs index 114b428..d2717ef 100644 --- a/xp-bot/src/commands/games/mod.rs +++ b/xp-bot/src/commands/games/mod.rs @@ -2,4 +2,5 @@ pub mod fish; pub mod roll; pub mod loot; pub mod daily; -pub mod trivia; \ No newline at end of file +pub mod trivia; +pub mod party; \ No newline at end of file diff --git a/xp-bot/src/commands/games/party.rs b/xp-bot/src/commands/games/party.rs new file mode 100644 index 0000000..066f21d --- /dev/null +++ b/xp-bot/src/commands/games/party.rs @@ -0,0 +1,230 @@ +use std::time::Duration; + +use serenity::{ + async_trait, + builder::{CreateApplicationCommand, CreateComponents}, + futures::StreamExt, + model::{ + self, + prelude::{ + application_command::ApplicationCommandInteraction, InteractionResponseType, UserId, + }, user::User, + }, + prelude::Context, +}; +use xp_db_connector::{guild::Guild, guild_member::GuildMember}; + +use crate::{ + commands::XpCommand, + utils::{ + colors, + utils::{calc_games_bulk, eligibility_helper, GameResult}, + }, +}; + +pub struct PartyCommand; + +#[async_trait] +impl XpCommand for PartyCommand { + fn name(&self) -> &'static str { + "party" + } + + fn register<'a>( + &self, + command: &'a mut CreateApplicationCommand, + ) -> &'a mut CreateApplicationCommand { + command + .name("party") + .description("Play games with a group of people.") + } + + async fn exec( + &self, + ctx: &Context, + command: &ApplicationCommandInteraction, + ) -> Result<(), Box> { + let guild = Guild::from_id(command.guild_id.unwrap().0).await?; + + if !guild.modules.games { + command + .create_interaction_response(ctx, |response| { + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| { + message.embed(|embed| { + embed.title("Games module disabled"); + embed.description( + format!("This module is disabled on this server. \ + An administrator can enable it [here](https://xp-bot.net/me/servers/{}/modules).", command.guild_id.unwrap().0).as_str(), + ); + embed.color(colors::red()) + }).ephemeral(true); + message + }) + }) + .await?; + + return Ok(()); + } + + if !eligibility_helper(command.user.id.0).await { + command + .create_interaction_response(ctx, |response| { + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| { + message.embed(|embed| { + embed.title("Vote required"); + embed.description( + "You need to vote for the bot to use this command. \ + You can vote [here](https://top.gg/bot/706935674800177193/vote).", + ); + embed.field("Don't want to vote?", "You can also get premium [here](https://xp-bot.net/premium).", false); + embed.color(colors::red()) + }).ephemeral(true); + message + }) + }) + .await?; + + return Ok(()); + } + + command + .create_interaction_response(ctx, |response| { + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| { + message.embed(|embed| { + embed.title("Join the party!"); + embed.description(format!( + "You need at least **2 people** to start a party and they need to join within **30 seconds**." + )); + embed.color(colors::blue()) + }); + + // components + message.components(|component| { + component.create_action_row(|action_row| { + action_row.create_button(|button| { + button + .label("Join") + .style(model::application::component::ButtonStyle::Primary) + .custom_id("join_party") + }); + action_row + }) + }); + message + }) + }) + .await?; + + let mut message = command.get_interaction_response(ctx).await?; + + let mut collector = message + .await_component_interactions(ctx) + .timeout(Duration::from_secs(30)) + .collect_limit(10) + .build(); + + let mut joined: Vec = Vec::new(); + + while let Some(interaction) = collector.next().await { + if !joined.contains(&interaction.user.id) { + joined.push(interaction.user.id); + + interaction + .create_interaction_response(ctx, |response| { + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| { + message.content(format!( + "**{}** joined the party!", + interaction.user.name + )); + message.ephemeral(true); + message + }) + }) + .await?; + } else { + interaction.defer(&ctx.http).await?; + } + } + + if joined.len() < 2 { + message + .edit(ctx, |message| { + message.embed(|embed| { + embed.title("Party cancelled"); + embed.description("Not enough people joined the party."); + embed.color(colors::red()) + }) + }) + .await?; + return Ok(()); + } + + let guild = Guild::from_id(command.guild_id.unwrap().0).await?; + + // calculate game xp for every participant + let mut games: Vec = Vec::new(); + let mut users: Vec = Vec::new(); + + for user_id in joined.clone() { + // cache users for result message + users.push(ctx.http.get_user(user_id.0).await?); + + let mut member = GuildMember::from_id(command.guild_id.unwrap().0, user_id.0).await?; + + let result = calc_games_bulk( + guild.values.rollXP, + guild.values.fishXP, + guild.values.lootXP, + ); + + member.xp += result.roll as u64 + result.fish as u64 + result.loot as u64; + + let _ = GuildMember::set_guild_member(command.guild_id.unwrap().0, user_id.0, member) + .await?; + + games.push(result); + } + + let winner = games + .iter() + .enumerate() + .max_by_key(|(_, game)| game.roll + game.fish + game.loot) + .unwrap() + .0; + + message + .edit(ctx, |message| { + message.set_components(CreateComponents::default()); + + message.embed(|embed| { + embed.title("Party results"); + embed.description(format!("{} people participated. **{}** won!", games.len(), users[winner].name)); + + for i in 0..joined.len() { + + embed.field( + format!("{}", users[i].name), + format!( + "**Roll**: {} xp\n**Fish**: {} xp\n**Loot**: {} xp", + games[i].roll, games[i].fish, games[i].loot + ), + false, + ); + } + + embed.color(colors::green()) + }) + }) + .await?; + + Ok(()) + } +} diff --git a/xp-bot/src/commands/mod.rs b/xp-bot/src/commands/mod.rs index 0973e9e..d15f073 100644 --- a/xp-bot/src/commands/mod.rs +++ b/xp-bot/src/commands/mod.rs @@ -32,4 +32,5 @@ pub const COMMANDS: &[&dyn XpCommand] = &[ &games::loot::LootCommand, &games::daily::DailyCommand, &games::trivia::TriviaCommand, + &games::party::PartyCommand, ]; diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index b8d4629..5ae2293 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -187,6 +187,10 @@ impl EventHandler for Handler { } async fn message(&self, ctx: Context, msg: Message) { + if msg.author.bot { + return (); + } + let user_id = msg.author.id.0; let guild_id = msg.guild_id.clone().unwrap().0; diff --git a/xp-bot/src/utils/topgg.rs b/xp-bot/src/utils/topgg.rs index f6f5f77..21e7bec 100644 --- a/xp-bot/src/utils/topgg.rs +++ b/xp-bot/src/utils/topgg.rs @@ -17,7 +17,7 @@ pub async fn check_user_vote(user_id: &u64) -> bool { let body = response.text().await.unwrap(); let json: serde_json::Value = serde_json::from_str(&body).unwrap(); - if json["voted"].as_bool().unwrap() { + if json["voted"].as_i64() == Some(1) { return true; } } diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index d285eaf..308f84b 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -1,3 +1,4 @@ +use rand::Rng; use serenity::{model::prelude::{ChannelId, RoleId}, builder::CreateMessage}; use xp_db_connector::{guild::Guild, user::User}; @@ -153,3 +154,19 @@ pub async fn send_level_up(guild: Guild, user_id: u64, current_level: i32, new_l }) .await; } + +pub struct GameResult { + pub roll: u32, + pub fish: u32, + pub loot: u32, +} + +pub fn calc_games_bulk(roll_xp: u32, fish_xp: u32, loot_xp: u32) -> GameResult { + let random_num = rand::thread_rng().gen_range(1..=6); + + GameResult { + roll: roll_xp * random_num, + fish: fish_xp, + loot: loot_xp, + } +} From c6f4d22cada7ac6e3160b18a14b1201ebf2bda0d Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Sun, 3 Sep 2023 19:08:55 +0200 Subject: [PATCH 09/36] feat: show xp until in /level if not reached (#70) --- xp-bot/src/commands/misc/level.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/xp-bot/src/commands/misc/level.rs b/xp-bot/src/commands/misc/level.rs index 498a3ae..526044e 100644 --- a/xp-bot/src/commands/misc/level.rs +++ b/xp-bot/src/commands/misc/level.rs @@ -61,15 +61,25 @@ impl XpCommand for LevelCommand { .unwrap() as i32; let required_xp = get_required_xp(level); + let mut need_until = String::new(); + if required_xp > guild_member.xp as usize { + need_until = format!( + "You still need **{} xp** to reach level **{}**.", + crate::utils::utils::format_number(required_xp as u64 - guild_member.xp), + level + ); + } + let _ = command.create_interaction_response(&ctx.http, |response| { response.interaction_response_data(|message| { message.embed(|embed: &mut CreateEmbed| { embed.title(format!("Level {}", level)).description(format!( - "You need **{} xp** to reach level **{}**.\n You currently have **{} xp** (**{}%**).", + "You need **{} xp** to reach level **{}**.\n You currently have **{} xp** (**{}%**).\n\n{}", crate::utils::utils::format_number(required_xp as u64), level, crate::utils::utils::format_number(guild_member.xp as u64), - (guild_member.xp as f32 / required_xp as f32 * 100.0).round() + (guild_member.xp as f32 / required_xp as f32 * 100.0).round(), + need_until )).color(colors::blue()) }) }) From 0f1116ff294533569027854398e7fe071379ce54 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 4 Sep 2023 13:38:54 +0200 Subject: [PATCH 10/36] Create assign.yml --- .github/workflows/assign.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/assign.yml diff --git a/.github/workflows/assign.yml b/.github/workflows/assign.yml new file mode 100644 index 0000000..629350d --- /dev/null +++ b/.github/workflows/assign.yml @@ -0,0 +1,19 @@ +name: Auto Assign +on: + issues: + types: [opened] + pull_request: + types: [opened] +jobs: + run: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: 'Auto-assign issue' + uses: pozil/auto-assign-issue@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + assignees: angelsflyinhell + numOfAssignee: 1 From 14d7217643060a038528a9235364bc3b958e88e3 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 4 Sep 2023 14:26:22 +0200 Subject: [PATCH 11/36] feat: game logic (#43) --- xp-bot/src/commands/games/fish.rs | 11 +++-- xp-bot/src/commands/games/loot.rs | 11 +++-- xp-bot/src/utils/utils.rs | 74 ++++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/xp-bot/src/commands/games/fish.rs b/xp-bot/src/commands/games/fish.rs index bc9d226..7698323 100644 --- a/xp-bot/src/commands/games/fish.rs +++ b/xp-bot/src/commands/games/fish.rs @@ -10,7 +10,7 @@ use crate::{ commands::XpCommand, utils::{ colors, - utils::{eligibility_helper, is_cooldowned, format_number}, + utils::{eligibility_helper, is_cooldowned, format_number, game_fish}, }, }; @@ -115,8 +115,10 @@ impl XpCommand for FishCommand { return Ok(()); } + let game_result = game_fish(guild.values.fishXP as i64); + // assign xp - guild_member.xp += guild.values.fishXP as u64; + guild_member.xp += game_result.xp as u64; // set new cooldown guild_member.timestamps.game_fish = Some(time_now as u64); @@ -134,8 +136,9 @@ impl XpCommand for FishCommand { .interaction_response_data(|message| { message.embed(|embed| { embed.description(format!( - ":fishing_pole_and_fish: | You caught a fish and gained **{}** xp!", - format_number(guild.values.fishXP as u64) + ":fishing_pole_and_fish: | You got **{}** xp for finding **{}**.", + format_number(game_result.xp as u64), + game_result.item )); embed.color(colors::green()) }) diff --git a/xp-bot/src/commands/games/loot.rs b/xp-bot/src/commands/games/loot.rs index c8fc1b3..9042298 100644 --- a/xp-bot/src/commands/games/loot.rs +++ b/xp-bot/src/commands/games/loot.rs @@ -10,7 +10,7 @@ use crate::{ commands::XpCommand, utils::{ colors, - utils::{eligibility_helper, format_number, is_cooldowned}, + utils::{eligibility_helper, format_number, game_loot, is_cooldowned}, }, }; @@ -117,8 +117,10 @@ impl XpCommand for LootCommand { return Ok(()); } + let game_result = game_loot(guild.values.lootXP as i64); + // assign xp - guild_member.xp += guild.values.lootXP as u64; + guild_member.xp += game_result.xp as u64; // set new cooldown guild_member.timestamps.game_loot = Some(time_now as u64); @@ -136,8 +138,9 @@ impl XpCommand for LootCommand { .interaction_response_data(|message| { message.embed(|embed| { embed.description(format!( - ":package: | You looted a crate and got **{}** xp!", - format_number(guild.values.lootXP as u64), + ":package: | You found **{}** crate and got **{}** xp!", + game_result.item, + format_number(game_result.xp as u64), )); embed.color(colors::green()) }) diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 308f84b..0f0a78a 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -166,7 +166,77 @@ pub fn calc_games_bulk(roll_xp: u32, fish_xp: u32, loot_xp: u32) -> GameResult { GameResult { roll: roll_xp * random_num, - fish: fish_xp, - loot: loot_xp, + fish: game_fish(fish_xp as i64).xp as u32, + loot: game_loot(loot_xp as i64).xp as u32, } } + +pub struct GameEventResult { + pub xp: i64, + pub item: String, +} + +pub fn game_loot(xp: i64) -> GameEventResult { + let random_num = rand::thread_rng().gen_range(1..=10); + let mut xp = xp; + + let item = match random_num { + 1 | 2 | 3 | 4 => "a common", + 5 | 6 | 7 => { + xp *= 2; + "a rare" + }, + 8 | 9 => { + xp *= 3; + "an epic" + }, + 10 => { + xp *= 4; + "a legendary" + }, + _ => "a common", + }; + + GameEventResult { + xp, + item: item.to_string(), + } +} + +pub fn game_fish(xp: i64) -> GameEventResult { + let random_num = rand::thread_rng().gen_range(1..=1000); + let mut xp = xp; + + let item = match random_num { + 1..=400 => { + xp = 0; + "an old shoe" + }, + 401..=700 => "a fish with a good personality", + 701..=900 => { + xp *= 3; + "an average-sized fish" + }, + 901..=998 => { + xp *= 4; + "a huge fish" + }, + 999 => { + xp *= 5; + "a humongous fish**. Like seriously... that's not gonna fit** " + }, + 1000 => { + xp *= 5; + "a bottle with a note! You can read it [here](https://pastebin.com/X3Fx81wN)" + }, + _ => { + xp = 0; + "an old shoe" + }, + }; + + GameEventResult { + xp, + item: item.to_string(), + } +} \ No newline at end of file From dc052fd1d647fc69f76895db07170e91922cccb0 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 4 Sep 2023 14:31:44 +0200 Subject: [PATCH 12/36] fix: remove button for expired party lobbies --- xp-bot/src/commands/games/party.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/xp-bot/src/commands/games/party.rs b/xp-bot/src/commands/games/party.rs index 066f21d..978e516 100644 --- a/xp-bot/src/commands/games/party.rs +++ b/xp-bot/src/commands/games/party.rs @@ -8,7 +8,8 @@ use serenity::{ self, prelude::{ application_command::ApplicationCommandInteraction, InteractionResponseType, UserId, - }, user::User, + }, + user::User, }, prelude::Context, }; @@ -157,6 +158,8 @@ impl XpCommand for PartyCommand { if joined.len() < 2 { message .edit(ctx, |message| { + message.set_components(CreateComponents::default()); + message.embed(|embed| { embed.title("Party cancelled"); embed.description("Not enough people joined the party."); @@ -206,10 +209,13 @@ impl XpCommand for PartyCommand { message.embed(|embed| { embed.title("Party results"); - embed.description(format!("{} people participated. **{}** won!", games.len(), users[winner].name)); + embed.description(format!( + "{} people participated. **{}** won!", + games.len(), + users[winner].name + )); for i in 0..joined.len() { - embed.field( format!("{}", users[i].name), format!( From ad7afd508ec92a06357e127150e914de9243108a Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 4 Sep 2023 14:35:37 +0200 Subject: [PATCH 13/36] feat: integrate new connector auth (#46) --- xp-db-connector/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xp-db-connector/src/lib.rs b/xp-db-connector/src/lib.rs index ae953a9..81987e1 100644 --- a/xp-db-connector/src/lib.rs +++ b/xp-db-connector/src/lib.rs @@ -19,7 +19,7 @@ where let response = client .get(format!("{}{}", base_url, url)) - .header("access", api_auth) + .header("Authorization", format!("Bearer {}", api_auth)) .send() .await? .json::() @@ -39,7 +39,7 @@ where let _ = client .post(format!("{}{}", base_url, url)) - .header("access", api_auth) + .header("Authorization", format!("Bearer {}", api_auth)) .json(&body) .send() .await?; @@ -58,7 +58,7 @@ where let _ = client .patch(format!("{}{}", base_url, url)) - .header("access", api_auth) + .header("Authorization", format!("Bearer {}", api_auth)) .json(&body) .send() .await?; @@ -74,7 +74,7 @@ async fn delete_json(url: String) -> Result<(), reqwest::Error> { let _ = client .delete(format!("{}{}", base_url, url)) - .header("access", api_auth) + .header("Authorization", format!("Bearer {}", api_auth)) .send() .await?; From e8a45308909998d7e68d9d7fadf50b48f5402daf Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 4 Sep 2023 14:39:29 +0200 Subject: [PATCH 14/36] feat: show last daily streak if broken (#31) --- xp-bot/src/commands/games/daily.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/xp-bot/src/commands/games/daily.rs b/xp-bot/src/commands/games/daily.rs index 22ec8fb..59458c9 100644 --- a/xp-bot/src/commands/games/daily.rs +++ b/xp-bot/src/commands/games/daily.rs @@ -121,19 +121,30 @@ impl XpCommand for DailyCommand { let daily_xp = 250; + let mut old_streak_msg = String::new(); // reset streak if last daily was claimed more than 48 hours ago - let streak = - if time_now - guild_member.timestamps.game_daily.unwrap_or(0) as i64 > 86400 * 1000 * 2 { - 1 - } else { - guild_member.streaks.game_daily.unwrap_or(0) + 1 - }; + let streak = if time_now - guild_member.timestamps.game_daily.unwrap_or(0) as i64 + > 86400 * 1000 * 2 + { + old_streak_msg = format!( + "Your old streak was **{}**.", + guild_member.streaks.game_daily.unwrap_or(0) + ); + 1 + } else { + guild_member.streaks.game_daily.unwrap_or(0) + 1 + }; guild_member.xp += daily_xp * streak; guild_member.timestamps.game_daily = Some(time_now as u64); guild_member.streaks.game_daily = Some(streak); - let _ = GuildMember::set_guild_member(command.guild_id.unwrap().0, command.user.id.0, guild_member).await?; + let _ = GuildMember::set_guild_member( + command.guild_id.unwrap().0, + command.user.id.0, + guild_member, + ) + .await?; command .create_interaction_response(ctx, |response| { @@ -142,9 +153,10 @@ impl XpCommand for DailyCommand { .interaction_response_data(|message| { message.embed(|embed| { embed.description(format!( - "You claimed **{}** xp. Your streak is now **{}**.", + "You claimed **{}** xp. Your streak is now **{}**.\n\n{}", format_number(daily_xp * streak), - streak + streak, + old_streak_msg )); embed.color(colors::green()) }) From 153b44bb23eb14fede1751623c1483316c7e63d3 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 4 Sep 2023 14:45:47 +0200 Subject: [PATCH 15/36] feat: gateway latency in about (#30) --- xp-bot/src/commands/misc/about.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xp-bot/src/commands/misc/about.rs b/xp-bot/src/commands/misc/about.rs index 4ac40d2..2829575 100644 --- a/xp-bot/src/commands/misc/about.rs +++ b/xp-bot/src/commands/misc/about.rs @@ -21,6 +21,10 @@ impl XpCommand for AboutCommand { } async fn exec(&self, ctx: &Context, command: &ApplicationCommandInteraction) -> Result<(), Box> { + let time_then = std::time::Instant::now(); + let _ = ctx.http.get_gateway().await?; + let latency = time_then.elapsed().as_millis(); + let _ = command.create_interaction_response(&ctx.http, |response| { response .kind(InteractionResponseType::ChannelMessageWithSource) @@ -32,7 +36,7 @@ impl XpCommand for AboutCommand { .field("Official Support Server", "[discord.gg](https://discord.xp-bot.net)", true) .field("Vote", "[top.gg](https://vote.xp-bot.net)", true) .field("Status", "[status](https://status.xp-bot.net)", true) - .footer(|footer| footer.text(format!("© 2020-2023 namespace.media - Shard {}", ctx.shard_id + 1))) + .footer(|footer| footer.text(format!("© 2020-2023 namespace.media - Shard {} - {}ms", ctx.shard_id + 1, latency))) .colour(colors::blue()) ).components( From 6414ff54e4c7567d1d5f553f0b1f5ad31c9126c5 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Wed, 6 Sep 2023 18:14:39 +0200 Subject: [PATCH 16/36] feat: update dockerfile structure --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 11c9d7e..c6f23eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM rust:latest WORKDIR /usr/src COPY . . -RUN cargo install --path xp-bot +RUN cargo build --release EXPOSE 80 -CMD ["cargo", "run", "--release"] \ No newline at end of file +CMD ["./target/release/xp-bot"] \ No newline at end of file From aa7c6a6c16d3712c3371f8ae6e90c986bc0dfc9c Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 8 Sep 2023 17:05:48 +0200 Subject: [PATCH 17/36] fix: daily limit does not work (#77) --- xp-bot/src/commands/games/daily.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xp-bot/src/commands/games/daily.rs b/xp-bot/src/commands/games/daily.rs index 59458c9..e18c066 100644 --- a/xp-bot/src/commands/games/daily.rs +++ b/xp-bot/src/commands/games/daily.rs @@ -135,7 +135,12 @@ impl XpCommand for DailyCommand { guild_member.streaks.game_daily.unwrap_or(0) + 1 }; - guild_member.xp += daily_xp * streak; + let member_xp = daily_xp * streak; + guild_member.xp += if member_xp > guild.values.maximumdailyxp as u64 { + guild.values.maximumdailyxp as u64 + } else { + member_xp + }; guild_member.timestamps.game_daily = Some(time_now as u64); guild_member.streaks.game_daily = Some(streak); From 145870f17aa7b9d5e85c1f9eb8a4bbe1e447a662 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 14:26:06 +0200 Subject: [PATCH 18/36] feat: handle level roles (#79) --- xp-bot/src/events/handler.rs | 76 ++++++++++++++--------- xp-bot/src/utils/utils.rs | 116 ++++++++++++++++++++++++++++++----- 2 files changed, 147 insertions(+), 45 deletions(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 5ae2293..153bafc 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -6,7 +6,7 @@ use serenity::{ }; use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; -use crate::{commands, utils::{colors, utils::{is_cooldowned, self, send_level_up}, math::calculate_level}}; +use crate::{commands, utils::{colors, utils::{is_cooldowned, self, send_level_up, handle_level_roles}, math::calculate_level}}; pub struct Handler; @@ -270,8 +270,12 @@ impl EventHandler for Handler { let current_level = calculate_level(&member.xp); let new_level = calculate_level(&(member.xp + xp as u64)); - if new_level > current_level && !member.settings.incognito.unwrap_or(false) { - send_level_up(guild.clone(), user_id, current_level, new_level, &ctx, msg.channel_id.0.clone(), &msg.author.name).await; + if new_level > current_level { + handle_level_roles(&guild.clone(), &user_id, &new_level, &ctx, msg.guild_id.clone().unwrap().0).await; + + if !member.settings.incognito.unwrap_or(false) { + send_level_up(guild.clone(), user_id, current_level, new_level, &ctx, msg.channel_id.0.clone(), &msg.author.name).await; + } } // add xp to user @@ -433,7 +437,7 @@ impl EventHandler for Handler { let current_level = calculate_level(&member.xp); let new_level = calculate_level(&(member.xp + xp as u64)); - if new_level > current_level && !member.settings.incognito.unwrap_or(false) { + if new_level > current_level { let username = ctx .http .get_user(user_id) @@ -442,14 +446,18 @@ impl EventHandler for Handler { .name .to_owned(); - send_level_up(guild, - user_id, - current_level, - new_level, - &ctx, - add_reaction.channel_id.0, - &username, - ).await; + handle_level_roles(&guild.clone(), &user_id, &new_level, &ctx, add_reaction.guild_id.unwrap().0).await; + + if !member.settings.incognito.unwrap_or(false) { + send_level_up(guild, + user_id, + current_level, + new_level, + &ctx, + add_reaction.channel_id.0, + &username, + ).await; + } } // add xp to user @@ -527,7 +535,7 @@ impl Handler { let current_level = calculate_level(&member.xp); let new_level = calculate_level(&(member.xp + xp as u64)); - if new_level > current_level && !member.settings.incognito.unwrap_or(false) { + if new_level > current_level { let username = ctx .http .get_user(left.user_id.0) @@ -536,14 +544,18 @@ impl Handler { .name .to_owned(); - send_level_up(guild.clone(), - left.user_id.0, - current_level, - new_level, - &ctx, - old.channel_id.unwrap().0, - &username, - ).await; + handle_level_roles(&guild.clone(), &left.user_id.0, &new_level, &ctx, old.guild_id.unwrap().0).await; + + if !member.settings.incognito.unwrap_or(false) { + send_level_up(guild.clone(), + left.user_id.0, + current_level, + new_level, + &ctx, + old.channel_id.unwrap().0, + &username, + ).await; + } } // send summary of voice time @@ -670,7 +682,7 @@ impl Handler { let current_level = calculate_level(&member.xp); let new_level = calculate_level(&(member.xp + xp as u64)); - if new_level > current_level && !member.settings.incognito.unwrap_or(false) { + if new_level > current_level { let username = ctx .http .get_user(moved.user_id.0) @@ -679,14 +691,18 @@ impl Handler { .name .to_owned(); - send_level_up(guild.clone(), - moved.user_id.0, - current_level, - new_level, - &ctx, - moved.channel_id.unwrap().0, - &username, - ).await; + handle_level_roles(&guild.clone(), &moved.user_id.0, &new_level, &ctx, moved.guild_id.unwrap().0).await; + + if !member.settings.incognito.unwrap_or(false) { + send_level_up(guild.clone(), + moved.user_id.0, + current_level, + new_level, + &ctx, + moved.channel_id.unwrap().0, + &username, + ).await; + } } // send summary of voice time diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 0f0a78a..3dadf02 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -1,8 +1,11 @@ use rand::Rng; -use serenity::{model::prelude::{ChannelId, RoleId}, builder::CreateMessage}; +use serenity::{ + builder::CreateMessage, + model::prelude::{ChannelId, RoleId}, +}; use xp_db_connector::{guild::Guild, user::User}; -use super::{topgg, colors}; +use super::{colors, topgg}; pub fn calculate_total_boost_percentage( guild: Guild, @@ -118,16 +121,29 @@ pub async fn eligibility_helper(user_id: u64) -> bool { } pub fn is_cooldowned(timestamp_now: u64, timestamp_then: u64, cooldown: u64) -> bool { - if timestamp_now - timestamp_then < cooldown { + if (timestamp_now as i64 - timestamp_then as i64) < cooldown as i64 { return true; } false } -pub async fn send_level_up(guild: Guild, user_id: u64, current_level: i32, new_level: i32, ctx: &serenity::client::Context, msg_channel_id: u64, msg_author_name: &String) { +pub async fn send_level_up( + guild: Guild, + user_id: u64, + current_level: i32, + new_level: i32, + ctx: &serenity::client::Context, + msg_channel_id: u64, + msg_author_name: &String, +) { let channel_id = if !guild.announce.current { - guild.logs.levelup.unwrap_or("".to_string()).parse::().unwrap() + guild + .logs + .levelup + .unwrap_or("".to_string()) + .parse::() + .unwrap() } else { msg_channel_id }; @@ -155,6 +171,76 @@ pub async fn send_level_up(guild: Guild, user_id: u64, current_level: i32, new_l .await; } +pub async fn handle_level_roles( + guild: &Guild, + user_id: &u64, + new_level: &i32, + ctx: &serenity::client::Context, + guild_id: u64, +) { + let roles = guild.levelroles.clone(); + let mut roles_to_add = roles + .iter() + .filter(|r| r.level <= *new_level) + .collect::>(); + + roles_to_add.sort_by(|a, b| b.level.cmp(&a.level)); + + let remove_reached_roles = guild.modules.removereachedlevelroles; + let single_rank_role = guild.modules.singlerankrole; + + if remove_reached_roles { + // the remove_reached_roles module removes levelroles, if the level of the user is lower than before and the levelrole is no longer within the levelrange + let roles_to_remove = roles + .iter() + .filter(|r| r.level < *new_level) + .collect::>(); + + for role in roles_to_remove { + let role_id = role.id.parse::().unwrap(); + let _ = ctx + .http + .remove_member_role( + guild_id, + *user_id, + role_id, + Some("Single Rank Role module is enabled."), + ) + .await; + } + } + + if single_rank_role { + // only add the highest level role + if let Some(role) = roles_to_add.first() { + let role_id = role.id.parse::().unwrap(); + let _ = ctx + .http + .add_member_role( + guild_id, + *user_id, + role_id, + Some("Single Rank Role module is enabled."), + ) + .await; + } + } else { + // add all roles + for role in roles_to_add { + let role_id = role.id.parse::().unwrap(); + let _ = ctx + .http + .add_member_role( + guild_id, + *user_id, + role_id, + Some("Single Rank Role module is disabled."), + ) + .await; + } + } +} + pub struct GameResult { pub roll: u32, pub fish: u32, @@ -185,15 +271,15 @@ pub fn game_loot(xp: i64) -> GameEventResult { 5 | 6 | 7 => { xp *= 2; "a rare" - }, + } 8 | 9 => { xp *= 3; "an epic" - }, + } 10 => { xp *= 4; "a legendary" - }, + } _ => "a common", }; @@ -211,32 +297,32 @@ pub fn game_fish(xp: i64) -> GameEventResult { 1..=400 => { xp = 0; "an old shoe" - }, + } 401..=700 => "a fish with a good personality", 701..=900 => { xp *= 3; "an average-sized fish" - }, + } 901..=998 => { xp *= 4; "a huge fish" - }, + } 999 => { xp *= 5; "a humongous fish**. Like seriously... that's not gonna fit** " - }, + } 1000 => { xp *= 5; "a bottle with a note! You can read it [here](https://pastebin.com/X3Fx81wN)" - }, + } _ => { xp = 0; "an old shoe" - }, + } }; GameEventResult { xp, item: item.to_string(), } -} \ No newline at end of file +} From a41c5f65bc64923eefae6af734974eb257257530 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 14:27:00 +0200 Subject: [PATCH 19/36] chore: remove auto assign workflow --- .github/workflows/assign.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/assign.yml diff --git a/.github/workflows/assign.yml b/.github/workflows/assign.yml deleted file mode 100644 index 629350d..0000000 --- a/.github/workflows/assign.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Auto Assign -on: - issues: - types: [opened] - pull_request: - types: [opened] -jobs: - run: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - name: 'Auto-assign issue' - uses: pozil/auto-assign-issue@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - assignees: angelsflyinhell - numOfAssignee: 1 From 800ed7cc8a597772072947e51c4350d0265d07a2 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 15:10:24 +0200 Subject: [PATCH 20/36] feat: comply with xpcs set xp specs (#82) --- xp-bot/src/events/handler.rs | 10 ++++++++-- xp-bot/src/utils/utils.rs | 37 +++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 153bafc..a99634a 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -6,7 +6,7 @@ use serenity::{ }; use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; -use crate::{commands, utils::{colors, utils::{is_cooldowned, self, send_level_up, handle_level_roles}, math::calculate_level}}; +use crate::{commands, utils::{colors, utils::{is_cooldowned, self, send_level_up, handle_level_roles, conform_xpc}, math::calculate_level}}; pub struct Handler; @@ -159,7 +159,9 @@ impl EventHandler for Handler { // extract user id from experimental_extract let user_id = experimental_extract.split("custom_id: \"reset_user_xp_input_").collect::>()[1].split("\"").collect::>()[0].parse::().unwrap(); - let guild_member = GuildMember::from_id(command.guild_id.unwrap().0, user_id).await.unwrap(); + let mut guild_member = GuildMember::from_id(command.guild_id.unwrap().0, user_id).await.unwrap(); + + guild_member = conform_xpc(guild_member, &ctx, &command.guild_id.unwrap().0, &user_id).await; let res = GuildMember::set_xp(command.guild_id.unwrap().0, user_id, &0, &guild_member).await.unwrap(); if res.is_err() { @@ -284,6 +286,7 @@ impl EventHandler for Handler { // set new cooldown member.timestamps.message_cooldown = Some(timestamp as u64); + member = conform_xpc(member, &ctx, &guild_id, &msg.author.id.0).await; // update database let _ = GuildMember::set_xp(guild_id, user_id, &member.xp, &member).await; } @@ -462,6 +465,7 @@ impl EventHandler for Handler { // add xp to user member.xp += xp as u64; + member = conform_xpc(member, &ctx, &guild_id, &add_reaction.user_id.unwrap().0).await; // update database let _ = GuildMember::set_xp(guild_id, user_id, &member.xp, &member).await; @@ -621,6 +625,7 @@ impl Handler { // add xp to user member.xp += xp as u64; + member = conform_xpc(member, &ctx, &guild_id.0, &left.user_id.0).await; // update database let _ = GuildMember::set_xp(guild_id.0, left.user_id.0, &member.xp, &member).await; @@ -768,6 +773,7 @@ impl Handler { // add xp to user member.xp += xp as u64; + member = conform_xpc(member, &ctx, &guild_id.0, &moved.user_id.0).await; // update database let _ = GuildMember::set_xp(guild_id.0, moved.user_id.0, &member.xp, &member).await; diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 3dadf02..35cd7d1 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -3,7 +3,7 @@ use serenity::{ builder::CreateMessage, model::prelude::{ChannelId, RoleId}, }; -use xp_db_connector::{guild::Guild, user::User}; +use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; use super::{colors, topgg}; @@ -326,3 +326,38 @@ pub fn game_fish(xp: i64) -> GameEventResult { item: item.to_string(), } } + +pub async fn conform_xpc( + mut member: GuildMember, + ctx: &serenity::client::Context, + guild_id: &u64, + user_id: &u64, +) -> GuildMember { + member.userData.avatar = Some( + ctx.http + .get_member(guild_id.to_owned(), user_id.to_owned()) + .await + .unwrap() + .avatar + .unwrap(), + ); + member.userData.banner = Some( + ctx.http + .get_member(guild_id.to_owned(), user_id.to_owned()) + .await + .unwrap() + .user + .banner + .unwrap(), + ); + member.userData.username = Some( + ctx.http + .get_member(guild_id.to_owned(), user_id.to_owned()) + .await + .unwrap() + .user + .name + .clone(), + ); + member +} From 8a60000471a8702a15e0887e3ca60aff5e6e0457 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 16:37:42 +0200 Subject: [PATCH 21/36] feat: XP welcome message (#9) --- xp-bot/src/events/handler.rs | 74 +++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index a99634a..7154c14 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -1,7 +1,7 @@ use log::{error, info}; use serenity::{ async_trait, - model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId}, voice::VoiceState}, + model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, GuildChannel, component::ButtonStyle, ReactionType}, voice::VoiceState, channel}, prelude::{Context, EventHandler}, }; use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; @@ -188,6 +188,78 @@ impl EventHandler for Handler { } } + // XP welcome message when it gets invited to a server + async fn guild_create(&self, ctx: Context, guild: serenity::model::guild::Guild, is_new: bool) { + if !is_new { + return (); + } + + // get first text channel and send a message + let channels = guild.channels; + + let mut channel_id = 0; + for channel in channels { + let channel_type = channel.1; + + match channel_type.guild() { + Some(channel) => { + if channel.kind == serenity::model::channel::ChannelType::Text { + channel_id = channel.id.0; + break; + } + } + None => {} + }; + }; + + ChannelId(channel_id).send_message(&ctx.http, |message| { + message.embed(|embed| { + embed.title("Welcome to XP 👋"); + embed.description("We are happy, that you chose XP for your server!\nXP is a leveling bot, that allows you to reward your members for their activity.\nIf you need help, feel free to join our [support server](https://discord.xp-bot.net)!"); + embed.field( + "Read our tutorials", + format!("- {}\n- {}\n- {}\n- {}", + "[Roles & Boosts](https://xp-bot.net/blog/guide_roles_and_boosts_1662020313458)", + "[Announcements](https://xp-bot.net/blog/guide_announcements_1660342055312)", + "[Values](https://xp-bot.net/blog/guide_values_1656883362214)", + "[XP, Moderation & Game Modules](https://xp-bot.net/blog/guide_xp__game_modules_1655300944128)" + ), false + ); + embed.color(colors::blue()); + embed + }); + message.components( + |components| components + .create_action_row(|action_row| action_row + .create_button(|button| button + .label("Server Dashboard") + .style(ButtonStyle::Link) + .emoji(ReactionType::Unicode("🛠️".to_string())) + .url(format!("https://xp-bot.net/servers/{}", &guild.id.to_string())) + ) + .create_button(|button| button + .label("Account Settings") + .style(ButtonStyle::Link) + .emoji(ReactionType::Unicode("🙋".to_string())) + .url("https://xp-bot.net/me") + ) + .create_button(|button| button + .label("Premium") + .style(ButtonStyle::Link) + .emoji(ReactionType::Unicode("👑".to_string())) + .url("https://xp-bot.net/premium") + ) + .create_button(|button| button + .label("Privacy Policy") + .style(ButtonStyle::Link) + .emoji(ReactionType::Unicode("🔖".to_string())) + .url("https://xp-bot.net/legal/privacy") + ) + ) + ) + }).await.unwrap(); + } + async fn message(&self, ctx: Context, msg: Message) { if msg.author.bot { return (); From 882eeb78867aeed45a09215246343dc3ca362959 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 16:39:10 +0200 Subject: [PATCH 22/36] fix: import warnings in handler.rs --- xp-bot/src/events/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 7154c14..19df61a 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -1,7 +1,7 @@ use log::{error, info}; use serenity::{ async_trait, - model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, GuildChannel, component::ButtonStyle, ReactionType}, voice::VoiceState, channel}, + model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, component::ButtonStyle, ReactionType}, voice::VoiceState}, prelude::{Context, EventHandler}, }; use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; From 93db962f2425a407c3c28338656b107ddeec7884 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 17:01:19 +0200 Subject: [PATCH 23/36] feat: implement role check for admin commands --- xp-bot/src/commands/admin/add.rs | 8 ++++++-- xp-bot/src/commands/admin/remove.rs | 8 ++++++-- xp-bot/src/commands/admin/set.rs | 8 ++++++-- xp-bot/src/commands/admin/setlevel.rs | 9 +++++++-- xp-bot/src/utils/utils.rs | 15 ++++++++++----- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/xp-bot/src/commands/admin/add.rs b/xp-bot/src/commands/admin/add.rs index 0f012f6..d799efa 100644 --- a/xp-bot/src/commands/admin/add.rs +++ b/xp-bot/src/commands/admin/add.rs @@ -10,9 +10,9 @@ use serenity::{ }, prelude::Context, }; -use xp_db_connector::guild_member::GuildMember; +use xp_db_connector::{guild_member::GuildMember, guild::Guild}; -use crate::{commands::XpCommand, utils::{colors, utils::format_number}}; +use crate::{commands::XpCommand, utils::{colors, utils::{format_number, handle_level_roles}, math::calculate_level}}; pub struct AddCommand; @@ -80,6 +80,10 @@ impl XpCommand for AddCommand { let new_amount = guild_member.xp + amount; + let guild = Guild::from_id(guild_id).await?; + let new_level = calculate_level(&new_amount); + handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + let _ = GuildMember::set_xp(guild_id, user, &new_amount, &guild_member).await?; command diff --git a/xp-bot/src/commands/admin/remove.rs b/xp-bot/src/commands/admin/remove.rs index bf578c3..1261104 100644 --- a/xp-bot/src/commands/admin/remove.rs +++ b/xp-bot/src/commands/admin/remove.rs @@ -10,9 +10,9 @@ use serenity::{ }, prelude::Context, }; -use xp_db_connector::guild_member::GuildMember; +use xp_db_connector::{guild_member::GuildMember, guild::Guild}; -use crate::{commands::XpCommand, utils::{colors, utils::format_number}}; +use crate::{commands::XpCommand, utils::{colors, utils::{format_number, handle_level_roles}, math::calculate_level}}; pub struct RemoveCommand; @@ -79,6 +79,10 @@ impl XpCommand for RemoveCommand { let new_amount = guild_member.xp - amount; + let guild = Guild::from_id(guild_id).await?; + let new_level = calculate_level(&new_amount); + handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + let _ = GuildMember::set_xp(guild_id, user, &new_amount, &guild_member).await?; command diff --git a/xp-bot/src/commands/admin/set.rs b/xp-bot/src/commands/admin/set.rs index 126f789..ce51758 100644 --- a/xp-bot/src/commands/admin/set.rs +++ b/xp-bot/src/commands/admin/set.rs @@ -10,9 +10,9 @@ use serenity::{ }, prelude::Context, }; -use xp_db_connector::guild_member::GuildMember; +use xp_db_connector::{guild_member::GuildMember, guild::Guild}; -use crate::{commands::XpCommand, utils::{colors, utils::format_number}}; +use crate::{commands::XpCommand, utils::{colors, utils::{format_number, handle_level_roles}, math::calculate_level}}; pub struct SetCommand; @@ -78,6 +78,10 @@ impl XpCommand for SetCommand { let guild_member = GuildMember::from_id(guild_id, user).await?; + let guild = Guild::from_id(guild_id).await?; + let new_level = calculate_level(&amount); + handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + let _ = GuildMember::set_xp(guild_id, user, &amount, &guild_member).await?; command diff --git a/xp-bot/src/commands/admin/setlevel.rs b/xp-bot/src/commands/admin/setlevel.rs index 0234505..762646b 100644 --- a/xp-bot/src/commands/admin/setlevel.rs +++ b/xp-bot/src/commands/admin/setlevel.rs @@ -10,11 +10,11 @@ use serenity::{ }, prelude::Context, }; -use xp_db_connector::guild_member::GuildMember; +use xp_db_connector::{guild_member::GuildMember, guild::Guild}; use crate::{ commands::XpCommand, - utils::{colors, math::get_required_xp}, + utils::{colors, math::{get_required_xp, calculate_level}, utils::handle_level_roles}, }; pub struct SetLevelCommand; @@ -82,6 +82,11 @@ impl XpCommand for SetLevelCommand { let required_xp = get_required_xp(level as i32); let guild_member = GuildMember::from_id(command.guild_id.unwrap().into(), user_id).await?; + + let guild = Guild::from_id(command.guild_id.unwrap().into()).await?; + let new_level = calculate_level(&(required_xp as u64)); + handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + let _ = GuildMember::set_xp( command.guild_id.unwrap().into(), user_id, diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 35cd7d1..8786f14 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -186,6 +186,8 @@ pub async fn handle_level_roles( roles_to_add.sort_by(|a, b| b.level.cmp(&a.level)); + log::info!("Roles to add: {:?}", roles_to_add); + let remove_reached_roles = guild.modules.removereachedlevelroles; let single_rank_role = guild.modules.singlerankrole; @@ -206,7 +208,8 @@ pub async fn handle_level_roles( role_id, Some("Single Rank Role module is enabled."), ) - .await; + .await + .unwrap(); } } @@ -222,7 +225,8 @@ pub async fn handle_level_roles( role_id, Some("Single Rank Role module is enabled."), ) - .await; + .await + .unwrap(); } } else { // add all roles @@ -236,7 +240,8 @@ pub async fn handle_level_roles( role_id, Some("Single Rank Role module is disabled."), ) - .await; + .await + .unwrap(); } } } @@ -339,7 +344,7 @@ pub async fn conform_xpc( .await .unwrap() .avatar - .unwrap(), + .expect(format!("User {} has no avatar.", user_id).as_str()), ); member.userData.banner = Some( ctx.http @@ -348,7 +353,7 @@ pub async fn conform_xpc( .unwrap() .user .banner - .unwrap(), + .expect(format!("User {} has no banner.", user_id).as_str()), ); member.userData.username = Some( ctx.http From 12d8c9c1b71a64edef2ee558790cb56d93fa4e05 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Mon, 11 Sep 2023 17:40:43 +0200 Subject: [PATCH 24/36] fix: user identification for role rewards --- xp-bot/src/commands/admin/add.rs | 4 +++- xp-bot/src/commands/admin/remove.rs | 3 ++- xp-bot/src/commands/admin/set.rs | 3 ++- xp-bot/src/commands/admin/setlevel.rs | 3 ++- xp-bot/src/utils/utils.rs | 4 ++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/xp-bot/src/commands/admin/add.rs b/xp-bot/src/commands/admin/add.rs index d799efa..e21043c 100644 --- a/xp-bot/src/commands/admin/add.rs +++ b/xp-bot/src/commands/admin/add.rs @@ -82,7 +82,6 @@ impl XpCommand for AddCommand { let guild = Guild::from_id(guild_id).await?; let new_level = calculate_level(&new_amount); - handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; let _ = GuildMember::set_xp(guild_id, user, &new_amount, &guild_member).await?; @@ -104,6 +103,9 @@ impl XpCommand for AddCommand { }) .await?; + handle_level_roles(&guild, &user, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + + Ok(()) } } diff --git a/xp-bot/src/commands/admin/remove.rs b/xp-bot/src/commands/admin/remove.rs index 1261104..2b3736d 100644 --- a/xp-bot/src/commands/admin/remove.rs +++ b/xp-bot/src/commands/admin/remove.rs @@ -81,7 +81,6 @@ impl XpCommand for RemoveCommand { let guild = Guild::from_id(guild_id).await?; let new_level = calculate_level(&new_amount); - handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; let _ = GuildMember::set_xp(guild_id, user, &new_amount, &guild_member).await?; @@ -103,6 +102,8 @@ impl XpCommand for RemoveCommand { }) .await?; + handle_level_roles(&guild, &user, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + Ok(()) } } diff --git a/xp-bot/src/commands/admin/set.rs b/xp-bot/src/commands/admin/set.rs index ce51758..8b6a48c 100644 --- a/xp-bot/src/commands/admin/set.rs +++ b/xp-bot/src/commands/admin/set.rs @@ -80,7 +80,6 @@ impl XpCommand for SetCommand { let guild = Guild::from_id(guild_id).await?; let new_level = calculate_level(&amount); - handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; let _ = GuildMember::set_xp(guild_id, user, &amount, &guild_member).await?; @@ -102,6 +101,8 @@ impl XpCommand for SetCommand { }) .await?; + handle_level_roles(&guild, &user, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + Ok(()) } } diff --git a/xp-bot/src/commands/admin/setlevel.rs b/xp-bot/src/commands/admin/setlevel.rs index 762646b..14f4573 100644 --- a/xp-bot/src/commands/admin/setlevel.rs +++ b/xp-bot/src/commands/admin/setlevel.rs @@ -85,7 +85,6 @@ impl XpCommand for SetLevelCommand { let guild = Guild::from_id(command.guild_id.unwrap().into()).await?; let new_level = calculate_level(&(required_xp as u64)); - handle_level_roles(&guild, &command.user.id.0, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; let _ = GuildMember::set_xp( command.guild_id.unwrap().into(), @@ -114,6 +113,8 @@ impl XpCommand for SetLevelCommand { }) .await; + handle_level_roles(&guild, &user_id, &new_level, &ctx, command.guild_id.clone().unwrap().0).await; + Ok(()) } } diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 8786f14..11e42bb 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -191,7 +191,7 @@ pub async fn handle_level_roles( let remove_reached_roles = guild.modules.removereachedlevelroles; let single_rank_role = guild.modules.singlerankrole; - if remove_reached_roles { + if remove_reached_roles || single_rank_role { // the remove_reached_roles module removes levelroles, if the level of the user is lower than before and the levelrole is no longer within the levelrange let roles_to_remove = roles .iter() @@ -206,7 +206,7 @@ pub async fn handle_level_roles( guild_id, *user_id, role_id, - Some("Single Rank Role module is enabled."), + Some("Removed reached roles module is enabled."), ) .await .unwrap(); From f7b9d2c748afd84440e2c7193b122ae6224f4503 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Tue, 12 Sep 2023 16:15:18 +0200 Subject: [PATCH 25/36] fix(vtm): invalidate timestamps after session ends (#75) --- xp-bot/src/events/handler.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 19df61a..5e1afde 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -575,7 +575,7 @@ impl Handler { pub async fn voice_leave(ctx: Context, guild_id: GuildId, old: Option, left: VoiceState) { let timestamp = chrono::Utc::now().timestamp() * 1000; - let user = User::from_id(left.user_id.0).await.unwrap(); + let mut user = User::from_id(left.user_id.0).await.unwrap(); let mut member = GuildMember::from_id(guild_id.0, left.user_id.0).await.unwrap(); let guild = Guild::from_id(guild_id.0).await.unwrap(); let log_channel_id = guild.clone().logs.voicetime; @@ -701,6 +701,10 @@ impl Handler { // update database let _ = GuildMember::set_xp(guild_id.0, left.user_id.0, &member.xp, &member).await; + + // invalidate timestamp + user.timestamps.join_voicechat = None; + let _ = User::set(left.user_id.0, user).await; } /* @@ -724,7 +728,7 @@ impl Handler { if moved.channel_id.unwrap().0 == afk_channel_id.unwrap().0 { let timestamp = chrono::Utc::now().timestamp() * 1000; - let user = User::from_id(moved.user_id.0).await.unwrap(); + let mut user = User::from_id(moved.user_id.0).await.unwrap(); let mut member = GuildMember::from_id(guild_id.0, moved.user_id.0).await.unwrap(); let guild = Guild::from_id(guild_id.0).await.unwrap(); let log_channel_id = guild.clone().logs.voicetime; @@ -849,6 +853,10 @@ impl Handler { // update database let _ = GuildMember::set_xp(guild_id.0, moved.user_id.0, &member.xp, &member).await; + + // invalidate timestamp + user.timestamps.join_voicechat = None; + let _ = User::set(moved.user_id.0, user).await; } // check if moved from voicechat is the guilds afk channel From 76f1d28eeb26728d0d331b875e11248e4ea52498 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Tue, 12 Sep 2023 16:22:25 +0200 Subject: [PATCH 26/36] fix(vtm): ignored voicechats no longer add to user xp (#88) --- xp-bot/src/events/handler.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 5e1afde..9e5c2d4 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -566,6 +566,11 @@ impl Handler { return (); } + // check if voice channel is ignored + if guild.ignored.channels.unwrap().contains(&joined.channel_id.unwrap().0.to_string()) { + return (); + } + // set new timestamp user.timestamps.join_voicechat = Some(timestamp as u64); @@ -585,6 +590,11 @@ impl Handler { return (); } + // check if voice channel is ignored + if guild.clone().ignored.channels.unwrap().contains(&left.channel_id.unwrap().0.to_string()) { + return (); + } + // calculate time in voice chat let last_timestamp = user.timestamps.join_voicechat.unwrap_or(0); let time_in_voicechat = (timestamp - last_timestamp as i64 - guild.values.voicejoincooldown as i64 * 1000) / 1000; @@ -726,11 +736,13 @@ impl Handler { }, }; - if moved.channel_id.unwrap().0 == afk_channel_id.unwrap().0 { + let guild = Guild::from_id(guild_id.0).await.unwrap(); + + // check if moved to voicechat is the guilds afk channel or ignore channel + if moved.channel_id.unwrap().0 == afk_channel_id.unwrap().0 || guild.clone().ignored.channels.unwrap().contains(&moved.channel_id.unwrap().0.to_string()) { let timestamp = chrono::Utc::now().timestamp() * 1000; let mut user = User::from_id(moved.user_id.0).await.unwrap(); let mut member = GuildMember::from_id(guild_id.0, moved.user_id.0).await.unwrap(); - let guild = Guild::from_id(guild_id.0).await.unwrap(); let log_channel_id = guild.clone().logs.voicetime; // check if voice module is enabled @@ -738,6 +750,11 @@ impl Handler { return (); } + // check if voice channel is ignored + if guild.clone().ignored.channels.unwrap().contains(&moved.channel_id.unwrap().0.to_string()) { + return (); + } + // calculate time in voice chat let last_timestamp = user.timestamps.join_voicechat.unwrap_or(0); let time_in_voicechat = (timestamp - last_timestamp as i64 - guild.values.voicejoincooldown as i64 * 1000) / 1000; @@ -859,16 +876,20 @@ impl Handler { let _ = User::set(moved.user_id.0, user).await; } - // check if moved from voicechat is the guilds afk channel - if moved.channel_id.unwrap().0 == afk_channel_id.unwrap().0 { + // check if moved from voicechat is the guilds afk channel or ignore channel + if moved.channel_id.unwrap().0 == afk_channel_id.unwrap().0 || guild.clone().ignored.channels.unwrap().contains(&moved.channel_id.unwrap().0.to_string()) { let timestamp = chrono::Utc::now().timestamp() * 1000; let mut user = User::from_id(moved.user_id.0).await.unwrap(); - let guild = Guild::from_id(guild_id.0).await.unwrap(); // check if voice module is enabled if !guild.modules.voicexp { return (); } + + // check if voice channel is ignored + if guild.clone().ignored.channels.unwrap().contains(&moved.channel_id.unwrap().0.to_string()) { + return (); + } // set new timestamp user.timestamps.join_voicechat = Some(timestamp as u64); From 981ad56eef02f2dab0874c9e4759bc0b0ff71043 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Tue, 12 Sep 2023 16:35:43 +0200 Subject: [PATCH 27/36] fix: false displayment of daily reward (#98) --- xp-bot/src/commands/games/daily.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xp-bot/src/commands/games/daily.rs b/xp-bot/src/commands/games/daily.rs index e18c066..9a3b4c5 100644 --- a/xp-bot/src/commands/games/daily.rs +++ b/xp-bot/src/commands/games/daily.rs @@ -136,11 +136,13 @@ impl XpCommand for DailyCommand { }; let member_xp = daily_xp * streak; - guild_member.xp += if member_xp > guild.values.maximumdailyxp as u64 { + let xp_to_add = if member_xp > guild.values.maximumdailyxp as u64 { guild.values.maximumdailyxp as u64 } else { member_xp }; + guild_member.xp += xp_to_add; + guild_member.timestamps.game_daily = Some(time_now as u64); guild_member.streaks.game_daily = Some(streak); @@ -159,7 +161,7 @@ impl XpCommand for DailyCommand { message.embed(|embed| { embed.description(format!( "You claimed **{}** xp. Your streak is now **{}**.\n\n{}", - format_number(daily_xp * streak), + format_number(xp_to_add), streak, old_streak_msg )); From 8be7b5df765881012f7fd6cfd12c520811262598 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Tue, 12 Sep 2023 17:08:00 +0200 Subject: [PATCH 28/36] feat: autorole support (#90) --- xp-bot/src/events/handler.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 9e5c2d4..6b26ab4 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -1,7 +1,7 @@ use log::{error, info}; use serenity::{ async_trait, - model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, component::ButtonStyle, ReactionType}, voice::VoiceState}, + model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, component::ButtonStyle, ReactionType, Member, RoleId}, voice::VoiceState}, prelude::{Context, EventHandler}, }; use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; @@ -260,6 +260,24 @@ impl EventHandler for Handler { }).await.unwrap(); } + async fn guild_member_addition(&self, ctx: Context, mut new_member: Member) { + let guild = Guild::from_id(new_member.guild_id.0).await.unwrap(); + + // get role that is assigned to level -1 + let autorole = guild.levelroles.iter().find(|role| role.level == -1); + + if autorole.is_none() { + return (); + } + + let autorole = autorole.unwrap(); + + log::info!("Assigning autorole {} to {}", autorole.id, new_member.user.name); + + // assign autorole + let _ = new_member.add_role(&ctx.http, RoleId(autorole.id.parse::().unwrap())).await; + } + async fn message(&self, ctx: Context, msg: Message) { if msg.author.bot { return (); From 204c0ee3418c6468d7f966f3684b445643baa0d0 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 15 Sep 2023 15:47:07 +0200 Subject: [PATCH 29/36] fix: vote-free servers not working (#102) --- xp-bot/src/commands/games/daily.rs | 2 +- xp-bot/src/commands/games/fish.rs | 2 +- xp-bot/src/commands/games/loot.rs | 2 +- xp-bot/src/commands/games/party.rs | 2 +- xp-bot/src/commands/games/roll.rs | 2 +- xp-bot/src/commands/games/trivia.rs | 2 +- xp-bot/src/utils/utils.rs | 7 ++++++- xp-db-connector/src/guild.rs | 19 +++++++++++++++++++ 8 files changed, 31 insertions(+), 7 deletions(-) diff --git a/xp-bot/src/commands/games/daily.rs b/xp-bot/src/commands/games/daily.rs index 9a3b4c5..b698e7b 100644 --- a/xp-bot/src/commands/games/daily.rs +++ b/xp-bot/src/commands/games/daily.rs @@ -60,7 +60,7 @@ impl XpCommand for DailyCommand { return Ok(()); } - if !eligibility_helper(command.user.id.0).await { + if !eligibility_helper(command.user.id.0, &command.guild_id.unwrap().0).await { command .create_interaction_response(ctx, |response| { response diff --git a/xp-bot/src/commands/games/fish.rs b/xp-bot/src/commands/games/fish.rs index 7698323..b24e411 100644 --- a/xp-bot/src/commands/games/fish.rs +++ b/xp-bot/src/commands/games/fish.rs @@ -58,7 +58,7 @@ impl XpCommand for FishCommand { return Ok(()); } - if !eligibility_helper(command.user.id.0).await { + if !eligibility_helper(command.user.id.0, &command.guild_id.unwrap().0).await { command .create_interaction_response(ctx, |response| { response diff --git a/xp-bot/src/commands/games/loot.rs b/xp-bot/src/commands/games/loot.rs index 9042298..526c707 100644 --- a/xp-bot/src/commands/games/loot.rs +++ b/xp-bot/src/commands/games/loot.rs @@ -58,7 +58,7 @@ impl XpCommand for LootCommand { return Ok(()); } - if !eligibility_helper(command.user.id.0).await { + if !eligibility_helper(command.user.id.0, &command.guild_id.unwrap().0).await { command .create_interaction_response(ctx, |response| { response diff --git a/xp-bot/src/commands/games/party.rs b/xp-bot/src/commands/games/party.rs index 978e516..c266744 100644 --- a/xp-bot/src/commands/games/party.rs +++ b/xp-bot/src/commands/games/party.rs @@ -69,7 +69,7 @@ impl XpCommand for PartyCommand { return Ok(()); } - if !eligibility_helper(command.user.id.0).await { + if !eligibility_helper(command.user.id.0, &command.guild_id.unwrap().0).await { command .create_interaction_response(ctx, |response| { response diff --git a/xp-bot/src/commands/games/roll.rs b/xp-bot/src/commands/games/roll.rs index 0606665..f6667b1 100644 --- a/xp-bot/src/commands/games/roll.rs +++ b/xp-bot/src/commands/games/roll.rs @@ -61,7 +61,7 @@ impl XpCommand for RollCommand { return Ok(()); } - if !eligibility_helper(command.user.id.0).await { + if !eligibility_helper(command.user.id.0, &command.guild_id.unwrap().0).await { command .create_interaction_response(ctx, |response| { response diff --git a/xp-bot/src/commands/games/trivia.rs b/xp-bot/src/commands/games/trivia.rs index 49857b8..f76907b 100644 --- a/xp-bot/src/commands/games/trivia.rs +++ b/xp-bot/src/commands/games/trivia.rs @@ -67,7 +67,7 @@ impl XpCommand for TriviaCommand { return Ok(()); } - if !eligibility_helper(command.user.id.0).await { + if !eligibility_helper(command.user.id.0, &command.guild_id.unwrap().0).await { command .create_interaction_response(ctx, |response| { response diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 11e42bb..2fa6cd1 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -104,8 +104,13 @@ pub fn format_number(number: u64) -> String { formatted_number.chars().rev().collect::() } -pub async fn eligibility_helper(user_id: u64) -> bool { +pub async fn eligibility_helper(user_id: u64, guild_id: &u64) -> bool { let user = User::is_premium(user_id).await.unwrap_or(false); + let vote_free = Guild::is_vote_free(guild_id).await.unwrap_or(false); + + if vote_free { + return true; + } if user { return true; diff --git a/xp-db-connector/src/guild.rs b/xp-db-connector/src/guild.rs index 5a603f7..70c1387 100644 --- a/xp-db-connector/src/guild.rs +++ b/xp-db-connector/src/guild.rs @@ -97,6 +97,13 @@ pub struct GuildAnnounce { pub ping: bool, } +#[allow(non_snake_case)] +#[derive(Deserialize, Clone, Debug)] +pub struct GuildPremiumResponse { + pub premium: bool, + pub voteFree: bool, +} + impl Guild { pub async fn from_id(guild_id: u64) -> DbResult { let response = crate::get_json::(format!("/guild/{}", guild_id)).await?; @@ -119,4 +126,16 @@ impl Guild { Ok(Ok(())) } + + pub async fn is_premium(guild_id: &u64) -> DbResult { + let response = crate::get_json::(format!("/guild/{}/premium", guild_id)).await?; + + Ok(response.premium) + } + + pub async fn is_vote_free(guild_id: &u64) -> DbResult { + let response = crate::get_json::(format!("/guild/{}/premium", guild_id)).await?; + + Ok(response.voteFree) + } } From 496c078336e868b2cbbfbcefc610e6012a9a3b5d Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 15 Sep 2023 15:52:39 +0200 Subject: [PATCH 30/36] fix(vtm): channel leave event does not assign xp --- xp-bot/src/events/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 6b26ab4..d16f3ad 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -609,7 +609,7 @@ impl Handler { } // check if voice channel is ignored - if guild.clone().ignored.channels.unwrap().contains(&left.channel_id.unwrap().0.to_string()) { + if guild.clone().ignored.channels.unwrap().contains(&old.clone().unwrap().channel_id.unwrap().0.to_string()) { return (); } From e66c34699b3709472aacdc4b617adccada1247c5 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Fri, 15 Sep 2023 15:58:45 +0200 Subject: [PATCH 31/36] chore: remove logs --- xp-bot/src/events/handler.rs | 2 -- xp-bot/src/utils/utils.rs | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index d16f3ad..9ce2b84 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -272,8 +272,6 @@ impl EventHandler for Handler { let autorole = autorole.unwrap(); - log::info!("Assigning autorole {} to {}", autorole.id, new_member.user.name); - // assign autorole let _ = new_member.add_role(&ctx.http, RoleId(autorole.id.parse::().unwrap())).await; } diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 2fa6cd1..6e3aa08 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -369,5 +369,6 @@ pub async fn conform_xpc( .name .clone(), ); + member } From cc412b240217dac96511b1d71b1fd74a4762cb59 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Wed, 20 Sep 2023 15:00:39 +0200 Subject: [PATCH 32/36] fix: xp assignment issues (#106) --- xp-bot/src/utils/utils.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index 6e3aa08..ff1310d 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -344,22 +344,31 @@ pub async fn conform_xpc( user_id: &u64, ) -> GuildMember { member.userData.avatar = Some( - ctx.http + match ctx + .http .get_member(guild_id.to_owned(), user_id.to_owned()) .await .unwrap() .avatar - .expect(format!("User {} has no avatar.", user_id).as_str()), + { + Some(avatar) => avatar, + None => "".to_string(), + }, ); + member.userData.banner = Some( - ctx.http + match ctx + .http .get_member(guild_id.to_owned(), user_id.to_owned()) .await .unwrap() - .user - .banner - .expect(format!("User {} has no banner.", user_id).as_str()), + .avatar + { + Some(banner) => banner, + None => "".to_string(), + }, ); + member.userData.username = Some( ctx.http .get_member(guild_id.to_owned(), user_id.to_owned()) From 9cc76d024ba2c89a11cb7bc104ce170b2d869ebe Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Wed, 20 Sep 2023 15:11:46 +0200 Subject: [PATCH 33/36] feat: do not send ranking card if user is incognito (#105) --- xp-bot/src/commands/misc/rank.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/xp-bot/src/commands/misc/rank.rs b/xp-bot/src/commands/misc/rank.rs index ec0580a..61a72bc 100644 --- a/xp-bot/src/commands/misc/rank.rs +++ b/xp-bot/src/commands/misc/rank.rs @@ -4,8 +4,9 @@ use serenity::{ model::prelude::{application_command::ApplicationCommandInteraction, InteractionResponseType}, prelude::Context, }; +use xp_db_connector::guild_member::GuildMember; -use crate::commands::XpCommand; +use crate::{commands::XpCommand, utils::colors}; pub struct RankCommand; @@ -36,17 +37,39 @@ impl XpCommand for RankCommand { ctx: &Context, command: &ApplicationCommandInteraction, ) -> Result<(), Box> { - let mut _user_id = command.user.id.0; + let mut user_id = command.user.id.0; match command.data.options.first() { Some(option) => { if let Some(user) = Some(option.value.as_ref().unwrap().clone()) { - _user_id = user.as_str().unwrap().parse::().unwrap(); + user_id = user.as_str().unwrap().parse::().unwrap(); } } None => {} } + let user = GuildMember::from_id(command.guild_id.unwrap().0, user_id.clone()) + .await + .unwrap(); + + if user.settings.incognito.unwrap_or(false) { + command + .create_interaction_response(&ctx.http, |response| { + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| { + message.embed(|embed| { + embed.description(format!("This user is incognito.")); + embed.color(colors::red()) + }); + message.ephemeral(true); + message + }) + }) + .await?; + return Ok(()); + } + command .create_interaction_response(&ctx.http, |response| { response @@ -55,7 +78,7 @@ impl XpCommand for RankCommand { message.content(format!( "https://bot-api.xp-bot.net/rank/{}/{}?cache={}", command.guild_id.unwrap().0, - _user_id, + user_id, chrono::Utc::now().timestamp() )) }) From a026dde5e9113c4a7c0eb9c45b475a458afe7c79 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Wed, 20 Sep 2023 15:19:54 +0200 Subject: [PATCH 34/36] fix: vote-free servers still require votes --- xp-bot/src/utils/utils.rs | 14 +++++++------- xp-db-connector/src/guild.rs | 6 ------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/xp-bot/src/utils/utils.rs b/xp-bot/src/utils/utils.rs index ff1310d..62c99a6 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -3,7 +3,9 @@ use serenity::{ builder::CreateMessage, model::prelude::{ChannelId, RoleId}, }; -use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; +use xp_db_connector::{ + guild::Guild, guild_member::GuildMember, guild_premium::GuildPremium, user::User, +}; use super::{colors, topgg}; @@ -105,19 +107,17 @@ pub fn format_number(number: u64) -> String { } pub async fn eligibility_helper(user_id: u64, guild_id: &u64) -> bool { - let user = User::is_premium(user_id).await.unwrap_or(false); - let vote_free = Guild::is_vote_free(guild_id).await.unwrap_or(false); - - if vote_free { + let guild = GuildPremium::from_id(guild_id.to_owned()).await.unwrap(); + if guild.voteFree { return true; } + let user = User::is_premium(user_id).await.unwrap_or(false); if user { return true; } let voted = topgg::check_user_vote(&user_id).await; - if voted { return true; } @@ -355,7 +355,7 @@ pub async fn conform_xpc( None => "".to_string(), }, ); - + member.userData.banner = Some( match ctx .http diff --git a/xp-db-connector/src/guild.rs b/xp-db-connector/src/guild.rs index 70c1387..b62d485 100644 --- a/xp-db-connector/src/guild.rs +++ b/xp-db-connector/src/guild.rs @@ -132,10 +132,4 @@ impl Guild { Ok(response.premium) } - - pub async fn is_vote_free(guild_id: &u64) -> DbResult { - let response = crate::get_json::(format!("/guild/{}/premium", guild_id)).await?; - - Ok(response.voteFree) - } } From a795d9f541bee9d487bade699063f08e3d69b33b Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Thu, 21 Sep 2023 09:57:52 +0200 Subject: [PATCH 35/36] feat: remove autonick addition if user is incognito (#108) --- xp-bot/src/events/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index 9ce2b84..d5791a1 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -412,7 +412,7 @@ impl EventHandler for Handler { current_nick = regex.replace(¤t_nick, "").to_string(); // if autonick is disabled, reset nickname to previous nickname and return - if !guild.modules.autonick { + if !guild.modules.autonick || member.settings.incognito.unwrap_or(false) { let _ = ctx .http .get_member(guild_id, user_id) From 110fa2caf8f9573ca6bc8d39ac59efc15a2dc802 Mon Sep 17 00:00:00 2001 From: angelsflyinhell Date: Sun, 24 Sep 2023 17:27:42 +0200 Subject: [PATCH 36/36] temp: testing build --- xp-bot/src/events/handler.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xp-bot/src/events/handler.rs b/xp-bot/src/events/handler.rs index d5791a1..3cd6611 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -1,7 +1,7 @@ use log::{error, info}; use serenity::{ async_trait, - model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, component::ButtonStyle, ReactionType, Member, RoleId}, voice::VoiceState}, + model::{prelude::{Activity, GuildId, Interaction, InteractionResponseType, Ready, Message, Reaction, ChannelId, component::ButtonStyle, ReactionType, Member, RoleId, GuildChannel}, voice::VoiceState}, prelude::{Context, EventHandler}, }; use xp_db_connector::{guild::Guild, guild_member::GuildMember, user::User}; @@ -32,7 +32,7 @@ impl EventHandler for Handler { info!("Cache is ready!"); // register slash commands - for guild in guilds { + /* for guild in guilds { let commands = GuildId::set_application_commands(&guild, &ctx.http, |commands| { for command in commands::COMMANDS { commands.create_application_command(|c| command.register(c)); @@ -46,7 +46,7 @@ impl EventHandler for Handler { } info!("Registered commands for guild {}", guild); - } + } */ } async fn interaction_create(&self, ctx: Context, interaction: Interaction) { @@ -915,3 +915,4 @@ impl Handler { } } } +