From e2134ce4872e211fb074dc8d2f0e0105afe7cb6a Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:37:57 -0400 Subject: [PATCH 01/44] add lastupdated field --- src/main/resources/data/tournaments.json | 1 + target/classes/data/tournaments.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/resources/data/tournaments.json b/src/main/resources/data/tournaments.json index cf9be7e..88b9a65 100644 --- a/src/main/resources/data/tournaments.json +++ b/src/main/resources/data/tournaments.json @@ -1,4 +1,5 @@ { + "lastUpdated": 1725138732, "tournaments": [ { "id": 0, diff --git a/target/classes/data/tournaments.json b/target/classes/data/tournaments.json index cf9be7e..88b9a65 100644 --- a/target/classes/data/tournaments.json +++ b/target/classes/data/tournaments.json @@ -1,4 +1,5 @@ { + "lastUpdated": 1725138732, "tournaments": [ { "id": 0, From 6fca3e1c4fc580afefcdb4c1f3aea09742fcbdb2 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:03:26 -0400 Subject: [PATCH 02/44] tournaments schema --- .../data/schemas/tournaments-schema.json | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/main/resources/data/schemas/tournaments-schema.json diff --git a/src/main/resources/data/schemas/tournaments-schema.json b/src/main/resources/data/schemas/tournaments-schema.json new file mode 100644 index 0000000..c35dabc --- /dev/null +++ b/src/main/resources/data/schemas/tournaments-schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Tournaments Schema", + "description": "Schema for tournament data, including the name, iteration, duration, and data for navigating the api to find specific stats", + "type": "object", + "properties": { + "lastUpdated": { + "type": "integer", + "description": "Unix timestamp of the last time the data was updated" + }, + "tournaments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The id of the tournament, denoted by the index in which it happened" + }, + "name": { + "type": "string", + "description": "The name of the tournament" + }, + "icon": { + "type": "string", + "description": "The path to the icon of the tournament, located at https://hypixel.net/styles/hypixel-v2/images/game-icons/{icon}" + }, + "iteration": { + "type": "integer", + "description": "The iteration of the tournament" + }, + "duration": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "description": "Unix timestamp of the start of the tournament" + }, + "end": { + "type": "integer", + "description": "Unix timestamp of the end of the tournament" + } + }, + "required": [ + "start", + "end" + ] + }, + "data": { + "type": "object", + "properties": { + "timeLimit": { + "type": "integer", + "description": "The maximum playtime for participants in minutes" + }, + "gameLimit": { + "type": "integer", + "description": "The maximum amount of games a participant can play" + }, + "tourneyField": { + "type": "string", + "description": "The field in the player's data that contains the tournament data" + }, + "wins": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins" + }, + "losses": { + "type": "string", + "description": "The field in the player's stats that contains the amount of losses" + }, + "winstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins in a row" + }, + "kills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills" + }, + "deaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of deaths" + }, + "killstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills in a row" + }, + "assists": { + "type": "string", + "description": "The field in the player's stats that contains the amount of assists" + }, + "finalKills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final kills" + }, + "finalDeaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final deaths" + } + }, + "oneOf": [ + { + "required": [ + "timeLimit" + ] + }, + { + "required": [ + "gameLimit" + ] + } + ], + "required": [ + "tourneyField", + "wins" + ], + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "icon", + "iteration", + "duration", + "data" + ] + } + } + } +} \ No newline at end of file From 83cd515ab2648b5046f95367cda7fc984db0ed2d Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:03:44 -0400 Subject: [PATCH 03/44] fix indents --- src/main/resources/data/mwclasses.json | 10 +- src/main/resources/data/tournaments.json | 316 +++++++++++------------ 2 files changed, 163 insertions(+), 163 deletions(-) diff --git a/src/main/resources/data/mwclasses.json b/src/main/resources/data/mwclasses.json index fe3e09e..f64e364 100644 --- a/src/main/resources/data/mwclasses.json +++ b/src/main/resources/data/mwclasses.json @@ -1,9 +1,9 @@ { "lastUpdated": 0, - "classes" : [ + "classes": [ { - "id" : "cow", - "name" : "Cow", + "id": "cow", + "name": "Cow", "skins": [ { "name": "Moo Brawler", @@ -625,8 +625,8 @@ ] }, { - "id" : "skeleton", - "name" : "Skeleton", + "id": "skeleton", + "name": "Skeleton", "skins": [ { "name": "Marksman", diff --git a/src/main/resources/data/tournaments.json b/src/main/resources/data/tournaments.json index 88b9a65..80724e3 100644 --- a/src/main/resources/data/tournaments.json +++ b/src/main/resources/data/tournaments.json @@ -11,16 +11,16 @@ "end": 1543186800 }, "data": { - "timeLimit" : 1200, - "tourneyField" : "bedwars4s_0", - "wins" : "Bedwars.tourney_bedwars4s_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_0_deaths_bedwars" + "timeLimit": 1200, + "tourneyField": "bedwars4s_0", + "wins": "Bedwars.tourney_bedwars4s_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_0_deaths_bedwars" } }, { @@ -33,11 +33,11 @@ "end": 1546815600 }, "data": { - "timeLimit" : 720, - "tourneyField" : "blitz_duo_0", - "wins" : "HungerGames.tourney_blitz_duo_0_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_0_kills", - "deaths" : "HungerGames.tourney_blitz_duo_0_deaths" + "timeLimit": 720, + "tourneyField": "blitz_duo_0", + "wins": "HungerGames.tourney_blitz_duo_0_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_0_kills", + "deaths": "HungerGames.tourney_blitz_duo_0_deaths" } }, { @@ -50,14 +50,14 @@ "end": 1552860000 }, "data": { - "timeLimit" : 480, - "tourneyField" : "sw_crazy_solo_0", - "wins" : "SkyWars.tourney_sw_crazy_solo_0_wins", - "losses" : "SkyWars.tourney_sw_crazy_solo_0_losses", - "winstreak" : "SkyWars.tourney_sw_crazy_solo_0_win_streak", - "kills" : "SkyWars.tourney_sw_crazy_solo_0_kills", - "deaths" : "SkyWars.tourney_sw_crazy_solo_0_deaths", - "killstreak" : "SkyWars.tourney_sw_crazy_solo_0_killstreak" + "timeLimit": 480, + "tourneyField": "sw_crazy_solo_0", + "wins": "SkyWars.tourney_sw_crazy_solo_0_wins", + "losses": "SkyWars.tourney_sw_crazy_solo_0_losses", + "winstreak": "SkyWars.tourney_sw_crazy_solo_0_win_streak", + "kills": "SkyWars.tourney_sw_crazy_solo_0_kills", + "deaths": "SkyWars.tourney_sw_crazy_solo_0_deaths", + "killstreak": "SkyWars.tourney_sw_crazy_solo_0_killstreak" } }, { @@ -70,11 +70,11 @@ "end": 1572213600 }, "data": { - "timeLimit" : 480, - "tourneyField" : "mcgo_defusal_0", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "timeLimit": 480, + "tourneyField": "mcgo_defusal_0", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -87,16 +87,16 @@ "end": 1576450800 }, "data": { - "gameLimit" : 75, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 75, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -109,11 +109,11 @@ "end": 1580684400 }, "data": { - "gameLimit" : 250, - "tourneyField" : "quake_solo2_0", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 250, + "tourneyField": "quake_solo2_0", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -126,14 +126,14 @@ "end": 1586124000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_0", - "wins" : "SkyWars.tourney_sw_insane_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_0_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_0_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_0_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_0", + "wins": "SkyWars.tourney_sw_insane_doubles_0_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_0_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_0_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_0_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_0_killstreak" } }, { @@ -146,16 +146,16 @@ "end": 1592172000 }, "data": { - "gameLimit" : 80, - "tourneyField" : "bedwars4s_1", - "wins" : "Bedwars.tourney_bedwars4s_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_1_deaths_bedwars" + "gameLimit": 80, + "tourneyField": "bedwars4s_1", + "wins": "Bedwars.tourney_bedwars4s_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_1_deaths_bedwars" } }, { @@ -168,9 +168,9 @@ "end": 1597010400 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_0", - "wins" : "GingerBread.tourney_gingerbread_solo_0_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_0", + "wins": "GingerBread.tourney_gingerbread_solo_0_gold_trophy" } }, { @@ -183,11 +183,11 @@ "end": 1603058400 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_1", - "wins" : "HungerGames.tourney_blitz_duo_1_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_1_kills", - "deaths" : "HungerGames.tourney_blitz_duo_1_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_1", + "wins": "HungerGames.tourney_blitz_duo_1_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_1_kills", + "deaths": "HungerGames.tourney_blitz_duo_1_deaths" } }, { @@ -200,11 +200,11 @@ "end": 1608505200 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_0", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney" + "gameLimit": 60, + "tourneyField": "grinch_simulator_0", + "wins": "Arcade.wins_grinch_simulator_v2_tourney", + "losses": "Arcade.losses_grinch_simulator_v2_tourney", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney" } }, { @@ -217,11 +217,11 @@ "end": 1615158000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "mcgo_defusal_1", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "gameLimit": 40, + "tourneyField": "mcgo_defusal_1", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -234,10 +234,10 @@ "end": 1629064800 }, "data": { - "gameLimit" : 120, - "tourneyField" : "tnt_run_0", - "wins" : "TNTGames.wins_tourney_tnt_run_0", - "losses" : "TNTGames.deaths_tourney_tnt_run_0" + "gameLimit": 120, + "tourneyField": "tnt_run_0", + "wins": "TNTGames.wins_tourney_tnt_run_0", + "losses": "TNTGames.deaths_tourney_tnt_run_0" } }, { @@ -250,14 +250,14 @@ "end": 1634508000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_1", - "wins" : "SkyWars.tourney_sw_insane_doubles_1_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_1_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_1_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_1_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_1_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_1_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_1", + "wins": "SkyWars.tourney_sw_insane_doubles_1_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_1_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_1_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_1_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_1_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_1_killstreak" } }, { @@ -270,11 +270,11 @@ "end": 1639954800 }, "data": { - "gameLimit" : 200, - "tourneyField" : "quake_solo2_1", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 200, + "tourneyField": "quake_solo2_1", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -287,16 +287,16 @@ "end": 1647813600 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -309,12 +309,12 @@ "end": 1660514400 }, "data": { - "gameLimit" : 50, - "tourneyField" : "mini_walls_0", - "wins" : "Arcade.wins_tourney_mini_walls_0", - "deaths" : "Arcade.deaths_tourney_mini_walls_0", - "kills" : "Arcade.kills_tourney_mini_walls_0", - "finalKills" : "Arcade.final_kills_tourney_mini_walls_0" + "gameLimit": 50, + "tourneyField": "mini_walls_0", + "wins": "Arcade.wins_tourney_mini_walls_0", + "deaths": "Arcade.deaths_tourney_mini_walls_0", + "kills": "Arcade.kills_tourney_mini_walls_0", + "finalKills": "Arcade.final_kills_tourney_mini_walls_0" } }, { @@ -327,9 +327,9 @@ "end": 1667167200 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_1", - "wins" : "GingerBread.tourney_gingerbread_solo_1_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_1", + "wins": "GingerBread.tourney_gingerbread_solo_1_gold_trophy" } }, { @@ -342,11 +342,11 @@ "end": 1679868000 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_2", - "wins" : "HungerGames.tourney_blitz_duo_2_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_2_kills", - "deaths" : "HungerGames.tourney_blitz_duo_2_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_2", + "wins": "HungerGames.tourney_blitz_duo_2_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_2_kills", + "deaths": "HungerGames.tourney_blitz_duo_2_deaths" } }, { @@ -359,11 +359,11 @@ "end": 1693163760 }, "data": { - "gameLimit" : 100, - "tourneyField" : "wool_wars_0", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" + "gameLimit": 100, + "tourneyField": "wool_wars_0", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" } }, { @@ -376,11 +376,11 @@ "end": 1694383200 }, "data": { - "gameLimit" : 40, - "tourneyField" : "wool_wars_1", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" + "gameLimit": 40, + "tourneyField": "wool_wars_1", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" } }, { @@ -393,16 +393,16 @@ "end": 1698012000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_1", - "wins" : "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_1", + "wins": "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" } }, { @@ -415,11 +415,11 @@ "end": 1702854000 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_1", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" + "gameLimit": 60, + "tourneyField": "grinch_simulator_1", + "wins": "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", + "losses": "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" } }, { @@ -432,10 +432,10 @@ "end": 1710712800 }, "data": { - "gameLimit" : 80, - "tourneyField" : "tnt_run_1", - "wins" : "TNTGames.wins_tourney_tnt_run_1", - "losses" : "TNTGames.deaths_tourney_tnt_run_1" + "gameLimit": 80, + "tourneyField": "tnt_run_1", + "wins": "TNTGames.wins_tourney_tnt_run_1", + "losses": "TNTGames.deaths_tourney_tnt_run_1" } }, { @@ -448,15 +448,15 @@ "end": 1719180000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_normal_doubles_0", - "wins" : "SkyWars.tourney_sw_normal_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_normal_doubles_0_losses", - "kills" : "SkyWars.tourney_sw_normal_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_normal_doubles_0_deaths", - "assists" : "SkyWars.tourney_sw_normal_doubles_0_assists", - "winstreak" : "SkyWars.tourney_sw_normal_doubles_0_win_streak", - "killstreak" : "SkyWars.tourney_sw_normal_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_normal_doubles_0", + "wins": "SkyWars.tourney_sw_normal_doubles_0_wins", + "losses": "SkyWars.tourney_sw_normal_doubles_0_losses", + "kills": "SkyWars.tourney_sw_normal_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_normal_doubles_0_deaths", + "assists": "SkyWars.tourney_sw_normal_doubles_0_assists", + "winstreak": "SkyWars.tourney_sw_normal_doubles_0_win_streak", + "killstreak": "SkyWars.tourney_sw_normal_doubles_0_killstreak" } } ] From 80a0cbaff643db8c5c65c992f1fb53532099fed6 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:43:55 -0400 Subject: [PATCH 04/44] fix indents --- target/classes/data/mwclasses.json | 10 +- target/classes/data/tournaments.json | 316 +++++++++++++-------------- 2 files changed, 163 insertions(+), 163 deletions(-) diff --git a/target/classes/data/mwclasses.json b/target/classes/data/mwclasses.json index fe3e09e..f64e364 100644 --- a/target/classes/data/mwclasses.json +++ b/target/classes/data/mwclasses.json @@ -1,9 +1,9 @@ { "lastUpdated": 0, - "classes" : [ + "classes": [ { - "id" : "cow", - "name" : "Cow", + "id": "cow", + "name": "Cow", "skins": [ { "name": "Moo Brawler", @@ -625,8 +625,8 @@ ] }, { - "id" : "skeleton", - "name" : "Skeleton", + "id": "skeleton", + "name": "Skeleton", "skins": [ { "name": "Marksman", diff --git a/target/classes/data/tournaments.json b/target/classes/data/tournaments.json index 88b9a65..80724e3 100644 --- a/target/classes/data/tournaments.json +++ b/target/classes/data/tournaments.json @@ -11,16 +11,16 @@ "end": 1543186800 }, "data": { - "timeLimit" : 1200, - "tourneyField" : "bedwars4s_0", - "wins" : "Bedwars.tourney_bedwars4s_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_0_deaths_bedwars" + "timeLimit": 1200, + "tourneyField": "bedwars4s_0", + "wins": "Bedwars.tourney_bedwars4s_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_0_deaths_bedwars" } }, { @@ -33,11 +33,11 @@ "end": 1546815600 }, "data": { - "timeLimit" : 720, - "tourneyField" : "blitz_duo_0", - "wins" : "HungerGames.tourney_blitz_duo_0_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_0_kills", - "deaths" : "HungerGames.tourney_blitz_duo_0_deaths" + "timeLimit": 720, + "tourneyField": "blitz_duo_0", + "wins": "HungerGames.tourney_blitz_duo_0_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_0_kills", + "deaths": "HungerGames.tourney_blitz_duo_0_deaths" } }, { @@ -50,14 +50,14 @@ "end": 1552860000 }, "data": { - "timeLimit" : 480, - "tourneyField" : "sw_crazy_solo_0", - "wins" : "SkyWars.tourney_sw_crazy_solo_0_wins", - "losses" : "SkyWars.tourney_sw_crazy_solo_0_losses", - "winstreak" : "SkyWars.tourney_sw_crazy_solo_0_win_streak", - "kills" : "SkyWars.tourney_sw_crazy_solo_0_kills", - "deaths" : "SkyWars.tourney_sw_crazy_solo_0_deaths", - "killstreak" : "SkyWars.tourney_sw_crazy_solo_0_killstreak" + "timeLimit": 480, + "tourneyField": "sw_crazy_solo_0", + "wins": "SkyWars.tourney_sw_crazy_solo_0_wins", + "losses": "SkyWars.tourney_sw_crazy_solo_0_losses", + "winstreak": "SkyWars.tourney_sw_crazy_solo_0_win_streak", + "kills": "SkyWars.tourney_sw_crazy_solo_0_kills", + "deaths": "SkyWars.tourney_sw_crazy_solo_0_deaths", + "killstreak": "SkyWars.tourney_sw_crazy_solo_0_killstreak" } }, { @@ -70,11 +70,11 @@ "end": 1572213600 }, "data": { - "timeLimit" : 480, - "tourneyField" : "mcgo_defusal_0", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "timeLimit": 480, + "tourneyField": "mcgo_defusal_0", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -87,16 +87,16 @@ "end": 1576450800 }, "data": { - "gameLimit" : 75, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 75, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -109,11 +109,11 @@ "end": 1580684400 }, "data": { - "gameLimit" : 250, - "tourneyField" : "quake_solo2_0", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 250, + "tourneyField": "quake_solo2_0", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -126,14 +126,14 @@ "end": 1586124000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_0", - "wins" : "SkyWars.tourney_sw_insane_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_0_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_0_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_0_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_0", + "wins": "SkyWars.tourney_sw_insane_doubles_0_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_0_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_0_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_0_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_0_killstreak" } }, { @@ -146,16 +146,16 @@ "end": 1592172000 }, "data": { - "gameLimit" : 80, - "tourneyField" : "bedwars4s_1", - "wins" : "Bedwars.tourney_bedwars4s_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_1_deaths_bedwars" + "gameLimit": 80, + "tourneyField": "bedwars4s_1", + "wins": "Bedwars.tourney_bedwars4s_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_1_deaths_bedwars" } }, { @@ -168,9 +168,9 @@ "end": 1597010400 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_0", - "wins" : "GingerBread.tourney_gingerbread_solo_0_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_0", + "wins": "GingerBread.tourney_gingerbread_solo_0_gold_trophy" } }, { @@ -183,11 +183,11 @@ "end": 1603058400 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_1", - "wins" : "HungerGames.tourney_blitz_duo_1_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_1_kills", - "deaths" : "HungerGames.tourney_blitz_duo_1_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_1", + "wins": "HungerGames.tourney_blitz_duo_1_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_1_kills", + "deaths": "HungerGames.tourney_blitz_duo_1_deaths" } }, { @@ -200,11 +200,11 @@ "end": 1608505200 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_0", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney" + "gameLimit": 60, + "tourneyField": "grinch_simulator_0", + "wins": "Arcade.wins_grinch_simulator_v2_tourney", + "losses": "Arcade.losses_grinch_simulator_v2_tourney", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney" } }, { @@ -217,11 +217,11 @@ "end": 1615158000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "mcgo_defusal_1", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "gameLimit": 40, + "tourneyField": "mcgo_defusal_1", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -234,10 +234,10 @@ "end": 1629064800 }, "data": { - "gameLimit" : 120, - "tourneyField" : "tnt_run_0", - "wins" : "TNTGames.wins_tourney_tnt_run_0", - "losses" : "TNTGames.deaths_tourney_tnt_run_0" + "gameLimit": 120, + "tourneyField": "tnt_run_0", + "wins": "TNTGames.wins_tourney_tnt_run_0", + "losses": "TNTGames.deaths_tourney_tnt_run_0" } }, { @@ -250,14 +250,14 @@ "end": 1634508000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_1", - "wins" : "SkyWars.tourney_sw_insane_doubles_1_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_1_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_1_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_1_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_1_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_1_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_1", + "wins": "SkyWars.tourney_sw_insane_doubles_1_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_1_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_1_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_1_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_1_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_1_killstreak" } }, { @@ -270,11 +270,11 @@ "end": 1639954800 }, "data": { - "gameLimit" : 200, - "tourneyField" : "quake_solo2_1", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 200, + "tourneyField": "quake_solo2_1", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -287,16 +287,16 @@ "end": 1647813600 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -309,12 +309,12 @@ "end": 1660514400 }, "data": { - "gameLimit" : 50, - "tourneyField" : "mini_walls_0", - "wins" : "Arcade.wins_tourney_mini_walls_0", - "deaths" : "Arcade.deaths_tourney_mini_walls_0", - "kills" : "Arcade.kills_tourney_mini_walls_0", - "finalKills" : "Arcade.final_kills_tourney_mini_walls_0" + "gameLimit": 50, + "tourneyField": "mini_walls_0", + "wins": "Arcade.wins_tourney_mini_walls_0", + "deaths": "Arcade.deaths_tourney_mini_walls_0", + "kills": "Arcade.kills_tourney_mini_walls_0", + "finalKills": "Arcade.final_kills_tourney_mini_walls_0" } }, { @@ -327,9 +327,9 @@ "end": 1667167200 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_1", - "wins" : "GingerBread.tourney_gingerbread_solo_1_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_1", + "wins": "GingerBread.tourney_gingerbread_solo_1_gold_trophy" } }, { @@ -342,11 +342,11 @@ "end": 1679868000 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_2", - "wins" : "HungerGames.tourney_blitz_duo_2_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_2_kills", - "deaths" : "HungerGames.tourney_blitz_duo_2_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_2", + "wins": "HungerGames.tourney_blitz_duo_2_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_2_kills", + "deaths": "HungerGames.tourney_blitz_duo_2_deaths" } }, { @@ -359,11 +359,11 @@ "end": 1693163760 }, "data": { - "gameLimit" : 100, - "tourneyField" : "wool_wars_0", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" + "gameLimit": 100, + "tourneyField": "wool_wars_0", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" } }, { @@ -376,11 +376,11 @@ "end": 1694383200 }, "data": { - "gameLimit" : 40, - "tourneyField" : "wool_wars_1", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" + "gameLimit": 40, + "tourneyField": "wool_wars_1", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" } }, { @@ -393,16 +393,16 @@ "end": 1698012000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_1", - "wins" : "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_1", + "wins": "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" } }, { @@ -415,11 +415,11 @@ "end": 1702854000 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_1", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" + "gameLimit": 60, + "tourneyField": "grinch_simulator_1", + "wins": "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", + "losses": "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" } }, { @@ -432,10 +432,10 @@ "end": 1710712800 }, "data": { - "gameLimit" : 80, - "tourneyField" : "tnt_run_1", - "wins" : "TNTGames.wins_tourney_tnt_run_1", - "losses" : "TNTGames.deaths_tourney_tnt_run_1" + "gameLimit": 80, + "tourneyField": "tnt_run_1", + "wins": "TNTGames.wins_tourney_tnt_run_1", + "losses": "TNTGames.deaths_tourney_tnt_run_1" } }, { @@ -448,15 +448,15 @@ "end": 1719180000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_normal_doubles_0", - "wins" : "SkyWars.tourney_sw_normal_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_normal_doubles_0_losses", - "kills" : "SkyWars.tourney_sw_normal_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_normal_doubles_0_deaths", - "assists" : "SkyWars.tourney_sw_normal_doubles_0_assists", - "winstreak" : "SkyWars.tourney_sw_normal_doubles_0_win_streak", - "killstreak" : "SkyWars.tourney_sw_normal_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_normal_doubles_0", + "wins": "SkyWars.tourney_sw_normal_doubles_0_wins", + "losses": "SkyWars.tourney_sw_normal_doubles_0_losses", + "kills": "SkyWars.tourney_sw_normal_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_normal_doubles_0_deaths", + "assists": "SkyWars.tourney_sw_normal_doubles_0_assists", + "winstreak": "SkyWars.tourney_sw_normal_doubles_0_win_streak", + "killstreak": "SkyWars.tourney_sw_normal_doubles_0_killstreak" } } ] From daf9b3732ac906bb46146263ec865de6287d9519 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:49:32 -0400 Subject: [PATCH 05/44] pretty colors --- src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 7eda52a..35a4daf 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -58,11 +58,11 @@ public static MessageEmbed makeErrorEmbed(String embedTitle, String embedContent embedTitle = toSkillIssue(embedTitle); embedContent = toSkillIssue(embedContent); } - return makeEmbed(":no_entry: " + embedTitle, null, embedContent, 0xff0000); + return makeEmbed(":no_entry: " + embedTitle, null, embedContent, 0xC95353); } public static MessageEmbed makeUpdateEmbed(String embedTitle, String embedContent) { - return makeEmbed(":mega: " + embedTitle, null, embedContent, 0xffef14); + return makeEmbed(":mega: " + embedTitle, null, embedContent, 0xEBD773); } public static MessageEmbed makeStaffRankChangeEmbed(String ign, String oldRank, String newRank, String position) { @@ -82,11 +82,11 @@ public static MessageEmbed makeStaffRankChangeEmbed(String ign, String oldRank, } public static MessageEmbed makeStatsEmbed(String embedTitle, String embedContent) { - return makeEmbed(embedTitle, null, embedContent, 0xf7cb72); + return makeEmbed(embedTitle, null, embedContent, 0x6D8FCE); } public static MessageEmbed makeStatsEmbed(String embedTitle, String embedSubtitle, String embedContent) { - return makeEmbed(embedTitle, embedSubtitle , embedContent, 0xf7cb72); + return makeEmbed(embedTitle, embedSubtitle , embedContent, 0x6D8FCE); } public static String getDiscordUsername(String id){ From a11864e605c164a91d3e9278115ad217896e25e1 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:50:32 -0400 Subject: [PATCH 06/44] set display name from mojang --- .../java/me/stuffy/stuffybot/profiles/HypixelProfile.java | 5 +++++ src/main/java/me/stuffy/stuffybot/utils/APIUtils.java | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index 1f6ca0d..6062263 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -59,6 +59,11 @@ public UUID getUuid() { return uuid; } + public HypixelProfile setDisplayName(String displayName) { + this.displayName = displayName; + return this; + } + public String getDisplayName() { return displayName; } diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index cf4c675..faba59b 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -28,7 +28,8 @@ public class APIUtils { static String mojangApiUrl = "https://api.mojang.com/"; public static HypixelProfile getHypixelProfile(String username) throws APIException { MojangProfile profile = getMojangProfile(username); - return getHypixelProfile(profile.getUuid()); + String ign = profile.getUsername(); + return getHypixelProfile(profile.getUuid()).setDisplayName(ign); } private static final LoadingCache hypixelProfileCache = CacheBuilder.newBuilder() From e506fa8bb874bcecb51615cb7cb1ef642799cd4e Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:51:25 -0400 Subject: [PATCH 07/44] Detect imminent shutdown --- src/main/java/me/stuffy/stuffybot/Bot.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 366922c..2468a52 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -58,6 +58,21 @@ public Bot() throws InterruptedException { // Start events new UpdateBotStatsEvent().startFixedRateEvent(); new ActiveEvents().startFixedRateEvent(); + + // Handle SIGTERM + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Logger.log(" Bot shutting down, saving data..."); + // Wait like 5 seconds for data to save + Logger.log(" Not going down without a fight!"); +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } + // #TODO: Upload logs + // #TODO: Update Bot stats + Logger.log(" Data saved, allowing for shutdown."); + })); } public static Bot getInstance() { return INSTANCE; From 7ea6c45f258b9bfdd6c2070444f340f8fb0220f5 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:52:06 -0400 Subject: [PATCH 08/44] rm todo --- src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index 6062263..a74eb3e 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -220,7 +220,7 @@ public Integer getWins() { // Mega Walls "Walls3.wins", - // Turbo Kart Racers TODO: Reduce to golds? + // Turbo Kart Racers "GingerBread.gold_trophy", "GingerBread.tourney_gingerbread_solo_1_gold_trophy", // SkyWars From 14bf429805e52e53cbd2b131489bb2af506cfe24 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:53:26 -0400 Subject: [PATCH 09/44] log api errors --- src/main/java/me/stuffy/stuffybot/utils/APIUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index faba59b..865e5f2 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -83,18 +83,22 @@ public static HypixelProfile fetchHypixelProfile(UUID uuid) throws APIException return new HypixelProfile(object.get("player").getAsJsonObject()); } case 400 -> { + logError(response.body()); logError("Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "A field is missing, this should never happen."); } case 403 -> { + logError(response.body()); logError("Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "Invalid API Key, contact the Stuffy immediately."); } case 429 -> { + logError(response.body()); logError("Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "Rate limited by Hypixel API, try again later."); } default -> { + logError(response.body()); logError("Unknown Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "I've never seen this error before."); } From 851b79da4d27842919aacacdc846b0c37185be81 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:54:14 -0400 Subject: [PATCH 10/44] support for username from uuid --- .../me/stuffy/stuffybot/utils/APIUtils.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index 865e5f2..7c19a57 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -169,6 +169,61 @@ public static MojangProfile fetchMojangProfile(String username) throws APIExcept return profile; } + private static final LoadingCache mojangProfileUUIDCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build( + new CacheLoader() { + public MojangProfile load(@NotNull UUID uuid) { + try{ + return fetchMojangProfile(uuid); + } catch (APIException e){ + throw new RuntimeException(e); + } + } + } + ); + + public static MojangProfile getMojangProfile(UUID uuid) throws APIException{ + try{ + return mojangProfileUUIDCache.getUnchecked(uuid); + } catch (RuntimeException e){ + if (e.getCause().getCause() instanceof APIException) { + throw (APIException) e.getCause().getCause(); + } else { + throw e; + } + } + } + + public static MojangProfile fetchMojangProfile(UUID uuid) throws APIException { + HttpRequest getRequest = HttpRequest.newBuilder() + .uri(URI.create(mojangSessionApiUrl + "session/minecraft/profile/" + uuid.toString())) + .build(); + HttpClient client = HttpClient.newHttpClient(); + HttpResponse response = client.sendAsync(getRequest, HttpResponse.BodyHandlers.ofString()).join(); + switch (response.statusCode()) { + case 200 -> { + JsonParser parser = new JsonParser(); + JsonElement element = parser.parse(response.body()); + JsonObject object = element.getAsJsonObject(); + String name = object.get("name").getAsString(); + return new MojangProfile(name, uuid); + } + case 204,404 -> { + logError("Mojang API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); + throw new APIException("Mojang", "There is no Minecraft account with that UUID."); + } + case 429 -> { + logError("Mojang API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); + throw new APIException("Mojang", "Rate limited by Mojang API, try again later."); + } + default -> { + logError("Unknown Mojang API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); + throw new APIException("Mojang", "I've never seen this error before."); + } + } + } + private static final LoadingCache achievementsCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) .build( From 760bd4a41248044eac5ee768ab21099d95ac632c Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:56:34 -0400 Subject: [PATCH 11/44] Github API, GlobalData --- pom.xml | 10 ++ src/main/java/me/stuffy/stuffybot/Bot.java | 32 ++++- .../stuffybot/events/UpdateBotStatsEvent.java | 10 +- .../interactions/InteractionHandler.java | 11 +- .../interactions/InteractionManager.java | 2 + .../stuffy/stuffybot/profiles/GlobalData.java | 62 +++++++++ .../me/stuffy/stuffybot/utils/APIUtils.java | 123 +++++++++++++++++- .../stuffy/stuffybot/utils/DiscordUtils.java | 17 ++- 8 files changed, 250 insertions(+), 17 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java diff --git a/pom.xml b/pom.xml index 6dfc478..8655fd9 100644 --- a/pom.xml +++ b/pom.xml @@ -41,5 +41,15 @@ guava 30.1-jre + + org.kohsuke + github-api + 1.324 + + + com.opencsv + opencsv + 5.5.2 + \ No newline at end of file diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 2468a52..2d8977d 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -4,6 +4,7 @@ import me.stuffy.stuffybot.events.ActiveEvents; import me.stuffy.stuffybot.events.UpdateBotStatsEvent; import me.stuffy.stuffybot.interactions.InteractionHandler; +import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.DiscordUtils; import me.stuffy.stuffybot.utils.Logger; import net.dv8tion.jda.api.JDA; @@ -18,13 +19,18 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import org.kohsuke.github.GitHub; import java.util.ArrayList; +import static me.stuffy.stuffybot.utils.APIUtils.connectToGitHub; + public class Bot extends ListenerAdapter { private static Bot INSTANCE; private final JDA jda; private Guild homeGuild; + private static GitHub GITHUB; + private static GlobalData GLOBAL_DATA; public Bot() throws InterruptedException { @@ -41,12 +47,17 @@ public Bot() throws InterruptedException { this.homeGuild = jda.getGuildById("795108903733952562"); assert this.homeGuild != null : "Failed to find home guild"; - // Log startup String time = DiscordUtils.discordTimeNow(); String self = jda.getSelfUser().getAsMention(); Logger.log(" Bot " + self + " started successfully " + time + "."); + // Initialize GitHub + GITHUB = connectToGitHub(); + + // Initialize Global Data + GLOBAL_DATA = new GlobalData(); + // Listen for interactions jda.addEventListener( new InteractionHandler() @@ -78,6 +89,14 @@ public static Bot getInstance() { return INSTANCE; } + public static GitHub getGitHub() { + return GITHUB; + } + + public static GlobalData getGlobalData() { + return GLOBAL_DATA; + } + public Guild getHomeGuild() { return this.homeGuild; } @@ -110,6 +129,7 @@ public void onGuildLeave(GuildLeaveEvent event) { public void registerCommands(String scope) { OptionData ignOption = new OptionData(OptionType.STRING, "ign", "The player's IGN", false); + OptionData ignOptionRequired = new OptionData(OptionType.STRING, "ign", "The player's IGN", true); // Create a list of commands first ArrayList commandList = new ArrayList<>(); // commandList.add(Commands.slash("help", "*Should* show a help message")); @@ -129,6 +149,16 @@ public void registerCommands(String scope) { commandList.add(Commands.slash("tournament", "Get tournament stats for a player") .addOptions(ignOption) .addOptions(new OptionData(OptionType.INTEGER, "tournament", "Which tournament to look at (Leave empty for latest)", false).setAutoComplete(true))); +// commandList.add(Commands.slash("achievements", "Get achievement stats for a player") +// .addOptions(ignOption) +// .addOptions(new OptionData(OptionType.STRING, "game", "Which game to look at", false).setAutoComplete(true)) +// .addOptions(new OptionData(OptionType.STRING, "type", "Which achievements to look at", false).addChoices( +// new Command.Choice("All", "all"), +// new Command.Choice("Challenge", "challenge"), +// new Command.Choice("Tiered", "tiered") +// ))); + commandList.add(Commands.slash("link", "Link a Minecraft account so you don't have to type your IGN every time") + .addOptions(ignOptionRequired)); if (scope.equals("local")) { diff --git a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java index 2c6ec7c..a3cb194 100644 --- a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java @@ -3,11 +3,12 @@ import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.utils.Logger; +import java.util.Map; import java.util.concurrent.TimeUnit; public class UpdateBotStatsEvent extends BaseEvent{ public UpdateBotStatsEvent() { - super("UpdateBotStats", 1, TimeUnit.HOURS); + super("UpdateBotStats", 6, TimeUnit.HOURS); } @Override @@ -16,8 +17,11 @@ protected void execute() { // How many commands have been run Bot bot = Bot.getInstance(); int totalServers = bot.getJDA().getGuilds().size(); - Logger.log(" Total servers: " + totalServers); -// int totalCommandsRun = bot.getStatisticsManager().getTotalCommandsRun(); + + Map recentInteractions = Bot.getGlobalData().getInteractions(); + // # TODO: Update the bot stats on the website + Bot.getGlobalData().clearInteractions(); + } } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index ae495fa..87ca6d4 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -2,6 +2,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import me.stuffy.stuffybot.utils.APIException; import me.stuffy.stuffybot.utils.InteractionException; import me.stuffy.stuffybot.utils.Logger; import me.stuffy.stuffybot.utils.StatisticsManager; @@ -35,7 +36,6 @@ import static me.stuffy.stuffybot.utils.APIUtils.getTournamentData; import static me.stuffy.stuffybot.utils.DiscordUtils.*; import static me.stuffy.stuffybot.utils.MiscUtils.genBase64; -import static me.stuffy.stuffybot.utils.MiscUtils.requiresIgn; public class InteractionHandler extends ListenerAdapter { private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); @@ -50,8 +50,13 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { ArrayList optionsArray = new ArrayList(); - if(requiresIgn(commandName) && event.getOption("ign") == null){ - String ign = getUsername(event); + if(event.getOption("ign") == null){ + String ign = null; + try { + ign = getUsername(event); + } catch (APIException e) { + event.getHook().sendMessageEmbeds(makeErrorEmbed(e.getAPIType() + " API Error", e.getMessage())).setEphemeral(true).queue(); + } optionsArray.add("ign=" + ign); } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java index c9bc6ba..30af66d 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java @@ -41,6 +41,8 @@ public static MessageCreateData getResponse(InteractionId interactionId) throws case "blitz" -> BlitzCommand.blitz(interactionId); case "megawalls" -> MegaWallsCommand.megawalls(interactionId); case "tournament" -> TournamentCommand.tournament(interactionId); + case "achievements" -> AchievementsCommand.achievements(interactionId); + case "link" -> LinkCommand.link(interactionId); default -> throw new InteractionException("Invalid command"); }; } catch (APIException e) { diff --git a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java new file mode 100644 index 0000000..102015d --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java @@ -0,0 +1,62 @@ +package me.stuffy.stuffybot.profiles; + +import com.opencsv.CSVReader; + +import java.io.StringReader; +import java.util.*; + +import static me.stuffy.stuffybot.utils.APIUtils.*; + +public class GlobalData { + private final Map linkedAccounts; + private final Map interactions; + + public GlobalData() { + String linkedContent = readFile(Objects.requireNonNull(getGitHubFile(getPrivateApiRepo(), "apis/linkeddb.csv"))); + + List csvData = new ArrayList<>(); + try (CSVReader reader = new CSVReader(new StringReader(linkedContent))) { + csvData = reader.readAll(); + } catch (Exception e) { + e.printStackTrace(); + } + + boolean firstRow = true; + Map linkedAccounts = new HashMap<>(); + for (String[] row : csvData) { + if(firstRow) { + firstRow = false; + continue; + } + try { + linkedAccounts.put(row[0], UUID.fromString(row[2])); + } catch (Exception e) { + e.printStackTrace(); + } + } + + this.linkedAccounts = linkedAccounts; + this.interactions = new HashMap<>(); + } + + public Map getLinkedAccounts() { + return this.linkedAccounts; + } + + public void addLinkedAccount(String discordId, UUID uuid) { + this.linkedAccounts.put(discordId, uuid); + } + + public void incrementInteractions(String discordId) { + this.interactions.put(discordId, this.interactions.getOrDefault(discordId, 0) + 1); + } + + public Map getInteractions() { + return this.interactions; + } + + public void clearInteractions() { + this.interactions.clear(); + } + +} diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index 7c19a57..fa4f812 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -6,26 +6,39 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.opencsv.CSVReader; +import com.opencsv.CSVWriter; +import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.commands.TournamentCommand; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.profiles.MojangProfile; import org.jetbrains.annotations.NotNull; +import org.kohsuke.github.GHContent; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; +import static me.stuffy.stuffybot.utils.Logger.log; import static me.stuffy.stuffybot.utils.Logger.logError; public class APIUtils { static String hypixelApiUrl = "https://api.hypixel.net/v2/"; static String mojangApiUrl = "https://api.mojang.com/"; + static String mojangSessionApiUrl = "https://sessionserver.mojang.com/"; + static String privateApiRepo = "stuffybot/PrivateAPI"; + static String publicApiRepo = "stuffybot/PublicAPI"; + public static HypixelProfile getHypixelProfile(String username) throws APIException { MojangProfile profile = getMojangProfile(username); String ign = profile.getUsername(); @@ -109,7 +122,7 @@ public static HypixelProfile fetchHypixelProfile(UUID uuid) throws APIException .expireAfterWrite(1, TimeUnit.HOURS) .build( new CacheLoader() { - public MojangProfile load(String username) { + public MojangProfile load(@NotNull String username) { try{ return fetchMojangProfile(username); } catch (APIException e){ @@ -274,4 +287,108 @@ public static JsonObject getTournamentData() { return null; } } + + public static GitHub connectToGitHub() { + try { + GitHub github = new GitHubBuilder().withOAuthToken(System.getenv("GITHUB_OAUTH")).build(); + log(" Connected to GitHub as " + github.getMyself().getLogin() + " successfully."); + return github; + } catch (IOException e) { + logError(" Failed to connect to GitHub: " + e.getMessage()); + return null; + } + } + + public static void updateGitHubFile(String repo, String path, String content, String message) { + try { + Bot.getGitHub().getRepository(repo).createContent() + .path(path) + .content(content) + .message(message) + .sha(Bot.getGitHub().getRepository(repo).getFileContent(path).getSha()) + .commit(); + log(" Updated file " + path + " in " + repo + " successfully."); + } catch (IOException e) { + logError(" Failed to update file " + path + " in " + repo + ": " + e.getMessage()); + } + } + + public static GHContent getGitHubFile(String repo, String path) { + try { + return Bot.getGitHub().getRepository(repo).getFileContent(path); + } catch (IOException e) { + logError(" Failed to get file " + path + " in " + repo + ": " + e.getMessage()); + return null; + } + } + + public static String getPrivateApiRepo() { + return privateApiRepo; + } + + public static void updateCommandStat(String discordId, String discordUsername, String commandName) { + // Update PrivateAPI/linkeddb.csv to update runner plus total interactions + // Update PublicAPI/bot.json to increment count for command stat on this bot + } + + public static String readFile(GHContent content) { + try { + InputStream inputStream = content.read(); + InputStreamReader reader = new InputStreamReader(inputStream); + StringBuilder file = new StringBuilder(); + int character; + while ((character = reader.read()) != -1) { + file.append((char) character); + } + return file.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void updateLinkedDB(String discordId, UUID uuid, String ign) { + String discordName = Objects.requireNonNull(Bot.getInstance().getJDA().getUserById(discordId)).getName(); + + GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); + if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); + + try { + // Read the CSV content + String linkedDBContent = readFile(linkedDB); + List csvData = new ArrayList<>(); + try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { + csvData = reader.readAll(); + } + + // Update the CSV content + boolean updated = false; + for (String[] row : csvData) { + if (row[0].equals(discordId)) { + row[1] = discordName; + row[2] = uuid.toString(); + row[3] = ign; + updated = true; + break; + } + } + if (!updated) { + csvData.add(new String[]{discordId, uuid.toString(), ign}); + } + + // Write the updated content back to the CSV file + StringWriter stringWriter = new StringWriter(); + try (CSVWriter writer = new CSVWriter(stringWriter)) { + writer.writeAll(csvData); + } + String updatedContent = stringWriter.toString(); + updateGitHubFile(privateApiRepo, "apis/linkeddb.csv", updatedContent, "`@" + discordName + "` linked as `" + ign + "`"); + Bot.getGlobalData().addLinkedAccount(discordId, uuid); + + } catch (Exception e) { + throw new RuntimeException(e); + } + + + } + } diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 35a4daf..160d45d 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -1,6 +1,7 @@ package me.stuffy.stuffybot.utils; import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.MojangProfile; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; @@ -13,11 +14,9 @@ import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; +import java.util.*; +import static me.stuffy.stuffybot.utils.APIUtils.getMojangProfile; import static me.stuffy.stuffybot.utils.MiscUtils.toSkillIssue; public class DiscordUtils { @@ -171,11 +170,15 @@ public static boolean isVerified(String userId) { return true; } - public static String getUsername(SlashCommandInteractionEvent event) { + public static String getUsername(SlashCommandInteractionEvent event) throws APIException { String username = event.getOption("ign") == null ? null : event.getOption("ign").getAsString(); if (username == null) { - // TODO: First, check the database - username = getDiscordUsername(event.getUser().getName()); + if (Bot.getGlobalData().getLinkedAccounts().containsKey(event.getUser().getId())) { + UUID uuid = Bot.getGlobalData().getLinkedAccounts().get(event.getUser().getId()); + username = getMojangProfile(uuid).getUsername(); + } else { + username = getDiscordUsername(event.getUser().getName()); + } } return username; } From ef4ae9f91647926a21c9fe8df5ab09ed3bf6c783 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:56:48 -0400 Subject: [PATCH 12/44] Account Linking --- .../stuffybot/commands/LinkCommand.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java diff --git a/src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java b/src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java new file mode 100644 index 0000000..f91b5f8 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java @@ -0,0 +1,71 @@ +package me.stuffy.stuffybot.commands; + +import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.interactions.InteractionId; +import me.stuffy.stuffybot.profiles.GlobalData; +import me.stuffy.stuffybot.profiles.HypixelProfile; +import me.stuffy.stuffybot.utils.APIException; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; +import static me.stuffy.stuffybot.utils.APIUtils.updateLinkedDB; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeEmbed; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; + +public class LinkCommand { + static Map linkDelay = new HashMap<>(); + static int linkDelayTime = 60000; + public static MessageCreateData link(InteractionId interactionId) throws APIException { + String ign = interactionId.getOptions().get("ign"); + String discordId = interactionId.getUserId(); + + HypixelProfile hypixelProfile = getHypixelProfile(ign); + + String mcUsername = hypixelProfile.getDisplayName(); + UUID uuid = hypixelProfile.getUuid(); + + // #TODO: Check if the user is verified + long current = System.currentTimeMillis(); + if (linkDelay.containsKey(discordId) && current - linkDelay.get(discordId) < linkDelayTime) { + return new MessageCreateBuilder() + .addEmbeds(makeErrorEmbed("Account linking failed", "Please wait a bit before trying to link your account again.")) + .build(); + } + + GlobalData globalData = Bot.getGlobalData(); + Map linkedAccounts = globalData.getLinkedAccounts(); + + if (linkedAccounts.containsKey(discordId) && linkedAccounts.get(discordId).equals(uuid)){ + return new MessageCreateBuilder() + .addEmbeds(makeErrorEmbed("Account linking failed", "You are already linked to this account.")) + .build(); + } + + linkDelay.put(discordId, current); + + try { + updateLinkedDB(discordId, uuid, mcUsername); + } catch (Exception e) { + e.printStackTrace(); + return new MessageCreateBuilder() + .addEmbeds(makeErrorEmbed("Account linking failed", "An error occurred while linking your account. Please try again later.")) + .build(); + } + + MessageEmbed linkEmbed = makeEmbed( + "Account Linked", + "Successfully linked as **" + mcUsername + "**!", + "You can now run commands without the ign parameter.", + 0x6AC672 + ); + + return new MessageCreateBuilder() + .addEmbeds(linkEmbed).build(); + } +} From b18cc94586060bf5ad88a3ebda7dba440b57f842 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:57:18 -0400 Subject: [PATCH 13/44] unfinished achievement stuff --- .../commands/AchievementsCommand.java | 57 +++++++++++++++++++ .../stuffybot/profiles/Achievement.java | 43 ++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java create mode 100644 src/main/java/me/stuffy/stuffybot/profiles/Achievement.java diff --git a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java new file mode 100644 index 0000000..09887c9 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java @@ -0,0 +1,57 @@ +package me.stuffy.stuffybot.commands; + +import com.google.gson.JsonObject; +import me.stuffy.stuffybot.interactions.InteractionId; +import me.stuffy.stuffybot.profiles.HypixelProfile; +import me.stuffy.stuffybot.utils.APIException; +import me.stuffy.stuffybot.utils.InvalidOptionException; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.util.Objects; + +import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; +import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; + +public class AchievementsCommand { + + public static MessageCreateData achievements(InteractionId interactionId) throws APIException, InvalidOptionException { + String ign = interactionId.getOption("ign"); + HypixelProfile hypixelProfile = getHypixelProfile(ign); + String username = hypixelProfile.getDisplayName(); + + String gameId = interactionId.getOption("game", "none"); + String viewType = interactionId.getOption("type", "all"); + + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + + if (achievementData == null) { + throw new APIException("Stuffy", "Failed to load achievement data."); + } + + JsonObject gameAchievements = null; + + for (String game : achievementData.keySet()) { + JsonObject gameData = achievementData.getAsJsonObject(game); + if (Objects.equals(game, gameId)) { + gameAchievements = gameData; + } + } + + if (gameAchievements == null) { + throw new InvalidOptionException("game", "Invalid game"); + } + + int total_points = 0; + int total_legacy_points = 0; + + String subtitle = " "; + String description = " "; + + + return new MessageCreateBuilder() + .addEmbeds(makeStatsEmbed(" ", subtitle, description)) + .build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/profiles/Achievement.java b/src/main/java/me/stuffy/stuffybot/profiles/Achievement.java new file mode 100644 index 0000000..b92bdb1 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/profiles/Achievement.java @@ -0,0 +1,43 @@ +package me.stuffy.stuffybot.profiles; + +import com.google.gson.JsonElement; + +public class Achievement { + private final String name; + private final String description; + private final int points; + private final boolean legacy; + private final boolean secret; + private final Type type; + + private enum Type { + CHALLENGE, + TIERED + }; + + public Achievement(JsonElement achievementData) { + this.name = achievementData.getAsJsonObject().get("name").getAsString(); + this.description = achievementData.getAsJsonObject().get("description").getAsString(); + this.points = achievementData.getAsJsonObject().get("points").getAsInt(); + this.legacy = achievementData.getAsJsonObject().get("legacy").getAsBoolean(); + this.secret = achievementData.getAsJsonObject().get("secret").getAsBoolean(); + this.type = Type.valueOf(achievementData.getAsJsonObject().get("type").getAsString()); + + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getPoints() { + return points; + } + + public boolean isLegacy() { + return legacy; + } +} From e6a558cae5e17937aa6246c2c57070244ea72673 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:57:38 -0400 Subject: [PATCH 14/44] tournament schema --- .../data/schemas/tournaments-schema.json | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 target/classes/data/schemas/tournaments-schema.json diff --git a/target/classes/data/schemas/tournaments-schema.json b/target/classes/data/schemas/tournaments-schema.json new file mode 100644 index 0000000..c35dabc --- /dev/null +++ b/target/classes/data/schemas/tournaments-schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Tournaments Schema", + "description": "Schema for tournament data, including the name, iteration, duration, and data for navigating the api to find specific stats", + "type": "object", + "properties": { + "lastUpdated": { + "type": "integer", + "description": "Unix timestamp of the last time the data was updated" + }, + "tournaments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The id of the tournament, denoted by the index in which it happened" + }, + "name": { + "type": "string", + "description": "The name of the tournament" + }, + "icon": { + "type": "string", + "description": "The path to the icon of the tournament, located at https://hypixel.net/styles/hypixel-v2/images/game-icons/{icon}" + }, + "iteration": { + "type": "integer", + "description": "The iteration of the tournament" + }, + "duration": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "description": "Unix timestamp of the start of the tournament" + }, + "end": { + "type": "integer", + "description": "Unix timestamp of the end of the tournament" + } + }, + "required": [ + "start", + "end" + ] + }, + "data": { + "type": "object", + "properties": { + "timeLimit": { + "type": "integer", + "description": "The maximum playtime for participants in minutes" + }, + "gameLimit": { + "type": "integer", + "description": "The maximum amount of games a participant can play" + }, + "tourneyField": { + "type": "string", + "description": "The field in the player's data that contains the tournament data" + }, + "wins": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins" + }, + "losses": { + "type": "string", + "description": "The field in the player's stats that contains the amount of losses" + }, + "winstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins in a row" + }, + "kills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills" + }, + "deaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of deaths" + }, + "killstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills in a row" + }, + "assists": { + "type": "string", + "description": "The field in the player's stats that contains the amount of assists" + }, + "finalKills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final kills" + }, + "finalDeaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final deaths" + } + }, + "oneOf": [ + { + "required": [ + "timeLimit" + ] + }, + { + "required": [ + "gameLimit" + ] + } + ], + "required": [ + "tourneyField", + "wins" + ], + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "icon", + "iteration", + "duration", + "data" + ] + } + } + } +} \ No newline at end of file From 88d05a8f91c71a542c25d8f4aec0eef72e27f55e Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:53:42 -0400 Subject: [PATCH 15/44] store bot data --- src/main/java/me/stuffy/stuffybot/Bot.java | 12 ++-- .../me/stuffy/stuffybot/events/BaseEvent.java | 2 +- .../stuffybot/events/UpdateBotStatsEvent.java | 30 +++++++-- .../interactions/InteractionHandler.java | 10 +++ .../stuffy/stuffybot/profiles/GlobalData.java | 40 +++++++++--- .../me/stuffy/stuffybot/utils/APIUtils.java | 64 ++++++++++++++++--- .../stuffy/stuffybot/utils/DiscordUtils.java | 1 - 7 files changed, 127 insertions(+), 32 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 2d8977d..d61dec6 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -73,15 +73,11 @@ public Bot() throws InterruptedException { // Handle SIGTERM Runtime.getRuntime().addShutdownHook(new Thread(() -> { Logger.log(" Bot shutting down, saving data..."); - // Wait like 5 seconds for data to save - Logger.log(" Not going down without a fight!"); -// try { -// Thread.sleep(5000); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } + // #TODO: Upload logs - // #TODO: Update Bot stats + + // Update Bot Stats + UpdateBotStatsEvent.publicExecute(); Logger.log(" Data saved, allowing for shutdown."); })); } diff --git a/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java b/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java index 5eeaeee..f5632a1 100644 --- a/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java @@ -21,7 +21,7 @@ public BaseEvent(String name, long interval, TimeUnit timeUnit) { } public void startFixedRateEvent() { - scheduler.scheduleAtFixedRate(this::execute, 0, interval, timeUnit); + scheduler.scheduleAtFixedRate(this::execute, interval, interval, timeUnit); } protected abstract void execute(); diff --git a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java index a3cb194..d970318 100644 --- a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java @@ -1,27 +1,45 @@ package me.stuffy.stuffybot.events; import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.Logger; import java.util.Map; import java.util.concurrent.TimeUnit; +import static me.stuffy.stuffybot.utils.APIUtils.updateBotStats; +import static me.stuffy.stuffybot.utils.APIUtils.updateUsersStats; + public class UpdateBotStatsEvent extends BaseEvent{ public UpdateBotStatsEvent() { - super("UpdateBotStats", 6, TimeUnit.HOURS); + super("UpdateBotStats", 30, TimeUnit.MINUTES); } @Override protected void execute() { - // How many servers the bot is in - // How many commands have been run + publicExecute(); + } + + public static void publicExecute() { + Logger.log(" Updating bot stats."); Bot bot = Bot.getInstance(); + GlobalData globalData = Bot.getGlobalData(); + int totalServers = bot.getJDA().getGuilds().size(); + Map uniqueUsers = globalData.getUniqueUsers(); + Map commandsRun = globalData.getCommandsRun(); + Map userCommandsRun = globalData.getUserCommandsRun(); - Map recentInteractions = Bot.getGlobalData().getInteractions(); - // # TODO: Update the bot stats on the website - Bot.getGlobalData().clearInteractions(); + if(commandsRun.isEmpty() && uniqueUsers.isEmpty()) { + Logger.log(" No data to update."); + return; + } + updateBotStats(totalServers, commandsRun); + updateUsersStats(uniqueUsers, userCommandsRun); + Logger.log(" Updated bot stats."); + globalData.clearCommandsRun(); + globalData.clearUniqueUsers(); } } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 87ca6d4..e9407ae 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -2,6 +2,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.APIException; import me.stuffy.stuffybot.utils.InteractionException; import me.stuffy.stuffybot.utils.Logger; @@ -75,6 +77,10 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray.toString()); + GlobalData globalData = Bot.getGlobalData(); + globalData.incrementCommandsRun(event.getUser().getId(), commandName); + globalData.addUniqueUser(event.getUser().getId(), event.getUser().getName()); + MessageCreateData response = null; try { response = getResponse(interactionId);; @@ -280,6 +286,10 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (event.getAuthor().isBot()) { return; } + String authorId = event.getAuthor().getId(); + String authorName = event.getAuthor().getName(); + Bot.getGlobalData().addUniqueUser(authorId, authorName); + Message.suppressContentIntentWarning(); String message = event.getMessage().getContentRaw(); if (message.toLowerCase().startsWith("ap!")) { diff --git a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java index 102015d..0632530 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java @@ -9,7 +9,9 @@ public class GlobalData { private final Map linkedAccounts; - private final Map interactions; + private final Map commandsRun; + private final Map uniqueUsers; + private final Map userCommandsRun; public GlobalData() { String linkedContent = readFile(Objects.requireNonNull(getGitHubFile(getPrivateApiRepo(), "apis/linkeddb.csv"))); @@ -29,6 +31,9 @@ public GlobalData() { continue; } try { + if(Objects.equals(row[2], "")){ + continue; + } linkedAccounts.put(row[0], UUID.fromString(row[2])); } catch (Exception e) { e.printStackTrace(); @@ -36,7 +41,9 @@ public GlobalData() { } this.linkedAccounts = linkedAccounts; - this.interactions = new HashMap<>(); + this.commandsRun = new HashMap<>(); + this.uniqueUsers = new HashMap<>(); + this.userCommandsRun = new HashMap<>(); } public Map getLinkedAccounts() { @@ -47,16 +54,33 @@ public void addLinkedAccount(String discordId, UUID uuid) { this.linkedAccounts.put(discordId, uuid); } - public void incrementInteractions(String discordId) { - this.interactions.put(discordId, this.interactions.getOrDefault(discordId, 0) + 1); + public void incrementCommandsRun(String runnerId, String commandName) { + this.commandsRun.put(commandName, this.commandsRun.getOrDefault(commandName, 0) + 1); + this.userCommandsRun.put(runnerId, this.userCommandsRun.getOrDefault(runnerId, 0) + 1); } - public Map getInteractions() { - return this.interactions; + public Map getCommandsRun() { + return this.commandsRun; } - public void clearInteractions() { - this.interactions.clear(); + public void clearCommandsRun() { + this.commandsRun.clear(); + this.userCommandsRun.clear(); } + public void addUniqueUser(String discordId, String discordName) { + this.uniqueUsers.put(discordId, discordName); + } + + public Map getUniqueUsers() { + return this.uniqueUsers; + } + + public void clearUniqueUsers() { + this.uniqueUsers.clear(); + } + + public Map getUserCommandsRun() { + return this.userCommandsRun; + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index fa4f812..2ea1077 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -23,10 +23,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; import static me.stuffy.stuffybot.utils.Logger.log; @@ -326,9 +323,60 @@ public static String getPrivateApiRepo() { return privateApiRepo; } - public static void updateCommandStat(String discordId, String discordUsername, String commandName) { - // Update PrivateAPI/linkeddb.csv to update runner plus total interactions - // Update PublicAPI/bot.json to increment count for command stat on this bot + + public static void updateBotStats(int totalServers, Map commandsRun) { + // TODO: Update PublicAPI/bot.json to update total servers and individual commands run + } + + public static void updateUsersStats(Map uniqueUsers, Map userCommandsRun) { + GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); + if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); + + try { + // Read the CSV content + String linkedDBContent = readFile(linkedDB); + List csvData; + try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { + csvData = reader.readAll(); + } + + int newUsers = 0; + int updatedUsers = 0; + int newCommandsRun = 0; + // Update the CSV content + for (String discordId : uniqueUsers.keySet()) { + String discordName = uniqueUsers.get(discordId); + int commandsRun = userCommandsRun.getOrDefault(discordId, 0); + newCommandsRun += commandsRun; + boolean updated = false; + for (String[] row : csvData) { + if (row[0].equals(discordId)) { + row[1] = discordName; + row[5] = String.valueOf(commandsRun + Integer.parseInt(row[5])); + updated = true; + updatedUsers++; + break; + } + } + if (!updated) { + newUsers++; + csvData.add(new String[]{discordId, discordName, "", "", "", String.valueOf(commandsRun)}); + } + } + + // Write the updated content back to the CSV file + StringWriter stringWriter = new StringWriter(); + try (CSVWriter writer = new CSVWriter(stringWriter)) { + writer.writeAll(csvData); + } + String updatedContent = stringWriter.toString(); + updateGitHubFile(privateApiRepo, "apis/linkeddb.csv", updatedContent, + "Added `" + newCommandsRun + "` new commands run by `" + newUsers + "` new users and `" + + updatedUsers + "` existing users."); + Logger.log(" Updated stats for " + newUsers + " new users and " + updatedUsers + " existing users."); + } catch (Exception e) { + throw new RuntimeException(e); + } } public static String readFile(GHContent content) { @@ -355,7 +403,7 @@ public static void updateLinkedDB(String discordId, UUID uuid, String ign) { try { // Read the CSV content String linkedDBContent = readFile(linkedDB); - List csvData = new ArrayList<>(); + List csvData; try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { csvData = reader.readAll(); } diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 160d45d..0c473ab 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -1,7 +1,6 @@ package me.stuffy.stuffybot.utils; import me.stuffy.stuffybot.Bot; -import me.stuffy.stuffybot.profiles.MojangProfile; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; From 0e96ce6f77223b475a471d744944bc19bb951055 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:55:40 -0400 Subject: [PATCH 16/44] update bot stats --- .../me/stuffy/stuffybot/utils/APIUtils.java | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index 2ea1077..6377c90 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -3,6 +3,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -325,7 +326,52 @@ public static String getPrivateApiRepo() { public static void updateBotStats(int totalServers, Map commandsRun) { - // TODO: Update PublicAPI/bot.json to update total servers and individual commands run + GHContent botStats = getGitHubFile(privateApiRepo, "apis/bot.json"); + if (botStats == null) throw new IllegalStateException("Failed to get bot.json from GitHub"); + + try { + // Read the JSON content + String botStatsContent = readFile(botStats); + JsonObject fullJson = JsonParser.parseString(botStatsContent).getAsJsonObject(); + + // Update the JSON content + fullJson.addProperty("lastUpdated", System.currentTimeMillis()); + JsonArray allBots = fullJson.get("bots").getAsJsonArray(); + String botId = Bot.getInstance().getJDA().getSelfUser().getId(); + + JsonObject bot = null; + for (JsonElement element : allBots) { + JsonObject currentBot = element.getAsJsonObject(); + if (currentBot.get("id").getAsString().equals(botId)) { + bot = currentBot; + break; + } + } + + if (bot == null) { + throw new IllegalStateException("Failed to find bot in bot.json"); + } + + bot.addProperty("servers", totalServers); + + JsonObject commands = bot.get("commandsRun").getAsJsonObject(); + + for (Map.Entry entry : commandsRun.entrySet()) { + String command = entry.getKey(); + int count = entry.getValue(); + int currentCount = 0; + if(commands.has(command)) currentCount = commands.get(command).getAsInt(); + commands.addProperty(command, currentCount + count); + } + + fullJson.add("bots", allBots); + + // Write the updated content back to the JSON file + updateGitHubFile(privateApiRepo, "apis/bot.json", fullJson.toString(), "Updated bot stats."); + log(" Updated bot stats for " + totalServers + " servers."); + } catch (Exception e) { + throw new RuntimeException(e); + } } public static void updateUsersStats(Map uniqueUsers, Map userCommandsRun) { From c7977d74eec27ccb98c0781ac43d19f4674e7de0 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:58:34 -0400 Subject: [PATCH 17/44] update every 2 hours, disregard empty updates --- .../stuffybot/events/UpdateBotStatsEvent.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java index d970318..a8c3270 100644 --- a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java @@ -12,7 +12,7 @@ public class UpdateBotStatsEvent extends BaseEvent{ public UpdateBotStatsEvent() { - super("UpdateBotStats", 30, TimeUnit.MINUTES); + super("UpdateBotStats", 2, TimeUnit.HOURS); } @Override @@ -31,13 +31,18 @@ public static void publicExecute() { Map commandsRun = globalData.getCommandsRun(); Map userCommandsRun = globalData.getUserCommandsRun(); - if(commandsRun.isEmpty() && uniqueUsers.isEmpty()) { + if(commandsRun.isEmpty()) { Logger.log(" No data to update."); - return; + } else { + updateBotStats(totalServers, commandsRun); + Logger.log(" Updated bot stats."); + } + if(uniqueUsers.isEmpty()) { + Logger.log(" No unique users to update."); + } else { + updateUsersStats(uniqueUsers, userCommandsRun); + Logger.log(" Updated user stats."); } - updateBotStats(totalServers, commandsRun); - updateUsersStats(uniqueUsers, userCommandsRun); - Logger.log(" Updated bot stats."); globalData.clearCommandsRun(); globalData.clearUniqueUsers(); From 88b336b38ee9113c021da0eb00e004798d05f2b8 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:44:10 -0400 Subject: [PATCH 18/44] log uploads. --- src/main/java/me/stuffy/stuffybot/Bot.java | 17 +++++++++--- .../me/stuffy/stuffybot/utils/APIUtils.java | 27 ++++++++++++++++++- .../me/stuffy/stuffybot/utils/Logger.java | 21 +++++++++++++-- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index d61dec6..bceefaf 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -5,6 +5,7 @@ import me.stuffy.stuffybot.events.UpdateBotStatsEvent; import me.stuffy.stuffybot.interactions.InteractionHandler; import me.stuffy.stuffybot.profiles.GlobalData; +import me.stuffy.stuffybot.utils.APIUtils; import me.stuffy.stuffybot.utils.DiscordUtils; import me.stuffy.stuffybot.utils.Logger; import net.dv8tion.jda.api.JDA; @@ -21,9 +22,12 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import org.kohsuke.github.GitHub; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import static me.stuffy.stuffybot.utils.APIUtils.connectToGitHub; +import static me.stuffy.stuffybot.utils.APIUtils.uploadLogs; public class Bot extends ListenerAdapter { private static Bot INSTANCE; @@ -48,9 +52,10 @@ public Bot() throws InterruptedException { assert this.homeGuild != null : "Failed to find home guild"; // Log startup - String time = DiscordUtils.discordTimeNow(); - String self = jda.getSelfUser().getAsMention(); - Logger.log(" Bot " + self + " started successfully " + time + "."); + String startupTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss")); + String self = jda.getSelfUser().getName(); + Logger.setLogName(startupTime); + Logger.log(" Bot " + self + " started successfully " + startupTime + "."); // Initialize GitHub GITHUB = connectToGitHub(); @@ -74,11 +79,15 @@ public Bot() throws InterruptedException { Runtime.getRuntime().addShutdownHook(new Thread(() -> { Logger.log(" Bot shutting down, saving data..."); - // #TODO: Upload logs + if(jda != null) { + jda.shutdown(); + } // Update Bot Stats UpdateBotStatsEvent.publicExecute(); Logger.log(" Data saved, allowing for shutdown."); + uploadLogs(); + Logger.log(" Data saved, allowing for shutdown."); })); } public static Bot getInstance() { diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index 6377c90..e72f8ae 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -311,6 +311,19 @@ public static void updateGitHubFile(String repo, String path, String content, St } } + public static void uploadGitHubFile(String repo, String path, String content, String message) { + try { + Bot.getGitHub().getRepository(repo).createContent() + .path(path) + .content(content) + .message(message) + .commit(); + log(" Uploaded file " + path + " in " + repo + " successfully."); + } catch (IOException e) { + logError(" Failed to upload file " + path + " in " + repo + ": " + e.getMessage()); + } + } + public static GHContent getGitHubFile(String repo, String path) { try { return Bot.getGitHub().getRepository(repo).getFileContent(path); @@ -481,8 +494,20 @@ public static void updateLinkedDB(String discordId, UUID uuid, String ign) { } catch (Exception e) { throw new RuntimeException(e); } + } + public static void uploadLogs() { + try { + // Read the logs content + String logsContent = String.join("\n", Logger.getLogs()); - } + String logName = Logger.getLogName(); + // Write the updated content back to the logs file + uploadGitHubFile(privateApiRepo, "apis/logs/" + Bot.getInstance().getJDA().getSelfUser().getAsTag() + "/" + logName + ".txt", logsContent, "Uploaded logs for " + logName + "."); + Logger.log(" Uploaded logs to GitHub."); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/Logger.java b/src/main/java/me/stuffy/stuffybot/utils/Logger.java index 40274b5..0cd9d67 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/Logger.java +++ b/src/main/java/me/stuffy/stuffybot/utils/Logger.java @@ -1,13 +1,17 @@ package me.stuffy.stuffybot.utils; +import java.util.ArrayList; +import java.util.List; + public class Logger { -// public Guild logGuild; -// public Channel logChannel; + private static final List logs = new ArrayList<>(); + private static String logName; public static void log(String message) { String date = java.time.LocalDate.now().toString(); String time = java.time.LocalTime.now().truncatedTo(java.time.temporal.ChronoUnit.SECONDS).toString(); message = "[" + date + " " + time + "] " + message; + logs.add(message); System.out.println(message); } @@ -15,6 +19,19 @@ public static void logError(String message) { String date = java.time.LocalDate.now().toString(); String time = java.time.LocalTime.now().truncatedTo(java.time.temporal.ChronoUnit.SECONDS).toString(); message = "[ERROR] [" + date + " " + time + "] " + message; + logs.add(message); System.out.println(message); } + + public static List getLogs() { + return logs; + } + + public static String getLogName() { + return logName; + } + + public static void setLogName(String name) { + logName = name; + } } From 1325683720682e802def8fab9f3a2816029272b6 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:34:24 -0400 Subject: [PATCH 19/44] fix updating linked database --- src/main/java/me/stuffy/stuffybot/Bot.java | 18 ++++++++++++++---- .../me/stuffy/stuffybot/utils/APIUtils.java | 6 ++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index bceefaf..7a15eeb 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -20,6 +20,7 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.requests.GatewayIntent; import org.kohsuke.github.GitHub; import java.time.LocalDateTime; @@ -84,10 +85,19 @@ public Bot() throws InterruptedException { } // Update Bot Stats - UpdateBotStatsEvent.publicExecute(); - Logger.log(" Data saved, allowing for shutdown."); - uploadLogs(); - Logger.log(" Data saved, allowing for shutdown."); + try{ + UpdateBotStatsEvent.publicExecute(); + Logger.log(" Bot Stats saved, allowing for shutdown."); + } catch (Exception e) { + Logger.log(" Failed to save Bot Stats, allowing for shutdown."); + } + try { + uploadLogs(); + Logger.log(" Logs uploaded, allowing for shutdown."); + } + catch (Exception e) { + Logger.log(" Failed to upload logs, allowing for shutdown."); + } })); } public static Bot getInstance() { diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index e72f8ae..5c52d43 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -11,6 +11,7 @@ import com.opencsv.CSVWriter; import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.commands.TournamentCommand; +import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.profiles.MojangProfile; import org.jetbrains.annotations.NotNull; @@ -411,6 +412,7 @@ public static void updateUsersStats(Map uniqueUsers, Map Date: Thu, 5 Sep 2024 00:35:12 -0400 Subject: [PATCH 20/44] fix typo --- .../me/stuffy/stuffybot/interactions/InteractionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index e9407ae..361dd2d 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -145,7 +145,7 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { } catch (InteractionException e) { event.deferEdit().queue(); MessageEmbed errorEmbed = makeErrorEmbed("Invalid Button Ownership", - "You can't use modify commands run by others.\n-# " + e.getMessage()); + "You can't use buttons on commands run by others.\n-# " + e.getMessage()); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; } From 99f518fe6fcdd10e77c9752cbd1b0352cdbe094a Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:35:50 -0400 Subject: [PATCH 21/44] message content stuff --- src/main/java/me/stuffy/stuffybot/Bot.java | 5 +++-- .../me/stuffy/stuffybot/interactions/InteractionHandler.java | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 7a15eeb..63a3214 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -42,8 +42,9 @@ public Bot() throws InterruptedException { INSTANCE = this; // Get token from env variable String token = System.getenv("BOT_TOKEN"); - JDABuilder builder = JDABuilder.createDefault(token) ; - builder.setActivity(Activity.customStatus("hating slash commands")); + JDABuilder builder = JDABuilder.createDefault(token); + builder.enableIntents(GatewayIntent.MESSAGE_CONTENT); // # TODO: Remove intents when possible + builder.setActivity(Activity.customStatus("almost ready...")); builder.addEventListeners(this); JDA jda = builder.build().awaitReady(); this.jda = jda; diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 361dd2d..d81754b 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -290,7 +290,6 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { String authorName = event.getAuthor().getName(); Bot.getGlobalData().addUniqueUser(authorId, authorName); - Message.suppressContentIntentWarning(); String message = event.getMessage().getContentRaw(); if (message.toLowerCase().startsWith("ap!")) { Logger.logError(" @" + event.getAuthor().getName() + ": " + message); From eaea3ed1c8c19b10088c4b1a03ed8ef224ce56a1 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 11 Sep 2024 02:18:26 -0400 Subject: [PATCH 22/44] PlayCommand Command --- src/main/java/me/stuffy/stuffybot/Bot.java | 9 ++-- .../commands/PlayCommandCommand.java | 53 +++++++++++++++++++ .../interactions/InteractionHandler.java | 47 +++++++++++++++- .../interactions/InteractionManager.java | 1 + .../me/stuffy/stuffybot/utils/APIUtils.java | 39 +++++++++++++- 5 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 63a3214..da49aba 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -54,7 +54,7 @@ public Bot() throws InterruptedException { assert this.homeGuild != null : "Failed to find home guild"; // Log startup - String startupTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss")); + String startupTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss")); String self = jda.getSelfUser().getName(); Logger.setLogName(startupTime); Logger.log(" Bot " + self + " started successfully " + startupTime + "."); @@ -81,9 +81,8 @@ public Bot() throws InterruptedException { Runtime.getRuntime().addShutdownHook(new Thread(() -> { Logger.log(" Bot shutting down, saving data..."); - if(jda != null) { - jda.shutdown(); - } + // Close JDA + jda.shutdown(); // Update Bot Stats try{ @@ -175,6 +174,8 @@ public void registerCommands(String scope) { // ))); commandList.add(Commands.slash("link", "Link a Minecraft account so you don't have to type your IGN every time") .addOptions(ignOptionRequired)); + commandList.add(Commands.slash("playcommand", "Lookup the command to quickly hop into a game") + .addOptions(new OptionData(OptionType.STRING, "game", "Search for a play command", true).setAutoComplete(true))); if (scope.equals("local")) { diff --git a/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java b/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java new file mode 100644 index 0000000..9dca682 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java @@ -0,0 +1,53 @@ +package me.stuffy.stuffybot.commands; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import me.stuffy.stuffybot.interactions.InteractionId; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import static me.stuffy.stuffybot.utils.APIUtils.getPlayCommands; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; + +public class PlayCommandCommand { + public static MessageCreateData playCommand(InteractionId interactionId) { + String input = interactionId.getOption("game"); + + JsonElement gameData = getPlayCommands().getAsJsonObject().get("gameData"); + if (gameData == null) { + return new MessageCreateBuilder().setContent("Failed to load play command data.").build(); + } + + JsonArray games = gameData.getAsJsonArray(); + for (JsonElement game : games) { + String gameName = game.getAsJsonObject().get("name").getAsString(); + JsonArray modes = game.getAsJsonObject().get("modes").getAsJsonArray(); + for (JsonElement mode : modes) { + if(!mode.getAsJsonObject().has("identifier") || !mode.getAsJsonObject().has("name")) { + continue; + } + String modeName = mode.getAsJsonObject().get("name").getAsString(); + String identifier = mode.getAsJsonObject().get("identifier").getAsString(); + String fullName; + if (gameName.equals(modeName)) { + fullName = gameName; + } else { + fullName = gameName + ": " + modeName; + } + if (input.equals(identifier)) { + return new MessageCreateBuilder().addEmbeds( + makeStatsEmbed("Play Command Search", "-# Use this to quickly join a game from anywhere." + + "\n**" + fullName + "**\n `/play " + identifier + "`") + ).build(); + } + } + } + + return new MessageCreateBuilder().addEmbeds( + makeErrorEmbed("Invalid Game or Mode", "I don't have a play command for that game or mode." + + "\n-# Try using the search feature.") + ).build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index d81754b..2507f0b 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -35,6 +35,7 @@ import java.util.stream.Stream; import static me.stuffy.stuffybot.interactions.InteractionManager.getResponse; +import static me.stuffy.stuffybot.utils.APIUtils.getPlayCommands; import static me.stuffy.stuffybot.utils.APIUtils.getTournamentData; import static me.stuffy.stuffybot.utils.DiscordUtils.*; import static me.stuffy.stuffybot.utils.MiscUtils.genBase64; @@ -228,7 +229,7 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent "Dreadlord", "Enderman", "Golem", "Herobrine", "Hunter", "Moleman", "Phoenix", "Pigman", "Pirate", "Renegade", "Shaman", "Shark", "Sheep", "Skeleton", "Snowman", "Spider", "Squid", "Werewolf", "Zombie"); for (String skin : skins) { - if (skin.toLowerCase().startsWith(currentInput.toLowerCase())) { + if (skin.toLowerCase().contains(currentInput.toLowerCase())) { options.add(skin); } } @@ -250,7 +251,7 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent if (commandOption.equals("tournament")) { List choices = new ArrayList<>(); tournamentMap.forEach((name, id) -> { - if (name.toLowerCase().startsWith(currentInput.toLowerCase()) && choices.size() <= 25){ + if (name.toLowerCase().contains(currentInput.toLowerCase()) && choices.size() <= 25){ choices.add(new Command.Choice(name, id)); } }); @@ -258,6 +259,48 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent break; } } + case "playcommand" -> { + if (commandOption.equals("game")) { + JsonElement gameData = getPlayCommands().getAsJsonObject().get("gameData"); + if (gameData == null) { + e.replyChoices(Collections.emptyList()).queue(); + break; + } + + List choices = new ArrayList<>(); + for (JsonElement entry : gameData.getAsJsonArray()) { + String gameName = entry.getAsJsonObject().get("name").getAsString(); + JsonElement modes = entry.getAsJsonObject().get("modes"); + if (modes == null) { + continue; + } + + for (JsonElement modeEntry : modes.getAsJsonArray()) { + if(!modeEntry.getAsJsonObject().has("name") || !modeEntry.getAsJsonObject().has("identifier")) { + continue; + } + String modeName = modeEntry.getAsJsonObject().get("name").getAsString(); + String fullGameName; + if (gameName.equals(modeName)) { + fullGameName = gameName; + } else { + fullGameName = gameName + ": " + modeName; + } + String identifier = modeEntry.getAsJsonObject().get("identifier").getAsString(); + if (fullGameName.toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(fullGameName, identifier)); + } + } + } + + if (choices.size() > 25) { + choices = choices.subList(0, 24); + } + + e.replyChoices(choices).queue(); + break; + } + } default -> { e.replyChoices(Collections.emptyList()).queue(); } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java index 30af66d..354abdc 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java @@ -43,6 +43,7 @@ public static MessageCreateData getResponse(InteractionId interactionId) throws case "tournament" -> TournamentCommand.tournament(interactionId); case "achievements" -> AchievementsCommand.achievements(interactionId); case "link" -> LinkCommand.link(interactionId); + case "playcommand" -> PlayCommandCommand.playCommand(interactionId); default -> throw new InteractionException("Invalid command"); }; } catch (APIException e) { diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index 5c52d43..7c65ef8 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -11,7 +11,6 @@ import com.opencsv.CSVWriter; import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.commands.TournamentCommand; -import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.profiles.MojangProfile; import org.jetbrains.annotations.NotNull; @@ -37,6 +36,7 @@ public class APIUtils { static String mojangSessionApiUrl = "https://sessionserver.mojang.com/"; static String privateApiRepo = "stuffybot/PrivateAPI"; static String publicApiRepo = "stuffybot/PublicAPI"; + static String stuffyApiUrl = "https://raw.githubusercontent.com/stuffybot/PublicAPI/main/"; public static HypixelProfile getHypixelProfile(String username) throws APIException { MojangProfile profile = getMojangProfile(username); @@ -274,8 +274,45 @@ private static JsonElement fetchAchievementsResources() { } } + private static final LoadingCache playCommandCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build( + new CacheLoader<>() { + public JsonElement load(@NotNull String key) { + return fetchPlayCommands(); + } + } + ); + + public static JsonElement getPlayCommands() { + return playCommandCache.getUnchecked("achievements"); + } + + private static JsonElement fetchPlayCommands() { + try { + HttpRequest getRequest = HttpRequest.newBuilder() + .uri(URI.create(stuffyApiUrl + "apis/playcommands.json")) + .build(); + HttpClient client = HttpClient.newHttpClient(); + HttpResponse response = client.send(getRequest, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + + JsonParser parser = new JsonParser(); + + return parser.parse(response.body()); + + } else { + throw new IllegalStateException("Unexpected response from Stuffy API: " + response.statusCode()); + } + } catch (Exception e) { + throw new RuntimeException("Failed to fetch play commands from Stuffy API", e); + } + } + public static JsonObject getTournamentData() { + // # TODO: Switch to https://raw.githubusercontent.com/stuffybot/PublicAPI/main/apis/tournaments.json try (InputStream inputStream = TournamentCommand.class.getResourceAsStream("/data/tournaments.json")) { assert inputStream != null; try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { From 75da862894ce3cb7cd55598861731c7d29fc4099 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:42:58 -0400 Subject: [PATCH 23/44] HelpCommand and updated links --- src/main/java/me/stuffy/stuffybot/Bot.java | 2 +- .../stuffybot/commands/HelpCommand.java | 44 +++++++++++++++++++ .../interactions/InteractionHandler.java | 2 +- .../interactions/InteractionManager.java | 1 + .../stuffy/stuffybot/utils/DiscordUtils.java | 7 ++- 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index da49aba..bbfa00a 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -147,7 +147,7 @@ public void registerCommands(String scope) { OptionData ignOptionRequired = new OptionData(OptionType.STRING, "ign", "The player's IGN", true); // Create a list of commands first ArrayList commandList = new ArrayList<>(); -// commandList.add(Commands.slash("help", "*Should* show a help message")); + commandList.add(Commands.slash("help", "Learn about the bot and its commands")); commandList.add(Commands.slash("pit", "Get Pit stats for a player") .addOptions(ignOption)); commandList.add(Commands.slash("stats", "Get Hypixel stats for a player") diff --git a/src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java b/src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java new file mode 100644 index 0000000..176e43c --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java @@ -0,0 +1,44 @@ +package me.stuffy.stuffybot.commands; + +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.util.HashMap; +import java.util.Map; + +import static me.stuffy.stuffybot.utils.DiscordUtils.makeEmbed; + +public class HelpCommand { + public static MessageCreateData help() { + Map commands = new HashMap<>(); + commands.put("`/link`", "Set a Default account to check when running commands"); + commands.put("`/stats`", "Get your Hypixel stats"); + commands.put("`/maxes`", "Get your maxed games"); + commands.put("`/playcommand`", "Lookup the command to quickly hop into a game"); + commands.put("`/tournament`", "Get your tournament stats"); + + StringBuilder commandList = new StringBuilder(); + for (Map.Entry entry : commands.entrySet()) { + commandList.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + + String helpText = "-# Bot Commands\n" + commandList; + helpText += """ + ...and more! + + -# Discord Server + Join the [discord server](https://discord.gg/X6WJT7WNVz) for help, feedback, and more! + We also announce Staff Rank changes, Hypixel leaks, and more! + + -# Source Code + The source code for this bot, its ToS, and Privacy Policy are + available on [GitHub](https://github.com/stuffyerface/stuffybot-java). + Feel free to contribute, report bugs, or suggest new features! + Don't forget to follow, star, and check out our API! + """; + + return new MessageCreateBuilder().addEmbeds( + makeEmbed("Stuffy Bot Help Menu", null, helpText, 0x570FF4, 30) + ).build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 2507f0b..88c789b 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -337,7 +337,7 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (message.toLowerCase().startsWith("ap!")) { Logger.logError(" @" + event.getAuthor().getName() + ": " + message); MessageCreateData data = new MessageCreateBuilder() - .addEmbeds(makeErrorEmbed("Outdated Command", "We no longer support chat based commands,\nInstead try using slash commands.\n-# Join our [Discord](https://discord.gg/zqVkUrUmzN) for more info.")) + .addEmbeds(makeErrorEmbed("Outdated Command", "We no longer support chat based commands,\nInstead try using slash commands.\n-# Join our [Discord](https://discord.gg/8jdmT5Db3Y) for more info.")) .build(); event.getMessage().reply( data diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java index 354abdc..6b136d1 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java @@ -44,6 +44,7 @@ public static MessageCreateData getResponse(InteractionId interactionId) throws case "achievements" -> AchievementsCommand.achievements(interactionId); case "link" -> LinkCommand.link(interactionId); case "playcommand" -> PlayCommandCommand.playCommand(interactionId); + case "help" -> HelpCommand.help(); default -> throw new InteractionException("Invalid command"); }; } catch (APIException e) { diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 0c473ab..4cf6f0b 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -19,12 +19,12 @@ import static me.stuffy.stuffybot.utils.MiscUtils.toSkillIssue; public class DiscordUtils { - public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor) { + public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor, Integer maxLines) { EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setTitle(embedTitle); String[] lines = embedContent.split("\n"); int lineCount = lines.length; - if (lineCount <= 15) { + if (lineCount <= maxLines) { if(embedSubtitle == null) embedBuilder.setDescription(embedContent); else @@ -51,6 +51,9 @@ public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, St return embedBuilder.build(); } + public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor) { + return makeEmbed(embedTitle, null, embedContent, embedColor, 15); + } public static MessageEmbed makeErrorEmbed(String embedTitle, String embedContent) { if (Calendar.getInstance().get(Calendar.MONTH) == Calendar.APRIL && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) == 1){ embedTitle = toSkillIssue(embedTitle); From 019d4e4265dd9160c347147b9e81f84be51656cd Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Fri, 13 Sep 2024 03:47:15 -0400 Subject: [PATCH 24/44] Achievement Command basic stuff --- src/main/java/me/stuffy/stuffybot/Bot.java | 18 ++- .../commands/AchievementsCommand.java | 33 ++++- .../stuffybot/profiles/HypixelProfile.java | 140 ++++++++++-------- .../me/stuffy/stuffybot/utils/MiscUtils.java | 30 ++++ 4 files changed, 150 insertions(+), 71 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index bbfa00a..9d5ecb5 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -16,6 +16,7 @@ import net.dv8tion.jda.api.events.guild.GuildJoinEvent; import net.dv8tion.jda.api.events.guild.GuildLeaveEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; @@ -164,14 +165,15 @@ public void registerCommands(String scope) { commandList.add(Commands.slash("tournament", "Get tournament stats for a player") .addOptions(ignOption) .addOptions(new OptionData(OptionType.INTEGER, "tournament", "Which tournament to look at (Leave empty for latest)", false).setAutoComplete(true))); -// commandList.add(Commands.slash("achievements", "Get achievement stats for a player") -// .addOptions(ignOption) -// .addOptions(new OptionData(OptionType.STRING, "game", "Which game to look at", false).setAutoComplete(true)) -// .addOptions(new OptionData(OptionType.STRING, "type", "Which achievements to look at", false).addChoices( -// new Command.Choice("All", "all"), -// new Command.Choice("Challenge", "challenge"), -// new Command.Choice("Tiered", "tiered") -// ))); + commandList.add(Commands.slash("achievements", "Get achievement stats for a player") + .addOptions(ignOption) + .addOptions(new OptionData(OptionType.STRING, "game", "Which game to look at", false).setAutoComplete(true)) + .addOptions(new OptionData(OptionType.STRING, "type", "Which achievements to look at", false).addChoices( + new Command.Choice("All", "all"), + new Command.Choice("Challenge", "challenge"), + new Command.Choice("Tiered", "tiered") + ) + )); commandList.add(Commands.slash("link", "Link a Minecraft account so you don't have to type your IGN every time") .addOptions(ignOptionRequired)); commandList.add(Commands.slash("playcommand", "Lookup the command to quickly hop into a game") diff --git a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java index 09887c9..f23e957 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java @@ -8,11 +8,14 @@ import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import java.text.DecimalFormat; import java.util.Objects; import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; +import static me.stuffy.stuffybot.utils.MiscUtils.getMaxAchievementPoints; +import static me.stuffy.stuffybot.utils.MiscUtils.getMaxAchievements; public class AchievementsCommand { @@ -24,13 +27,35 @@ public static MessageCreateData achievements(InteractionId interactionId) throws String gameId = interactionId.getOption("game", "none"); String viewType = interactionId.getOption("type", "all"); - JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + JsonObject gameAchievements = null; + + if (gameId.equals("none")) { + StringBuilder content = new StringBuilder(); + int unlockedAchievements = hypixelProfile.getAchievementsUnlocked(); + int maxAchievements = getMaxAchievements(); + int achievementPoints = hypixelProfile.getAchievementPoints(); + int maxAchievementPoints = getMaxAchievementPoints(); + + int legacyUnlocked = hypixelProfile.getLegacyAchievementsUnlocked(); + int legacyPoints = hypixelProfile.getLegacyAchievementPoints(); + + DecimalFormat thousands = new DecimalFormat("#,###"); + DecimalFormat percentage = new DecimalFormat("#.##"); - if (achievementData == null) { - throw new APIException("Stuffy", "Failed to load achievement data."); + content.append("Unlocked: **").append(thousands.format(unlockedAchievements)).append("**/").append(thousands.format(maxAchievements)).append(" (").append(percentage.format((double) unlockedAchievements / maxAchievements * 100)).append("%)\n"); + content.append("Points: **").append(thousands.format(achievementPoints)).append("**/").append(thousands.format(maxAchievementPoints)).append(" (").append(percentage.format((double) achievementPoints / maxAchievementPoints * 100)).append("%)\n\n"); + content.append("Legacy Unlocked: **").append(thousands.format(legacyUnlocked)).append("**\n"); + content.append("Legacy Points: **").append(thousands.format(legacyPoints)).append("**\n\n"); + + content.append("Easiest challenge: **").append(hypixelProfile.getEasiestChallenge()).append("**\n"); + content.append("Closest tiered: **").append(hypixelProfile.getEasiestTiered()).append("**\n"); + + return new MessageCreateBuilder() + .addEmbeds(makeStatsEmbed("Achievement Data for " + hypixelProfile.getDisplayName(), "why is not showing", content.toString())) + .build(); } - JsonObject gameAchievements = null; + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); for (String game : achievementData.keySet()) { JsonObject gameData = achievementData.getAsJsonObject(game); diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index a74eb3e..1778c53 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -12,16 +12,72 @@ import static me.stuffy.stuffybot.utils.MiscUtils.*; public class HypixelProfile { - private UUID uuid; + private final UUID uuid; + private final Rank rank; + private final JsonObject profile; private String displayName; - private Rank rank; - private JsonObject profile; + private final int achievementPoints; + private int achievementsUnlocked; + private int legacyAchievementPoints; + private int legacyAchievementsUnlocked; public HypixelProfile(JsonObject profile) { this.profile = profile.deepCopy(); this.uuid = MiscUtils.formatUUID(profile.get("uuid").getAsString()); this.displayName = profile.get("displayname").getAsString(); this.rank = determineRank(profile); + this.achievementPoints = getNestedJson(0, profile, "achievementPoints").getAsInt(); + + instantiateAchievements(); + } + + private void instantiateAchievements() { + int unlockCount = 0; + int unlockCountLegacy = 0; + int pointCountLegacy = 0; + + JsonObject achievements = getAchievements(); + List playerOneTime = achievements.get("achievementsOneTime").getAsJsonArray().asList(); + List playerOneTimeString = new ArrayList<>(); + for (JsonElement element : playerOneTime) { + try { + playerOneTimeString.add(element.getAsString()); + } catch (Exception ignored) { + } + } + JsonObject playerTiered = achievements.get("achievementsTiered").getAsJsonObject(); + JsonElement achievementsResources = getAchievementsResources(); + for (String game : achievementsResources.getAsJsonObject().keySet()) { + for (String oneTime : getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time").getAsJsonObject().keySet()) { + boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "legacy").getAsBoolean(); + if (playerOneTimeString.contains((game + "_" + oneTime.toLowerCase()))) { + if (!isLegacy) { + unlockCount++; + } else { + unlockCountLegacy++; + pointCountLegacy += getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "points").getAsInt(); + } + } + } + + for (String tiered : getNestedJson(achievementsResources.getAsJsonObject(), game, "tiered").getAsJsonObject().keySet()) { + boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "tiered", tiered, "legacy").getAsBoolean(); + for (JsonElement tier : getNestedJson(achievementsResources.getAsJsonObject(), game, "tiered", tiered, "tiers").getAsJsonArray()) { + int tierAmount = tier.getAsJsonObject().get("amount").getAsInt(); + if (getNestedJson(0, playerTiered, game + "_" + tiered.toLowerCase()).getAsInt() >= tierAmount) { + if (!isLegacy) { + unlockCount++; + } else { + unlockCountLegacy++; + pointCountLegacy += tier.getAsJsonObject().get("points").getAsInt(); + } + } + } + } + } + this.achievementsUnlocked = unlockCount; + this.legacyAchievementsUnlocked = unlockCountLegacy; + this.legacyAchievementPoints = pointCountLegacy; } private static Rank determineRank(JsonObject profile) { @@ -76,14 +132,6 @@ public String getDiscord() { return getNestedJson(profile, "socialMedia", "links", "DISCORD").getAsString(); } - public String[] getMaxedGames() { - // return an array of strings with the maxed games - JsonElement allAchievements = getAchievementsResources(); - JsonElement achievements = getNestedJson(profile, "achievements"); - - return new String[0]; - } - public JsonObject getProfile() { return profile; } @@ -108,12 +156,8 @@ public Integer getKarma() { return getNestedJson(profile, "karma").getAsInt(); } - public Integer getAchievementPoints() { - if (!profile.has("achievementPoints")) { - return 0; - } - - return getNestedJson(profile, "achievementPoints").getAsInt(); + public int getAchievementPoints() { + return this.achievementPoints; } public String getOnlineStatus() { @@ -367,48 +411,6 @@ public JsonObject getAchievements() { return combined; } - public Integer getLegacyAchievementPoints() { - int legacyPoints = 0; - JsonObject achievements = getAchievements(); - List playerOneTime = achievements.get("achievementsOneTime").getAsJsonArray().asList(); - List playerOneTimeString = new ArrayList<>(); - for (JsonElement element : playerOneTime) { - try { - playerOneTimeString.add(element.getAsString()); - } catch (Exception ignored) { - } - } - JsonObject playerTiered = achievements.get("achievementsTiered").getAsJsonObject(); - JsonElement achievementsResources = getAchievementsResources(); - for (String game : achievementsResources.getAsJsonObject().keySet()) { - for (String oneTime : getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time").getAsJsonObject().keySet()) { - boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "legacy").getAsBoolean(); - if (!isLegacy) { - continue; - } - if (playerOneTimeString.contains((game + "_" + oneTime.toLowerCase()))) { - legacyPoints += getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "points").getAsInt(); - } - } - - for (String tiered : getNestedJson(achievementsResources.getAsJsonObject(), game, "tiered").getAsJsonObject().keySet()) { - boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "tiered", tiered, "legacy").getAsBoolean(); - if (!isLegacy) { - continue; - } - - - for (JsonElement tier : getNestedJson(0, achievementsResources.getAsJsonObject(), game, "tiered", tiered, "tiers").getAsJsonArray()) { - int tierAmount = tier.getAsJsonObject().get("amount").getAsInt(); - if (getNestedJson(0, playerTiered, game + "_" + tiered.toLowerCase()).getAsInt() >= tierAmount) { - legacyPoints += tier.getAsJsonObject().get("points").getAsInt(); - } - } - } - } - return legacyPoints; - } - public Integer getPit(String stat) { JsonObject pitStats = getNestedJson(profile, "stats", "Pit").getAsJsonObject(); try { @@ -668,5 +670,25 @@ public Integer getStat(String field) { return 0; } } + + public int getAchievementsUnlocked() { + return achievementsUnlocked; + } + + public int getLegacyAchievementsUnlocked() { + return legacyAchievementsUnlocked; + } + + public int getLegacyAchievementPoints() { + return legacyAchievementPoints; + } + + public String getEasiestChallenge() { + return "`Game: Easy Challenge` (0.03%)"; + } + + public String getEasiestTiered() { + return "`Game: Close Tiered III` (97.78%)"; + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java index d6ff5d5..5a08db6 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java @@ -10,6 +10,8 @@ import java.util.Map; import java.util.UUID; +import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; + public class MiscUtils { private static final Gson gson = new Gson(); public static UUID formatUUID(String uuid) { @@ -201,5 +203,33 @@ public static String minutesFormatted(int minutes) { return remainingMinutes + "m"; return hours + "h " + remainingMinutes + "m"; } + public static int getMaxAchievements() { + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + int total = 0; + for (String game : achievementData.keySet()) { + JsonObject gameData = achievementData.getAsJsonObject(game); + + JsonObject oneTime = gameData.getAsJsonObject("one_time"); + JsonObject tiered = gameData.getAsJsonObject("tiered"); + + for (String key : oneTime.keySet()) { + boolean isLegacy = getNestedJson(false, oneTime, key, "legacy").getAsBoolean(); + if (!isLegacy) { + total++; + } + } + for (String key : tiered.keySet()) { + boolean isLegacy = getNestedJson(false, tiered, key, "legacy").getAsBoolean(); + if (!isLegacy) { + total+= getNestedJson(tiered, key, "tiers").getAsJsonArray().size(); + } + } + } + return total; + } + + public static int getMaxAchievementPoints() { + return 100000; + } } From b97a6b41b5a8d028632a9b07a5bedd4aa2e3daee Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:52:06 -0400 Subject: [PATCH 25/44] easiest challenge ap --- .../stuffybot/profiles/HypixelProfile.java | 21 +++++++++++++++---- .../me/stuffy/stuffybot/utils/MiscUtils.java | 8 +++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index 1778c53..539d42b 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -20,6 +20,8 @@ public class HypixelProfile { private int achievementsUnlocked; private int legacyAchievementPoints; private int legacyAchievementsUnlocked; + private String easiestChallenge; + private double easiestChallengeGlobalPercent; public HypixelProfile(JsonObject profile) { this.profile = profile.deepCopy(); @@ -36,6 +38,9 @@ private void instantiateAchievements() { int unlockCountLegacy = 0; int pointCountLegacy = 0; + String easiestChallenge = null; + double easiestChallengeGlobalPercent = 0; + JsonObject achievements = getAchievements(); List playerOneTime = achievements.get("achievementsOneTime").getAsJsonArray().asList(); List playerOneTimeString = new ArrayList<>(); @@ -57,6 +62,12 @@ private void instantiateAchievements() { unlockCountLegacy++; pointCountLegacy += getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "points").getAsInt(); } + } else { + double globalPercentUnlocked = getNestedJson(0.0, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "globalPercentUnlocked").getAsDouble(); + if (globalPercentUnlocked > easiestChallengeGlobalPercent) { + easiestChallengeGlobalPercent = globalPercentUnlocked; + easiestChallenge = game + ": " + getNestedJson("Unknown", achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "name").getAsString(); + } } } @@ -78,6 +89,8 @@ private void instantiateAchievements() { this.achievementsUnlocked = unlockCount; this.legacyAchievementsUnlocked = unlockCountLegacy; this.legacyAchievementPoints = pointCountLegacy; + this.easiestChallenge = easiestChallenge; + this.easiestChallengeGlobalPercent = easiestChallengeGlobalPercent; } private static Rank determineRank(JsonObject profile) { @@ -672,19 +685,19 @@ public Integer getStat(String field) { } public int getAchievementsUnlocked() { - return achievementsUnlocked; + return this.achievementsUnlocked; } public int getLegacyAchievementsUnlocked() { - return legacyAchievementsUnlocked; + return this.legacyAchievementsUnlocked; } public int getLegacyAchievementPoints() { - return legacyAchievementPoints; + return this.legacyAchievementPoints; } public String getEasiestChallenge() { - return "`Game: Easy Challenge` (0.03%)"; + return this.easiestChallenge + " (" + String.format("%.2f", this.easiestChallengeGlobalPercent) + "%)"; } public String getEasiestTiered() { diff --git a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java index 5a08db6..e51b7d2 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java @@ -45,6 +45,14 @@ public static JsonElement getNestedJson(Integer defaultValue, Object object, Str } } + public static JsonElement getNestedJson(Double defaultValue, Object object, String... keys) { + try { + return getNestedJson((JsonObject) object, keys); + } catch (IllegalArgumentException e) { + return stringToJson(defaultValue.toString()); + } + } + public static JsonElement getNestedJson(Boolean defaultValue, Object object, String... keys) { try { return getNestedJson((JsonObject) object, keys); From df9c0a1f4f8018a04e3962f223dc3198ad728699 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:59:24 -0400 Subject: [PATCH 26/44] easiest challenge ap --- .../me/stuffy/stuffybot/profiles/HypixelProfile.java | 2 +- src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index 539d42b..f676392 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -66,7 +66,7 @@ private void instantiateAchievements() { double globalPercentUnlocked = getNestedJson(0.0, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "globalPercentUnlocked").getAsDouble(); if (globalPercentUnlocked > easiestChallengeGlobalPercent) { easiestChallengeGlobalPercent = globalPercentUnlocked; - easiestChallenge = game + ": " + getNestedJson("Unknown", achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "name").getAsString(); + easiestChallenge = getNestedJson("Unknown", achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "name").getAsString(); } } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java index e51b7d2..0dfdd3c 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java @@ -238,6 +238,14 @@ public static int getMaxAchievements() { } public static int getMaxAchievementPoints() { - return 100000; + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + int total = 0; + for (String game : achievementData.keySet()) { + JsonObject gameData = achievementData.getAsJsonObject(game); + + int totalPoints = getNestedJson(0, gameData, "total_points").getAsInt(); + total += totalPoints; + } + return total; } } From 5447e00b2a1d48c2900094d8749d1433e2b0b58a Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:49:01 -0400 Subject: [PATCH 27/44] achievement command basics --- .../commands/AchievementsCommand.java | 165 +++++++++++++++--- .../interactions/InteractionHandler.java | 56 +++--- .../stuffy/stuffybot/utils/DiscordUtils.java | 2 +- .../me/stuffy/stuffybot/utils/MiscUtils.java | 27 ++- 4 files changed, 203 insertions(+), 47 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java index f23e957..774e1bf 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java @@ -1,35 +1,39 @@ package me.stuffy.stuffybot.commands; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import me.stuffy.stuffybot.interactions.InteractionId; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.utils.APIException; import me.stuffy.stuffybot.utils.InvalidOptionException; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; import java.text.DecimalFormat; -import java.util.Objects; +import java.util.*; import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; -import static me.stuffy.stuffybot.utils.MiscUtils.getMaxAchievementPoints; -import static me.stuffy.stuffybot.utils.MiscUtils.getMaxAchievements; +import static me.stuffy.stuffybot.utils.MiscUtils.*; public class AchievementsCommand { + private static final Map gameDataCache = new HashMap<>(); public static MessageCreateData achievements(InteractionId interactionId) throws APIException, InvalidOptionException { String ign = interactionId.getOption("ign"); HypixelProfile hypixelProfile = getHypixelProfile(ign); String username = hypixelProfile.getDisplayName(); - String gameId = interactionId.getOption("game", "none"); + String readableName = interactionId.getOption("game", "none"); String viewType = interactionId.getOption("type", "all"); JsonObject gameAchievements = null; - if (gameId.equals("none")) { + if (readableName.equals("none")) { StringBuilder content = new StringBuilder(); int unlockedAchievements = hypixelProfile.getAchievementsUnlocked(); int maxAchievements = getMaxAchievements(); @@ -45,38 +49,155 @@ public static MessageCreateData achievements(InteractionId interactionId) throws content.append("Unlocked: **").append(thousands.format(unlockedAchievements)).append("**/").append(thousands.format(maxAchievements)).append(" (").append(percentage.format((double) unlockedAchievements / maxAchievements * 100)).append("%)\n"); content.append("Points: **").append(thousands.format(achievementPoints)).append("**/").append(thousands.format(maxAchievementPoints)).append(" (").append(percentage.format((double) achievementPoints / maxAchievementPoints * 100)).append("%)\n\n"); content.append("Legacy Unlocked: **").append(thousands.format(legacyUnlocked)).append("**\n"); - content.append("Legacy Points: **").append(thousands.format(legacyPoints)).append("**\n\n"); + content.append("Legacy Points: **").append(thousands.format(legacyPoints)).append("**"); - content.append("Easiest challenge: **").append(hypixelProfile.getEasiestChallenge()).append("**\n"); - content.append("Closest tiered: **").append(hypixelProfile.getEasiestTiered()).append("**\n"); +// content.append("\n\nEasiest challenge: **").append(hypixelProfile.getEasiestChallenge()).append("**\n"); +// content.append("Closest tiered: **").append(hypixelProfile.getEasiestTiered()).append("**\n"); return new MessageCreateBuilder() - .addEmbeds(makeStatsEmbed("Achievement Data for " + hypixelProfile.getDisplayName(), "why is not showing", content.toString())) + .addEmbeds(makeStatsEmbed("Achievement Data for " + username, content.toString())) .build(); } JsonObject achievementData = getAchievementsResources().getAsJsonObject(); - - for (String game : achievementData.keySet()) { - JsonObject gameData = achievementData.getAsJsonObject(game); - if (Objects.equals(game, gameId)) { - gameAchievements = gameData; + String gameId = fromReadableName(readableName); + + String _id = interactionId.getId(); + if (gameDataCache.containsKey(_id)) { + gameAchievements = gameDataCache.get(_id); + } else { + for (String game : achievementData.keySet()) { + if (Objects.equals(game, gameId)) { + gameAchievements = achievementData.getAsJsonObject(game); + gameDataCache.put(_id, gameAchievements); + } } } if (gameAchievements == null) { - throw new InvalidOptionException("game", "Invalid game"); + throw new InvalidOptionException("game", readableName); + } + + MessageCreateBuilder messageCreateBuilder = new MessageCreateBuilder(); + + + Button allButton = Button.of(ButtonStyle.PRIMARY, interactionId.setOption("type", "all").getInteractionString(), "All"); + Button challengeButton = Button.of(ButtonStyle.PRIMARY, interactionId.setOption("type", "challenge").getInteractionString(), "Challenge"); + Button tieredButton = Button.of(ButtonStyle.PRIMARY, interactionId.setOption("type", "tiered").getInteractionString(), "Tiered"); + + String embedTitle = ""; + String embedContent = ""; + + List challengeUnlocked = new ArrayList<>(); + List challengeLocked = new ArrayList<>(); + int challengeMaxUnlocked = 0; + int challengeMaxPoints = 0; + int challengeUnlockedPoints = 0; + JsonArray challengeAchievements = hypixelProfile.getAchievements().get("achievementsOneTime").getAsJsonArray(); + List playerOneTimeString = new ArrayList<>(); + for (JsonElement element : challengeAchievements) { + try { + playerOneTimeString.add(element.getAsString()); + } catch (Exception ignored) { + } + } + for (String achievement : gameAchievements.get("one_time").getAsJsonObject().keySet()) { + String inData = gameId + "_" + achievement.toLowerCase(); + JsonObject achievementObject = gameAchievements.get("one_time").getAsJsonObject().get(achievement).getAsJsonObject(); + String readableAchievement = achievementObject.get("name").getAsString() + achievementObject.get("points").getAsString(); + int points = achievementObject.get("points").getAsInt(); + if(achievementObject.has("legacy")) { + if (achievementObject.get("legacy").getAsBoolean()) { + continue; + } + } + challengeMaxUnlocked++; + challengeMaxPoints += points; + + + if (playerOneTimeString.contains(inData)) { + challengeUnlockedPoints += points; + challengeUnlocked.add(readableAchievement); + } else { + challengeLocked.add(readableAchievement); + } } - int total_points = 0; - int total_legacy_points = 0; + int tieredMaxUnlocked = 0; + int tieredMaxPoints = 0; + int tieredUnlockedPoints = 0; + int tieredTotalUnlocked = 0; + + + JsonObject tieredAchievements = hypixelProfile.getAchievements().get("achievementsTiered").getAsJsonObject(); + for (String achievement : gameAchievements.get("tiered").getAsJsonObject().keySet()) { + JsonObject achievementObject = gameAchievements.get("tiered").getAsJsonObject().get(achievement).getAsJsonObject(); + if(achievementObject.has("legacy")) { + if (achievementObject.get("legacy").getAsBoolean()) { + continue; + } + } + String readableAchievement = achievementObject.get("name").getAsString(); + JsonArray tieredTiers = achievementObject.get("tiers").getAsJsonArray(); + String inData = gameId + "_" + achievement.toLowerCase(); + + int tierCount = 0; + int tieredUnlocked = 0; + for (JsonElement tier : tieredTiers) { + JsonObject tierObject = tier.getAsJsonObject(); + int points = tierObject.get("points").getAsInt(); + int amount = tierObject.get("amount").getAsInt(); + tieredMaxUnlocked++; + tieredMaxPoints += points; + tierCount++; + + if (tieredAchievements.has(inData)) { + int playerProgress = tieredAchievements.get(inData).getAsInt(); + if (playerProgress >= amount) { + tieredUnlocked++; + tieredUnlockedPoints += points; + } + } + } + tieredTotalUnlocked += tieredUnlocked; + } + + + if (Objects.equals(viewType, "challenge")) { + challengeButton = challengeButton.asDisabled(); + embedTitle = readableName + " Challenge Achievements for " + username; + + embedContent += "Unlocked: " + challengeUnlocked.size() + "/" + challengeMaxUnlocked + "\n"; + embedContent += "Points: " + challengeUnlockedPoints + "/" + challengeMaxPoints + "\n\n"; + } + if (Objects.equals(viewType, "tiered")) { + tieredButton = tieredButton.asDisabled(); + embedTitle = readableName + " Tiered Achievements for " + username; + + embedContent += "Unlocked: " + tieredTotalUnlocked + "/" + tieredMaxUnlocked + "\n"; + embedContent += "Points: " + tieredUnlockedPoints + "/" + tieredMaxPoints + "\n\n"; + } + if (Objects.equals(viewType, "all")) { + allButton = allButton.asDisabled(); + embedTitle = readableName + " Achievement Summary for " + username; + + int totalUnlocked = challengeUnlocked.size() + tieredTotalUnlocked; + int totalMaxUnlocked = challengeMaxUnlocked + tieredMaxUnlocked; + int totalPoints = challengeUnlockedPoints + tieredUnlockedPoints; + int totalMaxPoints = challengeMaxPoints + tieredMaxPoints; + embedContent += "Total Unlocked: " + totalUnlocked + "/" + totalMaxUnlocked + "\n"; + embedContent += "Challenge Unlocked: " + challengeUnlocked.size() + "/" + challengeMaxUnlocked + "\n"; + embedContent += "Tiered Unlocked: " + tieredTotalUnlocked + "/" + tieredMaxUnlocked + "\n\n"; + + embedContent += "Total Points: " + totalPoints + "/" + totalMaxPoints + "\n"; + embedContent += "Challenge Points: " + challengeUnlockedPoints + "/" + challengeMaxPoints + "\n"; + embedContent += "Tiered Points: " + tieredUnlockedPoints + "/" + tieredMaxPoints + "\n\n"; + } - String subtitle = " "; - String description = " "; + messageCreateBuilder.addActionRow(allButton, challengeButton, tieredButton); + messageCreateBuilder.addEmbeds(makeStatsEmbed(embedTitle, embedContent)); - return new MessageCreateBuilder() - .addEmbeds(makeStatsEmbed(" ", subtitle, description)) - .build(); + return messageCreateBuilder.build(); } } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 88c789b..7e46ca1 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -4,10 +4,7 @@ import com.google.gson.JsonObject; import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.profiles.GlobalData; -import me.stuffy.stuffybot.utils.APIException; -import me.stuffy.stuffybot.utils.InteractionException; -import me.stuffy.stuffybot.utils.Logger; -import me.stuffy.stuffybot.utils.StatisticsManager; +import me.stuffy.stuffybot.utils.*; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; @@ -38,6 +35,7 @@ import static me.stuffy.stuffybot.utils.APIUtils.getPlayCommands; import static me.stuffy.stuffybot.utils.APIUtils.getTournamentData; import static me.stuffy.stuffybot.utils.DiscordUtils.*; +import static me.stuffy.stuffybot.utils.MiscUtils.autoCompleteAchGames; import static me.stuffy.stuffybot.utils.MiscUtils.genBase64; public class InteractionHandler extends ListenerAdapter { @@ -53,7 +51,7 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { ArrayList optionsArray = new ArrayList(); - if(event.getOption("ign") == null){ + if (event.getOption("ign") == null) { String ign = null; try { ign = getUsername(event); @@ -66,7 +64,7 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { Pattern pattern = Pattern.compile("[,=:]"); for (OptionMapping option : event.getOptions()) { String optionString = option.getAsString(); - if(pattern.matcher(optionString).find()){ + if (pattern.matcher(optionString).find()) { MessageEmbed errorEmbed = makeErrorEmbed("Slash Command Error", "An error occurred while processing your command.\n-# Invalid character in option `" + option.getName() + "`"); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; @@ -84,7 +82,8 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { MessageCreateData response = null; try { - response = getResponse(interactionId);; + response = getResponse(interactionId); + ; } catch (InteractionException e) { MessageEmbed errorEmbed = makeErrorEmbed("Slash Command Error", "An error occurred while processing your command.\n-# " + e.getMessage()); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); @@ -97,7 +96,7 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { return; } - if(response != null) { + if (response != null) { event.getHook().sendMessage(response).queue(); } @@ -152,7 +151,7 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { } } - if (interactionId.getCommand().equals("verify")){ + if (interactionId.getCommand().equals("verify")) { verifyButton(event); return; } @@ -197,10 +196,10 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event) { toLog += " `" + mapping.getId() + "=" + mapping.getAsString() + "`"; } Logger.log(toLog); - if(event.getModalId().equals("verify")) { + if (event.getModalId().equals("verify")) { String ign = Objects.requireNonNull(event.getValue("ign")).getAsString(); String captcha = Objects.requireNonNull(event.getValue("captcha")).getAsString(); - if(!captcha.equals("stuffy")) { + if (!captcha.equals("stuffy")) { // TODO: Make this actually time out for 5 minutes MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again in " + discordTimeUnix(Instant.now().plusSeconds(300).toEpochMilli())); MessageCreateData data = new MessageCreateBuilder() @@ -220,14 +219,13 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent String commandName = e.getName(); String commandOption = e.getFocusedOption().getName(); String currentInput = e.getFocusedOption().getValue(); - switch (commandName) { case "megawalls" -> { if (commandOption.equals("skins")) { List options = new ArrayList<>(); List skins = Arrays.asList("Legendary", "Angel", "Arcanist", "Assassin", "Automaton", "Blaze", "Cow", "Creeper", "Dragon", - "Dreadlord", "Enderman", "Golem", "Herobrine", "Hunter", "Moleman", "Phoenix", "Pigman", "Pirate", "Renegade", - "Shaman", "Shark", "Sheep", "Skeleton", "Snowman", "Spider", "Squid", "Werewolf", "Zombie"); + "Dreadlord", "Enderman", "Golem", "Herobrine", "Hunter", "Moleman", "Phoenix", "Pigman", "Pirate", "Renegade", + "Shaman", "Shark", "Sheep", "Skeleton", "Snowman", "Spider", "Squid", "Werewolf", "Zombie"); for (String skin : skins) { if (skin.toLowerCase().contains(currentInput.toLowerCase())) { options.add(skin); @@ -244,19 +242,17 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent .toList(); e.replyChoices(choices).queue(); - break; } } case "tournament" -> { if (commandOption.equals("tournament")) { List choices = new ArrayList<>(); tournamentMap.forEach((name, id) -> { - if (name.toLowerCase().contains(currentInput.toLowerCase()) && choices.size() <= 25){ + if (name.toLowerCase().contains(currentInput.toLowerCase()) && choices.size() <= 25) { choices.add(new Command.Choice(name, id)); } }); e.replyChoices(choices).queue(); - break; } } case "playcommand" -> { @@ -276,7 +272,7 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent } for (JsonElement modeEntry : modes.getAsJsonArray()) { - if(!modeEntry.getAsJsonObject().has("name") || !modeEntry.getAsJsonObject().has("identifier")) { + if (!modeEntry.getAsJsonObject().has("name") || !modeEntry.getAsJsonObject().has("identifier")) { continue; } String modeName = modeEntry.getAsJsonObject().get("name").getAsString(); @@ -298,7 +294,23 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent } e.replyChoices(choices).queue(); - break; + } + } + case "achievements" -> { + if (commandOption.equals("game")) { + Map gameData = autoCompleteAchGames(); + List choices = new ArrayList<>(); + for (Map.Entry entry : gameData.entrySet()) { + if (entry.getValue().toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(entry.getValue(), entry.getValue())); + } + } + + if (choices.size() > 25) { + choices = choices.subList(0, 24); + } + + e.replyChoices(choices).queue(); } } default -> { @@ -329,9 +341,9 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (event.getAuthor().isBot()) { return; } - String authorId = event.getAuthor().getId(); - String authorName = event.getAuthor().getName(); - Bot.getGlobalData().addUniqueUser(authorId, authorName); +// String authorId = event.getAuthor().getId(); +// String authorName = event.getAuthor().getName(); +// Bot.getGlobalData().addUniqueUser(authorId, authorName); String message = event.getMessage().getContentRaw(); if (message.toLowerCase().startsWith("ap!")) { diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 4cf6f0b..77424ac 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -52,7 +52,7 @@ public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, St } public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor) { - return makeEmbed(embedTitle, null, embedContent, embedColor, 15); + return makeEmbed(embedTitle, embedSubtitle, embedContent, embedColor, 15); } public static MessageEmbed makeErrorEmbed(String embedTitle, String embedContent) { if (Calendar.getInstance().get(Calendar.MONTH) == Calendar.APRIL && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) == 1){ diff --git a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java index 0dfdd3c..4c4c661 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java @@ -167,8 +167,8 @@ public static String genBase64(Integer length){ return sb.toString(); } - public static String toReadableName(String resourcesName) { - Map resourceNames = new HashMap<>(); + private static Map getResourceNames() { + Map resourceNames = new HashMap<>(); resourceNames.put("arcade", "Arcade"); resourceNames.put("arena", "Arena Brawl"); resourceNames.put("bedwars", "Bed Wars"); @@ -201,9 +201,23 @@ public static String toReadableName(String resourcesName) { resourceNames.put("warlords", "Warlords"); resourceNames.put("woolgames", "Wool Games"); + return resourceNames; + } + + public static String toReadableName(String resourcesName) { + Map resourceNames = getResourceNames(); return resourceNames.getOrDefault(resourcesName, resourcesName); } + public static String fromReadableName(String readableName) { + Map resourceNames = getResourceNames(); + Map reverseMap = new HashMap<>(); + for (Map.Entry entry : resourceNames.entrySet()) { + reverseMap.put(entry.getValue(), entry.getKey()); + } + return reverseMap.getOrDefault(readableName, readableName); + } + public static String minutesFormatted(int minutes) { int hours = minutes / 60; int remainingMinutes = minutes % 60; @@ -248,4 +262,13 @@ public static int getMaxAchievementPoints() { } return total; } + + public static Map autoCompleteAchGames() { + Map games = new HashMap<>(); + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + for (String game : achievementData.keySet()) { + games.put(game, toReadableName(game)); + } + return games; + } } From 3892f0026a9e69e1486eca939b5464a3c1e1ac50 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:11:25 -0400 Subject: [PATCH 28/44] Search command --- src/main/java/me/stuffy/stuffybot/Bot.java | 2 + .../commands/PlayCommandCommand.java | 2 +- .../stuffybot/commands/SearchCommand.java | 117 ++++++++++++++++++ .../interactions/InteractionHandler.java | 45 ++++++- .../interactions/InteractionManager.java | 1 + 5 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 9d5ecb5..407f2aa 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -178,6 +178,8 @@ public void registerCommands(String scope) { .addOptions(ignOptionRequired)); commandList.add(Commands.slash("playcommand", "Lookup the command to quickly hop into a game") .addOptions(new OptionData(OptionType.STRING, "game", "Search for a play command", true).setAutoComplete(true))); + commandList.add(Commands.slash("search", "Search for an achievement by name, or description.") + .addOptions(new OptionData(OptionType.STRING, "search", "Search for an Achievement", true).setAutoComplete(true))); if (scope.equals("local")) { diff --git a/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java b/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java index 9dca682..60298ed 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java @@ -38,7 +38,7 @@ public static MessageCreateData playCommand(InteractionId interactionId) { } if (input.equals(identifier)) { return new MessageCreateBuilder().addEmbeds( - makeStatsEmbed("Play Command Search", "-# Use this to quickly join a game from anywhere." + + makeStatsEmbed("Play Command Search", "-# Use this to quickly join a game from anywhere.\n" + "\n**" + fullName + "**\n `/play " + identifier + "`") ).build(); } diff --git a/src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java new file mode 100644 index 0000000..c745038 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java @@ -0,0 +1,117 @@ +package me.stuffy.stuffybot.commands; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import me.stuffy.stuffybot.interactions.InteractionId; +import me.stuffy.stuffybot.profiles.HypixelProfile; +import me.stuffy.stuffybot.utils.APIException; +import me.stuffy.stuffybot.utils.Logger; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.text.DecimalFormat; +import java.util.ArrayList; + +import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; +import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; +import static me.stuffy.stuffybot.utils.MiscUtils.toReadableName; + +public class SearchCommand { + public static MessageCreateData search(InteractionId interactionId) { + try{ + String searchTerm = interactionId.getOptions().get("search"); + + String[] parts = searchTerm.split("_", 2); + String gameID = parts[0].toLowerCase(); + String achievementID = parts[1]; + + JsonObject achievementResources = getAchievementsResources().getAsJsonObject(); + JsonObject gameAchievements = achievementResources.get(gameID).getAsJsonObject(); + JsonObject gameOneTimes = gameAchievements.get("one_time").getAsJsonObject(); + JsonObject gameTiered = gameAchievements.get("tiered").getAsJsonObject(); + + String gameType = toReadableName(gameID); + + StringBuilder achievementBody = new StringBuilder(); + + if (gameOneTimes.has(achievementID)) { + // CHALLENGE ACHIEVEMENTS + JsonObject searchedAchievement = gameOneTimes.get(achievementID).getAsJsonObject(); + String achievementName = searchedAchievement.get("name").getAsString(); + String achievementDescription = searchedAchievement.get("description").getAsString(); + Integer achievementPoints = searchedAchievement.get("points").getAsInt(); + + achievementBody.append("-# ").append(gameType).append(" Challenge Achievement Found.\n"); + achievementBody.append("**").append(achievementName).append("** (+**").append(achievementPoints).append("** points)\n"); + achievementBody.append(achievementDescription).append("\n"); + + if (searchedAchievement.has("gamePercentUnlocked") && searchedAchievement.has("globalPercentUnlocked")){ + DecimalFormat df = new DecimalFormat("#0.00"); + Double gamePercentUnlocked = searchedAchievement.get("gamePercentUnlocked").getAsDouble(); + Double globalPercentUnlocked = searchedAchievement.get("globalPercentUnlocked").getAsDouble(); + achievementBody.append("\nUnlocked by `"); + achievementBody.append(df.format(gamePercentUnlocked)); + achievementBody.append("%` of ").append(gameType).append(", `"); + achievementBody.append(df.format(globalPercentUnlocked)); + achievementBody.append("%` of all players"); + } + + if(searchedAchievement.has("legacy") && searchedAchievement.get("legacy").getAsBoolean()){ + achievementBody.append("\n-# Legacy Achievement"); + } + } else if (gameTiered.has(achievementID)) { + // TIERED ACHIEVEMENTS + JsonObject searchedAchievement = gameTiered.get(achievementID).getAsJsonObject(); + String achievementName = searchedAchievement.get("name").getAsString(); + String achievementDescription = searchedAchievement.get("description").getAsString(); + + achievementBody.append("-# ").append(gameType).append(" Tiered Achievement Found.\n"); + achievementBody.append("**").append(achievementName).append("**\n"); + + DecimalFormat df = new DecimalFormat("#,###"); + JsonArray tiers = searchedAchievement.get("tiers").getAsJsonArray(); + Integer maxReq = 0; + StringBuilder tiersBuilder = new StringBuilder(); + tiersBuilder.append("```"); + for (JsonElement tier : tiers){ + int points = tier.getAsJsonObject().get("points").getAsInt(); + int amount = tier.getAsJsonObject().get("amount").getAsInt(); + tiersBuilder.append("\n ").append(df.format(amount)).append(" | ").append(points).append(" points"); + + if(amount > maxReq) { + maxReq = amount; + } + } + tiersBuilder.append("```"); + + achievementBody.append(achievementDescription.replace("%s", df.format(maxReq))).append("\n"); + achievementBody.append(tiersBuilder); + + if(searchedAchievement.has("legacy") && searchedAchievement.get("legacy").getAsBoolean()){ + achievementBody.append("\n-# Legacy Achievement"); + } + } else { + throw new Exception(); + } + + achievementBody.append("\n-# Internal ID: `").append(searchTerm).append("`."); + + return new MessageCreateBuilder().addEmbeds( + makeStatsEmbed("Achievement Search Result", achievementBody.toString()) + ).build(); + } catch (Exception e) { + Logger.logError(e.getMessage()); + MessageEmbed errorEmbed = makeErrorEmbed( + "Something went wrong", + "You should report this!" + ); + return new MessageCreateBuilder() + .addEmbeds(errorEmbed) + .build(); + } + } +} diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 7e46ca1..e2d3436 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -32,11 +32,9 @@ import java.util.stream.Stream; import static me.stuffy.stuffybot.interactions.InteractionManager.getResponse; -import static me.stuffy.stuffybot.utils.APIUtils.getPlayCommands; -import static me.stuffy.stuffybot.utils.APIUtils.getTournamentData; +import static me.stuffy.stuffybot.utils.APIUtils.*; import static me.stuffy.stuffybot.utils.DiscordUtils.*; -import static me.stuffy.stuffybot.utils.MiscUtils.autoCompleteAchGames; -import static me.stuffy.stuffybot.utils.MiscUtils.genBase64; +import static me.stuffy.stuffybot.utils.MiscUtils.*; public class InteractionHandler extends ListenerAdapter { private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); @@ -313,6 +311,45 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent e.replyChoices(choices).queue(); } } + case "search" -> { + if (commandOption.equals("search")) { + int searchCount = 0; + JsonObject achievementsResources = getAchievementsResources().getAsJsonObject(); + List choices = new ArrayList<>(); + + for (String game : achievementsResources.keySet()) { + if (searchCount == 25) { break; } + JsonObject gameAchievements = achievementsResources.get(game).getAsJsonObject(); + JsonObject gameOneTime = gameAchievements.get("one_time").getAsJsonObject(); + JsonObject gameTiered = gameAchievements.get("tiered").getAsJsonObject(); + for(String oneTimeID : gameOneTime.keySet()){ + if (searchCount == 25) { break; } + JsonObject oneTimeAchievement = gameOneTime.get(oneTimeID).getAsJsonObject(); + String achievementName = oneTimeAchievement.get("name").getAsString(); + String achievementDescription = oneTimeAchievement.get("description").getAsString(); + + if(achievementName.toLowerCase().contains(currentInput.toLowerCase()) || achievementDescription.toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(toReadableName(game) + ": " + achievementName, game.toUpperCase() + "_" + oneTimeID)); + searchCount++; + } + } + + for(String tieredID : gameTiered.keySet()){ + if (searchCount == 25) { break; } + JsonObject tieredAchievement = gameTiered.get(tieredID).getAsJsonObject(); + String achievementName = tieredAchievement.get("name").getAsString(); + String achievementDescription = tieredAchievement.get("description").getAsString(); + + if(achievementName.toLowerCase().contains(currentInput.toLowerCase()) || achievementDescription.toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(toReadableName(game) + ": " + achievementName, game.toUpperCase() + "_" + tieredID)); + searchCount++; + } + } + } + + e.replyChoices(choices).queue(); + } + } default -> { e.replyChoices(Collections.emptyList()).queue(); } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java index 6b136d1..ae50616 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java @@ -45,6 +45,7 @@ public static MessageCreateData getResponse(InteractionId interactionId) throws case "link" -> LinkCommand.link(interactionId); case "playcommand" -> PlayCommandCommand.playCommand(interactionId); case "help" -> HelpCommand.help(); + case "search" -> SearchCommand.search(interactionId); default -> throw new InteractionException("Invalid command"); }; } catch (APIException e) { From 1520e0bd5289d2473430e173b9807896333bfdd2 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:45:38 -0400 Subject: [PATCH 29/44] Refactor Global Data --- .../stuffybot/events/UpdateBotStatsEvent.java | 6 +-- .../stuffy/stuffybot/profiles/GlobalData.java | 47 ++++++++++++------- .../me/stuffy/stuffybot/utils/APIUtils.java | 2 +- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java index a8c3270..61a2cd1 100644 --- a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java @@ -27,9 +27,9 @@ public static void publicExecute() { int totalServers = bot.getJDA().getGuilds().size(); - Map uniqueUsers = globalData.getUniqueUsers(); - Map commandsRun = globalData.getCommandsRun(); - Map userCommandsRun = globalData.getUserCommandsRun(); + Map uniqueUsers = globalData.getSessionUniqueUsers(); + Map commandsRun = globalData.getSessionCommandsRun(); + Map userCommandsRun = globalData.getSessionUserCommandsRun(); if(commandsRun.isEmpty()) { Logger.log(" No data to update."); diff --git a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java index 0632530..8493bc4 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java @@ -3,15 +3,17 @@ import com.opencsv.CSVReader; import java.io.StringReader; +import java.lang.reflect.Array; import java.util.*; import static me.stuffy.stuffybot.utils.APIUtils.*; public class GlobalData { private final Map linkedAccounts; - private final Map commandsRun; - private final Map uniqueUsers; - private final Map userCommandsRun; + private final Map sessionCommandsRun; + private final Map sessionUniqueUsers; + private final Map sessionUserCommandsRun; + private final ArrayList verifiedAccounts; public GlobalData() { String linkedContent = readFile(Objects.requireNonNull(getGitHubFile(getPrivateApiRepo(), "apis/linkeddb.csv"))); @@ -25,6 +27,7 @@ public GlobalData() { boolean firstRow = true; Map linkedAccounts = new HashMap<>(); + ArrayList verifiedAccounts = new ArrayList(); for (String[] row : csvData) { if(firstRow) { firstRow = false; @@ -38,12 +41,16 @@ public GlobalData() { } catch (Exception e) { e.printStackTrace(); } + if(Objects.equals(row[4], "TRUE")){ + verifiedAccounts.add(row[0]); + } } this.linkedAccounts = linkedAccounts; - this.commandsRun = new HashMap<>(); - this.uniqueUsers = new HashMap<>(); - this.userCommandsRun = new HashMap<>(); + this.verifiedAccounts = verifiedAccounts; + this.sessionCommandsRun = new HashMap<>(); + this.sessionUniqueUsers = new HashMap<>(); + this.sessionUserCommandsRun = new HashMap<>(); } public Map getLinkedAccounts() { @@ -55,32 +62,36 @@ public void addLinkedAccount(String discordId, UUID uuid) { } public void incrementCommandsRun(String runnerId, String commandName) { - this.commandsRun.put(commandName, this.commandsRun.getOrDefault(commandName, 0) + 1); - this.userCommandsRun.put(runnerId, this.userCommandsRun.getOrDefault(runnerId, 0) + 1); + this.sessionCommandsRun.put(commandName, this.sessionCommandsRun.getOrDefault(commandName, 0) + 1); + this.sessionUserCommandsRun.put(runnerId, this.sessionUserCommandsRun.getOrDefault(runnerId, 0) + 1); } - public Map getCommandsRun() { - return this.commandsRun; + public Map getSessionCommandsRun() { + return this.sessionCommandsRun; } public void clearCommandsRun() { - this.commandsRun.clear(); - this.userCommandsRun.clear(); + this.sessionCommandsRun.clear(); + this.sessionUserCommandsRun.clear(); } public void addUniqueUser(String discordId, String discordName) { - this.uniqueUsers.put(discordId, discordName); + this.sessionUniqueUsers.put(discordId, discordName); } - public Map getUniqueUsers() { - return this.uniqueUsers; + public Map getSessionUniqueUsers() { + return this.sessionUniqueUsers; } public void clearUniqueUsers() { - this.uniqueUsers.clear(); + this.sessionUniqueUsers.clear(); + } + + public Map getSessionUserCommandsRun() { + return this.sessionUserCommandsRun; } - public Map getUserCommandsRun() { - return this.userCommandsRun; + public ArrayList getVerifiedAccounts() { + return verifiedAccounts; } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index 7c65ef8..fcc878b 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -493,7 +493,7 @@ public static String readFile(GHContent content) { } public static void updateLinkedDB(String discordId, UUID uuid, String ign) { - String discordName = Bot.getGlobalData().getUniqueUsers().getOrDefault(discordId, "NULL"); + String discordName = Bot.getGlobalData().getSessionUniqueUsers().getOrDefault(discordId, "NULL"); GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); From 63628e724a2d6e950b3e9dab374e73fcead57564 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:48:10 -0400 Subject: [PATCH 30/44] move verify handler code --- .../interactions/InteractionHandler.java | 28 ++---- .../stuffy/stuffybot/utils/DiscordUtils.java | 50 ---------- .../stuffy/stuffybot/utils/Verification.java | 93 +++++++++++++++++++ 3 files changed, 103 insertions(+), 68 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/utils/Verification.java diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index e2d3436..f2e2a9b 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -35,6 +35,7 @@ import static me.stuffy.stuffybot.utils.APIUtils.*; import static me.stuffy.stuffybot.utils.DiscordUtils.*; import static me.stuffy.stuffybot.utils.MiscUtils.*; +import static me.stuffy.stuffybot.utils.Verification.verifyModal; public class InteractionHandler extends ListenerAdapter { private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); @@ -81,7 +82,6 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { MessageCreateData response = null; try { response = getResponse(interactionId); - ; } catch (InteractionException e) { MessageEmbed errorEmbed = makeErrorEmbed("Slash Command Error", "An error occurred while processing your command.\n-# " + e.getMessage()); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); @@ -102,7 +102,11 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { String uid = interactionId.getId(); InteractionHook hook = event.getHook(); ScheduledFuture scheduledFuture = scheduler.schedule(() -> { - hook.editOriginalComponents().queue(); + try { + hook.editOriginalComponents().queue(); + } catch (Exception e) { + Logger.logError("Unable to remove original components, the message may have been deleted."); + } }, 30, TimeUnit.SECONDS); scheduledTasks.put(uid, scheduledFuture); @@ -149,8 +153,8 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { } } - if (interactionId.getCommand().equals("verify")) { - verifyButton(event); + if (interactionId.getCommand().equals("verify")) { //# TODO give permanent buttons better handling + Verification.verifyButton(event); return; } @@ -194,22 +198,10 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event) { toLog += " `" + mapping.getId() + "=" + mapping.getAsString() + "`"; } Logger.log(toLog); - if (event.getModalId().equals("verify")) { - String ign = Objects.requireNonNull(event.getValue("ign")).getAsString(); - String captcha = Objects.requireNonNull(event.getValue("captcha")).getAsString(); - if (!captcha.equals("stuffy")) { - // TODO: Make this actually time out for 5 minutes - MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again in " + discordTimeUnix(Instant.now().plusSeconds(300).toEpochMilli())); - MessageCreateData data = new MessageCreateBuilder() - .addEmbeds(errorEmbed) - .build(); - event.reply(data).setEphemeral(true).queue(); - return; - } - event.reply("You got the captcha right, " + ign).setEphemeral(true).queue(); + if (event.getModalId().equals("verify")) { + verifyModal(event); } - } @Override diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 77424ac..73cd295 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -3,15 +3,7 @@ import me.stuffy.stuffybot.Bot; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; -import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; import java.util.*; @@ -130,48 +122,6 @@ public static String discordTimeUnix(long timestamp) { return discordTimeUnix(timestamp, "R"); } - public static void verifyButton(ButtonInteractionEvent event) { - // Look up the user in the database, and check if they have already verified/linked - // If they are verified, verify them - // If they are linked, attempt to verify them - // If they are not linked, or the verification fails, prompt them to verify - - String userId = event.getUser().getId(); - if(isVerified(userId)){ - MessageCreateData data = new MessageCreateBuilder() - .setEmbeds(makeErrorEmbed("Verification Error", "You have already verified your identity, silly goose.")).build(); - event.reply(data).setEphemeral(true).queue(); - return; - } - - - Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") - .addComponents(ActionRow.of(TextInput.create("ign", "Minecraft Username", TextInputStyle.SHORT) - .setPlaceholder("Your Minecraft Username") - .setMaxLength(16) - .setMinLength(1) - .setRequired(true) - .build()), - ActionRow.of( - TextInput.create("captcha", "CAPTCHA", TextInputStyle.PARAGRAPH) - .setPlaceholder("Enter the word 'stuffy'.\n" + - "To prevent abuse, failing the CAPTCHA " + - "will result in a short timeout.") - .setRequired(false) - .build())) - .build(); - event.replyModal(modal).queue(); - } - - public static void updateRoles(User user, String ign, boolean announce) { - Bot bot = Bot.getInstance(); - // bot.getHomeGuild().getMember(user).modifyNickname(ign).queue(); - } - - public static boolean isVerified(String userId) { - return true; - } - public static String getUsername(SlashCommandInteractionEvent event) throws APIException { String username = event.getOption("ign") == null ? null : event.getOption("ign").getAsString(); if (username == null) { diff --git a/src/main/java/me/stuffy/stuffybot/utils/Verification.java b/src/main/java/me/stuffy/stuffybot/utils/Verification.java new file mode 100644 index 0000000..341d29f --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/utils/Verification.java @@ -0,0 +1,93 @@ +package me.stuffy.stuffybot.utils; + +import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.GlobalData; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; +import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Objects; + +import static me.stuffy.stuffybot.utils.DiscordUtils.discordTimeUnix; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; + +public class Verification { + public static void verifyButton(ButtonInteractionEvent event) { + User user = event.getUser(); + String userId = event.getUser().getId(); + + // #TODO if I am verified in linked db, update me according to info there + if(isVerified(userId)){ + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(DiscordUtils.makeErrorEmbed("Verification Error", "You have already verified your identity, silly goose.")).build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + // #TODO if I am linked, check if my mc account has this discord account listed in api, if yes, verifyUser() + // #TODO prompt the user with the modal, even if they are linked + // #TODO if the account does not have the discord linked, time out for 3 minutes, teach them how + // #TODO if all is valid, verify them + + Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") + .addComponents(ActionRow.of(TextInput.create("ign", "Minecraft Username", TextInputStyle.SHORT) + .setPlaceholder("Your Minecraft Username") + .setMaxLength(16) + .setMinLength(1) + .setRequired(true) + .build()), + ActionRow.of( + TextInput.create("captcha", "CAPTCHA", TextInputStyle.PARAGRAPH) + .setPlaceholder("Enter the word 'stuffy'.\n" + + "To prevent abuse, failing the CAPTCHA " + + "will result in a short timeout.") + .setRequired(false) + .build())) + .build(); + event.replyModal(modal).queue(); + } + + public static boolean isVerified(String userID) { + ArrayList verifiedAccounts = Bot.getGlobalData().getVerifiedAccounts(); + if (verifiedAccounts.contains(userID)) { + Logger.log("user " + userID + " is already verified"); + return true; + } + Logger.log("user " + userID + " is not verified"); + return false; + } + + public static void verifyModal(ModalInteractionEvent event) { + String ign = Objects.requireNonNull(event.getValue("ign")).getAsString(); + String captcha = Objects.requireNonNull(event.getValue("captcha")).getAsString(); + if (!captcha.equals("stuffy")) { + // TODO: Make this actually time out for 5 minutes + MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again in " + discordTimeUnix(Instant.now().plusSeconds(300).toEpochMilli())); + MessageCreateData data = new MessageCreateBuilder() + .addEmbeds(errorEmbed) + .build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + event.reply("You got the captcha right, " + ign).setEphemeral(true).queue(); + + } + + public static void verifyUser(User user) { + // #TODO check if anyone already has the account verified, unverify them first + // #TODO add this username,uuid to verified in linked db + // #TODO remove unverified role, add verified role + // #TODO add all roles that the player has already earned + } + +} From 09ccae4eec24c1bebafb943cc7e4f0c45d2c6741 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:15:09 -0400 Subject: [PATCH 31/44] setup setup command --- src/main/java/me/stuffy/stuffybot/Bot.java | 10 +++++ .../stuffybot/commands/SetupCommand.java | 41 +++++++++++++++++++ .../interactions/InteractionHandler.java | 15 +++++-- 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 407f2aa..a982708 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -17,6 +17,7 @@ import net.dv8tion.jda.api.events.guild.GuildLeaveEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.commands.Command; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; @@ -196,6 +197,15 @@ public void registerCommands(String scope) { } else { throw new IllegalArgumentException("Invalid scope: " + scope); } + + // Setup commands for home guild only + this.homeGuild.upsertCommand( + Commands.slash("setup", "Home guild setup command") + .setDefaultPermissions(DefaultMemberPermissions.DISABLED) + .addOptions(new OptionData(OptionType.STRING, "tosetup", "Which thing you wish to Setup", true).addChoices( + new Command.Choice("Verify", "verify") + )) + ).queue(); } public void clearCommands() { diff --git a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java new file mode 100644 index 0000000..65f409c --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java @@ -0,0 +1,41 @@ +package me.stuffy.stuffybot.commands; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +public class SetupCommand { + public static void setupLinkingButton(SlashCommandInteractionEvent event) { + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Verify or Update"); + embedBuilder.setDescription( + "Verification gives you permission to chat in this discord and have linked roles based on your Hypixel Stats. You must have a discord linked in game to do that. You can always use the button below to unverify your account, but you will lose all perks of being a verified user.\n" + + "-# :globe_with_meridians: You do __not__ need to verify your account to use commands inside or outside of this discord, receive announcements from Stuffy Bot, or any other feature we offer.\n" + + "\n" + + "If you just wish to link your account so slash commands will automatically assume your username for the `ign` field, you may use use " + + "/link" + // #TODO make this reference the /link command + ", which will not require verifying in game.\n" + + "\n" + + "If you've earned new accomplishments and want to update them, click the update button below.\n" + + "-# :warning: Stuffy Bot and Staff of this Discord will __never__ ask for your passwords or other personal information, please protect yourself online." + ); + embedBuilder.setFooter("Stuffy Bot by @stuffy"); + embedBuilder.setColor(0x3d84a2); + embedBuilder.build(); + + + MessageCreateData toBeSent = new MessageCreateBuilder().addEmbeds( + embedBuilder.build() + ).addActionRow( + Button.of(ButtonStyle.SECONDARY, "000:verify:null", "Verify"), + Button.of(ButtonStyle.SECONDARY, "000:update:null", "Update"), + Button.of(ButtonStyle.DANGER, "000:unverify:null", "Unverify") + ).build(); + + event.getChannel().sendMessage(toBeSent).queue(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index f2e2a9b..20fd236 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -5,8 +5,7 @@ import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.*; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageEmbed; + import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -22,7 +21,6 @@ import net.dv8tion.jda.api.utils.messages.MessageEditData; import org.jetbrains.annotations.NotNull; -import java.time.Instant; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -31,6 +29,7 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import static me.stuffy.stuffybot.commands.SetupCommand.setupLinkingButton; import static me.stuffy.stuffybot.interactions.InteractionManager.getResponse; import static me.stuffy.stuffybot.utils.APIUtils.*; import static me.stuffy.stuffybot.utils.DiscordUtils.*; @@ -49,6 +48,16 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { event.deferReply().queue(); ArrayList optionsArray = new ArrayList(); + if (commandName.equals("setup")) { + String tosetup = event.getOption("tosetup").getAsString(); + if (tosetup.equals("verify")) { + setupLinkingButton(event); + MessageEmbed successEmbed = makeEmbed("Verification Setup", "Successful setup", "The Verify Embed has been setup successfully.", 0x3d84a2); + event.getHook().setEphemeral(true).sendMessageEmbeds(successEmbed).queue(); + return; + } + } + if (event.getOption("ign") == null) { String ign = null; From b5283ce4377c089c130b14f0de5c447849a83dc8 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 19:03:55 -0400 Subject: [PATCH 32/44] verification handling --- .../stuffybot/commands/SetupCommand.java | 2 +- .../interactions/InteractionHandler.java | 26 ++++- .../stuffy/stuffybot/profiles/GlobalData.java | 8 ++ .../me/stuffy/stuffybot/utils/APIUtils.java | 34 +++++++ .../stuffy/stuffybot/utils/DiscordUtils.java | 11 +++ .../stuffy/stuffybot/utils/Verification.java | 96 ++++++++++++++++--- 6 files changed, 161 insertions(+), 16 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java index 65f409c..e80832f 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java @@ -17,7 +17,7 @@ public static void setupLinkingButton(SlashCommandInteractionEvent event) { "-# :globe_with_meridians: You do __not__ need to verify your account to use commands inside or outside of this discord, receive announcements from Stuffy Bot, or any other feature we offer.\n" + "\n" + "If you just wish to link your account so slash commands will automatically assume your username for the `ign` field, you may use use " + - "/link" + // #TODO make this reference the /link command + "" + ", which will not require verifying in game.\n" + "\n" + "If you've earned new accomplishments and want to update them, click the update button below.\n" + diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 20fd236..4e3c97a 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -162,9 +162,29 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { } } - if (interactionId.getCommand().equals("verify")) { //# TODO give permanent buttons better handling - Verification.verifyButton(event); - return; + // Verify Button + switch (interactionId.getCommand()) { + case "verify" -> { + Verification.verifyButton(event); + return; + } + + + // Update Button + case "update" -> { + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("OOPS!", "This feature is currently unavailable in preparation for an overhaul.")).build(); + event.reply(data).setEphemeral(true).queue(); + + return; + } + + + // Unverify Button + case "unverify" -> { + Verification.unverifyButton(event); + return; + } } event.deferEdit().queue(); diff --git a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java index 8493bc4..7bd42dd 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java @@ -94,4 +94,12 @@ public Map getSessionUserCommandsRun() { public ArrayList getVerifiedAccounts() { return verifiedAccounts; } + + public void setVerifiedAccount(String discordId, boolean verified) { + if(verified) { + this.verifiedAccounts.add(discordId); + } else { + this.verifiedAccounts.remove(discordId); + } + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index fcc878b..eb69cdb 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -492,6 +492,40 @@ public static String readFile(GHContent content) { } } + public static void setVerifiedStatus(String discordId, Boolean verified) { + GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); + if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); + + try { + // Read the CSV content + String linkedDBContent = readFile(linkedDB); + List csvData; + try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { + csvData = reader.readAll(); + } + + // Update the CSV content + for (String[] row : csvData) { + if (row[0].equals(discordId)) { + row[4] = verified ? "TRUE" : "FALSE"; + break; + } + } + + // Write the updated content back to the CSV file + StringWriter stringWriter = new StringWriter(); + try (CSVWriter writer = new CSVWriter(stringWriter)) { + writer.writeAll(csvData); + } + String updatedContent = stringWriter.toString(); + updateGitHubFile(privateApiRepo, "apis/linkeddb.csv", updatedContent, "`@" + Bot.getGlobalData().getSessionUniqueUsers().getOrDefault(discordId, "NULL") + "` verified status set to `" + verified + "`"); + Bot.getGlobalData().setVerifiedAccount(discordId, verified); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public static void updateLinkedDB(String discordId, UUID uuid, String ign) { String discordName = Bot.getGlobalData().getSessionUniqueUsers().getOrDefault(discordId, "NULL"); diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 73cd295..916804b 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -54,6 +54,17 @@ public static MessageEmbed makeErrorEmbed(String embedTitle, String embedContent return makeEmbed(":no_entry: " + embedTitle, null, embedContent, 0xC95353); } + public static MessageEmbed makeEmbedWithImage(String embedTitle, String embedSubtitle, String embedContent, String imageUrl, int embedColor) { + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle(embedTitle); + embedBuilder.setDescription("-# " + embedSubtitle + "\n" + embedContent); + embedBuilder.setColor(embedColor); + embedBuilder.setFooter("Stuffy Bot by @stuffy"); + embedBuilder.setTimestamp(new Date().toInstant()); + embedBuilder.setImage(imageUrl); + return embedBuilder.build(); + } + public static MessageEmbed makeUpdateEmbed(String embedTitle, String embedContent) { return makeEmbed(":mega: " + embedTitle, null, embedContent, 0xEBD773); } diff --git a/src/main/java/me/stuffy/stuffybot/utils/Verification.java b/src/main/java/me/stuffy/stuffybot/utils/Verification.java index 341d29f..2523009 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/Verification.java +++ b/src/main/java/me/stuffy/stuffybot/utils/Verification.java @@ -1,7 +1,7 @@ package me.stuffy.stuffybot.utils; import me.stuffy.stuffybot.Bot; -import me.stuffy.stuffybot.profiles.GlobalData; +import me.stuffy.stuffybot.profiles.HypixelProfile; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; @@ -15,29 +15,40 @@ import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; -import static me.stuffy.stuffybot.utils.DiscordUtils.discordTimeUnix; -import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; +import static me.stuffy.stuffybot.utils.APIUtils.*; +import static me.stuffy.stuffybot.utils.DiscordUtils.*; public class Verification { + private static final Map captchaTimeouts = new HashMap<>(); + public static void verifyButton(ButtonInteractionEvent event) { User user = event.getUser(); String userId = event.getUser().getId(); + if(captchaTimeouts.containsKey(userId)){ + if(captchaTimeouts.get(userId) > Instant.now().toEpochMilli()) { + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("Verification Error", "You have failed a CAPTCHA too recently. Please try again " + discordTimeUnix(captchaTimeouts.get(userId)) + ".")).build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + else { + captchaTimeouts.remove(userId); + } + } + // #TODO if I am verified in linked db, update me according to info there if(isVerified(userId)){ MessageCreateData data = new MessageCreateBuilder() - .setEmbeds(DiscordUtils.makeErrorEmbed("Verification Error", "You have already verified your identity, silly goose.")).build(); + .setEmbeds(makeErrorEmbed("Verification Error", "You have already verified your identity, silly goose.")).build(); event.reply(data).setEphemeral(true).queue(); return; } - // #TODO if I am linked, check if my mc account has this discord account listed in api, if yes, verifyUser() - // #TODO prompt the user with the modal, even if they are linked - // #TODO if the account does not have the discord linked, time out for 3 minutes, teach them how - // #TODO if all is valid, verify them - Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") .addComponents(ActionRow.of(TextInput.create("ign", "Minecraft Username", TextInputStyle.SHORT) .setPlaceholder("Your Minecraft Username") @@ -69,9 +80,20 @@ public static boolean isVerified(String userID) { public static void verifyModal(ModalInteractionEvent event) { String ign = Objects.requireNonNull(event.getValue("ign")).getAsString(); String captcha = Objects.requireNonNull(event.getValue("captcha")).getAsString(); + + if (!ign.matches("[a-zA-Z0-9_]+")) { + MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "Your Minecraft username may only contain letters, numbers, and underscores."); + MessageCreateData data = new MessageCreateBuilder() + .addEmbeds(errorEmbed) + .build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + if (!captcha.equals("stuffy")) { - // TODO: Make this actually time out for 5 minutes - MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again in " + discordTimeUnix(Instant.now().plusSeconds(300).toEpochMilli())); + long timeout = Instant.now().plusSeconds(120).toEpochMilli(); + captchaTimeouts.put(event.getUser().getId(), timeout); + MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again " + discordTimeUnix(timeout) + "."); MessageCreateData data = new MessageCreateBuilder() .addEmbeds(errorEmbed) .build(); @@ -79,8 +101,58 @@ public static void verifyModal(ModalInteractionEvent event) { return; } - event.reply("You got the captcha right, " + ign).setEphemeral(true).queue(); + HypixelProfile profile = null; + try { + profile = getHypixelProfile(ign); + } catch (APIException e) { + event.replyEmbeds(makeErrorEmbed("Hypixel API Error", e.errorMessage)).setEphemeral(true).queue(); + return; + } + if ( profile == null) { + event.replyEmbeds(makeErrorEmbed("Hypixel API Error", "The user " + ign + " does not seem to exist. SpoOo00oky")).setEphemeral(true).queue(); + return; + } + + ign = profile.getDisplayName(); + + String linkedDiscord = null; + try { + linkedDiscord = profile.getDiscord(); + } catch (Exception e) { + event.replyEmbeds(makeEmbedWithImage("Verification Error", "Check your linked discord in game","The user `" + ign + "` does not have a discord linked.\n\nFollow the steps below to correct this, and try again.", "https://i.imgur.com/HHs9nbZ.gif", 0xC95353)).setEphemeral(true).queue(); + return; + } + + String discordUsername = event.getUser().getName(); + if(!linkedDiscord.equals(discordUsername)){ + event.replyEmbeds(makeEmbedWithImage("Verification Error", "Check your linked discord in game", "The user `" + ign + "` has a different discord linked.\n In Game Linked Discord: `" + linkedDiscord + "`\n Your Discord Username: `" + discordUsername + "`\n\nFollow the steps below to correct this, and try again.", "https://i.imgur.com/HHs9nbZ.gif", 0xC95353)).setEphemeral(true).queue(); + return; + } + + + // User is verified, whoop-whoop + updateLinkedDB(event.getUser().getId(), profile.getUuid(), ign); + setVerifiedStatus(event.getUser().getId(), true); + event.replyEmbeds(DiscordUtils.makeEmbed("Verification Successful", "You have been verified.", "You may now enjoy all of the perks that come with that. You may unverify at any time.", 0x3d84a2)).setEphemeral(true).queue(); + } + + public static void unverifyButton(ButtonInteractionEvent event) { + User user = event.getUser(); + String userId = event.getUser().getId(); + if(!isVerified(userId)){ + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("Unverification Error", "You have not yet verified, there is nothing to undo!")).build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + setVerifiedStatus(userId, false); + + // TODO: Remove verified role, add unverified role + // TODO: Remove all roles that the player has already earned + MessageEmbed embed = DiscordUtils.makeEmbed("Unverify", "You have been unverified.", "You may verify again at any time.", 0x3d84a2); + event.replyEmbeds(embed).setEphemeral(true).queue(); } public static void verifyUser(User user) { From b7f874094ec08dc9d8758b0b8ec930db1d898669 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:13:41 -0400 Subject: [PATCH 33/44] config.properties --- src/main/java/me/stuffy/stuffybot/Bot.java | 17 +++++--- .../stuffybot/commands/SetupCommand.java | 5 ++- .../me/stuffy/stuffybot/utils/Config.java | 41 +++++++++++++++++++ .../stuffy/stuffybot/utils/Verification.java | 10 ++++- src/main/resources/config.properties | 5 +++ 5 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/utils/Config.java create mode 100644 src/main/resources/config.properties diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index a982708..fa176bd 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -5,8 +5,7 @@ import me.stuffy.stuffybot.events.UpdateBotStatsEvent; import me.stuffy.stuffybot.interactions.InteractionHandler; import me.stuffy.stuffybot.profiles.GlobalData; -import me.stuffy.stuffybot.utils.APIUtils; -import me.stuffy.stuffybot.utils.DiscordUtils; +import me.stuffy.stuffybot.utils.Config; import me.stuffy.stuffybot.utils.Logger; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; @@ -51,15 +50,17 @@ public Bot() throws InterruptedException { JDA jda = builder.build().awaitReady(); this.jda = jda; + String homeGuildID = Config.getHomeGuildId(); // Initialize home guild - this.homeGuild = jda.getGuildById("795108903733952562"); + this.homeGuild = jda.getGuildById(homeGuildID); assert this.homeGuild != null : "Failed to find home guild"; // Log startup String startupTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss")); String self = jda.getSelfUser().getName(); Logger.setLogName(startupTime); - Logger.log(" Bot " + self + " started successfully " + startupTime + "."); + String environment = Config.getEnvironment(); + Logger.log(" Bot " + self + " started successfully " + startupTime + ". Environment: " + environment); // Initialize GitHub GITHUB = connectToGitHub(); @@ -120,7 +121,13 @@ public Guild getHomeGuild() { public Role getVerifiedRole() { - return this.homeGuild.getRoleById("795118862940635216"); + String roleID = Config.getVerifiedRoleId(); + return this.homeGuild.getRoleById(roleID); + } + + public Role getNotVerifiedRole() { + String roleID = Config.getNotVerifiedRoleId(); + return this.homeGuild.getRoleById(roleID); } public static void main(String[] args) throws InterruptedException { diff --git a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java index e80832f..548f32b 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java @@ -1,5 +1,6 @@ package me.stuffy.stuffybot.commands; +import me.stuffy.stuffybot.utils.Config; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.components.buttons.Button; @@ -9,7 +10,7 @@ public class SetupCommand { public static void setupLinkingButton(SlashCommandInteractionEvent event) { - + String linkCommandId = Config.getLinkCommandId(); EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setTitle("Verify or Update"); embedBuilder.setDescription( @@ -17,7 +18,7 @@ public static void setupLinkingButton(SlashCommandInteractionEvent event) { "-# :globe_with_meridians: You do __not__ need to verify your account to use commands inside or outside of this discord, receive announcements from Stuffy Bot, or any other feature we offer.\n" + "\n" + "If you just wish to link your account so slash commands will automatically assume your username for the `ign` field, you may use use " + - "" + + "" + ", which will not require verifying in game.\n" + "\n" + "If you've earned new accomplishments and want to update them, click the update button below.\n" + diff --git a/src/main/java/me/stuffy/stuffybot/utils/Config.java b/src/main/java/me/stuffy/stuffybot/utils/Config.java new file mode 100644 index 0000000..d782762 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/utils/Config.java @@ -0,0 +1,41 @@ +package me.stuffy.stuffybot.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class Config { + private static final Properties properties = new Properties(); + + static { + try (InputStream input = Config.class.getClassLoader().getResourceAsStream("config.properties")) { + if (input == null) { + Logger.logError("Unable to find config.properties"); + throw new RuntimeException("Unable to find config.properties"); + } + properties.load(input); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String getEnvironment() { + return properties.getProperty("environment", "development"); + } + + public static String getHomeGuildId() { + return properties.getProperty("homeGuildId"); + } + + public static String getVerifiedRoleId() { + return properties.getProperty("verifiedRoleId"); + } + + public static String getNotVerifiedRoleId() { + return properties.getProperty("notVerifiedRoleId"); + } + + public static String getLinkCommandId() { + return properties.getProperty("linkCommandId"); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/utils/Verification.java b/src/main/java/me/stuffy/stuffybot/utils/Verification.java index 2523009..9030061 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/Verification.java +++ b/src/main/java/me/stuffy/stuffybot/utils/Verification.java @@ -133,6 +133,11 @@ public static void verifyModal(ModalInteractionEvent event) { // User is verified, whoop-whoop updateLinkedDB(event.getUser().getId(), profile.getUuid(), ign); setVerifiedStatus(event.getUser().getId(), true); + + Bot bot = Bot.getInstance(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).addRoleToMember(event.getUser(), bot.getVerifiedRole()).queue(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).removeRoleFromMember(event.getUser(), bot.getNotVerifiedRole()).queue(); + event.replyEmbeds(DiscordUtils.makeEmbed("Verification Successful", "You have been verified.", "You may now enjoy all of the perks that come with that. You may unverify at any time.", 0x3d84a2)).setEphemeral(true).queue(); } @@ -148,7 +153,10 @@ public static void unverifyButton(ButtonInteractionEvent event) { setVerifiedStatus(userId, false); - // TODO: Remove verified role, add unverified role + Bot bot = Bot.getInstance(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).removeRoleFromMember(event.getUser(), bot.getVerifiedRole()).queue(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).addRoleToMember(event.getUser(), bot.getNotVerifiedRole()).queue(); + // TODO: Remove all roles that the player has already earned MessageEmbed embed = DiscordUtils.makeEmbed("Unverify", "You have been unverified.", "You may verify again at any time.", 0x3d84a2); diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties new file mode 100644 index 0000000..559eb81 --- /dev/null +++ b/src/main/resources/config.properties @@ -0,0 +1,5 @@ +environment=development +homeGuildId=795108903733952562 +verifiedRoleId=795118862940635216 +notVerifiedRoleId=1356442418354061503 +linkCommandId=1281037425032040491 \ No newline at end of file From dcfad3980359dd69492bfb11d629c95f5c7078f3 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:27:34 -0400 Subject: [PATCH 34/44] custom status --- src/main/java/me/stuffy/stuffybot/Bot.java | 3 ++- src/main/java/me/stuffy/stuffybot/utils/Config.java | 4 ++++ src/main/resources/config.properties | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index fa176bd..6a941f2 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -45,7 +45,8 @@ public Bot() throws InterruptedException { String token = System.getenv("BOT_TOKEN"); JDABuilder builder = JDABuilder.createDefault(token); builder.enableIntents(GatewayIntent.MESSAGE_CONTENT); // # TODO: Remove intents when possible - builder.setActivity(Activity.customStatus("almost ready...")); + String customStatus = Config.getCustomStatus(); + builder.setActivity(Activity.customStatus(customStatus)); builder.addEventListeners(this); JDA jda = builder.build().awaitReady(); this.jda = jda; diff --git a/src/main/java/me/stuffy/stuffybot/utils/Config.java b/src/main/java/me/stuffy/stuffybot/utils/Config.java index d782762..b68132e 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/Config.java +++ b/src/main/java/me/stuffy/stuffybot/utils/Config.java @@ -38,4 +38,8 @@ public static String getNotVerifiedRoleId() { public static String getLinkCommandId() { return properties.getProperty("linkCommandId"); } + + public static String getCustomStatus() { + return properties.getProperty("customStatus"); + } } diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties index 559eb81..eb39c0b 100644 --- a/src/main/resources/config.properties +++ b/src/main/resources/config.properties @@ -2,4 +2,5 @@ environment=development homeGuildId=795108903733952562 verifiedRoleId=795118862940635216 notVerifiedRoleId=1356442418354061503 -linkCommandId=1281037425032040491 \ No newline at end of file +linkCommandId=1281037425032040491 +customStatus=april's foolin' \ No newline at end of file From 931508f373a7c87e04942d14cf2c3fcf24287440 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:40:14 -0400 Subject: [PATCH 35/44] Update config.properties --- src/main/resources/config.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties index eb39c0b..00d9804 100644 --- a/src/main/resources/config.properties +++ b/src/main/resources/config.properties @@ -1,6 +1,6 @@ -environment=development -homeGuildId=795108903733952562 -verifiedRoleId=795118862940635216 -notVerifiedRoleId=1356442418354061503 +environment=production +homeGuildId=818238263110008863 +verifiedRoleId=818283784763473952 +notVerifiedRoleId=1269849137982083215 linkCommandId=1281037425032040491 -customStatus=april's foolin' \ No newline at end of file +customStatus=Is it finally ready? From 3409cfe3bd268c42ecece420715430fc9001665c Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Mon, 31 Mar 2025 23:45:10 -0400 Subject: [PATCH 36/44] maven stuff --- Procfile | 1 + pom.xml | 33 ++++++++++++++++++++++++++++ target/maven-archiver/pom.properties | 4 +--- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 Procfile diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..871ab0a --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: java -jar .\target\stuffybot-java-1.0-SNAPSHOT.jar \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8655fd9..62e0e6a 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ + org.apache.maven.plugins maven-compiler-plugin @@ -23,6 +24,38 @@ 17 + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + me.stuffy.stuffybot.Bot + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + me.stuffy.stuffybot.Bot + + + + + + diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties index 2cceb68..3f162e5 100644 --- a/target/maven-archiver/pom.properties +++ b/target/maven-archiver/pom.properties @@ -1,5 +1,3 @@ -#Generated by Maven -#Wed Jun 28 17:50:57 EDT 2023 -groupId=me.stuffy artifactId=stuffybot-java +groupId=me.stuffy version=1.0-SNAPSHOT From f886531e514a15d6f993a353139d7e6e57e1f668 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:44:19 -0400 Subject: [PATCH 37/44] procfile slashes --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index 871ab0a..c03fdf3 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -worker: java -jar .\target\stuffybot-java-1.0-SNAPSHOT.jar \ No newline at end of file +worker: java -jar ./target/stuffybot-java-1.0-SNAPSHOT.jar \ No newline at end of file From a707c49b045a1e328b921b61cd73e57f72871cce Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:40:02 -0400 Subject: [PATCH 38/44] global vs local registration by environment scope --- src/main/java/me/stuffy/stuffybot/Bot.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 6a941f2..edd8e19 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -75,7 +75,8 @@ public Bot() throws InterruptedException { ); // Register commands "global"ly or "local"ly - registerCommands("local"); + String environmentScope = Config.getEnvironment().equals("production") ? "global" : "local"; + registerCommands(environmentScope); // Start events new UpdateBotStatsEvent().startFixedRateEvent(); From fef26ebd8be0b8a538b7f7d40c74e98fa74a242b Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:50:36 -0400 Subject: [PATCH 39/44] remove message content intents --- src/main/java/me/stuffy/stuffybot/Bot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index edd8e19..64b649a 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -44,7 +44,7 @@ public Bot() throws InterruptedException { // Get token from env variable String token = System.getenv("BOT_TOKEN"); JDABuilder builder = JDABuilder.createDefault(token); - builder.enableIntents(GatewayIntent.MESSAGE_CONTENT); // # TODO: Remove intents when possible +// builder.enableIntents(GatewayIntent.MESSAGE_CONTENT); // # TODO: Remove intents when possible String customStatus = Config.getCustomStatus(); builder.setActivity(Activity.customStatus(customStatus)); builder.addEventListeners(this); From 2b47b2736c6ae12d680c46ae132e35bd974712ba Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:28:30 -0400 Subject: [PATCH 40/44] add total users to bot stats --- .../stuffy/stuffybot/events/UpdateBotStatsEvent.java | 11 +++++++++-- src/main/java/me/stuffy/stuffybot/utils/APIUtils.java | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java index 61a2cd1..00e5076 100644 --- a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java @@ -3,6 +3,7 @@ import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.Logger; +import net.dv8tion.jda.api.entities.Guild; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -25,7 +26,13 @@ public static void publicExecute() { Bot bot = Bot.getInstance(); GlobalData globalData = Bot.getGlobalData(); - int totalServers = bot.getJDA().getGuilds().size(); + Guild[] guilds = bot.getJDA().getGuilds().toArray(new Guild[0]); + int totalUsers = 0; + int totalServers = 0; + for (Guild guild : guilds) { + totalServers++; + totalUsers += guild.getMemberCount(); + } Map uniqueUsers = globalData.getSessionUniqueUsers(); Map commandsRun = globalData.getSessionCommandsRun(); @@ -34,7 +41,7 @@ public static void publicExecute() { if(commandsRun.isEmpty()) { Logger.log(" No data to update."); } else { - updateBotStats(totalServers, commandsRun); + updateBotStats(totalServers, totalUsers, commandsRun); Logger.log(" Updated bot stats."); } if(uniqueUsers.isEmpty()) { diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index eb69cdb..26b250e 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -376,7 +376,7 @@ public static String getPrivateApiRepo() { } - public static void updateBotStats(int totalServers, Map commandsRun) { + public static void updateBotStats(int totalServers, int totalUsers, Map commandsRun) { GHContent botStats = getGitHubFile(privateApiRepo, "apis/bot.json"); if (botStats == null) throw new IllegalStateException("Failed to get bot.json from GitHub"); @@ -404,6 +404,7 @@ public static void updateBotStats(int totalServers, Map command } bot.addProperty("servers", totalServers); + bot.addProperty("totalusers", totalUsers); JsonObject commands = bot.get("commandsRun").getAsJsonObject(); From 23b3e09ea858e75c95cce54cdd014b877dc4a3b3 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:30:04 -0400 Subject: [PATCH 41/44] compile stuff --- .../compile/default-compile/createdFiles.lst | 41 ++++++++++++++++ .../compile/default-compile/inputFiles.lst | 48 ++++++++++++++----- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index f310943..47d8e20 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1 +1,42 @@ +me\stuffy\stuffybot\profiles\Rank.class +me\stuffy\stuffybot\utils\MiscUtils.class +me\stuffy\stuffybot\commands\MaxesCommand.class +me\stuffy\stuffybot\events\UpdateBotStatsEvent.class +me\stuffy\stuffybot\commands\MegaWallsCommand.class +me\stuffy\stuffybot\commands\SetupCommand.class +me\stuffy\stuffybot\profiles\GlobalData.class +me\stuffy\stuffybot\utils\DiscordUtils.class +me\stuffy\stuffybot\profiles\MojangProfile.class +me\stuffy\stuffybot\events\BaseEvent.class +me\stuffy\stuffybot\profiles\Achievement.class +me\stuffy\stuffybot\utils\APIUtils$3.class +me\stuffy\stuffybot\utils\Config.class +me\stuffy\stuffybot\profiles\HypixelProfile.class +me\stuffy\stuffybot\interactions\InteractionHandler.class +me\stuffy\stuffybot\events\ActiveEvents.class +me\stuffy\stuffybot\commands\BlitzCommand.class +me\stuffy\stuffybot\commands\PlayCommandCommand.class +me\stuffy\stuffybot\utils\InvalidOptionException.class +me\stuffy\stuffybot\utils\APIUtils$2.class +me\stuffy\stuffybot\interactions\InteractionManager.class +me\stuffy\stuffybot\utils\Verification.class +me\stuffy\stuffybot\profiles\DiscordUser.class +me\stuffy\stuffybot\utils\APIUtils$1.class +me\stuffy\stuffybot\commands\PitCommand.class +me\stuffy\stuffybot\commands\TkrCommand.class +me\stuffy\stuffybot\utils\InteractionException.class +me\stuffy\stuffybot\commands\StatsCommand.class +me\stuffy\stuffybot\utils\APIUtils$5.class me\stuffy\stuffybot\Bot.class +me\stuffy\stuffybot\utils\APIException.class +me\stuffy\stuffybot\commands\HelpCommand.class +me\stuffy\stuffybot\commands\LinkCommand.class +me\stuffy\stuffybot\commands\SearchCommand.class +me\stuffy\stuffybot\commands\AchievementsCommand.class +me\stuffy\stuffybot\utils\APIUtils.class +me\stuffy\stuffybot\interactions\InteractionId.class +me\stuffy\stuffybot\utils\Logger.class +me\stuffy\stuffybot\profiles\Achievement$Type.class +me\stuffy\stuffybot\commands\TournamentCommand.class +me\stuffy\stuffybot\utils\StatisticsManager.class +me\stuffy\stuffybot\utils\APIUtils$4.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index ff9febf..4e74a4c 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,14 +1,36 @@ -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\AdminForumPost.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\GameResources.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\HypixelApiUtils.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\PingCommand.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\StuffyCommand.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\UpdateStatistics.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\HypixelStatusUpdate.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\BaseEvent.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\MojangApiUtils.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\VerifyCommand.java C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\Bot.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\StaffRankChanges.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\BaseCommand.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\TimeUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\AchievementsCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\BlitzCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\HelpCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\LinkCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\MaxesCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\MegaWallsCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\PitCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\PlayCommandCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\SearchCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\SetupCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\StatsCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\TkrCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\TournamentCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\ActiveEvents.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\BaseEvent.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\UpdateBotStatsEvent.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\interactions\InteractionHandler.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\interactions\InteractionId.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\interactions\InteractionManager.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\Achievement.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\DiscordUser.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\GlobalData.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\HypixelProfile.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\MojangProfile.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\Rank.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\APIException.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\APIUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\Config.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\DiscordUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\InteractionException.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\InvalidOptionException.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\Logger.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\MiscUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\StatisticsManager.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\Verification.java From 0bcaafec0ffe8296dd984d47853b42fe1f9f7ddc Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Wed, 16 Apr 2025 17:21:32 -0400 Subject: [PATCH 42/44] fix issue with mega walls skins data not working --- src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index f676392..0c360d9 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -646,7 +646,7 @@ public Map getBlitzStats() { public Integer getMegaWallsStat(String asString) { try { - return getNestedJson(0, profile, "stats", "Walls3", asString).getAsJsonObject().getAsInt(); + return getNestedJson(0, profile, "stats", "Walls3", asString).getAsInt(); } catch (IllegalArgumentException e) { return 0; } From 3524872a1ac9ffb8a7900efaf1e4baa4f6cbedae Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Sat, 27 Dec 2025 05:36:09 -0500 Subject: [PATCH 43/44] Update to JDA 6.2.0 --- .gitignore | 3 ++ pom.xml | 15 +++++++- src/main/java/me/stuffy/stuffybot/Bot.java | 2 +- .../commands/AchievementsCommand.java | 7 ++-- .../stuffy/stuffybot/commands/PitCommand.java | 11 +++--- .../stuffybot/commands/SetupCommand.java | 9 +++-- .../stuffybot/commands/TournamentCommand.java | 7 ++-- .../interactions/InteractionHandler.java | 4 +- .../stuffy/stuffybot/utils/Verification.java | 37 ++++++++++--------- src/main/resources/config.properties | 2 +- 10 files changed, 58 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 524f096..fe809a4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +# Configuration files +*.properties \ No newline at end of file diff --git a/pom.xml b/pom.xml index 62e0e6a..46c65cc 100644 --- a/pom.xml +++ b/pom.xml @@ -58,11 +58,22 @@ + + + + com.fasterxml.jackson + jackson-bom + 2.15.4 + pom + import + + + net.dv8tion JDA - 5.0.0 + 6.2.0 com.google.code.gson @@ -72,7 +83,7 @@ com.google.guava guava - 30.1-jre + 33.4.8-jre org.kohsuke diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 64b649a..85a83d4 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -34,7 +34,7 @@ public class Bot extends ListenerAdapter { private static Bot INSTANCE; private final JDA jda; - private Guild homeGuild; + private final Guild homeGuild; private static GitHub GITHUB; private static GlobalData GLOBAL_DATA; diff --git a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java index 774e1bf..92d7c6b 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java @@ -7,8 +7,9 @@ import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.utils.APIException; import me.stuffy.stuffybot.utils.InvalidOptionException; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -194,7 +195,7 @@ public static MessageCreateData achievements(InteractionId interactionId) throws embedContent += "Tiered Points: " + tieredUnlockedPoints + "/" + tieredMaxPoints + "\n\n"; } - messageCreateBuilder.addActionRow(allButton, challengeButton, tieredButton); + messageCreateBuilder.setComponents(ActionRow.of(allButton, challengeButton, tieredButton)); messageCreateBuilder.addEmbeds(makeStatsEmbed(embedTitle, embedContent)); diff --git a/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java b/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java index e26ad92..4763075 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java @@ -4,6 +4,7 @@ import me.stuffy.stuffybot.interactions.InteractionId; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.utils.APIException; +import net.dv8tion.jda.api.components.actionrow.ActionRow; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -13,7 +14,7 @@ import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; import static me.stuffy.stuffybot.utils.MiscUtils.convertToRomanNumeral; -import static net.dv8tion.jda.api.interactions.components.buttons.Button.secondary; +import static net.dv8tion.jda.api.components.buttons.Button.secondary; public class PitCommand { @@ -48,9 +49,9 @@ public static MessageCreateData pit(InteractionId interactionId) throws APIExcep String newInteractionId = InteractionId.newCommand("pitDetailed", interactionId).getInteractionString(); return new MessageCreateBuilder() .addEmbeds(pitStats) - .addActionRow( + .setComponents(ActionRow.of( secondary(newInteractionId, "Challenge Achievement Progress") - ) + )) .build(); } @@ -88,9 +89,9 @@ public static MessageCreateData pitDetailed(InteractionId interactionId) throws String newInteractionId = InteractionId.newCommand("pit", interactionId).getInteractionString(); return new MessageCreateBuilder() .addEmbeds(extraPitStats) - .addActionRow( + .setComponents(ActionRow.of( secondary(newInteractionId, "Back to Pit Stats") - ) + )) .build(); } } diff --git a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java index 548f32b..28e75bb 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java @@ -2,9 +2,10 @@ import me.stuffy.stuffybot.utils.Config; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.components.actionrow.ActionRow; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -31,11 +32,11 @@ public static void setupLinkingButton(SlashCommandInteractionEvent event) { MessageCreateData toBeSent = new MessageCreateBuilder().addEmbeds( embedBuilder.build() - ).addActionRow( + ).setComponents(ActionRow.of( Button.of(ButtonStyle.SECONDARY, "000:verify:null", "Verify"), Button.of(ButtonStyle.SECONDARY, "000:update:null", "Update"), Button.of(ButtonStyle.DANGER, "000:unverify:null", "Unverify") - ).build(); + )).build(); event.getChannel().sendMessage(toBeSent).queue(); } diff --git a/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java b/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java index 8e715fc..c66ee4a 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java @@ -6,8 +6,9 @@ import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.utils.APIException; import me.stuffy.stuffybot.utils.InvalidOptionException; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -154,7 +155,7 @@ public static MessageCreateData tournament(InteractionId interactionId) throws A return new MessageCreateBuilder() .addEmbeds(makeStatsEmbed(emoji + " " + title.toString(), subtitle, description.toString())) - .addActionRow(buttons) + .setComponents(ActionRow.of(buttons)) .build(); } } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 4e3c97a..8b11576 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -5,7 +5,7 @@ import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.*; - import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -224,7 +224,7 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { public void onModalInteraction(@NotNull ModalInteractionEvent event) { String toLog = " @" + event.getUser().getName() + ": `" + event.getModalId() + "`"; for (ModalMapping mapping : event.getValues()) { - toLog += " `" + mapping.getId() + "=" + mapping.getAsString() + "`"; + toLog += " `" + mapping.getCustomId() + "=" + mapping.getAsString() + "`"; } Logger.log(toLog); diff --git a/src/main/java/me/stuffy/stuffybot/utils/Verification.java b/src/main/java/me/stuffy/stuffybot/utils/Verification.java index 9030061..fd110b4 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/Verification.java +++ b/src/main/java/me/stuffy/stuffybot/utils/Verification.java @@ -2,14 +2,14 @@ import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.profiles.HypixelProfile; +import net.dv8tion.jda.api.components.label.Label; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.components.textinput.TextInput; +import net.dv8tion.jda.api.components.textinput.TextInputStyle; +import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -49,21 +49,22 @@ public static void verifyButton(ButtonInteractionEvent event) { return; } - Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") - .addComponents(ActionRow.of(TextInput.create("ign", "Minecraft Username", TextInputStyle.SHORT) - .setPlaceholder("Your Minecraft Username") - .setMaxLength(16) - .setMinLength(1) - .setRequired(true) - .build()), - ActionRow.of( - TextInput.create("captcha", "CAPTCHA", TextInputStyle.PARAGRAPH) - .setPlaceholder("Enter the word 'stuffy'.\n" + - "To prevent abuse, failing the CAPTCHA " + - "will result in a short timeout.") - .setRequired(false) - .build())) + TextInput ign = TextInput.create("ign", TextInputStyle.SHORT) + .setPlaceholder("Your Minecraft Username") + .setMaxLength(16) + .setMinLength(1) + .setRequired(true) + .build(); + + TextInput captcha = TextInput.create("captcha", TextInputStyle.PARAGRAPH) + .setPlaceholder("Enter the word 'stuffy'.\n" + + "To prevent abuse, failing the CAPTCHA " + + "will result in a short timeout.") + .setRequired(false) .build(); + + Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") + .addComponents(Label.of("Minecraft Username", ign), Label.of("CAPTCHA", captcha)).build(); event.replyModal(modal).queue(); } diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties index eb39c0b..e737599 100644 --- a/src/main/resources/config.properties +++ b/src/main/resources/config.properties @@ -3,4 +3,4 @@ homeGuildId=795108903733952562 verifiedRoleId=795118862940635216 notVerifiedRoleId=1356442418354061503 linkCommandId=1281037425032040491 -customStatus=april's foolin' \ No newline at end of file +customStatus=working on something new \ No newline at end of file From dafb3c947486d2e0e5b9cca7fe6fd90d330a54a0 Mon Sep 17 00:00:00 2001 From: Stuffy <77872467+stuffyerface@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:56:08 -0500 Subject: [PATCH 44/44] Add /uuid Command Support User Installed Commands --- src/main/java/me/stuffy/stuffybot/Bot.java | 54 ++++++++------- .../stuffybot/commands/UuidCommand.java | 68 +++++++++++++++++++ .../interactions/InteractionHandler.java | 10 ++- .../interactions/InteractionManager.java | 1 + 4 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java index 85a83d4..4d3acd6 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -15,13 +15,15 @@ import net.dv8tion.jda.api.events.guild.GuildJoinEvent; import net.dv8tion.jda.api.events.guild.GuildLeaveEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import org.kohsuke.github.GitHub; import java.time.LocalDateTime; @@ -75,7 +77,11 @@ public Bot() throws InterruptedException { ); // Register commands "global"ly or "local"ly - String environmentScope = Config.getEnvironment().equals("production") ? "global" : "local"; + String environmentScope = switch (Config.getEnvironment()) { + case "development" -> "local"; + case "production", "development_global" -> "global"; + default -> throw new IllegalArgumentException("Invalid environment: " + Config.getEnvironment()); + }; registerCommands(environmentScope); // Start events @@ -153,29 +159,33 @@ public void onGuildLeave(GuildLeaveEvent event) { Logger.log(" Bot left guild: " + leftGuild.getName() + " (" + leftGuild.getId() + ")"); } - public void registerCommands(String scope) { + private SlashCommandData createSlashCommand(String name, String description) { + return Commands.slash(name, description).setContexts(InteractionContextType.ALL).setIntegrationTypes(IntegrationType.ALL); + } + + private void registerCommands(String scope) { OptionData ignOption = new OptionData(OptionType.STRING, "ign", "The player's IGN", false); OptionData ignOptionRequired = new OptionData(OptionType.STRING, "ign", "The player's IGN", true); // Create a list of commands first ArrayList commandList = new ArrayList<>(); - commandList.add(Commands.slash("help", "Learn about the bot and its commands")); - commandList.add(Commands.slash("pit", "Get Pit stats for a player") + commandList.add(createSlashCommand("help", "Learn about the bot and its commands")); + commandList.add(createSlashCommand("pit", "Get Pit stats for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("stats", "Get Hypixel stats for a player") + commandList.add(createSlashCommand("stats", "Get Hypixel stats for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("tkr", "Get TKR stats for a player") + commandList.add(createSlashCommand("tkr", "Get TKR stats for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("maxes", "Get maxed games for a player") + commandList.add(createSlashCommand("maxes", "Get maxed games for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("blitz", "Get Blitz Ultimate Kit xp for a player") + commandList.add(createSlashCommand("blitz", "Get Blitz Ultimate Kit xp for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("megawalls", "Get Mega Walls skins for a player") + commandList.add(createSlashCommand("megawalls", "Get Mega Walls skins for a player") .addOptions(ignOption) .addOptions(new OptionData(OptionType.STRING, "skins", "Which skins to look at", false).setAutoComplete(true))); - commandList.add(Commands.slash("tournament", "Get tournament stats for a player") + commandList.add(createSlashCommand("tournament", "Get tournament stats for a player") .addOptions(ignOption) .addOptions(new OptionData(OptionType.INTEGER, "tournament", "Which tournament to look at (Leave empty for latest)", false).setAutoComplete(true))); - commandList.add(Commands.slash("achievements", "Get achievement stats for a player") + commandList.add(createSlashCommand("achievements", "Get achievement stats for a player") .addOptions(ignOption) .addOptions(new OptionData(OptionType.STRING, "game", "Which game to look at", false).setAutoComplete(true)) .addOptions(new OptionData(OptionType.STRING, "type", "Which achievements to look at", false).addChoices( @@ -184,16 +194,18 @@ public void registerCommands(String scope) { new Command.Choice("Tiered", "tiered") ) )); - commandList.add(Commands.slash("link", "Link a Minecraft account so you don't have to type your IGN every time") + commandList.add(createSlashCommand("link", "Link a Minecraft account so you don't have to type your IGN every time") .addOptions(ignOptionRequired)); - commandList.add(Commands.slash("playcommand", "Lookup the command to quickly hop into a game") + commandList.add(createSlashCommand("playcommand", "Lookup the command to quickly hop into a game") .addOptions(new OptionData(OptionType.STRING, "game", "Search for a play command", true).setAutoComplete(true))); - commandList.add(Commands.slash("search", "Search for an achievement by name, or description.") + commandList.add(createSlashCommand("search", "Search for an achievement by name, or description.") .addOptions(new OptionData(OptionType.STRING, "search", "Search for an Achievement", true).setAutoComplete(true))); + commandList.add(createSlashCommand("uuid", "Get UUID info for a Minecraft player") + .addOptions(ignOptionRequired)); if (scope.equals("local")) { - //clearLocalCommands(); + jda.updateCommands().queue(); this.homeGuild.updateCommands().addCommands( commandList ).queue(); @@ -216,14 +228,4 @@ public void registerCommands(String scope) { )) ).queue(); } - - public void clearCommands() { - jda.updateCommands().queue(); - Logger.log(" Successfully cleared commands."); - } - - public void clearLocalCommands() { - this.homeGuild.updateCommands().queue(); - Logger.log(" Successfully cleared local commands."); - } } diff --git a/src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java b/src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java new file mode 100644 index 0000000..3279a2e --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java @@ -0,0 +1,68 @@ +package me.stuffy.stuffybot.commands; + +import me.stuffy.stuffybot.interactions.InteractionId; +import me.stuffy.stuffybot.profiles.MojangProfile; +import me.stuffy.stuffybot.utils.APIException; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.util.UUID; + +import static me.stuffy.stuffybot.utils.APIUtils.getMojangProfile; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; + +public class UuidCommand { + public static MessageCreateData uuid(InteractionId interactionId) throws APIException { + String ign = interactionId.getOptions().get("ign"); + MojangProfile mojangProfile = getMojangProfile(ign); + UUID uuid = mojangProfile.getUuid(); + UUIDStats uuidStats = new UUIDStats(mojangProfile.getUsername(), uuid); + + + MessageEmbed uuidEmbed = makeStatsEmbed("UUID Data", + "`" + uuidStats.username + "`'s UUID is `" + uuidStats.uuid + "`\n" + + "That's better than `" + uuidStats.getFormattedBetterThanPercentage() + "`% of all UUIDs!" + + "\n-# Position `#" + String.format("%,d", uuidStats.estimatedRank(65340094)) + "`." + ); + + return new MessageCreateBuilder() + .addEmbeds(uuidEmbed) + .build(); + + } + + private static class UUIDStats { + private final String username; + private final UUID uuid; + private final double betterThanPercentage; + + public UUIDStats(String username, UUID uuid) { + this.username = username; + this.uuid = uuid; + this.betterThanPercentage = getBetterThanPercentage(); + } + + private double getBetterThanPercentage() { + String characterRanking = "0123456789abcdef"; + String uuid = this.uuid.toString().replace("-", ""); + double totalScore = 0; + double remainingScore = 100.0; + for (int i = 0; i < uuid.length(); i++) { + int totalChars = characterRanking.length(); + int pos = characterRanking.indexOf(uuid.charAt(i)); + totalScore += remainingScore * (pos / (double) totalChars); + remainingScore = remainingScore / 16; + } + return totalScore; + } + + public int estimatedRank(int totalPlayers) { + return (int) (Math.ceil((1 - (this.betterThanPercentage / 100)) * totalPlayers)); + } + + public String getFormattedBetterThanPercentage() { + return String.format("%.2f", this.betterThanPercentage); + } + } +} diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index 8b11576..8d74a7f 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -82,13 +82,17 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { InteractionId interactionId = new InteractionId(id, commandName, event.getUser().getId(), optionsArray); - Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray.toString()); + if (event.getIntegrationOwners().isUserIntegration()) { + Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray); + } else { + Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray); + } GlobalData globalData = Bot.getGlobalData(); globalData.incrementCommandsRun(event.getUser().getId(), commandName); globalData.addUniqueUser(event.getUser().getId(), event.getUser().getName()); - MessageCreateData response = null; + MessageCreateData response; try { response = getResponse(interactionId); } catch (InteractionException e) { @@ -97,7 +101,7 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { return; } catch (Exception e) { MessageEmbed errorEmbed = makeErrorEmbed("Unknown Error", "Uh Oh! I have no idea what went wrong, report this.\n-# Everybody makes mistakes."); - Logger.logError("Unknown error in command: " + commandName + " " + optionsArray.toString() + " " + e.getMessage()); + Logger.logError("Unknown error in command: " + commandName + " " + optionsArray + " " + e.getMessage()); e.printStackTrace(); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java index ae50616..80f33bb 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java @@ -46,6 +46,7 @@ public static MessageCreateData getResponse(InteractionId interactionId) throws case "playcommand" -> PlayCommandCommand.playCommand(interactionId); case "help" -> HelpCommand.help(); case "search" -> SearchCommand.search(interactionId); + case "uuid" -> UuidCommand.uuid(interactionId); default -> throw new InteractionException("Invalid command"); }; } catch (APIException e) {