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 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"); -} diff --git a/xp-bot/src/commands/admin/add.rs b/xp-bot/src/commands/admin/add.rs index 0f012f6..e21043c 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,9 @@ 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); + let _ = GuildMember::set_xp(guild_id, user, &new_amount, &guild_member).await?; command @@ -100,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 bf578c3..2b3736d 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,9 @@ 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); + let _ = GuildMember::set_xp(guild_id, user, &new_amount, &guild_member).await?; command @@ -99,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 126f789..8b6a48c 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,9 @@ 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); + let _ = GuildMember::set_xp(guild_id, user, &amount, &guild_member).await?; command @@ -98,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 0234505..14f4573 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,10 @@ 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)); + let _ = GuildMember::set_xp( command.guild_id.unwrap().into(), user_id, @@ -109,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/commands/games/daily.rs b/xp-bot/src/commands/games/daily.rs index 22ec8fb..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 @@ -121,19 +121,37 @@ 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 - }; - - guild_member.xp += daily_xp * streak; + 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 + }; + + let member_xp = daily_xp * streak; + 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); - 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 +160,10 @@ impl XpCommand for DailyCommand { .interaction_response_data(|message| { message.embed(|embed| { embed.description(format!( - "You claimed **{}** xp. Your streak is now **{}**.", - format_number(daily_xp * streak), - streak + "You claimed **{}** xp. Your streak is now **{}**.\n\n{}", + format_number(xp_to_add), + streak, + old_streak_msg )); embed.color(colors::green()) }) diff --git a/xp-bot/src/commands/games/fish.rs b/xp-bot/src/commands/games/fish.rs index bc9d226..b24e411 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}, }, }; @@ -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 @@ -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..526c707 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}, }, }; @@ -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 @@ -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/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..c266744 --- /dev/null +++ b/xp-bot/src/commands/games/party.rs @@ -0,0 +1,236 @@ +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, &command.guild_id.unwrap().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.set_components(CreateComponents::default()); + + 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/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/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( 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()) }) }) 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() )) }) 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..3cd6611 100644 --- a/xp-bot/src/events/handler.rs +++ b/xp-bot/src/events/handler.rs @@ -1,12 +1,12 @@ 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, component::ButtonStyle, ReactionType, Member, RoleId, GuildChannel}, voice::VoiceState}, prelude::{Context, EventHandler}, }; 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, conform_xpc}, math::calculate_level}}; pub struct Handler; @@ -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) { @@ -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() { @@ -186,7 +188,99 @@ 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 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(); + + // 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 (); + } + let user_id = msg.author.id.0; let guild_id = msg.guild_id.clone().unwrap().0; @@ -266,8 +360,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 @@ -276,6 +374,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; } @@ -313,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) @@ -429,7 +528,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) @@ -438,18 +537,23 @@ 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 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; @@ -478,6 +582,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); @@ -487,7 +596,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; @@ -497,6 +606,11 @@ impl Handler { return (); } + // check if voice channel is ignored + if guild.clone().ignored.channels.unwrap().contains(&old.clone().unwrap().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; @@ -523,7 +637,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) @@ -532,14 +646,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 @@ -605,9 +723,14 @@ 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; + + // invalidate timestamp + user.timestamps.join_voicechat = None; + let _ = User::set(left.user_id.0, user).await; } /* @@ -629,11 +752,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 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; // check if voice module is enabled @@ -641,6 +766,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; @@ -666,7 +796,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) @@ -675,14 +805,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 @@ -748,21 +882,30 @@ 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; + + // 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 - 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); @@ -772,3 +915,4 @@ impl Handler { } } } + diff --git a/xp-bot/src/main.rs b/xp-bot/src/main.rs index ab0294c..29a0198 100644 --- a/xp-bot/src/main.rs +++ b/xp-bot/src/main.rs @@ -11,11 +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 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..62c99a6 100644 --- a/xp-bot/src/utils/utils.rs +++ b/xp-bot/src/utils/utils.rs @@ -1,7 +1,13 @@ -use serenity::{model::prelude::{ChannelId, RoleId}, builder::CreateMessage}; -use xp_db_connector::{guild::Guild, user::User}; +use rand::Rng; +use serenity::{ + builder::CreateMessage, + model::prelude::{ChannelId, RoleId}, +}; +use xp_db_connector::{ + guild::Guild, guild_member::GuildMember, guild_premium::GuildPremium, user::User, +}; -use super::{topgg, colors}; +use super::{colors, topgg}; pub fn calculate_total_boost_percentage( guild: Guild, @@ -100,15 +106,18 @@ pub fn format_number(number: u64) -> String { formatted_number.chars().rev().collect::() } -pub async fn eligibility_helper(user_id: u64) -> bool { - let user = User::is_premium(user_id).await.unwrap_or(false); +pub async fn eligibility_helper(user_id: u64, guild_id: &u64) -> bool { + 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; } @@ -117,16 +126,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 }; @@ -153,3 +175,209 @@ 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)); + + log::info!("Roles to add: {:?}", roles_to_add); + + let remove_reached_roles = guild.modules.removereachedlevelroles; + let single_rank_role = guild.modules.singlerankrole; + + 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() + .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("Removed reached roles module is enabled."), + ) + .await + .unwrap(); + } + } + + 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 + .unwrap(); + } + } 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 + .unwrap(); + } + } +} + +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: 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(), + } +} + +pub async fn conform_xpc( + mut member: GuildMember, + ctx: &serenity::client::Context, + guild_id: &u64, + user_id: &u64, +) -> GuildMember { + member.userData.avatar = Some( + match ctx + .http + .get_member(guild_id.to_owned(), user_id.to_owned()) + .await + .unwrap() + .avatar + { + Some(avatar) => avatar, + None => "".to_string(), + }, + ); + + member.userData.banner = Some( + match ctx + .http + .get_member(guild_id.to_owned(), user_id.to_owned()) + .await + .unwrap() + .avatar + { + Some(banner) => banner, + None => "".to_string(), + }, + ); + + member.userData.username = Some( + ctx.http + .get_member(guild_id.to_owned(), user_id.to_owned()) + .await + .unwrap() + .user + .name + .clone(), + ); + + member +} diff --git a/xp-db-connector/src/guild.rs b/xp-db-connector/src/guild.rs index 5a603f7..b62d485 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,10 @@ 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) + } } 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?;