From 517b2d81c1a8a5e5da8416022c4439b4e8847f5e Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:10:18 +0100 Subject: [PATCH 01/92] added github links to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb8dd85..143d0da 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ You can read about the full set of features in our lovely wiki [here](https://gi ## The team :) -Made with love by: Antti, Lucy, Leonie, Murphy, Floschy, Amber, Wilson, Riley, Kryal, Ian ❤️ +Made with love by: [Antti](https://github.com/Chicken), [Lucy](https://github.com/BlackDemonFire), [Leonie](https://github.com/KommentatorForAll), [Murphy](https://github.com/StrangeGirlMurph), [Floschy](https://github.com/flloschy), [Amber](https://github.com/uselessamber), [Wilson](https://github.com/WilsontheWolf), Riley, [Kryal](https://github.com/Da-Real-Kryall), [Ian](https://github.com/IanToujou) ❤️ We're all part of a game jam/hackothon server with monthly events and wanted to make our and your lives easier. Have fun coding! ## Usage & Development From 669896aadd3ead36518494351e1b92cf6a32b999 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:11:14 +0100 Subject: [PATCH 02/92] Delete old tracking system --- src/commands/ActivityTrackerCommand.ts | 222 ---------------- .../ActivityTrackerAutocomplete.ts | 33 --- .../buttons/ActivityTrackerButton.ts | 33 --- src/util/activity-tracker/admin.ts | 111 -------- src/util/activity-tracker/autocompletes.ts | 143 ----------- src/util/activity-tracker/blacklist.ts | 123 --------- src/util/activity-tracker/help.ts | 87 ------- src/util/activity-tracker/list.ts | 239 ------------------ src/util/activity-tracker/presence.ts | 42 --- src/util/activity-tracker/statistics.ts | 136 ---------- src/util/activity-tracker/trackerEmbed.ts | 88 ------- 11 files changed, 1257 deletions(-) delete mode 100644 src/commands/ActivityTrackerCommand.ts delete mode 100644 src/interactions/autocompleters/ActivityTrackerAutocomplete.ts delete mode 100644 src/interactions/buttons/ActivityTrackerButton.ts delete mode 100644 src/util/activity-tracker/admin.ts delete mode 100644 src/util/activity-tracker/autocompletes.ts delete mode 100644 src/util/activity-tracker/blacklist.ts delete mode 100644 src/util/activity-tracker/help.ts delete mode 100644 src/util/activity-tracker/list.ts delete mode 100644 src/util/activity-tracker/presence.ts delete mode 100644 src/util/activity-tracker/statistics.ts delete mode 100644 src/util/activity-tracker/trackerEmbed.ts diff --git a/src/commands/ActivityTrackerCommand.ts b/src/commands/ActivityTrackerCommand.ts deleted file mode 100644 index c17bccf..0000000 --- a/src/commands/ActivityTrackerCommand.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { - ChatInputCommandInteraction, - SlashCommandBuilder, - SlashCommandSubcommandsOnlyBuilder, -} from "discord.js"; -import { Command } from "../interactions/interactionClasses"; -import { blacklistAdd, blacklistRemove, blacklistShow } from "../util/activity-tracker/blacklist"; -import { statsAll, statsGame, statsMy } from "../util/activity-tracker/statistics"; -import { - adminBlacklistGame, - adminLook, - adminReset, - adminShow, - adminWhitelistGame, -} from "../util/activity-tracker/admin"; -import { list } from "../util/activity-tracker/list"; -import { hasAdminRole } from "../util/misc/permissions"; - -class ActivityTrackerCommand extends Command { - constructor() { - super("activity-tracker"); - } - - async execute(interaction: ChatInputCommandInteraction): Promise { - const group: string | null = interaction.options.getSubcommandGroup(); - const sub: string | null = interaction.options.getSubcommand(); - - if (group === "blacklist") { - if (sub === "add") { - await blacklistAdd(interaction); - } else if (sub === "remove") { - await blacklistRemove(interaction); - } else if (sub === "show") { - await blacklistShow(interaction); - } - } else if (group === "stats") { - if (sub === "my") { - await statsMy(interaction); - } else if (sub === "game") { - await statsGame(interaction); - } else if (sub === "all") { - await statsAll(interaction); - } - } else if (group === "admin") { - if (!(await hasAdminRole(interaction))) { - await interaction.reply({ - content: "You don't have the required permissions to use this command.", - ephemeral: true, - }); - } else if (sub === "reset") { - await adminReset(interaction); - } else if (sub === "blacklist") { - await adminBlacklistGame(interaction); - } else if (sub === "whitelist") { - await adminWhitelistGame(interaction); - } else if (sub === "look") { - await adminLook(interaction); - } else if (sub == "show") { - await adminShow(interaction); - } - } else if (sub == "list") { - await list(interaction); - } - } - - register(): - | SlashCommandSubcommandsOnlyBuilder - | Omit { - return new SlashCommandBuilder() - .setName("activity-tracker") - .setDescription("A game activity tracker to provide interesting insights in the people on this server.") - .addSubcommand((sub) => - sub - .setName("list") - .setDescription("Generates a list of the played games based on a selected sorting method.") - .addStringOption((opt) => - opt - .setName("sort") - .setDescription("Criteria to sort by. (default: logs chronologically)") - .addChoices( - { name: "logs chronologically", value: "log-history" }, - { name: "playtime per game", value: "playtime-per-game" }, - { name: "number of logs per game", value: "logs-per-game" }, - { name: "last log date per game", value: "log-date-per-game" } - ) - ) - .addStringOption((opt) => - opt - .setName("order") - .setDescription("Ordering. (default: decreasing)") - .addChoices( - { name: "decreasing", value: "decreasing" }, - { name: "increasing", value: "increasing" } - ) - ) - ) - .addSubcommandGroup((group) => - group - .setName("stats") - .setDescription("Gives you statistics based on the game activity.") - .addSubcommand((sub) => - sub - .setName("my") - .setDescription("Show statistics about your own logs.") - .addStringOption((opt) => - opt - .setName("game") - .setDescription("A single game to use for the stats. (default: all games)") - .setAutocomplete(true) - ) - ) - .addSubcommand((sub) => - sub - .setName("game") - .setDescription("Show statistics about a game across all logs.") - .addStringOption((opt) => - opt - .setName("game") - .setDescription("The game to use for the stats.") - .setAutocomplete(true) - .setRequired(true) - ) - .addBooleanOption((opt) => - opt - .setName("show-playtime") - .setDescription("Show the playtime of the game per player. (default: false)") - ) - ) - .addSubcommand((sub) => sub.setName("all").setDescription("Show statistics across all logs.")) - ) - .addSubcommandGroup((group) => - group - .setName("blacklist") - .setDescription( - "Manage your activity blacklist. (The activity of a blacklisted game will not be logged)" - ) - .addSubcommand((sub) => - sub - .setName("add") - .setDescription("Add a game to your blacklist.") - .addStringOption((opt) => - opt - .setName("game") - .setDescription("The game to blacklist.") - .setAutocomplete(true) - .setRequired(true) - ) - ) - .addSubcommand((sub) => - sub - .setName("remove") - .setDescription("Remove a game from your blacklist.") - .addStringOption((opt) => - opt - .setName("game") - .setDescription("The game to whitelist.") - .setAutocomplete(true) - .setRequired(true) - ) - ) - .addSubcommand((sub) => sub.setName("show").setDescription("See what is on your blacklist.")) - ) - .addSubcommandGroup((group) => - group - .setName("admin") - .setDescription("Admin commands to manage the activity tracker.") - .addSubcommand((sub) => - sub - .setName("reset") - .setDescription("Reset every log and blacklist entry.") - .addBooleanOption((opt) => - opt.setName("sure").setDescription("Are you really sure?").setRequired(true) - ) - .addStringOption((opt) => - opt - .setName("really") - .setDescription("Are you really sure you want to delete every entry?") - .addChoices( - { name: "No. I don't want to delete every log and blacklist entry!", value: "no" }, - { name: "Yes. I am sure. I want to delete every log and blacklist entry!", value: "yes" } - ) - .setRequired(true) - ) - ) - .addSubcommand((sub) => - sub - .setName("blacklist") - .setDescription("Add a game to the global blacklist.") - .addStringOption((opt) => - opt - .setName("game") - .setDescription("The game to blacklist.") - .setRequired(true) - .setAutocomplete(true) - ) - ) - .addSubcommand((sub) => - sub - .setName("whitelist") - .setDescription("Remove a game from the global blacklist.") - .addStringOption((opt) => - opt - .setName("game") - .setDescription("The game to whitelist.") - .setRequired(true) - .setAutocomplete(true) - ) - ) - .addSubcommand((sub) => sub.setName("show").setDescription("See what is on the global blacklist.")) - .addSubcommand((sub) => - sub - .setName("look") - .setDescription("View a users personal blacklist.") - .addUserOption((opt) => - opt.setName("user").setDescription("The user to view the blacklist from.").setRequired(true) - ) - ) - ); - } -} - -export default new ActivityTrackerCommand(); diff --git a/src/interactions/autocompleters/ActivityTrackerAutocomplete.ts b/src/interactions/autocompleters/ActivityTrackerAutocomplete.ts deleted file mode 100644 index 4e277ad..0000000 --- a/src/interactions/autocompleters/ActivityTrackerAutocomplete.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Autocompleter } from "../interactionClasses"; -import { AutocompleteInteraction } from "discord.js"; -import { - blacklistAdd, - blacklistAutocompletion, - blacklistRemove, - gamesAutocompletion, - statsMy, -} from "../../util/activity-tracker/autocompletes"; - -class ActivityTrackerAutocompleter extends Autocompleter { - constructor() { - super("activity-tracker"); - } - - async execute(interaction: AutocompleteInteraction): Promise { - const group = interaction.options.getSubcommandGroup(); - const sub = interaction.options.getSubcommand(); - - if (group === "admin") { - if (sub == "whitelist") await blacklistAutocompletion(interaction); - else if (sub == "blacklist") await gamesAutocompletion(interaction); - } else if (group === "stats") { - if (sub == "my") await statsMy(interaction); - else if (sub == "game") await gamesAutocompletion(interaction); - } else if (group === "blacklist") { - if (sub == "remove") await blacklistRemove(interaction); - else if (sub == "add") await blacklistAdd(interaction); - } - } -} - -export default new ActivityTrackerAutocompleter(); diff --git a/src/interactions/buttons/ActivityTrackerButton.ts b/src/interactions/buttons/ActivityTrackerButton.ts deleted file mode 100644 index 85164a6..0000000 --- a/src/interactions/buttons/ActivityTrackerButton.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Button } from "../interactionClasses"; -import { ButtonInteraction } from "discord.js"; -import { gameActivityTrackerEmbed } from "../../util/activity-tracker/trackerEmbed"; - -class ActivityTrackerButton extends Button { - constructor() { - super("activity-tracker"); - } - async execute(interaction: ButtonInteraction, args: string[]): Promise { - let offset = parseInt(args[1]); - const pages = parseInt(args[2]); - - const jump = Math.round(pages / 4); // jump by a quarter of the pages - - if (args[0] == "left") offset -= 1; - else if (args[0] == "jump-left") offset -= jump; - else if (args[0] == "right") offset += 1; - else if (args[0] == "jump-right") offset += jump; - - if (offset < 0) offset = pages == 1 ? 0 : pages + (offset % pages); - else offset = offset % pages; - - const sort = args[3]; - const order = args[4]; - - const [embed, row] = await gameActivityTrackerEmbed(sort, order, offset); - if (!embed) return; - - await interaction.update({ embeds: [embed], components: row ? [row] : [] }); - } -} - -export default new ActivityTrackerButton(); diff --git a/src/util/activity-tracker/admin.ts b/src/util/activity-tracker/admin.ts deleted file mode 100644 index fab1bf6..0000000 --- a/src/util/activity-tracker/admin.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; -import { addEmbedColor, addEmbedFooter } from "../misc/embeds"; -import { activityTrackerBlacklistDb, activityTrackerLogDb } from "../../db"; -import { getBlacklist } from "./help"; -import { logger } from "../../logger"; -import { blacklistCodes } from "./blacklist"; - -export async function adminReset(interaction: ChatInputCommandInteraction): Promise { - const sure: boolean = interaction.options.getBoolean("sure", true); - const really: string = interaction.options.getString("really", true); - - if (!(sure && really == "yes")) { - await interaction.reply({ - content: "It seems like your are not really sure...\nThe reset wasn't executed.", - ephemeral: true, - }); - return; - } else { - activityTrackerLogDb.clear(); - activityTrackerBlacklistDb.clear(); - activityTrackerBlacklistDb.ensure("general-user", []); - activityTrackerBlacklistDb.ensure("general-game", []); - - await interaction.reply({ - content: "Reset done! I deleted all the logs and cleared the blacklist.", - ephemeral: true, - }); - } -} - -export async function adminBlacklistGame(interaction: ChatInputCommandInteraction): Promise { - const game = interaction.options.getString("game", true); - activityTrackerBlacklistDb.push("general-game", game.toLowerCase()); - - const embed = new EmbedBuilder() - .setTitle("Added a game to blacklist!") - .setDescription(`No activity about "${game}" will be logged anymore.`); - await interaction.reply({ embeds: [addEmbedColor(embed)], ephemeral: true }); -} - -export async function adminWhitelistGame(interaction: ChatInputCommandInteraction): Promise { - const game = interaction.options.getString("game", true).toLowerCase(); - - if (game == blacklistCodes.emptyGlobalBlacklist) { - // blacklistCodes.emptyGlobalBlacklist is returned by the autocomplete when global blacklist is empty - interaction.reply({ content: "The global blacklist is empty...", ephemeral: true }); - } - - let blacklistedGames: string[] | undefined = activityTrackerBlacklistDb.get("general-game"); - - if (!blacklistedGames) { - await interaction.reply({ content: "Something went wrong...", ephemeral: true }); - logger.error( - "Something went wrong while trying to get the global blacklist. Couldn't get the 'general-game' key." - ); - return; - } - - blacklistedGames = blacklistedGames?.filter((e) => e !== game); - - activityTrackerBlacklistDb.set("general-game", blacklistedGames); - - const embed = new EmbedBuilder() - .setTitle("Removed a game from the global blacklist!") - .setDescription(`Removed "${game}" from global blacklist.`); - await interaction.reply({ embeds: [addEmbedColor(embed)], ephemeral: true }); -} - -export async function adminLook(interaction: ChatInputCommandInteraction): Promise { - const user = interaction.options.getUser("user", true); - const blacklist = await getBlacklist(user.id); - - if (blacklist?.length == 0 || blacklist == undefined) { - const embed = new EmbedBuilder() - .setTitle(`${user.tag}'s blacklist`) - .setDescription( - "Tracking status: `" + - (activityTrackerBlacklistDb.get("general-user")?.includes(user.id) ? "disabled" : "enabled") + - "`\nBlacklisted games: No game is blacklisted. Every game gets logged" - ); - await interaction.reply({ embeds: [addEmbedColor(embed)], ephemeral: true }); - return; - } - - const str = "`" + blacklist.join("`, `") + "`"; - - const embed = new EmbedBuilder() - .setTitle(`${user.tag}'s blacklist`) - .setDescription( - "Trackstatus: `" + - (activityTrackerBlacklistDb.get("general-user")?.includes(user.id) ? "disabled" : "enabled") + - "`\nBlacklisted games: " + - str - ); - await interaction.reply({ embeds: [addEmbedColor(embed)], ephemeral: true }); -} - -export async function adminShow(interaction: ChatInputCommandInteraction) { - const blacklist: string[] | undefined = activityTrackerBlacklistDb.get("general-game"); - - if (!blacklist || blacklist.length == 0) { - let embed = new EmbedBuilder().setTitle("The global blacklist is empty..."); - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed], ephemeral: true }); - return; - } - - const str = "`" + blacklist?.join("`, `") + "`"; - const embed = new EmbedBuilder().setTitle("Global Blacklist").setDescription(str); - await interaction.reply({ embeds: [addEmbedColor(embed)], ephemeral: true }); -} diff --git a/src/util/activity-tracker/autocompletes.ts b/src/util/activity-tracker/autocompletes.ts deleted file mode 100644 index e2ca233..0000000 --- a/src/util/activity-tracker/autocompletes.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { AutocompleteInteraction } from "discord.js"; -import { activityTrackerBlacklistDb, activityTrackerLogDb } from "../../db"; -import { splitId } from "./help"; -import { hasAdminRole } from "../misc/permissions"; -import { blacklistCodes } from "./blacklist"; - -export async function blacklistAutocompletion(interaction: AutocompleteInteraction) { - if (await hasAdminRole(interaction)) { - await interaction.respond([ - { - name: "[Admin permissions are required to use this command!]", - value: blacklistCodes.missingPermission, - }, - ]); - return; - } - - let options: string[] | undefined = activityTrackerBlacklistDb.get("general-game"); - - options = [...new Set(options)]; - - if (options?.length === 0 || !options) { - await interaction.respond([ - { - name: "[The global blacklist is empty...]", - value: blacklistCodes.emptyGlobalBlacklist, - }, - ]); - return; - } - - let map = filterAndMapAutocompletion(interaction, options); - map = map.slice(0, 25); - - await interaction.respond(map); -} - -export async function statsMy(interaction: AutocompleteInteraction) { - const allKeys = activityTrackerLogDb.keyArray(); - let games: string[] = []; - allKeys.forEach((e) => { - const { user, game } = splitId(e); - if (user !== interaction.user.id) return; - games.push(game); - }); - - games = [...new Set(games)]; - - if (games.length == 0) { - await interaction.respond(noLogsYet); - return; - } - - let map = filterAndMapAutocompletion(interaction, games); - map = map.slice(0, 25); - - await interaction.respond(map); -} - -export async function gamesAutocompletion(interaction: AutocompleteInteraction) { - const allKeys = activityTrackerLogDb.keyArray(); - let games: string[] = []; - allKeys.forEach((e) => { - const gameEntry = splitId(e).game; - games.push(gameEntry); - }); - - games = [...new Set(games)]; - - if (games.length == 0) { - await interaction.respond(noLogsYet); - return; - } - - let map = filterAndMapAutocompletion(interaction, games); - map = map.slice(0, 25); - - await interaction.respond(map); -} - -export async function blacklistRemove(interaction: AutocompleteInteraction) { - let options: string[] | undefined = activityTrackerBlacklistDb.get(interaction.user.id); - - options = [...new Set(options)]; - - if (activityTrackerBlacklistDb.get("general-user")?.includes(interaction.user.id)) { - await interaction.respond([ - { - name: "[Tracking is disabled for you. Select this to activate it again]", - value: blacklistCodes.trackingDisabled, - }, - ]); - return; - } - - if (options?.length === 0 || !options) { - await interaction.respond([{ name: "No games are on your blacklist", value: "empty-blacklist" }]); - return; - } - - let map = filterAndMapAutocompletion(interaction, options); - map = map.slice(0, 25); - - await interaction.respond(map); -} - -export async function blacklistAdd(interaction: AutocompleteInteraction) { - const allKeys = activityTrackerLogDb.keyArray(); - let games: string[] = []; - allKeys.forEach((e) => { - const { user, game } = splitId(e); - if (user !== interaction.user.id) return; - games.push(game); - }); - - games = [...new Set(games)]; - - if (games.length == 0) { - await interaction.respond(noLogsYet); - return; - } - - let map = filterAndMapAutocompletion(interaction, games); - map = map.slice(0, 24); - - map.push({ name: "[Disable your tracking for all games]", value: blacklistCodes.disableTracking }); - - await interaction.respond(map); -} - -const noLogsYet = [{ name: "Nothing has been logged yet...", value: "Nothing has been logged yet..." }]; - -function filterAndMapAutocompletion(interaction: AutocompleteInteraction, input: string[]) { - return input - .filter((c) => c.startsWith(interaction.options.getFocused().toLowerCase() as string)) - .map((c) => ({ - name: c - .replace(/(\b\w)/g, (e) => e.toUpperCase()) - .trim() - .slice(0, 100), - value: c.slice(0, 100), - })); -} diff --git a/src/util/activity-tracker/blacklist.ts b/src/util/activity-tracker/blacklist.ts deleted file mode 100644 index 5a24af8..0000000 --- a/src/util/activity-tracker/blacklist.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; -import { addEmbedColor, addEmbedFooter } from "../misc/embeds"; -import { activityTrackerBlacklistDb, activityTrackerLogDb } from "../../db"; -import { getBlacklist, splitId } from "./help"; - -export const blacklistCodes = { - emptyBlacklist: "empty-blacklist", - missingPermission: "missing-permission", - emptyGlobalBlacklist: "empty-global-blacklist", - trackingDisabled: "tracking-disabled", - disableTracking: "disable-tracking", -}; - -export async function blacklistAdd(interaction: ChatInputCommandInteraction): Promise { - const game = interaction.options.getString("game", true); - if (game == "Disable Tracking") { - activityTrackerBlacklistDb.push("general-user", interaction.user.id); - let embed = new EmbedBuilder() - .setTitle("Your game activity won't get logged anymore!") - .setDescription( - "Tracking is now disabled for you.\nTo activate it again use `/tracking blacklist remove`" - ); - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed], ephemeral: true }); - return; - } - - if (!activityTrackerBlacklistDb.has(interaction.user.id)) { - activityTrackerBlacklistDb.set(interaction.user.id, []); - } - activityTrackerBlacklistDb.push(interaction.user.id, game.toLowerCase()); - await interaction.reply({ content: `"${game}" is now on your blacklist!`, ephemeral: true }); - - if ( - !activityTrackerLogDb - .keyArray() - .map(splitId) - .map((e) => e.game) - .find((e) => e === game.toLowerCase()) - ) { - await interaction.followUp({ - content: - "No logs across all users of that game exists. Maybe double check if you didn't make a spelling error (capitalization doesn't matter) (use the autocomplete!).", - }); - } -} - -export async function blacklistRemove(interaction: ChatInputCommandInteraction): Promise { - const game = interaction.options.getString("game", true); - - if (game === blacklistCodes.trackingDisabled) { - let blacklistedUser = activityTrackerBlacklistDb.get("general-user"); - if (!blacklistedUser) { - await interaction.reply({ content: "Something went wrong", ephemeral: true }); - return; - } - - blacklistedUser = blacklistedUser.filter((e) => e !== interaction.user.id); - - activityTrackerBlacklistDb.set("general-user", blacklistedUser); - interaction.reply({ content: "Your tracking is activated again!", ephemeral: true }); - return; - } - - let blacklistedGames = activityTrackerBlacklistDb.get(interaction.user.id); - - if (game === blacklistCodes.emptyBlacklist) { - interaction.reply({ content: "You didn't blacklist any games yet.", ephemeral: true }); - return; - } - - if (!blacklistedGames) { - await interaction.reply({ - content: "Cant remove any games from your blacklist because no games are blacklisted!", - ephemeral: true, - }); - return; - } - - blacklistedGames = blacklistedGames.filter((e) => e !== game); - - activityTrackerBlacklistDb.set(interaction.user.id, blacklistedGames); - - let embed = new EmbedBuilder().setTitle(`"${game}" is now removed from the blacklist`); - embed = addEmbedColor(embed); - await interaction.reply({ embeds: [embed], ephemeral: true }); -} - -export async function blacklistShow(interaction: ChatInputCommandInteraction): Promise { - const blacklist = await getBlacklist(interaction.user.id); - - if (blacklist?.length == 0 || blacklist == undefined) { - let embed = new EmbedBuilder() - .setTitle("Your Blacklist") - .setDescription( - "Tracking status: `" + - (activityTrackerBlacklistDb.get("general-user")?.includes(interaction.user.id) - ? "disabled" - : "enabled") + - "`\nBlacklisted games: No game is blacklisted. Logging every game." - ); - - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed], ephemeral: true }); - return; - } - - const str = "`" + blacklist.join("`, `") + "`"; - - let embed = new EmbedBuilder() - .setTitle("Your blacklist") - .setDescription( - "Trackstatus: `" + - (activityTrackerBlacklistDb.get("general-user")?.includes(interaction.user.id) - ? "disabled" - : "enabled") + - "`\nBlacklisted games: " + - str - ); - embed = addEmbedFooter(embed); - - await interaction.reply({ embeds: [embed], ephemeral: true }); -} diff --git a/src/util/activity-tracker/help.ts b/src/util/activity-tracker/help.ts deleted file mode 100644 index c24599f..0000000 --- a/src/util/activity-tracker/help.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { activityTrackerBlacklistDb, activityTrackerLogDb } from "../../db"; -import { EmbedField } from "discord.js"; -import { discordRelativeTimestamp, durationToReadable } from "../misc/time"; -import { DateTime, Duration } from "luxon"; - -export async function getBlacklist(userid: string): Promise { - if (!activityTrackerBlacklistDb.has(userid)) return []; - return activityTrackerBlacklistDb.get(userid); -} - -export function getEntries(user?: string, game?: string): string[] { - let entries = activityTrackerLogDb.keyArray().map((e) => splitId(e)); - - if (user) { - entries = entries.filter((e) => e.user === user); - } - - if (game) { - entries = entries.filter((e) => e.game === game); - } - - return entries.map((e) => joinId(e)); -} - -export async function makeStats(entries: string[]): Promise> { - if (entries.length == 0) { - return []; - } - - let firstEntry = DateTime.now().plus({ years: 1000 }); - let lastEntry = DateTime.now().minus({ years: 1000 }); - let playTime = Duration.fromObject({ seconds: 0 }); - let longestRecord = Duration.fromObject({ seconds: 0 }); - let totalRecords = 0; - - entries.forEach((key) => { - const logs = activityTrackerLogDb.get(key); - logs?.forEach((log) => { - totalRecords += 1; - playTime = playTime.plus(log.duration); - if (log.date < firstEntry) firstEntry = log.date; - if (log.date > lastEntry) lastEntry = log.date; - if (log.duration > longestRecord) longestRecord = log.duration; - }); - }); - - let dayDifference = lastEntry.diff(firstEntry).as("days"); - dayDifference = dayDifference < 1 ? 1 : dayDifference; - - const fields = [ - { name: "Record Range", value: `≈ ${Math.round(dayDifference)} days`, inline: true }, - { name: "Total Playtime", value: durationToReadable(playTime, true), inline: true }, - { - name: "Playtime/Day", - value: durationToReadable( - Duration.fromObject({ seconds: Math.floor(playTime.as("seconds") / dayDifference) }), - true - ), - inline: true, - }, - { - name: "First Record", - value: `${discordRelativeTimestamp(firstEntry)}`, - inline: true, - }, - { - name: "Last Record", - value: `${discordRelativeTimestamp(lastEntry)}`, - inline: true, - }, - { name: "Longest Record", value: durationToReadable(longestRecord, true), inline: true }, - { name: "Total Records", value: totalRecords.toString(), inline: true }, - ]; - - return fields; -} - -/** Splits the key into the userID and game */ -export function splitId(id: string): { user: string; game: string } { - const [user, ...gameParts] = id.split("-"); - const game = gameParts.join("-"); - return { user: user, game: game }; -} - -function joinId(element: { user: string; game: string }): string { - return `${element.user}-${element.game}`; -} diff --git a/src/util/activity-tracker/list.ts b/src/util/activity-tracker/list.ts deleted file mode 100644 index b557317..0000000 --- a/src/util/activity-tracker/list.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { activityTrackerLogDb } from "../../db"; -import { ChatInputCommandInteraction } from "discord.js"; -import { splitId } from "./help"; -import { gameActivityTrackerEmbed } from "./trackerEmbed"; -import { discordRelativeTimestamp, discordTimestamp, durationToReadable } from "../misc/time"; -import { DateTime, Duration } from "luxon"; - -interface log { - user: string; - game: string; - date: DateTime; -} - -export async function logHistoryList(offset: number, order: string) { - const keys = activityTrackerLogDb.keyArray(); - const logs: log[] = []; - - keys.forEach((e) => { - const log = activityTrackerLogDb.get(e); - if (!log) return; - - const key = splitId(e); - const user = key.user; - let game = key.game; - - if (game.length > 14) { - game = game.slice(0, 13); - game = game + "…"; - } - - log.forEach((l) => { - logs.push({ user: `<@!${user}>`, game: game, date: l.date }); - }); - }); - - logs.sort((a, b) => (a.date > b.date ? -1 : 1)); // decreasing - if (order == "increasing") logs.reverse(); - - const games: string[] = []; - const timeAndUser: string[] = []; - - logs.forEach((e) => { - games.push(e.game.replace(/(\b\w)/g, (e) => e.toUpperCase())); - timeAndUser.push(`${discordTimestamp(e.date)} ⁘ ${e.user}`); - }); - - return [ - games.slice(offset, offset + 10), // games - timeAndUser.slice(offset, offset + 10), // values - Math.ceil(games.length / 10), // pages - ]; -} - -export async function playtimePerGameList(offset: number, order: string) { - offset *= 10; - - const keys = activityTrackerLogDb.keyArray(); - - const playtimePerGame = new Map(); - - keys.forEach((e) => { - const log = activityTrackerLogDb.get(e); - if (!log) return; - - const game = e.split("-")[1]; - - const logs = log.length; - const playtime = Duration.fromObject({ seconds: 0 }); - - if (logs == 0) return; - - log.forEach((entry) => { - playtime.plus(entry.duration); - }); - - if (!playtimePerGame.has(game)) { - playtimePerGame.set(game, playtime); - } else { - const time = playtimePerGame.get(game); - if (time) time.plus(playtime); - else return; - playtimePerGame.set(game, time); - } - }); - - const entries = Array.from(playtimePerGame.entries()); - - entries.sort((a, b) => (a[1] > b[1] ? -1 : 1)); // decreasing - if (order == "increasing") entries.reverse(); - - const sorted = new Map(entries); - - const games: string[] = []; - const values: string[] = []; - sorted.forEach(async (v, k) => { - k = k.replace(/(\b\w)/g, (e) => e.toUpperCase()); - if (k.length > 14) { - k = k.slice(0, 13); - k = k + "…"; - } - - games.push(k); - values.push(durationToReadable(v, true)); - }); - - return [ - games.slice(offset, offset + 10), // games - values.slice(offset, offset + 10), // values - Math.ceil(values.length / 10), // pages - ]; -} - -export async function logDatePerGameList(offset: number, order: string) { - offset *= 10; - - const keys = activityTrackerLogDb.keyArray(); - - const logDatePerGame = new Map(); - - keys.forEach((e) => { - const log = activityTrackerLogDb.get(e); - if (!log) return; - - const game = e.split("-")[1]; - - const logs = log.length; - let lastplayed = DateTime.now().minus({ years: 1000 }); - - if (logs == 0) return; - - log.forEach((entry) => { - if (entry.date > lastplayed) lastplayed = entry.date; - }); - - if (!logDatePerGame.has(game)) { - logDatePerGame.set(game, lastplayed); - } else { - const time = logDatePerGame.get(game); - if (!time) return; - if (lastplayed > time) { - logDatePerGame.set(game, lastplayed); - } - } - }); - - const entries = Array.from(logDatePerGame.entries()); - - entries.sort((a, b) => (a[1] > b[1] ? -1 : 1)); // decreasing - if (order == "increasing") entries.reverse(); - - const sorted = new Map(entries); - - const games: string[] = []; - const values: string[] = []; - sorted.forEach(async (v, k) => { - k = k.replace(/(\b\w)/g, (e) => e.toUpperCase()); - if (k.length > 14) { - k = k.slice(0, 13); - k = k + "…"; - } - - games.push(k); - values.push(`${discordTimestamp(v)} ⁘ ${discordRelativeTimestamp(v)}`); - }); - - return [ - games.slice(offset, offset + 10), // games - values.slice(offset, offset + 10), // values - Math.ceil(values.length / 10), // pages - ]; -} - -export async function logsPerGameList(offset: number, order: string) { - offset *= 10; - - const keys = activityTrackerLogDb.keyArray(); - - const logsPerGame = new Map(); - - keys.forEach((e) => { - const log = activityTrackerLogDb.get(e); - if (!log) return; - - const game = e.split("-")[1]; - - const logs = log.length; - if (logs == 0) return; - - if (!logsPerGame.has(game)) { - logsPerGame.set(game, logs); - } else { - let time = logsPerGame.get(game); - if (time) time += logs; - else return; - logsPerGame.set(game, time); - } - return; - }); - - const entries = Array.from(logsPerGame.entries()); - - entries.sort((a, b) => b[1] - a[1]); // decreasing - if (order == "increasing") entries.reverse(); - - const sorted = new Map(entries); - - const games: string[] = []; - const values: string[] = []; - sorted.forEach(async (v, k) => { - k = k.replace(/(\b\w)/g, (e) => e.toUpperCase()); - if (k.length > 14) { - k = k.slice(0, 13); - k = k + "…"; - } - - games.push(k); - values.push(`${v}`); - }); - - return [ - games.slice(offset, offset + 10), // games - values.slice(offset, offset + 10), // values - Math.ceil(values.length / 10), // pages - ]; -} - -export async function list(interaction: ChatInputCommandInteraction) { - await interaction.deferReply(); - const sort = interaction.options.getString("sort") || "log-history"; - const order = interaction.options.getString("order") || "decreasing"; - - const [embed, row] = await gameActivityTrackerEmbed(sort, order); - if (!embed) return; - - await interaction.editReply({ - embeds: [embed], - components: row ? [row] : [], - }); -} diff --git a/src/util/activity-tracker/presence.ts b/src/util/activity-tracker/presence.ts deleted file mode 100644 index e6ae578..0000000 --- a/src/util/activity-tracker/presence.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Activity, ActivityType, Presence } from "discord.js"; -import { activityTrackerBlacklistDb, activityTrackerLogDb } from "../../db"; -import { DateTime, Duration } from "luxon"; - -export async function getChangedActivities( - oldPresence: Presence | null, - newPresence: Presence -): Promise<{ started: Activity[]; stopped: Activity[] }> { - const oldActivities = oldPresence?.activities.filter((value) => value.type === ActivityType.Playing); - const newActivities = newPresence.activities.filter((value) => value.type === ActivityType.Playing); - - const stopped: Activity[] = []; - oldActivities?.forEach((activity) => { - if (!newActivities.some((newActivity) => newActivity.name === activity.name)) stopped.push(activity); - }); - - const started: Activity[] = []; - newActivities.forEach((activity) => { - if (!oldActivities?.some((oldActivity) => oldActivity.name === activity.name)) started.push(activity); - }); - - return { started, stopped }; -} - -export async function blacklistCheck(userid: string, elementName: string): Promise { - if (activityTrackerBlacklistDb.get("general-user")?.includes(userid)) return true; - if (activityTrackerBlacklistDb.get("general-game")?.includes(elementName)) return true; - if (activityTrackerBlacklistDb.has(userid)) { - if (activityTrackerBlacklistDb.get(userid)?.includes(elementName)) return true; - } - return false; -} - -export async function logTime(userid: string, elementName: string, timePlayed: number): Promise { - const name = elementName.replace(/[^\x20-\x7F]/g, "").trim(); - if (name.length == 0) return; - if (!activityTrackerLogDb.has(`${userid}-${name}`)) activityTrackerLogDb.set(`${userid}-${name}`, []); - activityTrackerLogDb.push(`${userid}-${name}`, { - duration: Duration.fromMillis(timePlayed), - date: DateTime.now(), - }); -} diff --git a/src/util/activity-tracker/statistics.ts b/src/util/activity-tracker/statistics.ts deleted file mode 100644 index abf8a1a..0000000 --- a/src/util/activity-tracker/statistics.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; -import { getEntries, makeStats, splitId } from "./help"; -import { addEmbedFooter } from "../misc/embeds"; -import { activityTrackerLogDb } from "../../db"; -import { durationToReadable } from "../misc/time"; -import { Duration } from "luxon"; - -export async function statsMy(interaction: ChatInputCommandInteraction): Promise { - let game = interaction.options.getString("game")?.toLowerCase(); - - const entries = getEntries(interaction.user.id, game); - - if (entries.length == 0) { - await interaction.reply({ - content: "No logs found...", - ephemeral: true, - }); - return; - } - - const fields = await makeStats(entries); - - if (game === null) { - const games: string[] = []; - entries.forEach((e) => { - games.push(splitId(e).game); - }); - - let embed = new EmbedBuilder() - .setTitle("Your stats across all games") - .addFields(fields) - .addFields({ name: "Games", value: `${games} unique games`, inline: true }); - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed] }); - return; - } - - if (game === undefined) { - game = "every logged game"; - } else { - game = game.replace(/(\b\w)/g, (e) => e.toUpperCase()); - } - - let embed = new EmbedBuilder().setTitle(`Your stats about ${game}`).addFields(fields); - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed] }); -} - -export async function statsGame(interaction: ChatInputCommandInteraction): Promise { - const game = interaction.options.getString("game", true).toLowerCase(); - const showPlaytime = interaction.options.getBoolean("show-playtime") ?? false; - - const entries = await getEntries(undefined, game); - const fields = await makeStats(entries); - - if (fields.length == 0) { - let embed = new EmbedBuilder().setTitle(`No logs found for ${game}...`); - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed] }); - return; - } - - const users: string[] = []; - entries.forEach((e) => { - users.push(splitId(e).user); - }); - if (users.length === 0) { - await interaction.reply({ content: "No records found...", ephemeral: true }); - return; - } - - let embed = new EmbedBuilder() - .setTitle(`Stats across all users for ${game.replace(/(\b\w)/g, (e) => e.toUpperCase())}!`) - .addFields(fields) - .addFields({ name: "Users", value: `${users.length} unique gaymers :)`, inline: true }); - - if (showPlaytime) { - const entries = activityTrackerLogDb.filter((val, key) => splitId(key).game === game); - let playtime = new Map(); - users.forEach((u) => { - playtime.set( - u, - Array.from(entries.filter((val, key) => splitId(key).user === u).values()) - .flat() - .reduce((a, b) => a.plus(b.duration), Duration.fromObject({ seconds: 0 })) - ); - }); - - playtime = new Map( - Array.from(playtime.entries()) - .filter((u) => u[1]) - .sort((a, b) => (a[1] > b[1] ? -1 : 1)) - .slice(0, 10) - ); - - embed.addFields({ - name: "Playtime top 10", - value: Array.from(playtime.entries()) - .map((e) => `${durationToReadable(e[1], true)} ⁘ <@${e[0]}>`) - .join("\n"), - }); - } - - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed] }); - return; -} - -export async function statsAll(interaction: ChatInputCommandInteraction): Promise { - const entries = getEntries(); - const fields = await makeStats(entries); - - if (fields.length == 0) { - await interaction.reply({ content: "No logs found...", ephemeral: true }); - return; - } - - const users: string[] = []; - const games: string[] = []; - entries.forEach((e) => { - const { user, game } = splitId(e); - if (!users.some((e) => e === user)) users.push(user); - if (!games.some((e) => e === game)) games.push(game); - }); - - let embed = new EmbedBuilder() - .setTitle("Stats across all users and games") - .addFields(fields) - .addFields( - { name: "Users", value: `${users.length} unique users`, inline: true }, - { name: "Games", value: `${games.length} unique games`, inline: true } - ); - - embed = addEmbedFooter(embed); - await interaction.reply({ embeds: [embed] }); -} diff --git a/src/util/activity-tracker/trackerEmbed.ts b/src/util/activity-tracker/trackerEmbed.ts deleted file mode 100644 index acd42a1..0000000 --- a/src/util/activity-tracker/trackerEmbed.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js"; -import { logDatePerGameList, logHistoryList, logsPerGameList, playtimePerGameList } from "./list"; -import { addEmbedFooter } from "../../util/misc/embeds"; - -export async function gameActivityTrackerEmbed( - sort: string, - order: string, - offset = 0 -): Promise<[EmbedBuilder | null, ActionRowBuilder | null]> { - let games; - let values; - let pages; - - if (sort == "playtime-per-game") { - [games, values, pages] = await playtimePerGameList(offset, order); - } else if (sort == "logs-per-game") { - [games, values, pages] = await logsPerGameList(offset, order); - } else if (sort == "log-date-per-game") { - [games, values, pages] = await logDatePerGameList(offset, order); - } else { - [games, values, pages] = await logHistoryList(offset, order); - } - - if (!Array.isArray(values) || !Array.isArray(games) || typeof pages !== "number") return [null, null]; - - if (values.length == 0) { - const embed = new EmbedBuilder() - .setTitle("Nothing to list...") - .setDescription("No activity has been logged yet."); - return [embed, null]; - } - - const text: { [key: string]: string } = { - "log-history": "in order they were logged", - "playtime-per-game": "by the playtime per game", - "logs-per-game": "by the number of logs per game", - "log-date-per-game": "last log date per game", - }; - - let embed = new EmbedBuilder() - .setTitle("Listing/Ranking") - .setDescription(`Logs sorted ${text[sort]}.\nList order is ${order}.`) - .addFields( - { - name: "#", - value: [...Array(games.length).keys()] - .map((i) => - (i + 10 * offset).toString().padStart((games.length + 10 * offset).toString().length, "0") - ) - .join("\n"), - inline: true, - }, - { name: "Game", value: games.join("\n"), inline: true }, - { name: "Value", value: values.join("\n"), inline: true } - ) - .setFooter({ text: `Page ${offset + 1}/${pages}` }); - - embed = addEmbedFooter(embed); - - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId(`game-activity-tracker.jump-left.${offset}.${pages}.${sort}.${order}`) - .setLabel("◀◀") - .setStyle(ButtonStyle.Primary) - .setDisabled(pages == 1), - new ButtonBuilder() - .setCustomId(`game-activity-tracker.left.${offset}.${pages}.${sort}.${order}`) - .setLabel("◀") - .setStyle(ButtonStyle.Primary) - .setDisabled(pages == 1), - new ButtonBuilder() - .setCustomId(`game-activity-tracker.right.${offset}.${pages}.${sort}.${order}`) - .setLabel("▶") - .setStyle(ButtonStyle.Primary) - .setDisabled(pages == 1), - new ButtonBuilder() - .setCustomId(`game-activity-tracker.jump-right.${offset}.${pages}.${sort}.${order}`) - .setLabel("▶▶") - .setStyle(ButtonStyle.Primary) - .setDisabled(pages == 1), - new ButtonBuilder() - .setCustomId(`game-activity-tracker.reload.${offset}.${pages}.${sort}.${order}`) - .setLabel("↺") - .setStyle(ButtonStyle.Success) - ); - - return [embed, row]; -} From 213a174cfa2c935e2b7725f9328f482c54155248 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:18:15 +0100 Subject: [PATCH 03/92] move config validation and interface, update config --- src/config.ts | 76 +---------------------- src/events/ready.ts | 2 +- src/interactions/interactionLoader.ts | 6 +- src/start.ts | 3 +- src/{ => util/config}/config-validate.ts | 32 +++++++--- src/util/config/configInterface.ts | 79 ++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 90 deletions(-) rename src/{ => util/config}/config-validate.ts (58%) create mode 100644 src/util/config/configInterface.ts diff --git a/src/config.ts b/src/config.ts index 1bdc51a..3dcbd17 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import { ColorResolvable, ComponentEmojiResolvable } from "discord.js"; +import { BotConfig } from "./util/config/configInterface"; // Configure your bot here. export const config: BotConfig = { @@ -50,80 +50,8 @@ export const config: BotConfig = { ], // --- Activity Tracker --- - logActivity: true, + activityLogRange: 5, // --- Birthday feature --- birthdayNotificationAt: 9, }; - -interface BotConfig { - // --- General --- - /** Name of the bot. */ - botName: string; - /** URL to a png or jpg of the logo of the bot. */ - iconURL: string; - /** URL to the github repository of the bot. Please share your code! */ - githubURL: string; - /** A description of the server that will be displayed in the welcome message. */ - serverDescription: string; - /** The main color the bot will use. */ - color: ColorResolvable; - - // --- Development --- - /** Also available: error, warn, info, http, verbose, debug, silly. */ - logLevel: "debug" | "info" | "warn" | "error" | "verbose"; - - // --- Management --- - /** Moderators of the server (to manage proposals) */ - moderatorRoleId: string; - /** Admins of the server (to manage the game activity tracker) */ - adminRoleId: string; - - // --- Polls & Coding Jams --- - /** Active Jam participants role name (will be pinged for announcements) */ - jamRoleName: string; - /** The text channel where the poll system will post polls. */ - pollChannelId: string; - /** The text channel where the jam system will post announcements to jams. */ - jamChannelId: string; - /** The category where the jam system will create channels for jams. */ - resultCategoryId: string; - - // --- Roles --- - /** Pronoun roles to pick from. First argument is the name of the role, second argument is the emoji id (You get the id by typing "\\[insert emoji]" in Discord). */ - pronounRoles: [string, ComponentEmojiResolvable | null][]; - /** Max 25 color roles to pick from. First argument is the name of the role, second argument is the color. */ - colorRoles: [string, ColorResolvable][]; - - // --- Activity Tracker --- - /** If User Game Activity should get logged */ - logActivity: boolean; - - // --- Birthday Feature --- - /** Can be any integer between 0 and 23. Defines at what time the birthday message should get send. */ - birthdayNotificationAt: - | 0 - | 1 - | 2 - | 3 - | 4 - | 5 - | 6 - | 7 - | 8 - | 9 - | 10 - | 11 - | 12 - | 13 - | 14 - | 15 - | 16 - | 17 - | 18 - | 19 - | 20 - | 21 - | 22 - | 23; -} diff --git a/src/events/ready.ts b/src/events/ready.ts index 3ff0d70..5d12965 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -3,7 +3,7 @@ import { ActivityType, Client } from "discord.js"; import { ctx } from "../ctx"; import { logger } from "../logger"; import CodingJamsCommand from "../commands/CodingJamsCommand"; -import { validateConfigParameters } from "../config-validate"; +import { validateConfigParameters } from "../util/config/config-validate"; import BirthdayCommand from "../commands/BirthdayCommand"; export default async function ready(client: Client) { diff --git a/src/interactions/interactionLoader.ts b/src/interactions/interactionLoader.ts index e47a684..2108346 100644 --- a/src/interactions/interactionLoader.ts +++ b/src/interactions/interactionLoader.ts @@ -1,7 +1,6 @@ import { Autocompleter, Button, Command, Modal, SelectMenu } from "./interactionClasses"; import { logger } from "../logger"; import { Client, Collection } from "discord.js"; -import { config } from "../config"; import fs from "fs"; export async function loadEvents(client: Client) { @@ -30,10 +29,7 @@ export async function loadCommands(): Promise> { .filter(isActive) .map(async (filename) => { const _command = (await import(`../commands/${filename}`)).default as Command; - if (config.logActivity || _command.name !== "activity-tracker") { - // When logActivity from the config is false every command except the activity-tracker should be loaded. If not this will always be true and all the commands will be loaded. - loadedCommands.set(_command.name, _command); - } + loadedCommands.set(_command.name, _command); }) ); return loadedCommands; diff --git a/src/start.ts b/src/start.ts index f7fab38..24dad6f 100644 --- a/src/start.ts +++ b/src/start.ts @@ -10,12 +10,11 @@ import { import { Client } from "discord.js"; import { ctx } from "./ctx"; import { logger } from "./logger"; -import { config } from "./config"; import { Settings } from "luxon"; logger.debug("Creating client..."); const client = new Client({ - intents: ["Guilds", "GuildMembers", config.logActivity ? "GuildPresences" : "0"], + intents: ["Guilds", "GuildMembers", "GuildPresences", "0"], }); function shutdown(info: unknown) { diff --git a/src/config-validate.ts b/src/util/config/config-validate.ts similarity index 58% rename from src/config-validate.ts rename to src/util/config/config-validate.ts index 0a00fbe..4442352 100644 --- a/src/config-validate.ts +++ b/src/util/config/config-validate.ts @@ -1,5 +1,5 @@ import { Guild } from "discord.js"; -import { config } from "./config"; +import { config } from "../../config"; import { channelExists, emojiExists, @@ -7,7 +7,8 @@ import { isValidHexColor, isValidURl, roleExists, -} from "./util/misc/verify"; +} from "../misc/verify"; + export async function validateConfigParameters(guild: Guild) { // URLs @@ -15,7 +16,7 @@ export async function validateConfigParameters(guild: Guild) { urls .map((url) => ({ url, valid: isValidURl(url) })) .forEach((url) => { - if (!url.valid) throw new Error(`Invalid URL: ${url.url}`); + if (!url.valid) throw new Error(`iconURL || githubURL: ${url} is not a valid URL`); }); // Colors @@ -23,7 +24,7 @@ export async function validateConfigParameters(guild: Guild) { colors .map((color) => ({ color, valid: isValidHexColor(color as string) })) .forEach((color) => { - if (!color.valid) throw new Error(`Invalid color: ${color.color}`); + if (!color.valid) throw new Error(`color || colorRoles: ${color} is not a valid Color`); }); // Strings @@ -33,7 +34,12 @@ export async function validateConfigParameters(guild: Guild) { str .map((string) => ({ string, valid: isNotEmpty(string) })) .forEach((string) => { - if (!string.valid) throw new Error(`Invalid string: ${string.string || "[empty string]"}`); + if (!string.valid) + throw new Error( + `botName || serverDescription || jamRoleName || colorRoles || pronounRoles: ${ + string.string || "[empty string]" + } is not a valid String` + ); }); // Roles @@ -41,7 +47,8 @@ export async function validateConfigParameters(guild: Guild) { roles .map((role) => ({ role, valid: roleExists(guild, role) })) .forEach(async (role) => { - if (!(await role.valid)) throw new Error(`Invalid role: ${role.role}`); + if (!(await role.valid)) + throw new Error(`adminRoleId || moderatorRoleId: ${role.role} is not a valid role ID`); }); // Channels @@ -49,7 +56,10 @@ export async function validateConfigParameters(guild: Guild) { channels .map((channel) => ({ channel, valid: channelExists(guild, channel) })) .forEach(async (channel) => { - if (!(await channel.valid)) throw new Error(`Invalid channel/category: ${channel.channel}`); + if (!(await channel.valid)) + throw new Error( + `hamChannelId || pollChannelId || resultCategoryId: ${channel.channel} is not a valid channel/category` + ); }); // Emojis @@ -60,6 +70,12 @@ export async function validateConfigParameters(guild: Guild) { valid: typeof emoji === "string" ? emojiExists(guild, emoji) : emoji === null, })) .forEach(async (emoji) => { - if (!(await emoji.valid)) throw new Error(`Invalid emoji: ${emoji.emoji}`); + if (!(await emoji.valid)) throw new Error(`pronounRoles: ${emoji.emoji} is not a valid emoji`); }); + + // Activity range + const activityRange = config.activityLogRange; + if (activityRange < 3 || activityRange > 10) { + throw new Error(`activityLogRange: ${activityRange} is not in range of 3 to 10`); + } } diff --git a/src/util/config/configInterface.ts b/src/util/config/configInterface.ts new file mode 100644 index 0000000..4f15555 --- /dev/null +++ b/src/util/config/configInterface.ts @@ -0,0 +1,79 @@ +import { ColorResolvable, ComponentEmojiResolvable } from "discord.js"; + +/** Interface used in ./src/config.ts */ +export interface BotConfig { + // --- General --- + /** Name of the bot. */ + botName: string; + /** URL to a png or jpg of the logo of the bot. */ + iconURL: string; + /** URL to the github repository of the bot. Please share your code! */ + githubURL: string; + /** A description of the server that will be displayed in the welcome message. */ + serverDescription: string; + /** The main color the bot will use. */ + color: ColorResolvable; + + // --- Development --- + /** Also available: error, warn, info, http, verbose, debug, silly. */ + logLevel: "debug" | "info" | "warn" | "error" | "verbose"; + + // --- Management --- + /** Moderators of the server (to manage proposals) */ + moderatorRoleId: string; + /** Admins of the server (to manage the game activity tracker) */ + adminRoleId: string; + + // --- Polls & Coding Jams --- + /** Active Jam participants role name (will be pinged for announcements) */ + jamRoleName: string; + /** The text channel where the poll system will post polls. */ + pollChannelId: string; + /** The text channel where the jam system will post announcements to jams. */ + jamChannelId: string; + /** The category where the jam system will create channels for jams. */ + resultCategoryId: string; + + // --- Roles --- + /** Pronoun roles to pick from. First argument is the name of the role, second argument is the emoji id (You get the id by typing "\\[insert emoji]" in Discord). */ + pronounRoles: [string, ComponentEmojiResolvable | null][]; + /** Max 25 color roles to pick from. First argument is the name of the role, second argument is the color. */ + colorRoles: [string, ColorResolvable][]; + + // --- Activity Tracker --- + /** Defines the range of how many \*latest log\* get stored and shown using some commands (eg. `/.. user last`) + * + * Also defines the number of stored and shown top users and games (eg. `/.. game top `) + * + * **Must be in range of 3 to 10!** + */ + activityLogRange: 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; + + // --- Birthday Feature --- + /** Can be any integer between 0 and 23. Defines at what time the birthday message should get send. */ + birthdayNotificationAt: + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22 + | 23; +} From dd372ce8d48ac63388b14f685d09bda083d94088 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:19:17 +0100 Subject: [PATCH 04/92] Add temporary command clearing command --- src/commands/ClearcmdsCommand.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/commands/ClearcmdsCommand.ts diff --git a/src/commands/ClearcmdsCommand.ts b/src/commands/ClearcmdsCommand.ts new file mode 100644 index 0000000..41a09b2 --- /dev/null +++ b/src/commands/ClearcmdsCommand.ts @@ -0,0 +1,19 @@ +import { Command } from "../interactions/interactionClasses"; +import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js"; + +class Update extends Command { + constructor() { + super("update"); + } + + async execute(interaction: ChatInputCommandInteraction): Promise { + interaction.client.application.commands.set([]); + await interaction.reply("Done"); + } + + register(): SlashCommandBuilder | Omit { + return new SlashCommandBuilder().setName("update").setDescription("clear all commands."); + } +} + +export default new Update(); From 8dd23d898e0e240262aa176e07fa5b310a335fc0 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:21:18 +0100 Subject: [PATCH 05/92] reformated db, redone tracking db and interfaces --- src/db.ts | 142 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 107 insertions(+), 35 deletions(-) diff --git a/src/db.ts b/src/db.ts index eee9212..8221a38 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,10 +1,33 @@ import Enmap from "enmap"; import { DateTime, Duration } from "luxon"; -// -- LaTeX Database -- +/* +-------------- +- MISC typed - +-------------- +*/ + +export type ISODate = string; // ISO Date +export type ISODuration = string; // ISO Duration +export type jamID = string; // key of jamDb +export type pollID = string; // key of pollDb +export type proposalID = string; // key of proposalDb +export type userID = string; // Discord user id + +/* +----------------- +-LaTeX Database - +----------------- +*/ + export const latexDb = new Enmap("latex"); // key: reply id -// -- Reminder Database -- +/* +--------------------- +- Reminder Database - +--------------------- +*/ + export const reminderTimeoutCache = new Map(); // key: unique id export const reminderDb = new Enmap({ @@ -28,42 +51,76 @@ interface InternalReminder { ping: string | null; } -// -- Activity Tracker Database -- -export const activityTrackerBlacklistDb = new Enmap("trackerBlacklist"); -activityTrackerBlacklistDb.ensure("general-user", []); -activityTrackerBlacklistDb.ensure("general-game", []); - -// key: "[user id]-[game]" -export const activityTrackerLogDb = new Enmap({ - name: "trackerLog", - serializer: (data) => data.map((e) => ({ duration: e.duration.toISO(), date: e.date.toISO() })), - deserializer: (data) => - data.map((e) => ({ - duration: Duration.fromISO(e.duration), - date: DateTime.fromISO(e.date, { setZone: true }), - })), -}); +/* +----------------------------- +- Activity Tracker Database - +----------------------------- +*/ -export interface ActivityLogEntry { - duration: Duration; - date: DateTime; +/** Interface for tracker logs */ +export interface TrackerLog { + /** When a log got logged */ + time: ISODate; + /** How long a game got played **(in seconds!!!)** */ + playtime: number; + /** User id of the User who just got logged */ + userid: userID; + /** Game id of the game which just got logged */ + gameName: string; } -interface InternalActivityLogEntry { - duration: ISODuration; - date: ISODate; +/** Interface for stripped down tracking logs */ +export interface TrackerSublog { + /** User- or Game- ID */ + id: userID; + /** How often a User or Game got logged */ + logs: number; + /** How long a User played a certain game + * (Or the other way around) + */ + playtime: number; } -// -- MISC -- -// Types -export type ISODate = string; // ISO Date -export type ISODuration = string; // ISO Duration -export type jamID = string; // key of jamDb -export type pollID = string; // key of pollDb -export type proposalID = string; // key of proposalDb -export type userID = string; // Discord user id +/** Interface for tracking users */ +export interface TrackerUser { + /** How much a user played */ + playtime: number; + /** Reference to the first log assosiated with the user */ + firstlog: string; + /** How often a user got logged */ + logs: number; + /** List of lastest logs */ + lastlogs: string[]; + /** Stripped down logs about how often a game got logged and how long it was played */ + games: TrackerSublog[]; +} + +/** Interface for tracking games */ +export interface TrackerGame { + /** How much a game got played */ + playtime: number; + /** Reference to the first log assosiated with the game */ + firstlog: string; + /** How often a game got logged */ + logs: number; + /** List of latest logs */ + lastlogs: string[]; + /** Stripped down logs about how often a user got logged and how long they played */ + users: TrackerSublog[]; +} + +export const trackerLogs = new Enmap({ name: "trackerLogs" }); +export const trackerUsers = new Enmap({ name: "trackerUsers" }); +export const trackerGames = new Enmap({ name: "trackerGames" }); +export const trackerBlacklist = new Enmap({ name: "trackerBlacklist" }); +trackerBlacklist.ensure("", []); + +/* +--------------------- +- Proposal Database - +--------------------- +*/ -// -- Proposal Database -- // key: unique id export const proposalDb = new Enmap({ name: "proposal", @@ -101,7 +158,12 @@ interface InternalProposal { created: ISODate; } -// -- Poll Database -- +/* +----------------- +- Poll Database - +----------------- +*/ + // key: unique id export const pollDb = new Enmap({ name: "poll", @@ -166,7 +228,12 @@ interface InternalPollEvent { date: ISODate; } -// -- Jam Database -- +/* +---------------- +- Jam Database - +-----..--------- +*/ + // key: unique id export const jamDb = new Enmap({ name: "jam", @@ -222,7 +289,12 @@ interface InternalJamEvent { date: ISODate; } -// -- Birthday Database -- +/* +--------------------- +- Birthday Database - +--------------------- +*/ + // key: user id export const birthdayDb = new Enmap({ name: "birthday", From b06166c656bad47ebf09d8079d2b57d1d5361e55 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:22:27 +0100 Subject: [PATCH 06/92] add temporary command to look into the tracking db --- src/commands/TrackerCommands.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/commands/TrackerCommands.ts diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts new file mode 100644 index 0000000..d377400 --- /dev/null +++ b/src/commands/TrackerCommands.ts @@ -0,0 +1,24 @@ +import { Command } from "../interactions/interactionClasses"; +import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js"; +import { trackerGames, trackerLogs, trackerUsers } from "../db"; +class Tracker extends Command { + constructor() { + super("tracker"); + } + + async execute(interaction: ChatInputCommandInteraction): Promise { + await interaction.reply( + `Logs\`\`\`json\n${JSON.stringify([ + ...trackerLogs.entries(), + ])}\n\`\`\`\n\nGames\`\`\`json\n${JSON.stringify([ + ...trackerGames.entries(), + ])}\n\`\`\`\n\nUsers\`\`\`json\n${JSON.stringify([...trackerUsers.entries()])}\n\`\`\`` + ); + } + + register(): SlashCommandBuilder | Omit { + return new SlashCommandBuilder().setName("tracker").setDescription("Desakjdhuöaoid"); + } +} + +export default new Tracker(); From 53c7d3229dfcc4250f6911d367ebcc999c51f880 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 22 Feb 2023 21:23:32 +0100 Subject: [PATCH 07/92] Updated presenceUpdate[ing] --- src/events/presenceUpdate.ts | 23 ++++--- src/util/tracker/presence.ts | 127 +++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 src/util/tracker/presence.ts diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index 1852389..4785a53 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -1,31 +1,32 @@ import { Presence } from "discord.js"; -import { config } from "../config"; -import { blacklistCheck, getChangedActivities, logTime } from "../util/activity-tracker/presence"; +import { getChangedActivities, isBlacklisted, logIt } from "../util/tracker/presence"; const userCache = new Map>(); export default async function presenceUpdate(oldPresence: Presence | null, newPresence: Presence) { - if (!config.logActivity || newPresence.user?.bot) return; + if (newPresence.user?.bot) return; const { started, stopped } = await getChangedActivities(oldPresence, newPresence); if (!started.length && !stopped.length) return; - const userid = newPresence.userId; + const userID = newPresence.userId; started.forEach(async (activity) => { - if (await blacklistCheck(userid, activity.name.toLowerCase())) return; - userCache.set(userid, { ...userCache.get(userid), [activity.name]: Date.now() }); + if (isBlacklisted(activity.name)) return; + userCache.set(userID, { ...userCache.get(userID), [activity.name]: Date.now() }); }); stopped.forEach(async (activity) => { - const cachedUser = userCache.get(userid); + const cachedUser = userCache.get(userID); if (!cachedUser) return; const start = cachedUser[activity.name]; if (!start) return; delete cachedUser[activity.name]; - const timePlayed = Date.now() - start; - if (timePlayed < 20000) return; - if (await blacklistCheck(userid, activity.name.toLowerCase())) return; - await logTime(userid, activity.name.toLowerCase(), timePlayed); + // Played time in secods ("~~" makes float to int) + const timePlayed = ~~((Date.now() - start) / 1000); + // filter out logs under 20 seconds + //if (timePlayed < 20) return; + + await logIt(activity.name, userID, timePlayed); }); } diff --git a/src/util/tracker/presence.ts b/src/util/tracker/presence.ts new file mode 100644 index 0000000..d48e65d --- /dev/null +++ b/src/util/tracker/presence.ts @@ -0,0 +1,127 @@ +import { Activity, ActivityType, Presence } from "discord.js"; +import { + trackerBlacklist, + TrackerGame, + trackerGames, + TrackerLog, + trackerLogs, + TrackerUser, + trackerUsers, +} from "../../db"; +import { config } from "../../config"; + +export async function getChangedActivities( + oldPresence: Presence | null, + newPresence: Presence +): Promise<{ started: Activity[]; stopped: Activity[] }> { + const oldActivities = oldPresence?.activities.filter((value) => value.type === ActivityType.Playing); + const newActivities = newPresence.activities.filter((value) => value.type === ActivityType.Playing); + + const stopped: Activity[] = []; + oldActivities?.forEach((activity) => { + if (!newActivities.some((newActivity) => newActivity.name === activity.name)) stopped.push(activity); + }); + + const started: Activity[] = []; + newActivities.forEach((activity) => { + if (!oldActivities?.some((oldActivity) => oldActivity.name === activity.name)) started.push(activity); + }); + + return { started, stopped }; +} + +/** Checks if the name of an game is on the blacklist (case gets ignored) */ +export function isBlacklisted(gameName: string): boolean { + return trackerBlacklist.get("")?.includes(gameName.toLowerCase()) || false; +} + +export async function logIt(gameName: string, userID: string, timePlayed: number): Promise { + gameName = gameName.toLowerCase(); + if (isBlacklisted(gameName)) return; + + const logID = (trackerLogs.count + 1).toString(); // make new a unique ID for the new log + ensure(gameName, userID, logID); // make sure user and game exist + updateUser(gameName, userID, timePlayed, logID); // update users db entry + updateGame(gameName, userID, timePlayed, logID); // update games db entry + addLog(gameName, userID, timePlayed, logID); // make a db entry for this new log +} + +function ensure(gameName: string, userID: string, logID: string) { + if (!trackerGames.has(gameName.toLowerCase())) { + const data: TrackerGame = { + playtime: 0, + firstlog: logID, + logs: 0, + lastlogs: [], + users: [], + }; + trackerGames.set(gameName.toLowerCase(), data); + } + if (!trackerUsers.has(userID)) { + const data: TrackerUser = { + playtime: 0, + firstlog: logID, + logs: 0, + lastlogs: [], + games: [], + }; + trackerUsers.set(userID, data); + } +} + +function updateUser(gameName: string, userID: string, timePlayed: number, logID: string) { + gameName = gameName.toLowerCase(); + + const data: TrackerUser | undefined = trackerUsers.get(userID); + + if (!data) return; + + // remove oldest log from the latest log history + if (data.lastlogs.length > config.activityLogRange) data.lastlogs.shift(); + data.lastlogs.push(logID); // add newest log to the log history + + data.logs += 1; + data.playtime += timePlayed; + + let gamelog = data.games.find((e) => e.id == gameName); + if (!gamelog) { + gamelog = data.games[data.games.push({ id: gameName, logs: 0, playtime: 0 }) - 1]; + } + gamelog.logs += 1; + gamelog.playtime += timePlayed; + + trackerUsers.set(userID, data); +} + +function updateGame(gameName: string, userID: string, timePlayed: number, logID: string) { + gameName = gameName.toLowerCase(); + + const data: TrackerGame | undefined = trackerGames.get(gameName); + if (!data) return; + + // remove oldest log from the latest log history + if (data.lastlogs.length > config.activityLogRange) data.lastlogs.shift(); + data.lastlogs.push(logID); // add newest log to the log history + + data.logs += 1; + data.playtime += timePlayed; + + let gamelog = data.users.find((e) => e.id == userID); + if (!gamelog) { + gamelog = data.users[data.users.push({ id: userID, logs: 0, playtime: 0 }) - 1]; + } + gamelog.logs += 1; + gamelog.playtime += timePlayed; + + trackerGames.set(gameName, data); +} + +function addLog(gameName: string, userID: string, timePlayed: number, logID: string) { + const data: TrackerLog = { + gameName: gameName, + userid: userID, + time: new Date().toISOString(), + playtime: timePlayed, + }; + trackerLogs.set(logID, data); +} From ed69a7f9cc17dcee78517fd0cb7f58924d085d8c Mon Sep 17 00:00:00 2001 From: Murphy Date: Fri, 10 Mar 2023 14:38:16 +0100 Subject: [PATCH 08/92] config validate order --- src/events/ready.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/events/ready.ts b/src/events/ready.ts index 5d12965..8ef4d98 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -22,19 +22,24 @@ export default async function ready(client: Client) { type: ActivityType.Watching, }); - logger.info("Publishing commands..."); + logger.debug("Publishing commands..."); await client.application?.commands.set( ctx.commands.map((cmd) => cmd.register().toJSON()), ctx.defaultGuild ); - logger.info("Starting coding-jam event scheduler..."); + if (!ctx.debugMode) { + logger.debug("Validating config parameters..."); + await validateConfigParameters(guild); + } + + logger.debug("Starting coding-jam event scheduler..."); CodingJamsCommand.startScheduler(client); - logger.info("Starting reminder scheduler..."); + logger.debug("Starting reminder scheduler..."); ReminderCommand.startScheduler(client); - logger.info("Starting birthday scheduler..."); + logger.debug("Starting birthday scheduler..."); BirthdayCommand.startScheduler(client); if (!guild.systemChannel) { @@ -44,10 +49,4 @@ export default async function ready(client: Client) { } logger.info("Setup successful!"); - - if (!ctx.debugMode) { - logger.debug("Validating config parameters..."); - await validateConfigParameters(guild); - logger.debug("Config parameters validated!"); - } } From 26519356baca7f4fb71bcdbd20abe5f119ce7593 Mon Sep 17 00:00:00 2001 From: Murphy Date: Fri, 10 Mar 2023 17:08:07 +0100 Subject: [PATCH 09/92] Switch to HSL --- LICENSE | 46 +++++++++++++++++++++++++++++++--------------- README.md | 8 ++++---- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/LICENSE b/LICENSE index 0e05bf5..b77b6af 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,37 @@ -ANTI-CAPITALIST SOFTWARE LICENSE (v 1.4) +THE HUMANE SOFTWARE LICENSE (v 1.0) -Copyright © 2022 Cowoding-Jams +Copyright © 2023 Cowoding-Jams -This is anti-capitalist software, released for free use by individuals and organizations that do not operate by capitalist principles. +This is humane software, released for free use by individuals and organizations +that do not operate by unmoral or for-profit principles and follow the Universal +Declaration of Human Rights by the United Nations. -Permission is hereby granted, free of charge, to any person or organization (the "User") obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, merge, distribute, and/or sell copies of the Software, subject to the following conditions: +Permission is hereby granted, free of charge, to any person or organization +(the "User") obtaining a copy of this software and associated documentation +files (the "Software"), to use, copy, modify, merge and/or distribute +copies of the Software, subject to the following conditions: -1. The above copyright notice and this permission notice shall be included in all copies or modified versions of the Software. +1. The above copyright notice and this permission notice shall be + included in all copies or modified versions of the Software. 2. The User is one of the following: -a. An individual person, laboring for themselves -b. A non-profit organization -c. An educational institution -d. An organization that seeks shared profit for all of its members, and allows non-members to set the cost of their labor - -3. If the User is an organization with owners, then all owners are workers and all workers are owners with equal equity and/or equal vote. - -4. If the User is an organization, then the User is not law enforcement or military, or working for or under either. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + a. An individual person, laboring for themselves + b. A non-profit organization + c. An educational institution + d. An organization that seeks shared profit for all of its members, + and allows non-members to set the cost of their labor + +3. The User must act under intentions that do not violate + the Universal Declaration of Human Rights by the United Nations. + +4. If the User is an organization, then the User is not + law enforcement or military, or working for or under either. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR +A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +See: diff --git a/README.md b/README.md index 143d0da..cc4888c 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ -This bot is made to autonomously manage a discord server with regular game jams/hackathons and make it a plasent and happy enviroment. +This bot is made to autonomously manage a discord server with regular game jams/hackathons and make it a pleasant and happy environment. It will collect the proposals for the jam, create polls for everyone to participate (with reactions directly on the server) and will give the server some more glamour with color roles for example. You can read about the full set of features in our lovely wiki [here](https://github.com/Cowoding-Jams/Jambo/wiki). ## The team :) -Made with love by: [Antti](https://github.com/Chicken), [Lucy](https://github.com/BlackDemonFire), [Leonie](https://github.com/KommentatorForAll), [Murphy](https://github.com/StrangeGirlMurph), [Floschy](https://github.com/flloschy), [Amber](https://github.com/uselessamber), [Wilson](https://github.com/WilsontheWolf), Riley, [Kryal](https://github.com/Da-Real-Kryall), [Ian](https://github.com/IanToujou) ❤️ -We're all part of a game jam/hackothon server with monthly events and wanted to make our and your lives easier. Have fun coding! +Made with love by: [Antti](https://github.com/Chicken), [Lucy](https://github.com/BlackDemonFire), [Leonie](https://github.com/KommentatorForAll), [Murphy](https://github.com/StrangeGirlMurph), [Floschy](https://github.com/flloschy), [Amber](https://github.com/uselessamber), [Wilson](https://github.com/WilsontheWolf) ❤️ +We're all part of a game jam/hackathon server with monthly events and wanted to make our and your lives easier. Have fun coding! ## Usage & Development @@ -18,4 +18,4 @@ To see how to use this bot on your own visit our [wiki](https://github.com/Cowod ## License -This project is licensed under the [Anti-Capitalist Software License](https://anticapitalist.software/). See [LICENSE](LICENSE). +This project is licensed under the [Humane Software License](https://github.com/StrangeGirlMurph/The-Humane-Software-License). See [LICENSE](LICENSE). From bd88f5b4dc8e2616656c335697128ca09ae2d92f Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 00:53:30 +0200 Subject: [PATCH 10/92] Tracker done - Formating and code cleaning later!! --- src/commands/ClearcmdsCommand.ts | 19 - src/commands/TrackerCommands.ts | 201 +++++++- src/db.ts | 2 +- src/events/presenceUpdate.ts | 2 +- .../autocompleters/TrackerAutocomple.ts | 25 + src/util/tracker/commands.ts | 429 ++++++++++++++++++ 6 files changed, 646 insertions(+), 32 deletions(-) delete mode 100644 src/commands/ClearcmdsCommand.ts create mode 100644 src/interactions/autocompleters/TrackerAutocomple.ts create mode 100644 src/util/tracker/commands.ts diff --git a/src/commands/ClearcmdsCommand.ts b/src/commands/ClearcmdsCommand.ts deleted file mode 100644 index 41a09b2..0000000 --- a/src/commands/ClearcmdsCommand.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Command } from "../interactions/interactionClasses"; -import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js"; - -class Update extends Command { - constructor() { - super("update"); - } - - async execute(interaction: ChatInputCommandInteraction): Promise { - interaction.client.application.commands.set([]); - await interaction.reply("Done"); - } - - register(): SlashCommandBuilder | Omit { - return new SlashCommandBuilder().setName("update").setDescription("clear all commands."); - } -} - -export default new Update(); diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index d377400..a2433fb 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -1,23 +1,202 @@ import { Command } from "../interactions/interactionClasses"; -import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js"; -import { trackerGames, trackerLogs, trackerUsers } from "../db"; +import { ChatInputCommandInteraction, SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder } from "discord.js"; +import { addBlacklist, gameLast, gameStats, gameTop, latest, logs, playtime, remBlacklist, stats, userLast, userStats, userTop } from "../util/tracker/commands"; class Tracker extends Command { constructor() { super("tracker"); } async execute(interaction: ChatInputCommandInteraction): Promise { - await interaction.reply( - `Logs\`\`\`json\n${JSON.stringify([ - ...trackerLogs.entries(), - ])}\n\`\`\`\n\nGames\`\`\`json\n${JSON.stringify([ - ...trackerGames.entries(), - ])}\n\`\`\`\n\nUsers\`\`\`json\n${JSON.stringify([...trackerUsers.entries()])}\n\`\`\`` - ); + const group = interaction.options.getSubcommandGroup() + const sub = interaction.options.getSubcommand() + + switch(group+sub){ + case "userstats": + await userStats(interaction) + return + case "userlast": + await userLast(interaction) + return + case "usertop": + await userTop(interaction) + return + case "gamestats": + await gameStats(interaction) + return + case "gamelast": + await gameLast(interaction) + return + case "gametop": + await gameTop(interaction) + return + case "blacklistadd": + await addBlacklist(interaction) + return + case "blacklistremove": + await remBlacklist(interaction) + return + case "nullplaytime": + await playtime(interaction) + return + case "nulllogs": + await logs(interaction) + return + case "nulllast": + await latest(interaction) + return + case "nullstats": + await stats(interaction) + return + } + + + } - register(): SlashCommandBuilder | Omit { - return new SlashCommandBuilder().setName("tracker").setDescription("Desakjdhuöaoid"); + register(): SlashCommandSubcommandsOnlyBuilder { + return new SlashCommandBuilder() + .setName("tracker") + .setDescription("The gameway to some cool stats about here being users") + .addSubcommandGroup(group => group + .setName("user") + .setDescription("Get the Tracking data about a certain user") + .addSubcommand(sub => sub + .setName("stats") + .setDescription("See some interesting statistics about a user") + .addUserOption(usr => usr + .setName("user") + .setDescription("go figure: the user") + ) + ) + .addSubcommand(sub => sub + .setName("last") + .setDescription("Take a look at the latest logs of a user") + .addUserOption(usr => usr + .setName("user") + .setDescription("go figure: the user") + ) + ) + .addSubcommand(sub => sub + .setName("top") + .setDescription("Lists you the most logged or played game by a user") + .addStringOption(opt => opt + .setName("filter") + .setDescription("logs or playtime?") + .addChoices( + {name:"logs", value:"logs"}, + {name:"playtime", value:"playtime"} + ) + .setRequired(true) + ) + .addUserOption(usr => usr + .setName("user") + .setDescription("go figure: the user") + ) + ) + ) + .addSubcommandGroup(group => group + .setName("game") + .setDescription("Get the tracking data about a certain game") + .addSubcommand(sub => sub + .setName("stats") + .setDescription("See some interesting statistics about a game") + .addStringOption(game => game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + ) + .addSubcommand(sub => sub + .setName("last") + .setDescription("Take a look at the latest logs of a game") + .addStringOption(game => game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + ) + .addSubcommand(sub => sub + .setName("top") + .setDescription("Lists you the most logged or played game by a game") + .addStringOption(game => game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + .addStringOption(opt => opt + .setName("filter") + .setDescription("logs or playtime?") + .addChoices( + {name:"logs", value:"logs"}, + {name:"playtime", value:"playtime"} + ) + .setRequired(true) + ) + ) + ) + .addSubcommandGroup(group => group + .setName("blacklist") + .setDescription("edit the playlist (ADMIN ONLY)") + .addSubcommand(sub => sub + .setName("add") + .setDescription("choose a game which shouldn't get logged anymore") + .addStringOption(sub => sub + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + ) + .addSubcommand(sub => sub + .setName("remove") + .setDescription("choose a game which should get logged again") + .addStringOption(sub => sub + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + ) + ) + .addSubcommand(sub => sub + .setName("playtime") + .setDescription("how may hours a game got played by a user") + .addStringOption(game => game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + .addUserOption(usr => usr + .setName("user") + .setDescription("go figure: the user") + ) + ) + .addSubcommand(sub => sub + .setName("logs") + .setDescription("See how many logs a user has in a given game") + .addStringOption(game => game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + .addUserOption(usr => usr + .setName("user") + .setDescription("go figure: the user") + ) + ) + .addSubcommand(sub => sub + .setName("last") + .setDescription("See the latest logs across the whole system") + ) + .addSubcommand(sub => sub + .setName("stats") + .setDescription("See some system stats") + ) } } diff --git a/src/db.ts b/src/db.ts index 8221a38..a7ca2cd 100644 --- a/src/db.ts +++ b/src/db.ts @@ -61,7 +61,7 @@ interface InternalReminder { export interface TrackerLog { /** When a log got logged */ time: ISODate; - /** How long a game got played **(in seconds!!!)** */ + /** How long a game got played */ playtime: number; /** User id of the User who just got logged */ userid: userID; diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index 4785a53..65f76b4 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -23,7 +23,7 @@ export default async function presenceUpdate(oldPresence: Presence | null, newPr delete cachedUser[activity.name]; // Played time in secods ("~~" makes float to int) - const timePlayed = ~~((Date.now() - start) / 1000); + const timePlayed = ~~((Date.now() - start)); // filter out logs under 20 seconds //if (timePlayed < 20) return; diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts new file mode 100644 index 0000000..3a0afd1 --- /dev/null +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -0,0 +1,25 @@ +import { Autocompleter } from "../interactionClasses"; +import { AutocompleteInteraction } from "discord.js"; +import { trackerGames } from "../../db"; + +class CountryAutocompleter extends Autocompleter { + constructor() { + super("tracker"); + } + + async execute(interaction: AutocompleteInteraction): Promise { + const games = Array.from(trackerGames.keys()); + const focus = interaction.options.getFocused().toLowerCase() as string + let filteredOptions = games.filter(g => g + .toLowerCase() + .startsWith(focus) + ); + if (filteredOptions.length > 25) filteredOptions = filteredOptions.slice(0, 25); + + const map = filteredOptions.map((c) => ({ name: c, value: c })); + + await interaction.respond(map); + } +} + +export default new CountryAutocompleter(); diff --git a/src/util/tracker/commands.ts b/src/util/tracker/commands.ts new file mode 100644 index 0000000..e708ad2 --- /dev/null +++ b/src/util/tracker/commands.ts @@ -0,0 +1,429 @@ +import { EmbedBuilder } from "@discordjs/builders"; +import { APIEmbedField, ChatInputCommandInteraction, GuildMember, User } from "discord.js"; +import { durationToReadable, discordTimestamp } from "../misc/time" +import { Duration } from "luxon"; +import { trackerUsers, trackerLogs, TrackerSublog, trackerGames, trackerBlacklist } from "../../db" +import { config } from "../../config" +import { hasAdminPerms } from "../misc/permissions"; + +function makeTimeString(timeMS:number) { + return durationToReadable(Duration.fromMillis(timeMS), true) +} + +function makeEmbed( + target:GuildMember, requester:User, + mostLogged:string, mostPlayed:string, + latestLogs:string, mostPlaytime:string, + firstSeen:number, totalLogs:number, + totalPlaytime:string, playtimePer:string) { + return new EmbedBuilder() + .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) + .setColor(target.displayColor) + .setTitle(`Tracking stats about ${target.displayName}`) + .setFooter({text:`Requested by ${requester.tag}`, iconURL: requester.displayAvatarURL({size:16})}) + .setTimestamp(Date.now()) + .addFields( + {inline:true, name:"Most logged games", value:mostLogged}, + {inline:true, name:"Most played games", value:mostPlayed}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Latest logs", value:latestLogs}, + {inline:true, name:"(Average) playtime per", value:playtimePer}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"(Average) most playtime/log", value:mostPlaytime}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Record range", value:`${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp(Math.floor(Date.now()/1000))}(now)\n${makeTimeString(Date.now() - firstSeen)}`}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Total logs", value:totalLogs.toString()}, + {inline:true, name:"Total playtime", value:totalPlaytime} + ) +} + +function getTotalPlaytime(userId:string) { + const data = trackerUsers.get(userId) + let totalPlaytime = 0 + data?.games.forEach(game => { + totalPlaytime += game.playtime + }) + return totalPlaytime +} +function getPlaytimePerLog(userId:string) { + const data = trackerUsers.get(userId) + if (!data) return 0 + return data.games.sort((a, b) => a.playtime/a.logs - b.playtime/b.logs).reverse() +} +function getTotalLogs(userId:string) { + const data = trackerUsers.get(userId) + let totalLogs = 0 + data?.games.forEach(game => { + totalLogs += game.logs + }) + return totalLogs +} +function getGames(userId:string) { + const games = trackerUsers.get(userId)?.games.length + if (!games) return 1 + return games +} +function sortGamesPlaytime(userId:string) { + const data = trackerUsers.get(userId) + return data?.games.sort((a, b) => b.playtime - a.playtime) +} +function sortGamesLogs(userId:string) { + const data = trackerUsers.get(userId) + return data?.games.sort((a, b) => b.logs - a.logs) +} +function latestLogs(userId:string) { + const data = trackerUsers.get(userId) + if (!data) return ["0"] + return data.lastlogs.reverse() +} + + + +export async function userStats(interaction:ChatInputCommandInteraction) { + const target = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user + if (!target) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const member = await interaction.guild?.members.fetch(target.id) + if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const games = sortGamesPlaytime(member.id) + const logs = sortGamesLogs(member.id) + if (!logs || !games) {await interaction.reply({content:"User not enogth logs", ephemeral:true}); return} + const totalPlaytime = getTotalPlaytime(member.id) + const totalLogs = getTotalLogs(member.id) + const userDB = trackerUsers.get(member.id) + if (!userDB) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const firstSeen = trackerLogs.get(userDB.firstlog)?.time + if (!firstSeen) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const range = Date.now() - new Date(firstSeen).getTime() + const playtimePerLog = getPlaytimePerLog(member.id) + if (playtimePerLog == 0) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const latest = playtimePerLog.at(-1) + if (!latest) {await interaction.reply({content:"User not found", ephemeral:true}); return} + + const embed = makeEmbed( + member, + interaction.user, + logs.slice(0, 5).map(game => `${game.id}: ${game.logs}`).join("\n"), + games.slice(0, 5).map(game => `${game.id}: ${makeTimeString(game.playtime)}`).join("\n"), + latestLogs(member.id).map(log => `${trackerLogs.get(log)?.gameName}`).join("\n"), + `${playtimePerLog[0].id}: ${makeTimeString(playtimePerLog[0].playtime/playtimePerLog[0].logs)} - ${playtimePerLog[0].logs}logs\nTotal time: ${makeTimeString(playtimePerLog[0].playtime)}`, + new Date(firstSeen).getTime(), + totalLogs, + makeTimeString(totalPlaytime), + `day: ${makeTimeString(Math.round(totalPlaytime/(range/(86400 * 1000))))}\nweek: ${makeTimeString(Math.round(totalPlaytime/(range/604800000)))}\nmonth: ${makeTimeString(Math.round(totalPlaytime/(range/2628000000)))}\ngame: ${makeTimeString(Math.round(totalPlaytime/getGames(member.id)))}\nlog: ${makeTimeString(Math.round(totalPlaytime/totalLogs))}` + ) + + await interaction.reply({embeds:[embed]}) +} +export async function userLast(interaction:ChatInputCommandInteraction) { + const target = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user + if (!target) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const member = await interaction.guild?.members.fetch(target.id) + if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const dbEntry = trackerUsers.get(member.id) + if (!dbEntry) {await interaction.reply({content:"User not enogth logs", ephemeral:true}); return} + const logs = dbEntry.lastlogs.slice(0, 5).reverse() + let fields: APIEmbedField[] = [] + + logs.forEach(log => { + const entry = trackerLogs.get(log) + if (!entry) return + fields.push({inline:true, name:entry.gameName, value:`time: \nplayed time: ${makeTimeString(entry.playtime)}`}) + }) + + fields.push({inline:true, name:"_ _", value:"_ _"}) + + + const embed = new EmbedBuilder() + .setColor(member.displayColor) + .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) + .setTitle(`Latest logs by ${member?.displayName}`) + .addFields(...fields) + + await interaction.reply({embeds:[embed]}) +} +export async function userTop(interaction:ChatInputCommandInteraction) { + const target = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user + if (!target) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const member = await interaction.guild?.members.fetch(target.id) + if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} + + const filter = interaction.options.getString('filter', true) + + let games:TrackerSublog[] | undefined = [] + if (filter == "logs") { + games = sortGamesLogs(member.id) + } else if (filter == "playtime") { + games = sortGamesPlaytime(member.id) + } else { + await interaction.reply({content:"invalid filter", ephemeral:true}) + return + } + + if (!games) { + await interaction.reply({content:"not many logs", ephemeral:true}) + return + } + + games = games.slice(0, 5) + + let fields: APIEmbedField[] = [] + games.forEach(game => { + fields.push({ + inline: true, + name: game.id, + value: filter == "playtime" ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` : `${game.logs} logs\n${makeTimeString(new Date(game.playtime).getTime())}` + }) + }) + + fields.push({inline:true, name:"_ _", value:"_ _"}) + + const embed = new EmbedBuilder() + .setColor(member.displayColor) + .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) + .setTitle(`Top games (${filter}) by ${member?.displayName}`) + .addFields(...fields) + + await interaction.reply({embeds:[embed]}) +} +export async function playtime(interaction:ChatInputCommandInteraction) { + const targetUser = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user + if (!targetUser) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const member = await interaction.guild?.members.fetch(targetUser.id) + if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} + + const targetGame = interaction.options.getString('game', true) + const db = trackerUsers.get(member.id)?.games.filter(game => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()) + + if (!db) {await interaction.reply({content:"User not existing or not played game", ephemeral:true}); return} + + const playtime = db[0].playtime + + const embed = new EmbedBuilder() + .setTitle(`${member.displayName} has played ${makeTimeString(playtime)} of ${targetGame}`) + .setColor(member.displayColor) + + await interaction.reply({embeds:[embed]}) +} +export async function logs(interaction:ChatInputCommandInteraction) { + const targetUser = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user + if (!targetUser) {await interaction.reply({content:"User not found", ephemeral:true}); return} + const member = await interaction.guild?.members.fetch(targetUser.id) + if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} + + const targetGame = interaction.options.getString('game', true) + const db = trackerUsers.get(member.id)?.games.filter(game => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()) + + if (!db) {await interaction.reply({content:"User not existing or not played game", ephemeral:true}); return} + + const logs = db[0].logs + + const embed = new EmbedBuilder() + .setTitle(`${member.displayName} has ${logs} logs in ${targetGame}`) + .setColor(member.displayColor) + + await interaction.reply({embeds:[embed]}) +} +export async function latest(interaction:ChatInputCommandInteraction) { + const fields:APIEmbedField[] = [] + + for (let i = trackerLogs.count; i > trackerLogs.count - config.activityLogRange; i--) { + const data = trackerLogs.get(i.toString()) + if (!data) return + fields.push({ + inline: true, + name: data.gameName, + value: `user: <@${data.userid}>\ntime: \ntime played: ${makeTimeString(data.playtime)}` + }) + } + + fields.push({inline:true, name:"_ _", value:"_ _"}) + + const embed = new EmbedBuilder() + .setTitle("Latest logs") + .setColor(0) + .addFields(...fields) + + await interaction.reply({embeds:[embed]}) + +} +export async function stats(interaction:ChatInputCommandInteraction) { + + const mostLoggedGame = trackerGames.array().sort((a, b) => a.logs - b.logs).reverse().slice(0, 5).map(game => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`).join("\n") + const mostPlayedGame = trackerGames.array().sort((a, b) => a.playtime - b.playtime).reverse().slice(0, 5).map(game => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`).join("\n") + const mostLoggedUser = trackerUsers.array().sort((a, b) => a.logs - b.logs).reverse().splice(0, 5).map(user => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`).join("\n") + const mostPlayedUser = trackerUsers.array().sort((a, b) => a.playtime - b.playtime).reverse().splice(0, 5).map(user => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`).join("\n") + const latestLogs = trackerLogs.array().reverse().slice(0, 5).map(log => ` <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}`).join("\n") + const totalPlaytime = trackerGames.array().map(game => game.playtime).reduce((partialSum, a) => partialSum + a, 0) + const totalLogs = trackerGames.array().map(game => game.logs).reduce((partialSum, a) => partialSum + a, 0) + const firstSeen = new Date(trackerLogs.array()[0].time).getTime() + const range = Date.now() - firstSeen + const games = trackerGames.count + const playtimePer = `day: ${makeTimeString(Math.round(totalPlaytime/(range/(86400 * 1000))))}\nweek: ${makeTimeString(Math.round(totalPlaytime/(range/604800000)))}\nmonth: ${makeTimeString(Math.round(totalPlaytime/(range/2628000000)))}\ngame: ${makeTimeString(Math.round(totalPlaytime/games))}\nlog: ${makeTimeString(Math.round(totalPlaytime/totalLogs))}` + const tmp = trackerGames.array().sort((a, b) => b.playtime/b.logs - a.playtime/b.logs)[0] + const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString(tmp.playtime/tmp.logs)} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}` + + const embed = new EmbedBuilder() + .setTitle("System stats") + .setColor(0) + .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) + .setTimestamp(Date.now()) + .addFields( + {inline:true, name:"Most logged games", value:mostLoggedGame}, + {inline:true, name:"Most played games", value:mostPlayedGame}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Most logged users", value:mostLoggedUser}, + {inline:true, name:"Users with most playtime", value:mostPlayedUser}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Latest logs", value:latestLogs}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"(Average) playtime per", value:playtimePer}, + {inline:true, name:"(Average) most playtime/log", value:mostPlaytime}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Record range", value:`${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp(Math.floor(Date.now()/1000))}(now)\n${makeTimeString(Date.now() - firstSeen)}`}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Total logs", value:totalLogs.toString()}, + {inline:true, name:"Total playtime", value:makeTimeString(totalPlaytime)} + ) + + + await interaction.reply({embeds:[embed]}) +} +export async function gameStats(interaction:ChatInputCommandInteraction) { + const game = interaction.options.getString('game', true) + const db = trackerGames.get(game.toLocaleLowerCase()) + if (!db) {await interaction.reply({content: "not existingngngng", ephemeral:true}); return} + + const mostPlayed = db.users.sort((a, b)=> b.playtime - a.playtime).slice(0, 5).map(user => `<@${user.id}>: ${makeTimeString(user.playtime)}`).join("\n") + const mostLogged = db.users.sort((a, b)=> b.logs - a.logs).slice(0, 5).map(user => `<@${user.id}>: ${user.logs} logs`).join("\n") + const latestLogs = db.lastlogs.map(log => `<@${trackerLogs.get(log)?.userid}>`).join("\n") + const totalPlaytime = db.playtime + const totalLogs = db.logs + const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime() + const range = Date.now() - firstSeen + const users = db.users.length + const playtimePer = `day: ${makeTimeString(Math.round(totalPlaytime/(range/(86400 * 1000))))}\nweek: ${makeTimeString(Math.round(totalPlaytime/(range/604800000)))}\nmonth: ${makeTimeString(Math.round(totalPlaytime/(range/2628000000)))}\nuser: ${makeTimeString(Math.round(totalPlaytime/users))}\nlog: ${makeTimeString(Math.round(totalPlaytime/totalLogs))}` + const tmp = db.users.sort((a, b)=> b.playtime/b.logs - a.playtime/a.logs)[0] + const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime/tmp.logs)} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}` + + const embed = new EmbedBuilder() + .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) + .setColor(0) + .setTitle(`Tracking stats about ${game}`) + .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) + .setTimestamp(Date.now()) + .addFields( + {inline:true, name:"Most logged user", value:mostLogged}, + {inline:true, name:"User with most playtime", value:mostPlayed}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Latest logs", value:latestLogs}, + {inline:true, name:"(Average) playtime per", value:playtimePer}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"(Average) most playtime/log", value:mostPlaytime}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Record range", value:`${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp(Math.floor(Date.now()/1000))}(now)\n${makeTimeString(Date.now() - firstSeen)}`}, + {inline:false, name:"_ _", value:"_ _"}, + {inline:true, name:"Total logs", value:totalLogs.toString()}, + {inline:true, name:"Total playtime", value:makeTimeString(totalPlaytime)} + ) + + await interaction.reply({embeds:[embed]}) +} +export async function gameLast(interaction:ChatInputCommandInteraction) { + const target = interaction.options.getString('game', true) + const db = trackerGames.get(target) + if (!db) {await interaction.reply({content:"Game not found or no logs", ephemeral:true}); return} + + const logs = db.lastlogs.slice(0, 5).reverse() + let fields: APIEmbedField[] = [] + + + await logs.forEach(async log => { + const entry = trackerLogs.get(log) + if (!entry) return + const user = await interaction.client.users.fetch(entry.userid) + if (!user) return + fields.push({inline:true, name:user.username, value:`time: \nplayed time: ${makeTimeString(entry.playtime)}`}) + }) + + fields.push({inline:true, name:"_ _", value:"_ _"}) + + + const embed = new EmbedBuilder() + .setColor(0) + .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) + .setTitle(`Latest logs of ${target}`) + .addFields(...fields) + .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) + .setTimestamp(Date.now()) + + await interaction.reply({embeds:[embed]}) +} +export async function gameTop(interaction:ChatInputCommandInteraction) { + const target = interaction.options.getString('game', true) + if (!target) {await interaction.reply({content:"Game not found", ephemeral:true}); return} + const db = trackerGames.get(target) + if (!db) {await interaction.reply({content:"Game not found or no log", ephemeral:true}); return} + + const filter = interaction.options.getString('filter', true) + if (filter == "playtime") { + } else if (filter == "logs") { + } else { + interaction.reply({content: "Invalid filter", ephemeral:true}) + return + } + + const users = db.users.sort((a, b) => filter == "playtime" ? (b.playtime - a.playtime) : (b.logs - a.logs)).splice(0, 5) + let fields:APIEmbedField[] = [] + + await users.forEach(async user => { + fields.push({ + inline: true, + name: (await interaction.client.users.fetch(user.id)).username, + value: filter == "playtime" ? makeTimeString(user.playtime) + "\n" + user.logs + " logs" : user.logs + " logs\n" + makeTimeString(user.playtime) + }) + }) + + fields.push({inline:true, name:"_ _", value:"_ _"}) + + const embed = new EmbedBuilder() + .setColor(0) + .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) + .setTitle(`Top user (${filter}) by ${target}`) + .addFields(...fields) + .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) + .setTimestamp(Date.now()) + + await interaction.reply({embeds:[embed]}) + +} +export async function addBlacklist(interaction:ChatInputCommandInteraction) { + if (!(await hasAdminPerms(interaction))) { + interaction.reply({content:"only admin can do this", ephemeral:true}) + return + } + + const game = interaction.options.getString('game', true); + if (trackerBlacklist.get("")?.find(g => g.toLowerCase() == game.toLowerCase())) { + interaction.reply({content:"Game already on blacklist", ephemeral:true}) + return + } + trackerBlacklist.push("", game.toLowerCase()) + interaction.reply({content:"Game added", ephemeral:true}) +} +export async function remBlacklist(interaction:ChatInputCommandInteraction) { + if (!(await hasAdminPerms(interaction))) { + interaction.reply({content:"only admin can do this", ephemeral:true}) + return + } + + const game = interaction.options.getString('game', true); + const db = trackerBlacklist.get("") + if (!db) return + if (db?.find(g => g.toLowerCase() == game.toLowerCase())) { + trackerBlacklist.set("", db.filter(g=> g.toLowerCase() != game.toLowerCase())) + interaction.reply({content:"Game removed", ephemeral:true}) + return + } + interaction.reply({content:"Game not on blacklist", ephemeral:true}) +} \ No newline at end of file From e4de05c8217aa96b4cf10cb2b7644ae799ab786e Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 16:38:09 +0200 Subject: [PATCH 11/92] prettify --- src/commands/TrackerCommands.ts | 323 +++--- src/events/presenceUpdate.ts | 2 +- .../autocompleters/TrackerAutocomple.ts | 9 +- src/util/config/config-validate.ts | 1 - src/util/tracker/commands.ts | 1032 ++++++++++------- 5 files changed, 799 insertions(+), 568 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index a2433fb..2bf3e37 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -1,202 +1,205 @@ import { Command } from "../interactions/interactionClasses"; -import { ChatInputCommandInteraction, SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder } from "discord.js"; -import { addBlacklist, gameLast, gameStats, gameTop, latest, logs, playtime, remBlacklist, stats, userLast, userStats, userTop } from "../util/tracker/commands"; +import { + ChatInputCommandInteraction, + SlashCommandBuilder, + SlashCommandSubcommandsOnlyBuilder, +} from "discord.js"; +import { + addBlacklist, + gameLast, + gameStats, + gameTop, + latest, + logs, + playtime, + remBlacklist, + stats, + userLast, + userStats, + userTop, +} from "../util/tracker/commands"; class Tracker extends Command { constructor() { super("tracker"); } async execute(interaction: ChatInputCommandInteraction): Promise { - const group = interaction.options.getSubcommandGroup() - const sub = interaction.options.getSubcommand() + const group = interaction.options.getSubcommandGroup(); + const sub = interaction.options.getSubcommand(); - switch(group+sub){ + switch (group + sub) { case "userstats": - await userStats(interaction) - return + await userStats(interaction); + return; case "userlast": - await userLast(interaction) - return + await userLast(interaction); + return; case "usertop": - await userTop(interaction) - return + await userTop(interaction); + return; case "gamestats": - await gameStats(interaction) - return + await gameStats(interaction); + return; case "gamelast": - await gameLast(interaction) - return + await gameLast(interaction); + return; case "gametop": - await gameTop(interaction) - return + await gameTop(interaction); + return; case "blacklistadd": - await addBlacklist(interaction) - return + await addBlacklist(interaction); + return; case "blacklistremove": - await remBlacklist(interaction) - return + await remBlacklist(interaction); + return; case "nullplaytime": - await playtime(interaction) - return + await playtime(interaction); + return; case "nulllogs": - await logs(interaction) - return + await logs(interaction); + return; case "nulllast": - await latest(interaction) - return + await latest(interaction); + return; case "nullstats": - await stats(interaction) - return + await stats(interaction); + return; } - - - } register(): SlashCommandSubcommandsOnlyBuilder { return new SlashCommandBuilder() .setName("tracker") .setDescription("The gameway to some cool stats about here being users") - .addSubcommandGroup(group => group - .setName("user") - .setDescription("Get the Tracking data about a certain user") - .addSubcommand(sub => sub - .setName("stats") - .setDescription("See some interesting statistics about a user") - .addUserOption(usr => usr - .setName("user") - .setDescription("go figure: the user") + .addSubcommandGroup((group) => + group + .setName("user") + .setDescription("Get the Tracking data about a certain user") + .addSubcommand((sub) => + sub + .setName("stats") + .setDescription("See some interesting statistics about a user") + .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) - ) - .addSubcommand(sub => sub - .setName("last") - .setDescription("Take a look at the latest logs of a user") - .addUserOption(usr => usr - .setName("user") - .setDescription("go figure: the user") - ) - ) - .addSubcommand(sub => sub - .setName("top") - .setDescription("Lists you the most logged or played game by a user") - .addStringOption(opt => opt - .setName("filter") - .setDescription("logs or playtime?") - .addChoices( - {name:"logs", value:"logs"}, - {name:"playtime", value:"playtime"} - ) - .setRequired(true) + .addSubcommand((sub) => + sub + .setName("last") + .setDescription("Take a look at the latest logs of a user") + .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) + ) + .addSubcommand((sub) => + sub + .setName("top") + .setDescription("Lists you the most logged or played game by a user") + .addStringOption((opt) => + opt + .setName("filter") + .setDescription("logs or playtime?") + .addChoices({ name: "logs", value: "logs" }, { name: "playtime", value: "playtime" }) + .setRequired(true) + ) + .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) - .addUserOption(usr => usr - .setName("user") - .setDescription("go figure: the user") - ) - ) ) - .addSubcommandGroup(group => group - .setName("game") - .setDescription("Get the tracking data about a certain game") - .addSubcommand(sub => sub - .setName("stats") - .setDescription("See some interesting statistics about a game") - .addStringOption(game => game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) + .addSubcommandGroup((group) => + group + .setName("game") + .setDescription("Get the tracking data about a certain game") + .addSubcommand((sub) => + sub + .setName("stats") + .setDescription("See some interesting statistics about a game") + .addStringOption((game) => + game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + ) + .addSubcommand((sub) => + sub + .setName("last") + .setDescription("Take a look at the latest logs of a game") + .addStringOption((game) => + game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) ) - ) - .addSubcommand(sub => sub - .setName("last") - .setDescription("Take a look at the latest logs of a game") - .addStringOption(game => game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - ) - .addSubcommand(sub => sub - .setName("top") - .setDescription("Lists you the most logged or played game by a game") - .addStringOption(game => game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - .addStringOption(opt => opt - .setName("filter") - .setDescription("logs or playtime?") - .addChoices( - {name:"logs", value:"logs"}, - {name:"playtime", value:"playtime"} - ) - .setRequired(true) + .addSubcommand((sub) => + sub + .setName("top") + .setDescription("Lists you the most logged or played game by a game") + .addStringOption((game) => + game + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) + .addStringOption((opt) => + opt + .setName("filter") + .setDescription("logs or playtime?") + .addChoices({ name: "logs", value: "logs" }, { name: "playtime", value: "playtime" }) + .setRequired(true) + ) ) - ) ) - .addSubcommandGroup(group => group - .setName("blacklist") - .setDescription("edit the playlist (ADMIN ONLY)") - .addSubcommand(sub => sub - .setName("add") - .setDescription("choose a game which shouldn't get logged anymore") - .addStringOption(sub => sub - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) + .addSubcommandGroup((group) => + group + .setName("blacklist") + .setDescription("edit the playlist (ADMIN ONLY)") + .addSubcommand((sub) => + sub + .setName("add") + .setDescription("choose a game which shouldn't get logged anymore") + .addStringOption((sub) => + sub + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) ) - ) - .addSubcommand(sub => sub - .setName("remove") - .setDescription("choose a game which should get logged again") - .addStringOption(sub => sub - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) + .addSubcommand((sub) => + sub + .setName("remove") + .setDescription("choose a game which should get logged again") + .addStringOption((sub) => + sub + .setName("game") + .setDescription("go figure: the game") + .setAutocomplete(true) + .setRequired(true) + ) ) - ) ) - .addSubcommand(sub => sub - .setName("playtime") - .setDescription("how may hours a game got played by a user") - .addStringOption(game => game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - .addUserOption(usr => usr - .setName("user") - .setDescription("go figure: the user") - ) - ) - .addSubcommand(sub => sub - .setName("logs") - .setDescription("See how many logs a user has in a given game") - .addStringOption(game => game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - .addUserOption(usr => usr - .setName("user") - .setDescription("go figure: the user") - ) + .addSubcommand((sub) => + sub + .setName("playtime") + .setDescription("how may hours a game got played by a user") + .addStringOption((game) => + game.setName("game").setDescription("go figure: the game").setAutocomplete(true).setRequired(true) + ) + .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) - .addSubcommand(sub => sub - .setName("last") - .setDescription("See the latest logs across the whole system") + .addSubcommand((sub) => + sub + .setName("logs") + .setDescription("See how many logs a user has in a given game") + .addStringOption((game) => + game.setName("game").setDescription("go figure: the game").setAutocomplete(true).setRequired(true) + ) + .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) - .addSubcommand(sub => sub - .setName("stats") - .setDescription("See some system stats") + .addSubcommand((sub) => + sub.setName("last").setDescription("See the latest logs across the whole system") ) + .addSubcommand((sub) => sub.setName("stats").setDescription("See some system stats")); } } diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index 65f76b4..acd7675 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -23,7 +23,7 @@ export default async function presenceUpdate(oldPresence: Presence | null, newPr delete cachedUser[activity.name]; // Played time in secods ("~~" makes float to int) - const timePlayed = ~~((Date.now() - start)); + const timePlayed = ~~(Date.now() - start); // filter out logs under 20 seconds //if (timePlayed < 20) return; diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 3a0afd1..ddd2de4 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -9,17 +9,14 @@ class CountryAutocompleter extends Autocompleter { async execute(interaction: AutocompleteInteraction): Promise { const games = Array.from(trackerGames.keys()); - const focus = interaction.options.getFocused().toLowerCase() as string - let filteredOptions = games.filter(g => g - .toLowerCase() - .startsWith(focus) - ); + const focus = interaction.options.getFocused().toLowerCase() as string; + let filteredOptions = games.filter((g) => g.toLowerCase().startsWith(focus)); if (filteredOptions.length > 25) filteredOptions = filteredOptions.slice(0, 25); const map = filteredOptions.map((c) => ({ name: c, value: c })); await interaction.respond(map); - } + } } export default new CountryAutocompleter(); diff --git a/src/util/config/config-validate.ts b/src/util/config/config-validate.ts index 4442352..a848919 100644 --- a/src/util/config/config-validate.ts +++ b/src/util/config/config-validate.ts @@ -9,7 +9,6 @@ import { roleExists, } from "../misc/verify"; - export async function validateConfigParameters(guild: Guild) { // URLs const urls = [config.iconURL, config.githubURL]; diff --git a/src/util/tracker/commands.ts b/src/util/tracker/commands.ts index e708ad2..9c9b20e 100644 --- a/src/util/tracker/commands.ts +++ b/src/util/tracker/commands.ts @@ -1,429 +1,661 @@ import { EmbedBuilder } from "@discordjs/builders"; import { APIEmbedField, ChatInputCommandInteraction, GuildMember, User } from "discord.js"; -import { durationToReadable, discordTimestamp } from "../misc/time" +import { discordTimestamp, durationToReadable } from "../misc/time"; import { Duration } from "luxon"; -import { trackerUsers, trackerLogs, TrackerSublog, trackerGames, trackerBlacklist } from "../../db" -import { config } from "../../config" +import { trackerBlacklist, trackerGames, trackerLogs, TrackerSublog, trackerUsers } from "../../db"; +import { config } from "../../config"; import { hasAdminPerms } from "../misc/permissions"; -function makeTimeString(timeMS:number) { - return durationToReadable(Duration.fromMillis(timeMS), true) +function makeTimeString(timeMS: number) { + return durationToReadable(Duration.fromMillis(timeMS), true); } function makeEmbed( - target:GuildMember, requester:User, - mostLogged:string, mostPlayed:string, - latestLogs:string, mostPlaytime:string, - firstSeen:number, totalLogs:number, - totalPlaytime:string, playtimePer:string) { - return new EmbedBuilder() - .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) - .setColor(target.displayColor) - .setTitle(`Tracking stats about ${target.displayName}`) - .setFooter({text:`Requested by ${requester.tag}`, iconURL: requester.displayAvatarURL({size:16})}) - .setTimestamp(Date.now()) - .addFields( - {inline:true, name:"Most logged games", value:mostLogged}, - {inline:true, name:"Most played games", value:mostPlayed}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Latest logs", value:latestLogs}, - {inline:true, name:"(Average) playtime per", value:playtimePer}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"(Average) most playtime/log", value:mostPlaytime}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Record range", value:`${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp(Math.floor(Date.now()/1000))}(now)\n${makeTimeString(Date.now() - firstSeen)}`}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Total logs", value:totalLogs.toString()}, - {inline:true, name:"Total playtime", value:totalPlaytime} - ) + target: GuildMember, + requester: User, + mostLogged: string, + mostPlayed: string, + latestLogs: string, + mostPlaytime: string, + firstSeen: number, + totalLogs: number, + totalPlaytime: string, + playtimePer: string +) { + return new EmbedBuilder() + .setAuthor({ + name: "Jambo", + url: "https://github.com/Cowoding-Jams/Jambo", + iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", + }) + .setColor(target.displayColor) + .setTitle(`Tracking stats about ${target.displayName}`) + .setFooter({ text: `Requested by ${requester.tag}`, iconURL: requester.displayAvatarURL({ size: 16 }) }) + .setTimestamp(Date.now()) + .addFields( + { inline: true, name: "Most logged games", value: mostLogged }, + { inline: true, name: "Most played games", value: mostPlayed }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: totalPlaytime } + ); } -function getTotalPlaytime(userId:string) { - const data = trackerUsers.get(userId) - let totalPlaytime = 0 - data?.games.forEach(game => { - totalPlaytime += game.playtime - }) - return totalPlaytime +function getTotalPlaytime(userId: string) { + const data = trackerUsers.get(userId); + let totalPlaytime = 0; + data?.games.forEach((game) => { + totalPlaytime += game.playtime; + }); + return totalPlaytime; } -function getPlaytimePerLog(userId:string) { - const data = trackerUsers.get(userId) - if (!data) return 0 - return data.games.sort((a, b) => a.playtime/a.logs - b.playtime/b.logs).reverse() +function getPlaytimePerLog(userId: string) { + const data = trackerUsers.get(userId); + if (!data) return 0; + return data.games.sort((a, b) => a.playtime / a.logs - b.playtime / b.logs).reverse(); } -function getTotalLogs(userId:string) { - const data = trackerUsers.get(userId) - let totalLogs = 0 - data?.games.forEach(game => { - totalLogs += game.logs - }) - return totalLogs +function getTotalLogs(userId: string) { + const data = trackerUsers.get(userId); + let totalLogs = 0; + data?.games.forEach((game) => { + totalLogs += game.logs; + }); + return totalLogs; } -function getGames(userId:string) { - const games = trackerUsers.get(userId)?.games.length - if (!games) return 1 - return games +function getGames(userId: string) { + const games = trackerUsers.get(userId)?.games.length; + if (!games) return 1; + return games; } -function sortGamesPlaytime(userId:string) { - const data = trackerUsers.get(userId) - return data?.games.sort((a, b) => b.playtime - a.playtime) +function sortGamesPlaytime(userId: string) { + const data = trackerUsers.get(userId); + return data?.games.sort((a, b) => b.playtime - a.playtime); } -function sortGamesLogs(userId:string) { - const data = trackerUsers.get(userId) - return data?.games.sort((a, b) => b.logs - a.logs) +function sortGamesLogs(userId: string) { + const data = trackerUsers.get(userId); + return data?.games.sort((a, b) => b.logs - a.logs); } -function latestLogs(userId:string) { - const data = trackerUsers.get(userId) - if (!data) return ["0"] - return data.lastlogs.reverse() +function latestLogs(userId: string) { + const data = trackerUsers.get(userId); + if (!data) return ["0"]; + return data.lastlogs.reverse(); } - - -export async function userStats(interaction:ChatInputCommandInteraction) { - const target = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user - if (!target) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const member = await interaction.guild?.members.fetch(target.id) - if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const games = sortGamesPlaytime(member.id) - const logs = sortGamesLogs(member.id) - if (!logs || !games) {await interaction.reply({content:"User not enogth logs", ephemeral:true}); return} - const totalPlaytime = getTotalPlaytime(member.id) - const totalLogs = getTotalLogs(member.id) - const userDB = trackerUsers.get(member.id) - if (!userDB) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const firstSeen = trackerLogs.get(userDB.firstlog)?.time - if (!firstSeen) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const range = Date.now() - new Date(firstSeen).getTime() - const playtimePerLog = getPlaytimePerLog(member.id) - if (playtimePerLog == 0) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const latest = playtimePerLog.at(-1) - if (!latest) {await interaction.reply({content:"User not found", ephemeral:true}); return} - - const embed = makeEmbed( - member, - interaction.user, - logs.slice(0, 5).map(game => `${game.id}: ${game.logs}`).join("\n"), - games.slice(0, 5).map(game => `${game.id}: ${makeTimeString(game.playtime)}`).join("\n"), - latestLogs(member.id).map(log => `${trackerLogs.get(log)?.gameName}`).join("\n"), - `${playtimePerLog[0].id}: ${makeTimeString(playtimePerLog[0].playtime/playtimePerLog[0].logs)} - ${playtimePerLog[0].logs}logs\nTotal time: ${makeTimeString(playtimePerLog[0].playtime)}`, - new Date(firstSeen).getTime(), - totalLogs, - makeTimeString(totalPlaytime), - `day: ${makeTimeString(Math.round(totalPlaytime/(range/(86400 * 1000))))}\nweek: ${makeTimeString(Math.round(totalPlaytime/(range/604800000)))}\nmonth: ${makeTimeString(Math.round(totalPlaytime/(range/2628000000)))}\ngame: ${makeTimeString(Math.round(totalPlaytime/getGames(member.id)))}\nlog: ${makeTimeString(Math.round(totalPlaytime/totalLogs))}` - ) - - await interaction.reply({embeds:[embed]}) +export async function userStats(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + if (!target) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const member = await interaction.guild?.members.fetch(target.id); + if (member == undefined) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const games = sortGamesPlaytime(member.id); + const logs = sortGamesLogs(member.id); + if (!logs || !games) { + await interaction.reply({ content: "User not enogth logs", ephemeral: true }); + return; + } + const totalPlaytime = getTotalPlaytime(member.id); + const totalLogs = getTotalLogs(member.id); + const userDB = trackerUsers.get(member.id); + if (!userDB) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const firstSeen = trackerLogs.get(userDB.firstlog)?.time; + if (!firstSeen) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const range = Date.now() - new Date(firstSeen).getTime(); + const playtimePerLog = getPlaytimePerLog(member.id); + if (playtimePerLog == 0) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const latest = playtimePerLog.at(-1); + if (!latest) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + + const embed = makeEmbed( + member, + interaction.user, + logs + .slice(0, 5) + .map((game) => `${game.id}: ${game.logs}`) + .join("\n"), + games + .slice(0, 5) + .map((game) => `${game.id}: ${makeTimeString(game.playtime)}`) + .join("\n"), + latestLogs(member.id) + .map((log) => `${trackerLogs.get(log)?.gameName}`) + .join("\n"), + `${playtimePerLog[0].id}: ${makeTimeString(playtimePerLog[0].playtime / playtimePerLog[0].logs)} - ${ + playtimePerLog[0].logs + }logs\nTotal time: ${makeTimeString(playtimePerLog[0].playtime)}`, + new Date(firstSeen).getTime(), + totalLogs, + makeTimeString(totalPlaytime), + `day: ${makeTimeString(Math.round(totalPlaytime / (range / (86400 * 1000))))}\nweek: ${makeTimeString( + Math.round(totalPlaytime / (range / 604800000)) + )}\nmonth: ${makeTimeString(Math.round(totalPlaytime / (range / 2628000000)))}\ngame: ${makeTimeString( + Math.round(totalPlaytime / getGames(member.id)) + )}\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}` + ); + + await interaction.reply({ embeds: [embed] }); } -export async function userLast(interaction:ChatInputCommandInteraction) { - const target = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user - if (!target) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const member = await interaction.guild?.members.fetch(target.id) - if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const dbEntry = trackerUsers.get(member.id) - if (!dbEntry) {await interaction.reply({content:"User not enogth logs", ephemeral:true}); return} - const logs = dbEntry.lastlogs.slice(0, 5).reverse() - let fields: APIEmbedField[] = [] - - logs.forEach(log => { - const entry = trackerLogs.get(log) - if (!entry) return - fields.push({inline:true, name:entry.gameName, value:`time: \nplayed time: ${makeTimeString(entry.playtime)}`}) - }) - - fields.push({inline:true, name:"_ _", value:"_ _"}) - - - const embed = new EmbedBuilder() - .setColor(member.displayColor) - .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) - .setTitle(`Latest logs by ${member?.displayName}`) - .addFields(...fields) - - await interaction.reply({embeds:[embed]}) +export async function userLast(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + if (!target) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const member = await interaction.guild?.members.fetch(target.id); + if (member == undefined) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const dbEntry = trackerUsers.get(member.id); + if (!dbEntry) { + await interaction.reply({ content: "User not enogth logs", ephemeral: true }); + return; + } + const logs = dbEntry.lastlogs.slice(0, 5).reverse(); + const fields: APIEmbedField[] = []; + + logs.forEach((log) => { + const entry = trackerLogs.get(log); + if (!entry) return; + fields.push({ + inline: true, + name: entry.gameName, + value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(member.displayColor) + .setAuthor({ + name: "Jambo", + url: "https://github.com/Cowoding-Jams/Jambo", + iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", + }) + .setTitle(`Latest logs by ${member?.displayName}`) + .addFields(...fields); + + await interaction.reply({ embeds: [embed] }); } -export async function userTop(interaction:ChatInputCommandInteraction) { - const target = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user - if (!target) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const member = await interaction.guild?.members.fetch(target.id) - if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} - - const filter = interaction.options.getString('filter', true) - - let games:TrackerSublog[] | undefined = [] - if (filter == "logs") { - games = sortGamesLogs(member.id) - } else if (filter == "playtime") { - games = sortGamesPlaytime(member.id) - } else { - await interaction.reply({content:"invalid filter", ephemeral:true}) - return - } - - if (!games) { - await interaction.reply({content:"not many logs", ephemeral:true}) - return - } - - games = games.slice(0, 5) - - let fields: APIEmbedField[] = [] - games.forEach(game => { - fields.push({ - inline: true, - name: game.id, - value: filter == "playtime" ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` : `${game.logs} logs\n${makeTimeString(new Date(game.playtime).getTime())}` - }) - }) - - fields.push({inline:true, name:"_ _", value:"_ _"}) - - const embed = new EmbedBuilder() - .setColor(member.displayColor) - .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) - .setTitle(`Top games (${filter}) by ${member?.displayName}`) - .addFields(...fields) - - await interaction.reply({embeds:[embed]}) +export async function userTop(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + if (!target) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const member = await interaction.guild?.members.fetch(target.id); + if (member == undefined) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + + const filter = interaction.options.getString("filter", true); + + let games: TrackerSublog[] | undefined = []; + if (filter == "logs") { + games = sortGamesLogs(member.id); + } else if (filter == "playtime") { + games = sortGamesPlaytime(member.id); + } else { + await interaction.reply({ content: "invalid filter", ephemeral: true }); + return; + } + + if (!games) { + await interaction.reply({ content: "not many logs", ephemeral: true }); + return; + } + + games = games.slice(0, 5); + + const fields: APIEmbedField[] = []; + games.forEach((game) => { + fields.push({ + inline: true, + name: game.id, + value: + filter == "playtime" + ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` + : `${game.logs} logs\n${makeTimeString(new Date(game.playtime).getTime())}`, + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(member.displayColor) + .setAuthor({ + name: "Jambo", + url: "https://github.com/Cowoding-Jams/Jambo", + iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", + }) + .setTitle(`Top games (${filter}) by ${member?.displayName}`) + .addFields(...fields); + + await interaction.reply({ embeds: [embed] }); } -export async function playtime(interaction:ChatInputCommandInteraction) { - const targetUser = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user - if (!targetUser) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const member = await interaction.guild?.members.fetch(targetUser.id) - if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} - - const targetGame = interaction.options.getString('game', true) - const db = trackerUsers.get(member.id)?.games.filter(game => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()) - - if (!db) {await interaction.reply({content:"User not existing or not played game", ephemeral:true}); return} - - const playtime = db[0].playtime - - const embed = new EmbedBuilder() - .setTitle(`${member.displayName} has played ${makeTimeString(playtime)} of ${targetGame}`) - .setColor(member.displayColor) - - await interaction.reply({embeds:[embed]}) +export async function playtime(interaction: ChatInputCommandInteraction) { + const targetUser = interaction.options.getUser("user") + ? interaction.options.getUser("user") + : interaction.user; + if (!targetUser) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const member = await interaction.guild?.members.fetch(targetUser.id); + if (member == undefined) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + + const targetGame = interaction.options.getString("game", true); + const db = trackerUsers + .get(member.id) + ?.games.filter((game) => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()); + + if (!db) { + await interaction.reply({ content: "User not existing or not played game", ephemeral: true }); + return; + } + + const playtime = db[0].playtime; + + const embed = new EmbedBuilder() + .setTitle(`${member.displayName} has played ${makeTimeString(playtime)} of ${targetGame}`) + .setColor(member.displayColor); + + await interaction.reply({ embeds: [embed] }); } -export async function logs(interaction:ChatInputCommandInteraction) { - const targetUser = interaction.options.getUser('user') ? interaction.options.getUser('user') : interaction.user - if (!targetUser) {await interaction.reply({content:"User not found", ephemeral:true}); return} - const member = await interaction.guild?.members.fetch(targetUser.id) - if (member == undefined) {await interaction.reply({content:"User not found", ephemeral:true}); return} - - const targetGame = interaction.options.getString('game', true) - const db = trackerUsers.get(member.id)?.games.filter(game => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()) - - if (!db) {await interaction.reply({content:"User not existing or not played game", ephemeral:true}); return} - - const logs = db[0].logs - - const embed = new EmbedBuilder() - .setTitle(`${member.displayName} has ${logs} logs in ${targetGame}`) - .setColor(member.displayColor) - - await interaction.reply({embeds:[embed]}) +export async function logs(interaction: ChatInputCommandInteraction) { + const targetUser = interaction.options.getUser("user") + ? interaction.options.getUser("user") + : interaction.user; + if (!targetUser) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + const member = await interaction.guild?.members.fetch(targetUser.id); + if (member == undefined) { + await interaction.reply({ content: "User not found", ephemeral: true }); + return; + } + + const targetGame = interaction.options.getString("game", true); + const db = trackerUsers + .get(member.id) + ?.games.filter((game) => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()); + + if (!db) { + await interaction.reply({ content: "User not existing or not played game", ephemeral: true }); + return; + } + + const logs = db[0].logs; + + const embed = new EmbedBuilder() + .setTitle(`${member.displayName} has ${logs} logs in ${targetGame}`) + .setColor(member.displayColor); + + await interaction.reply({ embeds: [embed] }); } -export async function latest(interaction:ChatInputCommandInteraction) { - const fields:APIEmbedField[] = [] - - for (let i = trackerLogs.count; i > trackerLogs.count - config.activityLogRange; i--) { - const data = trackerLogs.get(i.toString()) - if (!data) return - fields.push({ - inline: true, - name: data.gameName, - value: `user: <@${data.userid}>\ntime: \ntime played: ${makeTimeString(data.playtime)}` - }) - } - - fields.push({inline:true, name:"_ _", value:"_ _"}) - - const embed = new EmbedBuilder() - .setTitle("Latest logs") - .setColor(0) - .addFields(...fields) - - await interaction.reply({embeds:[embed]}) - +export async function latest(interaction: ChatInputCommandInteraction) { + const fields: APIEmbedField[] = []; + + for (let i = trackerLogs.count; i > trackerLogs.count - config.activityLogRange; i--) { + const data = trackerLogs.get(i.toString()); + if (!data) return; + fields.push({ + inline: true, + name: data.gameName, + value: `user: <@${data.userid}>\ntime: \ntime played: ${makeTimeString( + data.playtime + )}`, + }); + } + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setTitle("Latest logs") + .setColor(0) + .addFields(...fields); + + await interaction.reply({ embeds: [embed] }); +} +export async function stats(interaction: ChatInputCommandInteraction) { + const mostLoggedGame = trackerGames + .array() + .sort((a, b) => a.logs - b.logs) + .reverse() + .slice(0, 5) + .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) + .join("\n"); + const mostPlayedGame = trackerGames + .array() + .sort((a, b) => a.playtime - b.playtime) + .reverse() + .slice(0, 5) + .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) + .join("\n"); + const mostLoggedUser = trackerUsers + .array() + .sort((a, b) => a.logs - b.logs) + .reverse() + .splice(0, 5) + .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) + .join("\n"); + const mostPlayedUser = trackerUsers + .array() + .sort((a, b) => a.playtime - b.playtime) + .reverse() + .splice(0, 5) + .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) + .join("\n"); + const latestLogs = trackerLogs + .array() + .reverse() + .slice(0, 5) + .map( + (log) => + ` <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` + ) + .join("\n"); + const totalPlaytime = trackerGames + .array() + .map((game) => game.playtime) + .reduce((partialSum, a) => partialSum + a, 0); + const totalLogs = trackerGames + .array() + .map((game) => game.logs) + .reduce((partialSum, a) => partialSum + a, 0); + const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); + const range = Date.now() - firstSeen; + const games = trackerGames.count; + const playtimePer = `day: ${makeTimeString( + Math.round(totalPlaytime / (range / (86400 * 1000))) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / 2628000000)) + )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( + Math.round(totalPlaytime / totalLogs) + )}`; + const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; + const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString( + tmp.playtime / tmp.logs + )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + + const embed = new EmbedBuilder() + .setTitle("System stats") + .setColor(0) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()) + .addFields( + { inline: true, name: "Most logged games", value: mostLoggedGame }, + { inline: true, name: "Most played games", value: mostPlayedGame }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Most logged users", value: mostLoggedUser }, + { inline: true, name: "Users with most playtime", value: mostPlayedUser }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + ); + + await interaction.reply({ embeds: [embed] }); } -export async function stats(interaction:ChatInputCommandInteraction) { - - const mostLoggedGame = trackerGames.array().sort((a, b) => a.logs - b.logs).reverse().slice(0, 5).map(game => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`).join("\n") - const mostPlayedGame = trackerGames.array().sort((a, b) => a.playtime - b.playtime).reverse().slice(0, 5).map(game => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`).join("\n") - const mostLoggedUser = trackerUsers.array().sort((a, b) => a.logs - b.logs).reverse().splice(0, 5).map(user => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`).join("\n") - const mostPlayedUser = trackerUsers.array().sort((a, b) => a.playtime - b.playtime).reverse().splice(0, 5).map(user => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`).join("\n") - const latestLogs = trackerLogs.array().reverse().slice(0, 5).map(log => ` <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}`).join("\n") - const totalPlaytime = trackerGames.array().map(game => game.playtime).reduce((partialSum, a) => partialSum + a, 0) - const totalLogs = trackerGames.array().map(game => game.logs).reduce((partialSum, a) => partialSum + a, 0) - const firstSeen = new Date(trackerLogs.array()[0].time).getTime() - const range = Date.now() - firstSeen - const games = trackerGames.count - const playtimePer = `day: ${makeTimeString(Math.round(totalPlaytime/(range/(86400 * 1000))))}\nweek: ${makeTimeString(Math.round(totalPlaytime/(range/604800000)))}\nmonth: ${makeTimeString(Math.round(totalPlaytime/(range/2628000000)))}\ngame: ${makeTimeString(Math.round(totalPlaytime/games))}\nlog: ${makeTimeString(Math.round(totalPlaytime/totalLogs))}` - const tmp = trackerGames.array().sort((a, b) => b.playtime/b.logs - a.playtime/b.logs)[0] - const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString(tmp.playtime/tmp.logs)} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}` - - const embed = new EmbedBuilder() - .setTitle("System stats") - .setColor(0) - .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) - .setTimestamp(Date.now()) - .addFields( - {inline:true, name:"Most logged games", value:mostLoggedGame}, - {inline:true, name:"Most played games", value:mostPlayedGame}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Most logged users", value:mostLoggedUser}, - {inline:true, name:"Users with most playtime", value:mostPlayedUser}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Latest logs", value:latestLogs}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"(Average) playtime per", value:playtimePer}, - {inline:true, name:"(Average) most playtime/log", value:mostPlaytime}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Record range", value:`${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp(Math.floor(Date.now()/1000))}(now)\n${makeTimeString(Date.now() - firstSeen)}`}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Total logs", value:totalLogs.toString()}, - {inline:true, name:"Total playtime", value:makeTimeString(totalPlaytime)} - ) - - - await interaction.reply({embeds:[embed]}) +export async function gameStats(interaction: ChatInputCommandInteraction) { + const game = interaction.options.getString("game", true); + const db = trackerGames.get(game.toLocaleLowerCase()); + if (!db) { + await interaction.reply({ content: "not existingngngng", ephemeral: true }); + return; + } + + const mostPlayed = db.users + .sort((a, b) => b.playtime - a.playtime) + .slice(0, 5) + .map((user) => `<@${user.id}>: ${makeTimeString(user.playtime)}`) + .join("\n"); + const mostLogged = db.users + .sort((a, b) => b.logs - a.logs) + .slice(0, 5) + .map((user) => `<@${user.id}>: ${user.logs} logs`) + .join("\n"); + const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); + const totalPlaytime = db.playtime; + const totalLogs = db.logs; + const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + const range = Date.now() - firstSeen; + const users = db.users.length; + const playtimePer = `day: ${makeTimeString( + Math.round(totalPlaytime / (range / (86400 * 1000))) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / 2628000000)) + )}\nuser: ${makeTimeString(Math.round(totalPlaytime / users))}\nlog: ${makeTimeString( + Math.round(totalPlaytime / totalLogs) + )}`; + const tmp = db.users.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; + const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ + tmp.logs + } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + + const embed = new EmbedBuilder() + .setAuthor({ + name: "Jambo", + url: "https://github.com/Cowoding-Jams/Jambo", + iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", + }) + .setColor(0) + .setTitle(`Tracking stats about ${game}`) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()) + .addFields( + { inline: true, name: "Most logged user", value: mostLogged }, + { inline: true, name: "User with most playtime", value: mostPlayed }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + ); + + await interaction.reply({ embeds: [embed] }); } -export async function gameStats(interaction:ChatInputCommandInteraction) { - const game = interaction.options.getString('game', true) - const db = trackerGames.get(game.toLocaleLowerCase()) - if (!db) {await interaction.reply({content: "not existingngngng", ephemeral:true}); return} - - const mostPlayed = db.users.sort((a, b)=> b.playtime - a.playtime).slice(0, 5).map(user => `<@${user.id}>: ${makeTimeString(user.playtime)}`).join("\n") - const mostLogged = db.users.sort((a, b)=> b.logs - a.logs).slice(0, 5).map(user => `<@${user.id}>: ${user.logs} logs`).join("\n") - const latestLogs = db.lastlogs.map(log => `<@${trackerLogs.get(log)?.userid}>`).join("\n") - const totalPlaytime = db.playtime - const totalLogs = db.logs - const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime() - const range = Date.now() - firstSeen - const users = db.users.length - const playtimePer = `day: ${makeTimeString(Math.round(totalPlaytime/(range/(86400 * 1000))))}\nweek: ${makeTimeString(Math.round(totalPlaytime/(range/604800000)))}\nmonth: ${makeTimeString(Math.round(totalPlaytime/(range/2628000000)))}\nuser: ${makeTimeString(Math.round(totalPlaytime/users))}\nlog: ${makeTimeString(Math.round(totalPlaytime/totalLogs))}` - const tmp = db.users.sort((a, b)=> b.playtime/b.logs - a.playtime/a.logs)[0] - const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime/tmp.logs)} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}` - - const embed = new EmbedBuilder() - .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) - .setColor(0) - .setTitle(`Tracking stats about ${game}`) - .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) - .setTimestamp(Date.now()) - .addFields( - {inline:true, name:"Most logged user", value:mostLogged}, - {inline:true, name:"User with most playtime", value:mostPlayed}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Latest logs", value:latestLogs}, - {inline:true, name:"(Average) playtime per", value:playtimePer}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"(Average) most playtime/log", value:mostPlaytime}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Record range", value:`${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp(Math.floor(Date.now()/1000))}(now)\n${makeTimeString(Date.now() - firstSeen)}`}, - {inline:false, name:"_ _", value:"_ _"}, - {inline:true, name:"Total logs", value:totalLogs.toString()}, - {inline:true, name:"Total playtime", value:makeTimeString(totalPlaytime)} - ) - - await interaction.reply({embeds:[embed]}) +export async function gameLast(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getString("game", true); + const db = trackerGames.get(target); + if (!db) { + await interaction.reply({ content: "Game not found or no logs", ephemeral: true }); + return; + } + + const logs = db.lastlogs.slice(0, 5).reverse(); + const fields: APIEmbedField[] = []; + + await logs.forEach(async (log) => { + const entry = trackerLogs.get(log); + if (!entry) return; + const user = await interaction.client.users.fetch(entry.userid); + if (!user) return; + fields.push({ + inline: true, + name: user.username, + value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(0) + .setAuthor({ + name: "Jambo", + url: "https://github.com/Cowoding-Jams/Jambo", + iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", + }) + .setTitle(`Latest logs of ${target}`) + .addFields(...fields) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()); + + await interaction.reply({ embeds: [embed] }); } -export async function gameLast(interaction:ChatInputCommandInteraction) { - const target = interaction.options.getString('game', true) - const db = trackerGames.get(target) - if (!db) {await interaction.reply({content:"Game not found or no logs", ephemeral:true}); return} - - const logs = db.lastlogs.slice(0, 5).reverse() - let fields: APIEmbedField[] = [] - - - await logs.forEach(async log => { - const entry = trackerLogs.get(log) - if (!entry) return - const user = await interaction.client.users.fetch(entry.userid) - if (!user) return - fields.push({inline:true, name:user.username, value:`time: \nplayed time: ${makeTimeString(entry.playtime)}`}) - }) - - fields.push({inline:true, name:"_ _", value:"_ _"}) - - - const embed = new EmbedBuilder() - .setColor(0) - .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) - .setTitle(`Latest logs of ${target}`) - .addFields(...fields) - .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) - .setTimestamp(Date.now()) - - await interaction.reply({embeds:[embed]}) +export async function gameTop(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getString("game", true); + if (!target) { + await interaction.reply({ content: "Game not found", ephemeral: true }); + return; + } + const db = trackerGames.get(target); + if (!db) { + await interaction.reply({ content: "Game not found or no log", ephemeral: true }); + return; + } + + const filter = interaction.options.getString("filter", true); + if (filter == "playtime") { + (""); + } else if (filter == "logs") { + (""); + } else { + interaction.reply({ content: "Invalid filter", ephemeral: true }); + return; + } + + const users = db.users + .sort((a, b) => (filter == "playtime" ? b.playtime - a.playtime : b.logs - a.logs)) + .splice(0, 5); + const fields: APIEmbedField[] = []; + + await users.forEach(async (user) => { + fields.push({ + inline: true, + name: (await interaction.client.users.fetch(user.id)).username, + value: + filter == "playtime" + ? makeTimeString(user.playtime) + "\n" + user.logs + " logs" + : user.logs + " logs\n" + makeTimeString(user.playtime), + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(0) + .setAuthor({ + name: "Jambo", + url: "https://github.com/Cowoding-Jams/Jambo", + iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", + }) + .setTitle(`Top user (${filter}) by ${target}`) + .addFields(...fields) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()); + + await interaction.reply({ embeds: [embed] }); } -export async function gameTop(interaction:ChatInputCommandInteraction) { - const target = interaction.options.getString('game', true) - if (!target) {await interaction.reply({content:"Game not found", ephemeral:true}); return} - const db = trackerGames.get(target) - if (!db) {await interaction.reply({content:"Game not found or no log", ephemeral:true}); return} - - const filter = interaction.options.getString('filter', true) - if (filter == "playtime") { - } else if (filter == "logs") { - } else { - interaction.reply({content: "Invalid filter", ephemeral:true}) - return - } - - const users = db.users.sort((a, b) => filter == "playtime" ? (b.playtime - a.playtime) : (b.logs - a.logs)).splice(0, 5) - let fields:APIEmbedField[] = [] - - await users.forEach(async user => { - fields.push({ - inline: true, - name: (await interaction.client.users.fetch(user.id)).username, - value: filter == "playtime" ? makeTimeString(user.playtime) + "\n" + user.logs + " logs" : user.logs + " logs\n" + makeTimeString(user.playtime) - }) - }) - - fields.push({inline:true, name:"_ _", value:"_ _"}) - - const embed = new EmbedBuilder() - .setColor(0) - .setAuthor({name:"Jambo", url:"https://github.com/Cowoding-Jams/Jambo", iconURL:"https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png"}) - .setTitle(`Top user (${filter}) by ${target}`) - .addFields(...fields) - .setFooter({text:`Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({size:16})}) - .setTimestamp(Date.now()) - - await interaction.reply({embeds:[embed]}) - +export async function addBlacklist(interaction: ChatInputCommandInteraction) { + if (!(await hasAdminPerms(interaction))) { + interaction.reply({ content: "only admin can do this", ephemeral: true }); + return; + } + + const game = interaction.options.getString("game", true); + if (trackerBlacklist.get("")?.find((g) => g.toLowerCase() == game.toLowerCase())) { + interaction.reply({ content: "Game already on blacklist", ephemeral: true }); + return; + } + trackerBlacklist.push("", game.toLowerCase()); + interaction.reply({ content: "Game added", ephemeral: true }); } -export async function addBlacklist(interaction:ChatInputCommandInteraction) { - if (!(await hasAdminPerms(interaction))) { - interaction.reply({content:"only admin can do this", ephemeral:true}) - return - } - - const game = interaction.options.getString('game', true); - if (trackerBlacklist.get("")?.find(g => g.toLowerCase() == game.toLowerCase())) { - interaction.reply({content:"Game already on blacklist", ephemeral:true}) - return - } - trackerBlacklist.push("", game.toLowerCase()) - interaction.reply({content:"Game added", ephemeral:true}) +export async function remBlacklist(interaction: ChatInputCommandInteraction) { + if (!(await hasAdminPerms(interaction))) { + interaction.reply({ content: "only admin can do this", ephemeral: true }); + return; + } + + const game = interaction.options.getString("game", true); + const db = trackerBlacklist.get(""); + if (!db) return; + if (db?.find((g) => g.toLowerCase() == game.toLowerCase())) { + trackerBlacklist.set( + "", + db.filter((g) => g.toLowerCase() != game.toLowerCase()) + ); + interaction.reply({ content: "Game removed", ephemeral: true }); + return; + } + interaction.reply({ content: "Game not on blacklist", ephemeral: true }); } -export async function remBlacklist(interaction:ChatInputCommandInteraction) { - if (!(await hasAdminPerms(interaction))) { - interaction.reply({content:"only admin can do this", ephemeral:true}) - return - } - - const game = interaction.options.getString('game', true); - const db = trackerBlacklist.get("") - if (!db) return - if (db?.find(g => g.toLowerCase() == game.toLowerCase())) { - trackerBlacklist.set("", db.filter(g=> g.toLowerCase() != game.toLowerCase())) - interaction.reply({content:"Game removed", ephemeral:true}) - return - } - interaction.reply({content:"Game not on blacklist", ephemeral:true}) -} \ No newline at end of file From dccd2d7904019dac26596a729822e51f533e3449 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 18:24:17 +0200 Subject: [PATCH 12/92] removed tracker config --- src/config.ts | 3 --- src/util/config/config-validate.ts | 6 ------ src/util/config/configInterface.ts | 9 --------- 3 files changed, 18 deletions(-) diff --git a/src/config.ts b/src/config.ts index 3dcbd17..a59f368 100644 --- a/src/config.ts +++ b/src/config.ts @@ -49,9 +49,6 @@ export const config: BotConfig = { ["Sea Serpent", "#5db9cf"], ], - // --- Activity Tracker --- - activityLogRange: 5, - // --- Birthday feature --- birthdayNotificationAt: 9, }; diff --git a/src/util/config/config-validate.ts b/src/util/config/config-validate.ts index a848919..caa68fe 100644 --- a/src/util/config/config-validate.ts +++ b/src/util/config/config-validate.ts @@ -71,10 +71,4 @@ export async function validateConfigParameters(guild: Guild) { .forEach(async (emoji) => { if (!(await emoji.valid)) throw new Error(`pronounRoles: ${emoji.emoji} is not a valid emoji`); }); - - // Activity range - const activityRange = config.activityLogRange; - if (activityRange < 3 || activityRange > 10) { - throw new Error(`activityLogRange: ${activityRange} is not in range of 3 to 10`); - } } diff --git a/src/util/config/configInterface.ts b/src/util/config/configInterface.ts index 4f15555..cb85724 100644 --- a/src/util/config/configInterface.ts +++ b/src/util/config/configInterface.ts @@ -40,15 +40,6 @@ export interface BotConfig { /** Max 25 color roles to pick from. First argument is the name of the role, second argument is the color. */ colorRoles: [string, ColorResolvable][]; - // --- Activity Tracker --- - /** Defines the range of how many \*latest log\* get stored and shown using some commands (eg. `/.. user last`) - * - * Also defines the number of stored and shown top users and games (eg. `/.. game top `) - * - * **Must be in range of 3 to 10!** - */ - activityLogRange: 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; - // --- Birthday Feature --- /** Can be any integer between 0 and 23. Defines at what time the birthday message should get send. */ birthdayNotificationAt: From 33c3cf20e24bed63dc0a3587d3a61ff1a581cf82 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 18:25:15 +0200 Subject: [PATCH 13/92] oved everythin into new files --- src/commands/TrackerCommands.ts | 22 +- src/util/tracker/blacklistCommands.ts | 38 ++ src/util/tracker/commands.ts | 661 -------------------------- src/util/tracker/gameCommands.ts | 176 +++++++ src/util/tracker/helper.ts | 33 ++ src/util/tracker/messages.ts | 23 + src/util/tracker/presence.ts | 5 +- src/util/tracker/subCommands.ts | 210 ++++++++ src/util/tracker/userCommands.ts | 190 ++++++++ 9 files changed, 678 insertions(+), 680 deletions(-) create mode 100644 src/util/tracker/blacklistCommands.ts delete mode 100644 src/util/tracker/commands.ts create mode 100644 src/util/tracker/gameCommands.ts create mode 100644 src/util/tracker/helper.ts create mode 100644 src/util/tracker/messages.ts create mode 100644 src/util/tracker/subCommands.ts create mode 100644 src/util/tracker/userCommands.ts diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 2bf3e37..235b85c 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -4,20 +4,10 @@ import { SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, } from "discord.js"; -import { - addBlacklist, - gameLast, - gameStats, - gameTop, - latest, - logs, - playtime, - remBlacklist, - stats, - userLast, - userStats, - userTop, -} from "../util/tracker/commands"; +import { latest, logs, playtime, stats } from "../util/tracker/subCommands"; +import { userLast, userStats, userTop } from "../util/tracker/userCommands"; +import { gameLast, gameStats, gameTop } from "../util/tracker/gameCommands"; +import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; class Tracker extends Command { constructor() { super("tracker"); @@ -183,7 +173,7 @@ class Tracker extends Command { .setName("playtime") .setDescription("how may hours a game got played by a user") .addStringOption((game) => - game.setName("game").setDescription("go figure: the game").setAutocomplete(true).setRequired(true) + game.setName("game").setDescription("go figure: the game").setAutocomplete(true) ) .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) @@ -192,7 +182,7 @@ class Tracker extends Command { .setName("logs") .setDescription("See how many logs a user has in a given game") .addStringOption((game) => - game.setName("game").setDescription("go figure: the game").setAutocomplete(true).setRequired(true) + game.setName("game").setDescription("go figure: the game").setAutocomplete(true) ) .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts new file mode 100644 index 0000000..64c0f51 --- /dev/null +++ b/src/util/tracker/blacklistCommands.ts @@ -0,0 +1,38 @@ +import { ChatInputCommandInteraction } from "discord.js"; +import { trackerBlacklist } from "../../db"; +import { hasAdminPerms } from "../misc/permissions"; +import { ADMINONLY, GAMEADDED, GAMENOTBLACKLIST, GAMEONBLACKLIST, GAMEREMOVED } from "./messages"; + +export async function addBlacklist(interaction: ChatInputCommandInteraction) { + if (!(await hasAdminPerms(interaction))) { + interaction.reply(ADMINONLY); + return; + } + + const game = interaction.options.getString("game", true); + if (trackerBlacklist.get("")?.find((g) => g.toLowerCase() == game.toLowerCase())) { + interaction.reply(GAMEONBLACKLIST); + return; + } + trackerBlacklist.push("", game.toLowerCase()); + interaction.reply(GAMEADDED(game)); +} +export async function remBlacklist(interaction: ChatInputCommandInteraction) { + if (!(await hasAdminPerms(interaction))) { + interaction.reply(ADMINONLY); + return; + } + + const game = interaction.options.getString("game", true); + const db = trackerBlacklist.get(""); + if (!db) return; + if (db?.find((g) => g.toLowerCase() == game.toLowerCase())) { + trackerBlacklist.set( + "", + db.filter((g) => g.toLowerCase() != game.toLowerCase()) + ); + interaction.reply(GAMEREMOVED(game)); + return; + } + interaction.reply(GAMENOTBLACKLIST); +} diff --git a/src/util/tracker/commands.ts b/src/util/tracker/commands.ts deleted file mode 100644 index 9c9b20e..0000000 --- a/src/util/tracker/commands.ts +++ /dev/null @@ -1,661 +0,0 @@ -import { EmbedBuilder } from "@discordjs/builders"; -import { APIEmbedField, ChatInputCommandInteraction, GuildMember, User } from "discord.js"; -import { discordTimestamp, durationToReadable } from "../misc/time"; -import { Duration } from "luxon"; -import { trackerBlacklist, trackerGames, trackerLogs, TrackerSublog, trackerUsers } from "../../db"; -import { config } from "../../config"; -import { hasAdminPerms } from "../misc/permissions"; - -function makeTimeString(timeMS: number) { - return durationToReadable(Duration.fromMillis(timeMS), true); -} - -function makeEmbed( - target: GuildMember, - requester: User, - mostLogged: string, - mostPlayed: string, - latestLogs: string, - mostPlaytime: string, - firstSeen: number, - totalLogs: number, - totalPlaytime: string, - playtimePer: string -) { - return new EmbedBuilder() - .setAuthor({ - name: "Jambo", - url: "https://github.com/Cowoding-Jams/Jambo", - iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", - }) - .setColor(target.displayColor) - .setTitle(`Tracking stats about ${target.displayName}`) - .setFooter({ text: `Requested by ${requester.tag}`, iconURL: requester.displayAvatarURL({ size: 16 }) }) - .setTimestamp(Date.now()) - .addFields( - { inline: true, name: "Most logged games", value: mostLogged }, - { inline: true, name: "Most played games", value: mostPlayed }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: totalPlaytime } - ); -} - -function getTotalPlaytime(userId: string) { - const data = trackerUsers.get(userId); - let totalPlaytime = 0; - data?.games.forEach((game) => { - totalPlaytime += game.playtime; - }); - return totalPlaytime; -} -function getPlaytimePerLog(userId: string) { - const data = trackerUsers.get(userId); - if (!data) return 0; - return data.games.sort((a, b) => a.playtime / a.logs - b.playtime / b.logs).reverse(); -} -function getTotalLogs(userId: string) { - const data = trackerUsers.get(userId); - let totalLogs = 0; - data?.games.forEach((game) => { - totalLogs += game.logs; - }); - return totalLogs; -} -function getGames(userId: string) { - const games = trackerUsers.get(userId)?.games.length; - if (!games) return 1; - return games; -} -function sortGamesPlaytime(userId: string) { - const data = trackerUsers.get(userId); - return data?.games.sort((a, b) => b.playtime - a.playtime); -} -function sortGamesLogs(userId: string) { - const data = trackerUsers.get(userId); - return data?.games.sort((a, b) => b.logs - a.logs); -} -function latestLogs(userId: string) { - const data = trackerUsers.get(userId); - if (!data) return ["0"]; - return data.lastlogs.reverse(); -} - -export async function userStats(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; - if (!target) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const member = await interaction.guild?.members.fetch(target.id); - if (member == undefined) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const games = sortGamesPlaytime(member.id); - const logs = sortGamesLogs(member.id); - if (!logs || !games) { - await interaction.reply({ content: "User not enogth logs", ephemeral: true }); - return; - } - const totalPlaytime = getTotalPlaytime(member.id); - const totalLogs = getTotalLogs(member.id); - const userDB = trackerUsers.get(member.id); - if (!userDB) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const firstSeen = trackerLogs.get(userDB.firstlog)?.time; - if (!firstSeen) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const range = Date.now() - new Date(firstSeen).getTime(); - const playtimePerLog = getPlaytimePerLog(member.id); - if (playtimePerLog == 0) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const latest = playtimePerLog.at(-1); - if (!latest) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - - const embed = makeEmbed( - member, - interaction.user, - logs - .slice(0, 5) - .map((game) => `${game.id}: ${game.logs}`) - .join("\n"), - games - .slice(0, 5) - .map((game) => `${game.id}: ${makeTimeString(game.playtime)}`) - .join("\n"), - latestLogs(member.id) - .map((log) => `${trackerLogs.get(log)?.gameName}`) - .join("\n"), - `${playtimePerLog[0].id}: ${makeTimeString(playtimePerLog[0].playtime / playtimePerLog[0].logs)} - ${ - playtimePerLog[0].logs - }logs\nTotal time: ${makeTimeString(playtimePerLog[0].playtime)}`, - new Date(firstSeen).getTime(), - totalLogs, - makeTimeString(totalPlaytime), - `day: ${makeTimeString(Math.round(totalPlaytime / (range / (86400 * 1000))))}\nweek: ${makeTimeString( - Math.round(totalPlaytime / (range / 604800000)) - )}\nmonth: ${makeTimeString(Math.round(totalPlaytime / (range / 2628000000)))}\ngame: ${makeTimeString( - Math.round(totalPlaytime / getGames(member.id)) - )}\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}` - ); - - await interaction.reply({ embeds: [embed] }); -} -export async function userLast(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; - if (!target) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const member = await interaction.guild?.members.fetch(target.id); - if (member == undefined) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const dbEntry = trackerUsers.get(member.id); - if (!dbEntry) { - await interaction.reply({ content: "User not enogth logs", ephemeral: true }); - return; - } - const logs = dbEntry.lastlogs.slice(0, 5).reverse(); - const fields: APIEmbedField[] = []; - - logs.forEach((log) => { - const entry = trackerLogs.get(log); - if (!entry) return; - fields.push({ - inline: true, - name: entry.gameName, - value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, - }); - }); - - fields.push({ inline: true, name: "_ _", value: "_ _" }); - - const embed = new EmbedBuilder() - .setColor(member.displayColor) - .setAuthor({ - name: "Jambo", - url: "https://github.com/Cowoding-Jams/Jambo", - iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", - }) - .setTitle(`Latest logs by ${member?.displayName}`) - .addFields(...fields); - - await interaction.reply({ embeds: [embed] }); -} -export async function userTop(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; - if (!target) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const member = await interaction.guild?.members.fetch(target.id); - if (member == undefined) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - - const filter = interaction.options.getString("filter", true); - - let games: TrackerSublog[] | undefined = []; - if (filter == "logs") { - games = sortGamesLogs(member.id); - } else if (filter == "playtime") { - games = sortGamesPlaytime(member.id); - } else { - await interaction.reply({ content: "invalid filter", ephemeral: true }); - return; - } - - if (!games) { - await interaction.reply({ content: "not many logs", ephemeral: true }); - return; - } - - games = games.slice(0, 5); - - const fields: APIEmbedField[] = []; - games.forEach((game) => { - fields.push({ - inline: true, - name: game.id, - value: - filter == "playtime" - ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` - : `${game.logs} logs\n${makeTimeString(new Date(game.playtime).getTime())}`, - }); - }); - - fields.push({ inline: true, name: "_ _", value: "_ _" }); - - const embed = new EmbedBuilder() - .setColor(member.displayColor) - .setAuthor({ - name: "Jambo", - url: "https://github.com/Cowoding-Jams/Jambo", - iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", - }) - .setTitle(`Top games (${filter}) by ${member?.displayName}`) - .addFields(...fields); - - await interaction.reply({ embeds: [embed] }); -} -export async function playtime(interaction: ChatInputCommandInteraction) { - const targetUser = interaction.options.getUser("user") - ? interaction.options.getUser("user") - : interaction.user; - if (!targetUser) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const member = await interaction.guild?.members.fetch(targetUser.id); - if (member == undefined) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - - const targetGame = interaction.options.getString("game", true); - const db = trackerUsers - .get(member.id) - ?.games.filter((game) => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()); - - if (!db) { - await interaction.reply({ content: "User not existing or not played game", ephemeral: true }); - return; - } - - const playtime = db[0].playtime; - - const embed = new EmbedBuilder() - .setTitle(`${member.displayName} has played ${makeTimeString(playtime)} of ${targetGame}`) - .setColor(member.displayColor); - - await interaction.reply({ embeds: [embed] }); -} -export async function logs(interaction: ChatInputCommandInteraction) { - const targetUser = interaction.options.getUser("user") - ? interaction.options.getUser("user") - : interaction.user; - if (!targetUser) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - const member = await interaction.guild?.members.fetch(targetUser.id); - if (member == undefined) { - await interaction.reply({ content: "User not found", ephemeral: true }); - return; - } - - const targetGame = interaction.options.getString("game", true); - const db = trackerUsers - .get(member.id) - ?.games.filter((game) => game.id.toLocaleLowerCase() == targetGame.toLocaleLowerCase()); - - if (!db) { - await interaction.reply({ content: "User not existing or not played game", ephemeral: true }); - return; - } - - const logs = db[0].logs; - - const embed = new EmbedBuilder() - .setTitle(`${member.displayName} has ${logs} logs in ${targetGame}`) - .setColor(member.displayColor); - - await interaction.reply({ embeds: [embed] }); -} -export async function latest(interaction: ChatInputCommandInteraction) { - const fields: APIEmbedField[] = []; - - for (let i = trackerLogs.count; i > trackerLogs.count - config.activityLogRange; i--) { - const data = trackerLogs.get(i.toString()); - if (!data) return; - fields.push({ - inline: true, - name: data.gameName, - value: `user: <@${data.userid}>\ntime: \ntime played: ${makeTimeString( - data.playtime - )}`, - }); - } - - fields.push({ inline: true, name: "_ _", value: "_ _" }); - - const embed = new EmbedBuilder() - .setTitle("Latest logs") - .setColor(0) - .addFields(...fields); - - await interaction.reply({ embeds: [embed] }); -} -export async function stats(interaction: ChatInputCommandInteraction) { - const mostLoggedGame = trackerGames - .array() - .sort((a, b) => a.logs - b.logs) - .reverse() - .slice(0, 5) - .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) - .join("\n"); - const mostPlayedGame = trackerGames - .array() - .sort((a, b) => a.playtime - b.playtime) - .reverse() - .slice(0, 5) - .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) - .join("\n"); - const mostLoggedUser = trackerUsers - .array() - .sort((a, b) => a.logs - b.logs) - .reverse() - .splice(0, 5) - .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) - .join("\n"); - const mostPlayedUser = trackerUsers - .array() - .sort((a, b) => a.playtime - b.playtime) - .reverse() - .splice(0, 5) - .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) - .join("\n"); - const latestLogs = trackerLogs - .array() - .reverse() - .slice(0, 5) - .map( - (log) => - ` <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` - ) - .join("\n"); - const totalPlaytime = trackerGames - .array() - .map((game) => game.playtime) - .reduce((partialSum, a) => partialSum + a, 0); - const totalLogs = trackerGames - .array() - .map((game) => game.logs) - .reduce((partialSum, a) => partialSum + a, 0); - const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); - const range = Date.now() - firstSeen; - const games = trackerGames.count; - const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / (86400 * 1000))) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / 2628000000)) - )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( - Math.round(totalPlaytime / totalLogs) - )}`; - const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; - const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString( - tmp.playtime / tmp.logs - )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; - - const embed = new EmbedBuilder() - .setTitle("System stats") - .setColor(0) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()) - .addFields( - { inline: true, name: "Most logged games", value: mostLoggedGame }, - { inline: true, name: "Most played games", value: mostPlayedGame }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Most logged users", value: mostLoggedUser }, - { inline: true, name: "Users with most playtime", value: mostPlayedUser }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } - ); - - await interaction.reply({ embeds: [embed] }); -} -export async function gameStats(interaction: ChatInputCommandInteraction) { - const game = interaction.options.getString("game", true); - const db = trackerGames.get(game.toLocaleLowerCase()); - if (!db) { - await interaction.reply({ content: "not existingngngng", ephemeral: true }); - return; - } - - const mostPlayed = db.users - .sort((a, b) => b.playtime - a.playtime) - .slice(0, 5) - .map((user) => `<@${user.id}>: ${makeTimeString(user.playtime)}`) - .join("\n"); - const mostLogged = db.users - .sort((a, b) => b.logs - a.logs) - .slice(0, 5) - .map((user) => `<@${user.id}>: ${user.logs} logs`) - .join("\n"); - const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); - const totalPlaytime = db.playtime; - const totalLogs = db.logs; - const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); - const range = Date.now() - firstSeen; - const users = db.users.length; - const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / (86400 * 1000))) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / 2628000000)) - )}\nuser: ${makeTimeString(Math.round(totalPlaytime / users))}\nlog: ${makeTimeString( - Math.round(totalPlaytime / totalLogs) - )}`; - const tmp = db.users.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; - const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ - tmp.logs - } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; - - const embed = new EmbedBuilder() - .setAuthor({ - name: "Jambo", - url: "https://github.com/Cowoding-Jams/Jambo", - iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", - }) - .setColor(0) - .setTitle(`Tracking stats about ${game}`) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()) - .addFields( - { inline: true, name: "Most logged user", value: mostLogged }, - { inline: true, name: "User with most playtime", value: mostPlayed }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } - ); - - await interaction.reply({ embeds: [embed] }); -} -export async function gameLast(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getString("game", true); - const db = trackerGames.get(target); - if (!db) { - await interaction.reply({ content: "Game not found or no logs", ephemeral: true }); - return; - } - - const logs = db.lastlogs.slice(0, 5).reverse(); - const fields: APIEmbedField[] = []; - - await logs.forEach(async (log) => { - const entry = trackerLogs.get(log); - if (!entry) return; - const user = await interaction.client.users.fetch(entry.userid); - if (!user) return; - fields.push({ - inline: true, - name: user.username, - value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, - }); - }); - - fields.push({ inline: true, name: "_ _", value: "_ _" }); - - const embed = new EmbedBuilder() - .setColor(0) - .setAuthor({ - name: "Jambo", - url: "https://github.com/Cowoding-Jams/Jambo", - iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", - }) - .setTitle(`Latest logs of ${target}`) - .addFields(...fields) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()); - - await interaction.reply({ embeds: [embed] }); -} -export async function gameTop(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getString("game", true); - if (!target) { - await interaction.reply({ content: "Game not found", ephemeral: true }); - return; - } - const db = trackerGames.get(target); - if (!db) { - await interaction.reply({ content: "Game not found or no log", ephemeral: true }); - return; - } - - const filter = interaction.options.getString("filter", true); - if (filter == "playtime") { - (""); - } else if (filter == "logs") { - (""); - } else { - interaction.reply({ content: "Invalid filter", ephemeral: true }); - return; - } - - const users = db.users - .sort((a, b) => (filter == "playtime" ? b.playtime - a.playtime : b.logs - a.logs)) - .splice(0, 5); - const fields: APIEmbedField[] = []; - - await users.forEach(async (user) => { - fields.push({ - inline: true, - name: (await interaction.client.users.fetch(user.id)).username, - value: - filter == "playtime" - ? makeTimeString(user.playtime) + "\n" + user.logs + " logs" - : user.logs + " logs\n" + makeTimeString(user.playtime), - }); - }); - - fields.push({ inline: true, name: "_ _", value: "_ _" }); - - const embed = new EmbedBuilder() - .setColor(0) - .setAuthor({ - name: "Jambo", - url: "https://github.com/Cowoding-Jams/Jambo", - iconURL: "https://github.com/Cowoding-Jams/Jambo/blob/main/images/Robot.png", - }) - .setTitle(`Top user (${filter}) by ${target}`) - .addFields(...fields) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()); - - await interaction.reply({ embeds: [embed] }); -} -export async function addBlacklist(interaction: ChatInputCommandInteraction) { - if (!(await hasAdminPerms(interaction))) { - interaction.reply({ content: "only admin can do this", ephemeral: true }); - return; - } - - const game = interaction.options.getString("game", true); - if (trackerBlacklist.get("")?.find((g) => g.toLowerCase() == game.toLowerCase())) { - interaction.reply({ content: "Game already on blacklist", ephemeral: true }); - return; - } - trackerBlacklist.push("", game.toLowerCase()); - interaction.reply({ content: "Game added", ephemeral: true }); -} -export async function remBlacklist(interaction: ChatInputCommandInteraction) { - if (!(await hasAdminPerms(interaction))) { - interaction.reply({ content: "only admin can do this", ephemeral: true }); - return; - } - - const game = interaction.options.getString("game", true); - const db = trackerBlacklist.get(""); - if (!db) return; - if (db?.find((g) => g.toLowerCase() == game.toLowerCase())) { - trackerBlacklist.set( - "", - db.filter((g) => g.toLowerCase() != game.toLowerCase()) - ); - interaction.reply({ content: "Game removed", ephemeral: true }); - return; - } - interaction.reply({ content: "Game not on blacklist", ephemeral: true }); -} diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts new file mode 100644 index 0000000..70ac445 --- /dev/null +++ b/src/util/tracker/gameCommands.ts @@ -0,0 +1,176 @@ +import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; +import { config } from "../../config"; +import { trackerGames, trackerLogs } from "../../db"; +import { discordTimestamp } from "../misc/time"; +import { makeTimeString } from "./helper"; +import { GAMENOENTRY, INVALIDFILTER } from "./messages"; + +export async function gameStats(interaction: ChatInputCommandInteraction) { + const game = interaction.options.getString("game", true); + const db = trackerGames.get(game.toLocaleLowerCase()); + if (!db) { + await interaction.reply(GAMENOENTRY); + return; + } + + const mostPlayed = db.users + .sort((a, b) => b.playtime - a.playtime) + .slice(0, 5) + .map((user) => `<@${user.id}>: ${makeTimeString(user.playtime)}`) + .join("\n"); + const mostLogged = db.users + .sort((a, b) => b.logs - a.logs) + .slice(0, 5) + .map((user) => `<@${user.id}>: ${user.logs} logs`) + .join("\n"); + const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); + const totalPlaytime = db.playtime; + const totalLogs = db.logs; + const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + const range = Date.now() - firstSeen; + const users = db.users.length; + const playtimePer = `day: ${makeTimeString( + Math.round(totalPlaytime / (range / (86400 * 1000))) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / 2628000000)) + )}\nuser: ${makeTimeString(Math.round(totalPlaytime / users))}\nlog: ${makeTimeString( + Math.round(totalPlaytime / totalLogs) + )}`; + const tmp = db.users.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; + const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ + tmp.logs + } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + + const embed = new EmbedBuilder() + .setAuthor({ + name: config.botName, + url: config.githubURL, + iconURL: config.iconURL, + }) + .setColor(config.color) + .setTitle(`Tracking stats about ${game}`) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()) + .addFields( + { inline: true, name: "Most logged user", value: mostLogged }, + { inline: true, name: "User with most playtime", value: mostPlayed }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + ); + + await interaction.reply({ embeds: [embed] }); +} +export async function gameLast(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getString("game", true); + const db = trackerGames.get(target); + if (!db) { + await interaction.reply(GAMENOENTRY); + return; + } + + const logs = db.lastlogs.slice(0, 5).reverse(); + const fields: APIEmbedField[] = []; + + await logs.forEach(async (log) => { + const entry = trackerLogs.get(log); + if (!entry) return; + const user = await interaction.client.users.fetch(entry.userid); + if (!user) return; + fields.push({ + inline: true, + name: user.username, + value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(config.color) + .setAuthor({ + name: config.botName, + url: config.githubURL, + iconURL: config.iconURL, + }) + .setTitle(`Latest logs of ${target}`) + .addFields(...fields) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()); + + await interaction.reply({ embeds: [embed] }); +} +export async function gameTop(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getString("game", true); + const db = trackerGames.get(target); + if (!db) { + await interaction.reply(GAMENOENTRY); + return; + } + + const filter = interaction.options.getString("filter", true); + if (filter == "playtime") { + (""); + } else if (filter == "logs") { + (""); + } else { + interaction.reply(INVALIDFILTER); + return; + } + + const users = db.users + .sort((a, b) => (filter == "playtime" ? b.playtime - a.playtime : b.logs - a.logs)) + .splice(0, 5); + const fields: APIEmbedField[] = []; + + await users.forEach(async (user) => { + fields.push({ + inline: true, + name: (await interaction.client.users.fetch(user.id)).username, + value: + filter == "playtime" + ? makeTimeString(user.playtime) + "\n" + user.logs + " logs" + : user.logs + " logs\n" + makeTimeString(user.playtime), + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + config.botName; + const embed = new EmbedBuilder() + .setColor(config.color) + .setAuthor({ + name: config.botName, + url: config.githubURL, + iconURL: config.iconURL, + }) + .setTitle(`Top user (${filter}) by ${target}`) + .addFields(...fields) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()); + + await interaction.reply({ embeds: [embed] }); +} diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts new file mode 100644 index 0000000..d6343fe --- /dev/null +++ b/src/util/tracker/helper.ts @@ -0,0 +1,33 @@ +import { EmbedBuilder, InteractionReplyOptions } from "discord.js"; +import { Duration } from "luxon"; +import { trackerUsers } from "../../db"; +import { durationToReadable } from "../misc/time"; + +export function makeTimeString(timeMS: number) { + return durationToReadable(Duration.fromMillis(timeMS), true); +} +export function sortGamesPlaytime(userId: string) { + const data = trackerUsers.get(userId); + return data?.games.sort((a, b) => b.playtime - a.playtime); +} +export function sortGamesLogs(userId: string) { + const data = trackerUsers.get(userId); + return data?.games.sort((a, b) => b.logs - a.logs); +} +export function errorEmbed(title: string, info: string): InteractionReplyOptions { + return { + embeds: [ + new EmbedBuilder() + .setTitle(`ERROR: ${title}`) + .setDescription(`further info:\n\`\`\`${info}\`\`\``) + .setColor([255, 0, 0]), + ], + ephemeral: true, + }; +} +export function confirmEmbed(title: string): InteractionReplyOptions { + return { + embeds: [new EmbedBuilder().setTitle(title).setColor([255, 0, 0])], + ephemeral: true, + }; +} diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts new file mode 100644 index 0000000..05d4c6c --- /dev/null +++ b/src/util/tracker/messages.ts @@ -0,0 +1,23 @@ +import { confirmEmbed, errorEmbed } from "./helper"; + +export const USERNOTFOUND = errorEmbed("user not found", "Cant get user option or interaction user."); +export const MEMBERNOTFOUND = errorEmbed( + "member not found", + "Cant get guild member, user may not be on this server anymore." +); +export const INVALIDFILTER = errorEmbed("invalid filter", 'Given filter needs to be "logs" or "playtime"'); +export const USERNOENTRY = errorEmbed("no database entry", "Given user has no entrys in the database."); +export const USERNOGAMEENTRY = errorEmbed( + "no database entry", + "Given user has no entrys in the database associated with given the game." +); +export const GAMENOENTRY = errorEmbed("no database entry", "Given game has no entry in the database."); +export const ADMINONLY = errorEmbed("admin only", "This command can only be executed by an admin."); +export const GAMEONBLACKLIST = errorEmbed("already on blacklist", "Given game is already on the blacklist."); +export const GAMENOTBLACKLIST = errorEmbed("no on blacklist", "Given game is not on the blacklist."); +export const NOUSERANDGAMEGIVEN = errorEmbed( + "invalid options", + "game nor user where given. This command needs at lest one of both." +); +export const GAMEADDED = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); +export const GAMEREMOVED = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); diff --git a/src/util/tracker/presence.ts b/src/util/tracker/presence.ts index d48e65d..7ac195d 100644 --- a/src/util/tracker/presence.ts +++ b/src/util/tracker/presence.ts @@ -8,7 +8,6 @@ import { TrackerUser, trackerUsers, } from "../../db"; -import { config } from "../../config"; export async function getChangedActivities( oldPresence: Presence | null, @@ -77,7 +76,7 @@ function updateUser(gameName: string, userID: string, timePlayed: number, logID: if (!data) return; // remove oldest log from the latest log history - if (data.lastlogs.length > config.activityLogRange) data.lastlogs.shift(); + if (data.lastlogs.length >= 5) data.lastlogs.shift(); data.lastlogs.push(logID); // add newest log to the log history data.logs += 1; @@ -100,7 +99,7 @@ function updateGame(gameName: string, userID: string, timePlayed: number, logID: if (!data) return; // remove oldest log from the latest log history - if (data.lastlogs.length > config.activityLogRange) data.lastlogs.shift(); + if (data.lastlogs.length >= 5) data.lastlogs.shift(); data.lastlogs.push(logID); // add newest log to the log history data.logs += 1; diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts new file mode 100644 index 0000000..c0ef314 --- /dev/null +++ b/src/util/tracker/subCommands.ts @@ -0,0 +1,210 @@ +import { EmbedBuilder } from "discord.js"; +import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; +import { discordTimestamp } from "../misc/time"; +import { trackerGames, trackerLogs, trackerUsers } from "../../db"; +import { config } from "../../config"; +import { makeTimeString } from "./helper"; +import { GAMENOENTRY, USERNOENTRY, USERNOGAMEENTRY } from "./messages"; + +export async function playtime(interaction: ChatInputCommandInteraction) { + const targetUser = interaction.options.getUser("user"); + const targetGame = interaction.options.getString("game"); + let text = ""; + + if (!targetUser && !targetGame) { + let playtime = 0; + trackerGames.array().forEach((game) => (playtime += game.playtime)); + text = `The whole system has tracked ${makeTimeString(playtime)} of playtime`; + } else if (!targetUser && targetGame) { + const db = trackerGames.get(targetGame.toLowerCase()); + if (!db) { + await interaction.reply(GAMENOENTRY); + return; + } + text = `${targetGame} has ${makeTimeString(db.playtime)} of playtime`; + } else if (targetUser && !targetGame) { + const db = trackerUsers.get(targetUser.id); + if (!db) { + await interaction.reply(USERNOENTRY); + return; + } + text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime`; + } else if (targetUser && targetGame) { + const db = trackerUsers + .get(targetUser.id) + ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); + if (!db) { + await interaction.reply(USERNOGAMEENTRY); + return; + } + text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime in ${targetGame}`; + } else { + return; + } + + await interaction.reply({ + embeds: [new EmbedBuilder().setTitle(text).setColor(config.color)], + }); +} +export async function logs(interaction: ChatInputCommandInteraction) { + const targetUser = interaction.options.getUser("user"); + const targetGame = interaction.options.getString("game"); + let text = ""; + + if (!targetUser && !targetGame) { + let logs = 0; + trackerGames.array().forEach((game) => (logs += game.logs)); + text = `The whole system has tracked ${logs} times`; + } else if (!targetUser && targetGame) { + const db = trackerGames.get(targetGame.toLowerCase()); + if (!db) { + await interaction.reply(GAMENOENTRY); + return; + } + text = `${targetGame} was played ${db.logs} times`; + } else if (targetUser && !targetGame) { + const db = trackerUsers.get(targetUser.id); + if (!db) { + await interaction.reply(USERNOENTRY); + return; + } + text = `${targetUser.username} was logged ${db.logs} times`; + } else if (targetUser && targetGame) { + const db = trackerUsers + .get(targetUser.id) + ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); + if (!db) { + await interaction.reply(USERNOGAMEENTRY); + return; + } + text = `${targetUser.username} has played ${targetGame} ${db.logs} times`; + } else { + return; + } + + await interaction.reply({ + embeds: [new EmbedBuilder().setTitle(text).setColor(config.color)], + }); +} +export async function latest(interaction: ChatInputCommandInteraction) { + const fields: APIEmbedField[] = []; + + const logs = trackerLogs.array().slice(0, 5).reverse(); + logs.forEach((log) => + fields.push({ + inline: true, + name: log.gameName, + value: `user: <@${log.userid}>\ntime: \nplayed time: ${makeTimeString( + log.playtime + )}`, + }) + ); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setTitle("Latest logs") + .setColor(config.color) + .addFields(...fields); + + await interaction.reply({ embeds: [embed] }); +} +export async function stats(interaction: ChatInputCommandInteraction) { + const mostLoggedGame = trackerGames + .array() + .sort((a, b) => a.logs - b.logs) + .reverse() + .slice(0, 5) + .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) + .join("\n"); + const mostPlayedGame = trackerGames + .array() + .sort((a, b) => a.playtime - b.playtime) + .reverse() + .slice(0, 5) + .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) + .join("\n"); + const mostLoggedUser = trackerUsers + .array() + .sort((a, b) => a.logs - b.logs) + .reverse() + .splice(0, 5) + .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) + .join("\n"); + const mostPlayedUser = trackerUsers + .array() + .sort((a, b) => a.playtime - b.playtime) + .reverse() + .splice(0, 5) + .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) + .join("\n"); + const latestLogs = trackerLogs + .array() + .reverse() + .slice(0, 5) + .map( + (log) => + ` <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` + ) + .join("\n"); + const totalPlaytime = trackerGames + .array() + .map((game) => game.playtime) + .reduce((partialSum, a) => partialSum + a, 0); + const totalLogs = trackerGames + .array() + .map((game) => game.logs) + .reduce((partialSum, a) => partialSum + a, 0); + const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); + const range = Date.now() - firstSeen; + const games = trackerGames.count; + const playtimePer = `day: ${makeTimeString( + Math.round(totalPlaytime / (range / (86400 * 1000))) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / 2628000000)) + )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( + Math.round(totalPlaytime / totalLogs) + )}`; + const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; + const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString( + tmp.playtime / tmp.logs + )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + + const embed = new EmbedBuilder() + .setTitle("System stats") + .setColor(config.color) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()) + .addFields( + { inline: true, name: "Most logged games", value: mostLoggedGame }, + { inline: true, name: "Most played games", value: mostPlayedGame }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Most logged users", value: mostLoggedUser }, + { inline: true, name: "Users with most playtime", value: mostPlayedUser }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + ); + + await interaction.reply({ embeds: [embed] }); +} diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts new file mode 100644 index 0000000..f3cd9cf --- /dev/null +++ b/src/util/tracker/userCommands.ts @@ -0,0 +1,190 @@ +import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; +import { config } from "../../config"; +import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; +import { discordTimestamp } from "../misc/time"; +import { makeTimeString, sortGamesLogs, sortGamesPlaytime } from "./helper"; +import { INVALIDFILTER, MEMBERNOTFOUND, USERNOENTRY, USERNOTFOUND } from "./messages"; + +export async function userStats(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + if (!target) { + await interaction.reply(USERNOTFOUND); + return; + } + const member = await interaction.guild?.members.fetch(target.id); + if (!member) { + await interaction.reply(MEMBERNOTFOUND); + return; + } + const db = trackerUsers.get(target.id); + if (!db) { + await interaction.reply(USERNOENTRY); + return; + } + + const mostPlayed = db.games + .sort((a, b) => b.playtime - a.playtime) + .slice(0, 5) + .map((game) => `${game.id}: ${makeTimeString(game.playtime)}`) + .join("\n"); + const mostLogged = db.games + .sort((a, b) => b.logs - a.logs) + .slice(0, 5) + .map((game) => `${game.id}: ${game.logs} logs`) + .join("\n"); + const latestLogs = db.lastlogs.map((log) => `${trackerLogs.get(log)?.gameName}`).join("\n"); + const totalPlaytime = db.playtime; + const totalLogs = db.logs; + const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + const range = Date.now() - firstSeen; + const games = db.games.length; + const playtimePer = `day: ${makeTimeString( + Math.round(totalPlaytime / (range / (86400 * 1000))) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / 2628000000)) + )}\nuser: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( + Math.round(totalPlaytime / totalLogs) + )}`; + const tmp = db.games.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; + const mostPlaytime = `${tmp.id}: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ + tmp.logs + } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + + const embed = new EmbedBuilder() + .setAuthor({ + name: config.botName, + url: config.githubURL, + iconURL: config.iconURL, + }) + .setColor(member.displayColor) + .setTitle(`Tracking stats about ${member.displayName}`) + .setFooter({ + text: `Requested by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 16 }), + }) + .setTimestamp(Date.now()) + .addFields( + { inline: true, name: "Most logged game", value: mostLogged }, + { inline: true, name: "Game with most playtime", value: mostPlayed }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + ); + + await interaction.reply({ embeds: [embed] }); +} +export async function userLast(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + if (!target) { + await interaction.reply(USERNOTFOUND); + return; + } + const member = await interaction.guild?.members.fetch(target.id); + if (member == undefined) { + await interaction.reply(MEMBERNOTFOUND); + return; + } + const db = trackerUsers.get(member.id); + if (!db) { + await interaction.reply(USERNOENTRY); + return; + } + const logs = db.lastlogs.reverse(); + const fields: APIEmbedField[] = []; + + logs.forEach((log) => { + const entry = trackerLogs.get(log); + if (!entry) return; + fields.push({ + inline: true, + name: entry.gameName, + value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(member.displayColor) + .setAuthor({ + name: config.botName, + url: config.githubURL, + iconURL: config.iconURL, + }) + .setTitle(`Latest logs by ${member?.displayName}`) + .addFields(...fields); + + await interaction.reply({ embeds: [embed] }); +} +export async function userTop(interaction: ChatInputCommandInteraction) { + const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + if (!target) { + await interaction.reply(USERNOTFOUND); + return; + } + const member = await interaction.guild?.members.fetch(target.id); + if (member == undefined) { + await interaction.reply(MEMBERNOTFOUND); + return; + } + + const filter = interaction.options.getString("filter", true); + + let games: TrackerSublog[] | undefined = []; + if (filter == "logs") { + games = sortGamesLogs(member.id); + } else if (filter == "playtime") { + games = sortGamesPlaytime(member.id); + } else { + await interaction.reply(INVALIDFILTER); + return; + } + + if (!games) { + await interaction.reply(USERNOENTRY); + return; + } + + games = games.slice(0, 5); + + const fields: APIEmbedField[] = []; + games.forEach((game) => { + fields.push({ + inline: true, + name: game.id, + value: + filter == "playtime" + ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` + : `${game.logs} logs\n${makeTimeString(new Date(game.playtime).getTime())}`, + }); + }); + + fields.push({ inline: true, name: "_ _", value: "_ _" }); + + const embed = new EmbedBuilder() + .setColor(member.displayColor) + .setAuthor({ + name: config.botName, + url: config.githubURL, + iconURL: config.iconURL, + }) + .setTitle(`Top games (${filter}) by ${member?.displayName}`) + .addFields(...fields); + + await interaction.reply({ embeds: [embed] }); +} From fe1b67b7b9b533cd72435a66be798984413cb73f Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 20:15:20 +0200 Subject: [PATCH 14/92] comments --- src/util/tracker/blacklistCommands.ts | 12 ++++- src/util/tracker/gameCommands.ts | 62 +++++++++++++++++-------- src/util/tracker/helper.ts | 9 +++- src/util/tracker/messages.ts | 15 ++++-- src/util/tracker/presence.ts | 10 ++-- src/util/tracker/subCommands.ts | 66 ++++++++++++++++++++------- src/util/tracker/userCommands.ts | 35 ++++++++++++-- 7 files changed, 158 insertions(+), 51 deletions(-) diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts index 64c0f51..09982f7 100644 --- a/src/util/tracker/blacklistCommands.ts +++ b/src/util/tracker/blacklistCommands.ts @@ -4,29 +4,36 @@ import { hasAdminPerms } from "../misc/permissions"; import { ADMINONLY, GAMEADDED, GAMENOTBLACKLIST, GAMEONBLACKLIST, GAMEREMOVED } from "./messages"; export async function addBlacklist(interaction: ChatInputCommandInteraction) { + // check for admin permissions if (!(await hasAdminPerms(interaction))) { interaction.reply(ADMINONLY); return; } + // get game option const game = interaction.options.getString("game", true); + // check if game is already on blacklist if (trackerBlacklist.get("")?.find((g) => g.toLowerCase() == game.toLowerCase())) { interaction.reply(GAMEONBLACKLIST); return; } + // add game to blacklist trackerBlacklist.push("", game.toLowerCase()); interaction.reply(GAMEADDED(game)); } export async function remBlacklist(interaction: ChatInputCommandInteraction) { + // check for admin permissions if (!(await hasAdminPerms(interaction))) { interaction.reply(ADMINONLY); return; } - + + // get game option const game = interaction.options.getString("game", true); const db = trackerBlacklist.get(""); - if (!db) return; + if (!db) return; // make ts happy... if (db?.find((g) => g.toLowerCase() == game.toLowerCase())) { + // remove game trackerBlacklist.set( "", db.filter((g) => g.toLowerCase() != game.toLowerCase()) @@ -34,5 +41,6 @@ export async function remBlacklist(interaction: ChatInputCommandInteraction) { interaction.reply(GAMEREMOVED(game)); return; } + // send error interaction.reply(GAMENOTBLACKLIST); } diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 70ac445..b6490c1 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -6,29 +6,39 @@ import { makeTimeString } from "./helper"; import { GAMENOENTRY, INVALIDFILTER } from "./messages"; export async function gameStats(interaction: ChatInputCommandInteraction) { - const game = interaction.options.getString("game", true); - const db = trackerGames.get(game.toLocaleLowerCase()); + // get game option + const targetGame = interaction.options.getString("game", true); + + // load tracker Games db + const db = trackerGames.get(targetGame.toLocaleLowerCase()); if (!db) { await interaction.reply(GAMENOENTRY); return; } + // get top 5 played games and make a string const mostPlayed = db.users .sort((a, b) => b.playtime - a.playtime) .slice(0, 5) .map((user) => `<@${user.id}>: ${makeTimeString(user.playtime)}`) .join("\n"); + // get top 5 logged games and make a string const mostLogged = db.users .sort((a, b) => b.logs - a.logs) .slice(0, 5) .map((user) => `<@${user.id}>: ${user.logs} logs`) .join("\n"); + // format latest logs into a string const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); + // get total playtime, logs and users const totalPlaytime = db.playtime; const totalLogs = db.logs; + const users = db.users.length; + // get first log of game (first log is iso string) const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + // calculate the range from first log to now const range = Date.now() - firstSeen; - const users = db.users.length; + // calculate daly/weekly/monthy and per-log average playtime const playtimePer = `day: ${makeTimeString( Math.round(totalPlaytime / (range / (86400 * 1000))) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( @@ -36,7 +46,9 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { )}\nuser: ${makeTimeString(Math.round(totalPlaytime / users))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) )}`; + // temporary sorted list of most playtime/log (users) const tmp = db.users.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; + // make most playtime/log string const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; @@ -48,7 +60,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { iconURL: config.iconURL, }) .setColor(config.color) - .setTitle(`Tracking stats about ${game}`) + .setTitle(`Tracking stats about ${targetGame}`) .setFooter({ text: `Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL({ size: 16 }), @@ -78,21 +90,31 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } export async function gameLast(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getString("game", true); - const db = trackerGames.get(target); + // get the target game + const targetGame = interaction.options.getString("game", true); + // load games db + const db = trackerGames.get(targetGame); if (!db) { await interaction.reply(GAMENOENTRY); return; } - const logs = db.lastlogs.slice(0, 5).reverse(); + // get latest logs + const logs = db.lastlogs.reverse(); + // store future embed fields const fields: APIEmbedField[] = []; + // make embed field for every log await logs.forEach(async (log) => { + // get and validate log (db.lastlogs is a list of numbers as strings, not the actual log) const entry = trackerLogs.get(log); if (!entry) return; + // get user who owns the log const user = await interaction.client.users.fetch(entry.userid); + // skip if user doesnt exist anymo if (!user) return; + + // make field fields.push({ inline: true, name: user.username, @@ -102,6 +124,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { }); }); + // add a last field so embed looks better formated fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() @@ -111,7 +134,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { url: config.githubURL, iconURL: config.iconURL, }) - .setTitle(`Latest logs of ${target}`) + .setTitle(`Latest logs of ${targetGame}`) .addFields(...fields) .setFooter({ text: `Requested by ${interaction.user.tag}`, @@ -122,28 +145,30 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } export async function gameTop(interaction: ChatInputCommandInteraction) { - const target = interaction.options.getString("game", true); - const db = trackerGames.get(target); + // get target game + const targetGame = interaction.options.getString("game", true); + // load games db + const db = trackerGames.get(targetGame); if (!db) { await interaction.reply(GAMENOENTRY); return; } + // get and validate filter const filter = interaction.options.getString("filter", true); - if (filter == "playtime") { - (""); - } else if (filter == "logs") { - (""); - } else { + if (!(filter == "playtime" || filter == "logs")) { interaction.reply(INVALIDFILTER); return; } - + // get users who played the game and sort them based of the filter and limit range to 0..5 const users = db.users .sort((a, b) => (filter == "playtime" ? b.playtime - a.playtime : b.logs - a.logs)) .splice(0, 5); + + // future embed fields const fields: APIEmbedField[] = []; + // add a field per user await users.forEach(async (user) => { fields.push({ inline: true, @@ -155,8 +180,9 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { }); }); + // add one final field for formating purposes fields.push({ inline: true, name: "_ _", value: "_ _" }); - config.botName; + const embed = new EmbedBuilder() .setColor(config.color) .setAuthor({ @@ -164,7 +190,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { url: config.githubURL, iconURL: config.iconURL, }) - .setTitle(`Top user (${filter}) by ${target}`) + .setTitle(`Top user (${filter}) by ${targetGame}`) .addFields(...fields) .setFooter({ text: `Requested by ${interaction.user.tag}`, diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index d6343fe..b34bf82 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -3,31 +3,36 @@ import { Duration } from "luxon"; import { trackerUsers } from "../../db"; import { durationToReadable } from "../misc/time"; +/** Uses `durationToReadable` from src/util/misc/time.ts but without the need to parse a Duration object */ export function makeTimeString(timeMS: number) { return durationToReadable(Duration.fromMillis(timeMS), true); } +/** Sorts users games after playtime of a user (most playtime at index 0)*/ export function sortGamesPlaytime(userId: string) { const data = trackerUsers.get(userId); return data?.games.sort((a, b) => b.playtime - a.playtime); } +/** Sorts users games after amount of lofs of a user (most logs at index 0) */ export function sortGamesLogs(userId: string) { const data = trackerUsers.get(userId); return data?.games.sort((a, b) => b.logs - a.logs); } +/** Make an error embed */ export function errorEmbed(title: string, info: string): InteractionReplyOptions { return { embeds: [ new EmbedBuilder() .setTitle(`ERROR: ${title}`) .setDescription(`further info:\n\`\`\`${info}\`\`\``) - .setColor([255, 0, 0]), + .setColor([0, 255, 0]), ], ephemeral: true, }; } +/** Make an success embe */ export function confirmEmbed(title: string): InteractionReplyOptions { return { - embeds: [new EmbedBuilder().setTitle(title).setColor([255, 0, 0])], + embeds: [new EmbedBuilder().setTitle(title).setColor([0, 255, 0])], ephemeral: true, }; } diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index 05d4c6c..034d8b0 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -1,23 +1,30 @@ import { confirmEmbed, errorEmbed } from "./helper"; +/** Used when a user cant be fetched by `interaction.options.getUser("user")` or `interaction.user` */ export const USERNOTFOUND = errorEmbed("user not found", "Cant get user option or interaction user."); +/** Used when `interaction.guild.members.get(userID)` does work with given user */ export const MEMBERNOTFOUND = errorEmbed( "member not found", "Cant get guild member, user may not be on this server anymore." ); +/** Used when the filter option is not "playtime" or "logs" */ export const INVALIDFILTER = errorEmbed("invalid filter", 'Given filter needs to be "logs" or "playtime"'); +/** Used when a given user is not found in the tracker database */ export const USERNOENTRY = errorEmbed("no database entry", "Given user has no entrys in the database."); +/** Used when a given game is not found in users tracker database entry */ export const USERNOGAMEENTRY = errorEmbed( "no database entry", "Given user has no entrys in the database associated with given the game." ); +//** Used when a given game is not found in the tracker database */ export const GAMENOENTRY = errorEmbed("no database entry", "Given game has no entry in the database."); +/** Used when a non admin user tryes to use a admin only command */ export const ADMINONLY = errorEmbed("admin only", "This command can only be executed by an admin."); +/** Used when there is an attempt to add a game to the blacklist while its already on there */ export const GAMEONBLACKLIST = errorEmbed("already on blacklist", "Given game is already on the blacklist."); +/** User when there is an attempt to remove a game from the blacklist whole its not on there */ export const GAMENOTBLACKLIST = errorEmbed("no on blacklist", "Given game is not on the blacklist."); -export const NOUSERANDGAMEGIVEN = errorEmbed( - "invalid options", - "game nor user where given. This command needs at lest one of both." -); +/** Used to confirm the adding of a given game to the blacklist */ export const GAMEADDED = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); +/** Used to confirm the removal of a given game from the blacklist */ export const GAMEREMOVED = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); diff --git a/src/util/tracker/presence.ts b/src/util/tracker/presence.ts index 7ac195d..84cd7a2 100644 --- a/src/util/tracker/presence.ts +++ b/src/util/tracker/presence.ts @@ -33,7 +33,7 @@ export async function getChangedActivities( export function isBlacklisted(gameName: string): boolean { return trackerBlacklist.get("")?.includes(gameName.toLowerCase()) || false; } - +/** Logs a new entry to the logs, user and games db */ export async function logIt(gameName: string, userID: string, timePlayed: number): Promise { gameName = gameName.toLowerCase(); if (isBlacklisted(gameName)) return; @@ -44,7 +44,7 @@ export async function logIt(gameName: string, userID: string, timePlayed: number updateGame(gameName, userID, timePlayed, logID); // update games db entry addLog(gameName, userID, timePlayed, logID); // make a db entry for this new log } - +/** Ensures that there is a entry in the user and game db before continuing*/ function ensure(gameName: string, userID: string, logID: string) { if (!trackerGames.has(gameName.toLowerCase())) { const data: TrackerGame = { @@ -67,7 +67,7 @@ function ensure(gameName: string, userID: string, logID: string) { trackerUsers.set(userID, data); } } - +/** updates the latest logs, logs, game and playtime of a user */ function updateUser(gameName: string, userID: string, timePlayed: number, logID: string) { gameName = gameName.toLowerCase(); @@ -91,7 +91,7 @@ function updateUser(gameName: string, userID: string, timePlayed: number, logID: trackerUsers.set(userID, data); } - +/** updates the latest logs, logs, users and playtime of a game*/ function updateGame(gameName: string, userID: string, timePlayed: number, logID: string) { gameName = gameName.toLowerCase(); @@ -114,7 +114,7 @@ function updateGame(gameName: string, userID: string, timePlayed: number, logID: trackerGames.set(gameName, data); } - +/** Make a new log */ function addLog(gameName: string, userID: string, timePlayed: number, logID: string) { const data: TrackerLog = { gameName: gameName, diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index c0ef314..9cfcef6 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -7,29 +7,35 @@ import { makeTimeString } from "./helper"; import { GAMENOENTRY, USERNOENTRY, USERNOGAMEENTRY } from "./messages"; export async function playtime(interaction: ChatInputCommandInteraction) { + // get target user and game const targetUser = interaction.options.getUser("user"); const targetGame = interaction.options.getString("game"); - let text = ""; + let text = ""; // used later in the final embed - if (!targetUser && !targetGame) { + // make matching text for each case + if (!targetUser && !targetGame) { // no user, no game let playtime = 0; + // count all played times of all games together trackerGames.array().forEach((game) => (playtime += game.playtime)); text = `The whole system has tracked ${makeTimeString(playtime)} of playtime`; - } else if (!targetUser && targetGame) { + } else if (!targetUser && targetGame) { // no user, game + // load db and get target game const db = trackerGames.get(targetGame.toLowerCase()); if (!db) { await interaction.reply(GAMENOENTRY); return; } text = `${targetGame} has ${makeTimeString(db.playtime)} of playtime`; - } else if (targetUser && !targetGame) { + } else if (targetUser && !targetGame) {// user, no game + // load db and get target user const db = trackerUsers.get(targetUser.id); if (!db) { await interaction.reply(USERNOENTRY); return; } text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime`; - } else if (targetUser && targetGame) { + } else if (targetUser && targetGame) { // user, game + // load db and get user.games and find target game in there const db = trackerUsers .get(targetUser.id) ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); @@ -39,6 +45,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { } text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime in ${targetGame}`; } else { + // shouldnt happen but just in case a return return; } @@ -47,29 +54,35 @@ export async function playtime(interaction: ChatInputCommandInteraction) { }); } export async function logs(interaction: ChatInputCommandInteraction) { + // get target user and game const targetUser = interaction.options.getUser("user"); const targetGame = interaction.options.getString("game"); - let text = ""; + let text = ""; // used later in the final embed - if (!targetUser && !targetGame) { + // make matching text for each case + if (!targetUser && !targetGame) { // no u ser, no game let logs = 0; + // count all logs of all games together trackerGames.array().forEach((game) => (logs += game.logs)); text = `The whole system has tracked ${logs} times`; - } else if (!targetUser && targetGame) { + } else if (!targetUser && targetGame) { // no user, game + // get target db game entry const db = trackerGames.get(targetGame.toLowerCase()); if (!db) { await interaction.reply(GAMENOENTRY); return; } text = `${targetGame} was played ${db.logs} times`; - } else if (targetUser && !targetGame) { + } else if (targetUser && !targetGame) { // user, no game + // get target db user entry const db = trackerUsers.get(targetUser.id); if (!db) { await interaction.reply(USERNOENTRY); return; } text = `${targetUser.username} was logged ${db.logs} times`; - } else if (targetUser && targetGame) { + } else if (targetUser && targetGame) { // user, game + // load db and get user.games and find target game in there const db = trackerUsers .get(targetUser.id) ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); @@ -87,9 +100,12 @@ export async function logs(interaction: ChatInputCommandInteraction) { }); } export async function latest(interaction: ChatInputCommandInteraction) { + // future embed fields const fields: APIEmbedField[] = []; - + + // latest system logs const logs = trackerLogs.array().slice(0, 5).reverse(); + // make embed for each log logs.forEach((log) => fields.push({ inline: true, @@ -102,6 +118,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { }) ); + // add empty field for better formating fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() @@ -112,6 +129,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } export async function stats(interaction: ChatInputCommandInteraction) { + // get 5 most logged games and make string const mostLoggedGame = trackerGames .array() .sort((a, b) => a.logs - b.logs) @@ -119,6 +137,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) .join("\n"); + // get 5 most played games and make string const mostPlayedGame = trackerGames .array() .sort((a, b) => a.playtime - b.playtime) @@ -126,6 +145,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) .join("\n"); + // get 5 most logged users and make string const mostLoggedUser = trackerUsers .array() .sort((a, b) => a.logs - b.logs) @@ -133,6 +153,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .splice(0, 5) .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) .join("\n"); + // get 5 most playtime users and make string const mostPlayedUser = trackerUsers .array() .sort((a, b) => a.playtime - b.playtime) @@ -140,6 +161,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .splice(0, 5) .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) .join("\n"); + // get latest system wide lgos and make string const latestLogs = trackerLogs .array() .reverse() @@ -151,17 +173,22 @@ export async function stats(interaction: ChatInputCommandInteraction) { )}:t> <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` ) .join("\n"); + // get total playtime of all games const totalPlaytime = trackerGames .array() .map((game) => game.playtime) .reduce((partialSum, a) => partialSum + a, 0); - const totalLogs = trackerGames - .array() - .map((game) => game.logs) - .reduce((partialSum, a) => partialSum + a, 0); + // get amount of logs + const totalLogs = trackerLogs.count + // get amount of games + const games = trackerGames.count; + // get amout of users + const users = trackerUsers.count + // get first log (time is iso string) const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); + // make range from first log to now const range = Date.now() - firstSeen; - const games = trackerGames.count; + // calculate average playtime per day/week/month/game/log const playtimePer = `day: ${makeTimeString( Math.round(totalPlaytime / (range / (86400 * 1000))) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( @@ -169,7 +196,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) )}`; + // temporary sorted list based on playtime/log const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; + // make string from temporary list const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString( tmp.playtime / tmp.logs )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; @@ -203,7 +232,10 @@ export async function stats(interaction: ChatInputCommandInteraction) { }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total games", value: games.toString()}, + { inline: true, name: "Total users", value: users.toString()} ); await interaction.reply({ embeds: [embed] }); diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index f3cd9cf..462773c 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -6,38 +6,48 @@ import { makeTimeString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { INVALIDFILTER, MEMBERNOTFOUND, USERNOENTRY, USERNOTFOUND } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { + // get target user and default to command executer if not given const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { await interaction.reply(USERNOTFOUND); return; } + // get the member (to use their display name and color) const member = await interaction.guild?.members.fetch(target.id); if (!member) { await interaction.reply(MEMBERNOTFOUND); return; } + // load db and get taret user const db = trackerUsers.get(target.id); if (!db) { await interaction.reply(USERNOENTRY); return; } + // make sorted list of most played games and make string const mostPlayed = db.games .sort((a, b) => b.playtime - a.playtime) .slice(0, 5) .map((game) => `${game.id}: ${makeTimeString(game.playtime)}`) .join("\n"); + // make sorted list of most logged games and make string const mostLogged = db.games .sort((a, b) => b.logs - a.logs) .slice(0, 5) .map((game) => `${game.id}: ${game.logs} logs`) .join("\n"); + // get latest logs and make string const latestLogs = db.lastlogs.map((log) => `${trackerLogs.get(log)?.gameName}`).join("\n"); + // get total users playtime, logs and games const totalPlaytime = db.playtime; + const games = db.games.length; const totalLogs = db.logs; + // get first log (time is iso string) const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + // make range from first log to now const range = Date.now() - firstSeen; - const games = db.games.length; + // calculate average paytime per day/week/month/user/log const playtimePer = `day: ${makeTimeString( Math.round(totalPlaytime / (range / (86400 * 1000))) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( @@ -45,7 +55,9 @@ export async function userStats(interaction: ChatInputCommandInteraction) { )}\nuser: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) )}`; + // temporary sorted list of games playtime/log const tmp = db.games.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; + // make string from first element of temporary list const mostPlaytime = `${tmp.id}: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; @@ -71,6 +83,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "(Average) playtime per", value: playtimePer }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: true, name:"Total played games", value:games.toString()}, { inline: false, name: "_ _", value: "_ _" }, { inline: true, @@ -87,25 +100,32 @@ export async function userStats(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } export async function userLast(interaction: ChatInputCommandInteraction) { + // get target user, if not given default to executer const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { await interaction.reply(USERNOTFOUND); return; } + // get member from target const member = await interaction.guild?.members.fetch(target.id); if (member == undefined) { await interaction.reply(MEMBERNOTFOUND); return; } + // get targets db entry const db = trackerUsers.get(member.id); if (!db) { await interaction.reply(USERNOENTRY); return; } + // get latest logs const logs = db.lastlogs.reverse(); + // future embed fields const fields: APIEmbedField[] = []; + // make field for every log logs.forEach((log) => { + // get log entry (latest logs is list of number as string, not the actual log data) const entry = trackerLogs.get(log); if (!entry) return; fields.push({ @@ -116,7 +136,8 @@ export async function userLast(interaction: ChatInputCommandInteraction) { )}:t>\nplayed time: ${makeTimeString(entry.playtime)}`, }); }); - + + // add extra field for better formating fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() @@ -132,19 +153,22 @@ export async function userLast(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } export async function userTop(interaction: ChatInputCommandInteraction) { + // get target user, default to executer if not given const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { await interaction.reply(USERNOTFOUND); return; } + // get the member from target const member = await interaction.guild?.members.fetch(target.id); if (member == undefined) { await interaction.reply(MEMBERNOTFOUND); return; } - + // get filter const filter = interaction.options.getString("filter", true); + // load sorted list based on filter let games: TrackerSublog[] | undefined = []; if (filter == "logs") { games = sortGamesLogs(member.id); @@ -155,14 +179,18 @@ export async function userTop(interaction: ChatInputCommandInteraction) { return; } + // return if no games are being listed if (!games) { await interaction.reply(USERNOENTRY); return; } + // limit list to a range from 0 to 5 games = games.slice(0, 5); + // future embed fields const fields: APIEmbedField[] = []; + // make field for every game based on filter games.forEach((game) => { fields.push({ inline: true, @@ -174,6 +202,7 @@ export async function userTop(interaction: ChatInputCommandInteraction) { }); }); + // add one extra field for better formating fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() From 288b4d520c4e94fd88f961dbd583b5f439f09257 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 20:17:03 +0200 Subject: [PATCH 15/92] prettify --- src/util/tracker/blacklistCommands.ts | 2 +- src/util/tracker/gameCommands.ts | 6 ++--- src/util/tracker/subCommands.ts | 36 ++++++++++++++++----------- src/util/tracker/userCommands.ts | 6 ++--- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts index 09982f7..7df5153 100644 --- a/src/util/tracker/blacklistCommands.ts +++ b/src/util/tracker/blacklistCommands.ts @@ -27,7 +27,7 @@ export async function remBlacklist(interaction: ChatInputCommandInteraction) { interaction.reply(ADMINONLY); return; } - + // get game option const game = interaction.options.getString("game", true); const db = trackerBlacklist.get(""); diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index b6490c1..61ce867 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -8,7 +8,7 @@ import { GAMENOENTRY, INVALIDFILTER } from "./messages"; export async function gameStats(interaction: ChatInputCommandInteraction) { // get game option const targetGame = interaction.options.getString("game", true); - + // load tracker Games db const db = trackerGames.get(targetGame.toLocaleLowerCase()); if (!db) { @@ -164,7 +164,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { const users = db.users .sort((a, b) => (filter == "playtime" ? b.playtime - a.playtime : b.logs - a.logs)) .splice(0, 5); - + // future embed fields const fields: APIEmbedField[] = []; @@ -182,7 +182,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { // add one final field for formating purposes fields.push({ inline: true, name: "_ _", value: "_ _" }); - + const embed = new EmbedBuilder() .setColor(config.color) .setAuthor({ diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 9cfcef6..b10c1e2 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -13,12 +13,14 @@ export async function playtime(interaction: ChatInputCommandInteraction) { let text = ""; // used later in the final embed // make matching text for each case - if (!targetUser && !targetGame) { // no user, no game + if (!targetUser && !targetGame) { + // no user, no game let playtime = 0; // count all played times of all games together trackerGames.array().forEach((game) => (playtime += game.playtime)); text = `The whole system has tracked ${makeTimeString(playtime)} of playtime`; - } else if (!targetUser && targetGame) { // no user, game + } else if (!targetUser && targetGame) { + // no user, game // load db and get target game const db = trackerGames.get(targetGame.toLowerCase()); if (!db) { @@ -26,7 +28,8 @@ export async function playtime(interaction: ChatInputCommandInteraction) { return; } text = `${targetGame} has ${makeTimeString(db.playtime)} of playtime`; - } else if (targetUser && !targetGame) {// user, no game + } else if (targetUser && !targetGame) { + // user, no game // load db and get target user const db = trackerUsers.get(targetUser.id); if (!db) { @@ -34,7 +37,8 @@ export async function playtime(interaction: ChatInputCommandInteraction) { return; } text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime`; - } else if (targetUser && targetGame) { // user, game + } else if (targetUser && targetGame) { + // user, game // load db and get user.games and find target game in there const db = trackerUsers .get(targetUser.id) @@ -60,12 +64,14 @@ export async function logs(interaction: ChatInputCommandInteraction) { let text = ""; // used later in the final embed // make matching text for each case - if (!targetUser && !targetGame) { // no u ser, no game + if (!targetUser && !targetGame) { + // no u ser, no game let logs = 0; // count all logs of all games together trackerGames.array().forEach((game) => (logs += game.logs)); text = `The whole system has tracked ${logs} times`; - } else if (!targetUser && targetGame) { // no user, game + } else if (!targetUser && targetGame) { + // no user, game // get target db game entry const db = trackerGames.get(targetGame.toLowerCase()); if (!db) { @@ -73,7 +79,8 @@ export async function logs(interaction: ChatInputCommandInteraction) { return; } text = `${targetGame} was played ${db.logs} times`; - } else if (targetUser && !targetGame) { // user, no game + } else if (targetUser && !targetGame) { + // user, no game // get target db user entry const db = trackerUsers.get(targetUser.id); if (!db) { @@ -81,7 +88,8 @@ export async function logs(interaction: ChatInputCommandInteraction) { return; } text = `${targetUser.username} was logged ${db.logs} times`; - } else if (targetUser && targetGame) { // user, game + } else if (targetUser && targetGame) { + // user, game // load db and get user.games and find target game in there const db = trackerUsers .get(targetUser.id) @@ -102,7 +110,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { export async function latest(interaction: ChatInputCommandInteraction) { // future embed fields const fields: APIEmbedField[] = []; - + // latest system logs const logs = trackerLogs.array().slice(0, 5).reverse(); // make embed for each log @@ -179,12 +187,12 @@ export async function stats(interaction: ChatInputCommandInteraction) { .map((game) => game.playtime) .reduce((partialSum, a) => partialSum + a, 0); // get amount of logs - const totalLogs = trackerLogs.count + const totalLogs = trackerLogs.count; // get amount of games const games = trackerGames.count; // get amout of users - const users = trackerUsers.count - // get first log (time is iso string) + const users = trackerUsers.count; + // get first log (time is iso string) const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); // make range from first log to now const range = Date.now() - firstSeen; @@ -234,8 +242,8 @@ export async function stats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Total logs", value: totalLogs.toString() }, { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total games", value: games.toString()}, - { inline: true, name: "Total users", value: users.toString()} + { inline: true, name: "Total games", value: games.toString() }, + { inline: true, name: "Total users", value: users.toString() } ); await interaction.reply({ embeds: [embed] }); diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 462773c..45897f3 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -83,7 +83,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "(Average) playtime per", value: playtimePer }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: true, name:"Total played games", value:games.toString()}, + { inline: true, name: "Total played games", value: games.toString() }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, @@ -136,7 +136,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { )}:t>\nplayed time: ${makeTimeString(entry.playtime)}`, }); }); - + // add extra field for better formating fields.push({ inline: true, name: "_ _", value: "_ _" }); @@ -159,7 +159,7 @@ export async function userTop(interaction: ChatInputCommandInteraction) { await interaction.reply(USERNOTFOUND); return; } - // get the member from target + // get the member from target const member = await interaction.guild?.members.fetch(target.id); if (member == undefined) { await interaction.reply(MEMBERNOTFOUND); From 0017172a679142961a1aec3e42bbb19cf88810ee Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 20:26:30 +0200 Subject: [PATCH 16/92] spelling... xd --- src/commands/TrackerCommands.ts | 2 +- src/util/tracker/gameCommands.ts | 10 +++++----- src/util/tracker/helper.ts | 4 ++-- src/util/tracker/messages.ts | 2 +- src/util/tracker/subCommands.ts | 8 ++++---- src/util/tracker/userCommands.ts | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 235b85c..a11e231 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -60,7 +60,7 @@ class Tracker extends Command { register(): SlashCommandSubcommandsOnlyBuilder { return new SlashCommandBuilder() .setName("tracker") - .setDescription("The gameway to some cool stats about here being users") + .setDescription("The gateway to some cool stats about here being users") .addSubcommandGroup((group) => group .setName("user") diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 61ce867..3544c36 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -38,7 +38,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); // calculate the range from first log to now const range = Date.now() - firstSeen; - // calculate daly/weekly/monthy and per-log average playtime + // calculate daily/weekly/monthly and per-log average playtime const playtimePer = `day: ${makeTimeString( Math.round(totalPlaytime / (range / (86400 * 1000))) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( @@ -67,7 +67,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { }) .setTimestamp(Date.now()) .addFields( - { inline: true, name: "Most logged user", value: mostLogged }, + { inline: true, name: "Most logged users", value: mostLogged }, { inline: true, name: "User with most playtime", value: mostPlayed }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Latest logs", value: latestLogs }, @@ -111,7 +111,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { if (!entry) return; // get user who owns the log const user = await interaction.client.users.fetch(entry.userid); - // skip if user doesnt exist anymo + // skip if user doesnt exist anymore if (!user) return; // make field @@ -124,7 +124,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { }); }); - // add a last field so embed looks better formated + // add a last field so embed looks better formatted fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() @@ -180,7 +180,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { }); }); - // add one final field for formating purposes + // add one final field for formatting purposes fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index b34bf82..d40aaad 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -12,7 +12,7 @@ export function sortGamesPlaytime(userId: string) { const data = trackerUsers.get(userId); return data?.games.sort((a, b) => b.playtime - a.playtime); } -/** Sorts users games after amount of lofs of a user (most logs at index 0) */ +/** Sorts users games after amount of logs of a user (most logs at index 0) */ export function sortGamesLogs(userId: string) { const data = trackerUsers.get(userId); return data?.games.sort((a, b) => b.logs - a.logs); @@ -29,7 +29,7 @@ export function errorEmbed(title: string, info: string): InteractionReplyOptions ephemeral: true, }; } -/** Make an success embe */ +/** Make an success embed */ export function confirmEmbed(title: string): InteractionReplyOptions { return { embeds: [new EmbedBuilder().setTitle(title).setColor([0, 255, 0])], diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index 034d8b0..c1b52d9 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -10,7 +10,7 @@ export const MEMBERNOTFOUND = errorEmbed( /** Used when the filter option is not "playtime" or "logs" */ export const INVALIDFILTER = errorEmbed("invalid filter", 'Given filter needs to be "logs" or "playtime"'); /** Used when a given user is not found in the tracker database */ -export const USERNOENTRY = errorEmbed("no database entry", "Given user has no entrys in the database."); +export const USERNOENTRY = errorEmbed("no database entry", "Given user has no entries in the database."); /** Used when a given game is not found in users tracker database entry */ export const USERNOGAMEENTRY = errorEmbed( "no database entry", diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index b10c1e2..8bc4b00 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -49,7 +49,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { } text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime in ${targetGame}`; } else { - // shouldnt happen but just in case a return + // shouldn't happen but just in case a return return; } @@ -126,7 +126,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { }) ); - // add empty field for better formating + // add empty field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() @@ -169,7 +169,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .splice(0, 5) .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) .join("\n"); - // get latest system wide lgos and make string + // get latest system wide logs and make string const latestLogs = trackerLogs .array() .reverse() @@ -190,7 +190,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { const totalLogs = trackerLogs.count; // get amount of games const games = trackerGames.count; - // get amout of users + // get amount of users const users = trackerUsers.count; // get first log (time is iso string) const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 45897f3..5fdb5c6 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -18,7 +18,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { await interaction.reply(MEMBERNOTFOUND); return; } - // load db and get taret user + // load db and get target user const db = trackerUsers.get(target.id); if (!db) { await interaction.reply(USERNOENTRY); @@ -137,7 +137,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { }); }); - // add extra field for better formating + // add extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() @@ -202,7 +202,7 @@ export async function userTop(interaction: ChatInputCommandInteraction) { }); }); - // add one extra field for better formating + // add one extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = new EmbedBuilder() From 48e0f273e3de6083bb3e2b026689367bff0c94f8 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 13 Apr 2023 20:45:19 +0200 Subject: [PATCH 17/92] Found two little things to fix, else i didnt find anything wrong --- src/events/presenceUpdate.ts | 2 -- src/interactions/autocompleters/TrackerAutocomple.ts | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index acd7675..ef2bdfc 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -24,8 +24,6 @@ export default async function presenceUpdate(oldPresence: Presence | null, newPr // Played time in secods ("~~" makes float to int) const timePlayed = ~~(Date.now() - start); - // filter out logs under 20 seconds - //if (timePlayed < 20) return; await logIt(activity.name, userID, timePlayed); }); diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index ddd2de4..ffeee65 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -2,7 +2,7 @@ import { Autocompleter } from "../interactionClasses"; import { AutocompleteInteraction } from "discord.js"; import { trackerGames } from "../../db"; -class CountryAutocompleter extends Autocompleter { +class Tracker extends Autocompleter { constructor() { super("tracker"); } @@ -19,4 +19,4 @@ class CountryAutocompleter extends Autocompleter { } } -export default new CountryAutocompleter(); +export default new Tracker(); From 1f3bc93c07ed29e86e74cec12cf73b647ca0cef9 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 15 Apr 2023 14:35:18 +0200 Subject: [PATCH 18/92] removed Footer and Timestamp, fixed and added two embed strings --- src/util/tracker/gameCommands.ts | 19 ++----------------- src/util/tracker/subCommands.ts | 11 +++-------- src/util/tracker/userCommands.ts | 7 +------ 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 3544c36..1beda11 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -61,11 +61,6 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { }) .setColor(config.color) .setTitle(`Tracking stats about ${targetGame}`) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()) .addFields( { inline: true, name: "Most logged users", value: mostLogged }, { inline: true, name: "User with most playtime", value: mostPlayed }, @@ -135,12 +130,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { iconURL: config.iconURL, }) .setTitle(`Latest logs of ${targetGame}`) - .addFields(...fields) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()); + .addFields(...fields); await interaction.reply({ embeds: [embed] }); } @@ -191,12 +181,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { iconURL: config.iconURL, }) .setTitle(`Top user (${filter}) by ${targetGame}`) - .addFields(...fields) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()); + .addFields(...fields); await interaction.reply({ embeds: [embed] }); } diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 8bc4b00..c29632a 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -201,9 +201,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { Math.round(totalPlaytime / (range / (86400 * 1000))) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( Math.round(totalPlaytime / (range / 2628000000)) - )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( - Math.round(totalPlaytime / totalLogs) - )}`; + )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nuser: ${makeTimeString( + Math.round(totalPlaytime / users) + )}log: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; // temporary sorted list based on playtime/log const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; // make string from temporary list @@ -214,11 +214,6 @@ export async function stats(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setTitle("System stats") .setColor(config.color) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()) .addFields( { inline: true, name: "Most logged games", value: mostLoggedGame }, { inline: true, name: "Most played games", value: mostPlayedGame }, diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 5fdb5c6..400db54 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -52,7 +52,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { Math.round(totalPlaytime / (range / (86400 * 1000))) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( Math.round(totalPlaytime / (range / 2628000000)) - )}\nuser: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( + )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) )}`; // temporary sorted list of games playtime/log @@ -70,11 +70,6 @@ export async function userStats(interaction: ChatInputCommandInteraction) { }) .setColor(member.displayColor) .setTitle(`Tracking stats about ${member.displayName}`) - .setFooter({ - text: `Requested by ${interaction.user.tag}`, - iconURL: interaction.user.displayAvatarURL({ size: 16 }), - }) - .setTimestamp(Date.now()) .addFields( { inline: true, name: "Most logged game", value: mostLogged }, { inline: true, name: "Game with most playtime", value: mostPlayed }, From 7434643609560f1f0a99532a058dbfca23280aa9 Mon Sep 17 00:00:00 2001 From: Floschy <69321390+flloschy@users.noreply.github.com> Date: Thu, 20 Apr 2023 19:31:51 +0200 Subject: [PATCH 19/92] Cleaner Logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Murphy Sünnenwold --- src/util/tracker/gameCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 1beda11..7c6f187 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -113,9 +113,9 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: user.username, - value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, + )}:t>\n${makeTimeString(entry.playtime)}`, }); }); From 1e0cc94a25cfc8ece059459e005ebff3f551d770 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:02:59 +0200 Subject: [PATCH 20/92] compacted commands --- src/commands/TrackerCommands.ts | 277 ++++++++++++++----------------- src/util/tracker/gameCommands.ts | 10 +- src/util/tracker/userCommands.ts | 11 +- 3 files changed, 131 insertions(+), 167 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index a11e231..37369d2 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -1,6 +1,7 @@ import { Command } from "../interactions/interactionClasses"; import { ChatInputCommandInteraction, + EmbedBuilder, SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, } from "discord.js"; @@ -14,182 +15,154 @@ class Tracker extends Command { } async execute(interaction: ChatInputCommandInteraction): Promise { - const group = interaction.options.getSubcommandGroup(); - const sub = interaction.options.getSubcommand(); + const sub = interaction.options.getSubcommand() + const gam = interaction.options.getString('game') + const sta = interaction.options.getString('statistic') ? interaction.options.getString('statistic') : "stats" + const act = interaction.options.getString("action") + let exectued = false - switch (group + sub) { - case "userstats": - await userStats(interaction); - return; - case "userlast": - await userLast(interaction); - return; - case "usertop": - await userTop(interaction); - return; - case "gamestats": - await gameStats(interaction); - return; - case "gamelast": - await gameLast(interaction); - return; - case "gametop": - await gameTop(interaction); - return; - case "blacklistadd": - await addBlacklist(interaction); - return; - case "blacklistremove": - await remBlacklist(interaction); - return; - case "nullplaytime": - await playtime(interaction); - return; - case "nulllogs": - await logs(interaction); - return; - case "nulllast": - await latest(interaction); - return; - case "nullstats": - await stats(interaction); - return; + switch (sub) { + case "user": + if (sta == "playtime" && gam) { + await playtime(interaction) + exectued = true + } else if (sta == "logs" && gam) { + await logs(interaction) + exectued = true + } else if (sta == "general statistics") { + await userStats(interaction) + exectued = true + } else if (sta == "top 5 most played games") { + await userTop(interaction, "playtime") + exectued = true + } else if (sta == "top 5 most logged games") { + await userTop(interaction, "logs") + exectued = true + } else if (sta == "Latest 5 logs") { + await userLast(interaction) + exectued = true + } + return + case "game": + if (sta == "general statistics") { + await gameStats(interaction) + exectued = true + } else if (sta == "top 5 most played games") { + await gameTop(interaction, "playtime") + exectued = true + } else if (sta == "top 5 most logged games") { + await gameTop(interaction, "logs") + exectued = true + } else if (sta == "Latest 5 logs") { + await gameLast(interaction) + exectued = true + } + return + case "blacklist": + if (act == "add") { + await addBlacklist(interaction) + exectued = true + } else if (act == "rem") { + await remBlacklist(interaction) + exectued = true + } + return + case "latest": + await latest(interaction) + exectued = true + return + case "statistics": + await stats(interaction) + exectued = true + return } + + if (!exectued) { + await interaction.reply({embeds: [ + new EmbedBuilder() + .setTitle("invalid options") + .setDescription("this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.") + .setColor([255, 255, 0]) + ]}) + } + } register(): SlashCommandSubcommandsOnlyBuilder { return new SlashCommandBuilder() .setName("tracker") .setDescription("The gateway to some cool stats about here being users") - .addSubcommandGroup((group) => - group + .addSubcommand((sub) => + sub .setName("user") - .setDescription("Get the Tracking data about a certain user") - .addSubcommand((sub) => - sub - .setName("stats") - .setDescription("See some interesting statistics about a user") - .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) - ) - .addSubcommand((sub) => - sub - .setName("last") - .setDescription("Take a look at the latest logs of a user") - .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) + .setDescription("Get tracking stats about a user") + .addUserOption((opt) => + opt + .setName("user") + .setDescription("the user") + ) + .addStringOption((opt) => + opt + .setName("game") + .setDescription("the game") + .setAutocomplete(true) ) - .addSubcommand((sub) => - sub - .setName("top") - .setDescription("Lists you the most logged or played game by a user") - .addStringOption((opt) => - opt - .setName("filter") - .setDescription("logs or playtime?") - .addChoices({ name: "logs", value: "logs" }, { name: "playtime", value: "playtime" }) - .setRequired(true) - ) - .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) + .addStringOption((opt) => + opt + .setName("statistic") + .setDescription("ye") + .setAutocomplete(true) ) ) - .addSubcommandGroup((group) => - group + .addSubcommand((sub) => + sub .setName("game") - .setDescription("Get the tracking data about a certain game") - .addSubcommand((sub) => - sub - .setName("stats") - .setDescription("See some interesting statistics about a game") - .addStringOption((game) => - game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) + .setDescription("Get tracking stats about a game") + .addStringOption((opt) => + opt + .setName("game") + .setDescription("the game") + .setRequired(true) + .setAutocomplete(true) ) - .addSubcommand((sub) => - sub - .setName("last") - .setDescription("Take a look at the latest logs of a game") - .addStringOption((game) => - game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - ) - .addSubcommand((sub) => - sub - .setName("top") - .setDescription("Lists you the most logged or played game by a game") - .addStringOption((game) => - game - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - .addStringOption((opt) => - opt - .setName("filter") - .setDescription("logs or playtime?") - .addChoices({ name: "logs", value: "logs" }, { name: "playtime", value: "playtime" }) - .setRequired(true) + .addStringOption((opt) => + opt + .setName("statistic") + .setDescription("ye") + .addChoices( + {name:"general statistics", value:"general statistics"}, + {name:"top 5 most played games", value:"top 5 most played games"}, + {name:"top 5 most logged games", value:"top 5 most logged games"}, + {name:"Latest 5 logs", value:"Latest 5 logs"} ) ) ) - .addSubcommandGroup((group) => - group + .addSubcommand((sub) => + sub .setName("blacklist") - .setDescription("edit the playlist (ADMIN ONLY)") - .addSubcommand((sub) => - sub - .setName("add") - .setDescription("choose a game which shouldn't get logged anymore") - .addStringOption((sub) => - sub - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) - ) - ) - .addSubcommand((sub) => - sub - .setName("remove") - .setDescription("choose a game which should get logged again") - .addStringOption((sub) => - sub - .setName("game") - .setDescription("go figure: the game") - .setAutocomplete(true) - .setRequired(true) + .setDescription("ADMIN ONLY") + .addStringOption((opt) => + opt + .setName("action") + .setDescription("yo") + .addChoices( + {name:"add", value:"add"}, + {name:"remove", value:"rem"} ) + .setRequired(true) ) - ) - .addSubcommand((sub) => - sub - .setName("playtime") - .setDescription("how may hours a game got played by a user") - .addStringOption((game) => - game.setName("game").setDescription("go figure: the game").setAutocomplete(true) - ) - .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) - ) - .addSubcommand((sub) => - sub - .setName("logs") - .setDescription("See how many logs a user has in a given game") - .addStringOption((game) => - game.setName("game").setDescription("go figure: the game").setAutocomplete(true) + .addStringOption((opt) => + opt + .setName("game") + .setDescription("the game") + .setRequired(true) + .setAutocomplete(true) ) - .addUserOption((usr) => usr.setName("user").setDescription("go figure: the user")) ) .addSubcommand((sub) => - sub.setName("last").setDescription("See the latest logs across the whole system") + sub.setName("latest").setDescription("See the latest logs across the whole system") ) - .addSubcommand((sub) => sub.setName("stats").setDescription("See some system stats")); + .addSubcommand((sub) => sub.setName("statistics").setDescription("See some system stats")); } } diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 7c6f187..011cb73 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -3,7 +3,7 @@ import { config } from "../../config"; import { trackerGames, trackerLogs } from "../../db"; import { discordTimestamp } from "../misc/time"; import { makeTimeString } from "./helper"; -import { GAMENOENTRY, INVALIDFILTER } from "./messages"; +import { GAMENOENTRY } from "./messages"; export async function gameStats(interaction: ChatInputCommandInteraction) { // get game option @@ -134,7 +134,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } -export async function gameTop(interaction: ChatInputCommandInteraction) { +export async function gameTop(interaction: ChatInputCommandInteraction, filter: string) { // get target game const targetGame = interaction.options.getString("game", true); // load games db @@ -144,12 +144,6 @@ export async function gameTop(interaction: ChatInputCommandInteraction) { return; } - // get and validate filter - const filter = interaction.options.getString("filter", true); - if (!(filter == "playtime" || filter == "logs")) { - interaction.reply(INVALIDFILTER); - return; - } // get users who played the game and sort them based of the filter and limit range to 0..5 const users = db.users .sort((a, b) => (filter == "playtime" ? b.playtime - a.playtime : b.logs - a.logs)) diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 400db54..1554d7c 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -3,7 +3,7 @@ import { config } from "../../config"; import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; import { discordTimestamp } from "../misc/time"; import { makeTimeString, sortGamesLogs, sortGamesPlaytime } from "./helper"; -import { INVALIDFILTER, MEMBERNOTFOUND, USERNOENTRY, USERNOTFOUND } from "./messages"; +import { MEMBERNOTFOUND, USERNOENTRY, USERNOTFOUND } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { // get target user and default to command executer if not given @@ -126,9 +126,9 @@ export async function userLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: entry.gameName, - value: `time: \nplayed time: ${makeTimeString(entry.playtime)}`, + )}:t>\n${makeTimeString(entry.playtime)}`, }); }); @@ -147,7 +147,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { await interaction.reply({ embeds: [embed] }); } -export async function userTop(interaction: ChatInputCommandInteraction) { +export async function userTop(interaction: ChatInputCommandInteraction, filter: string) { // get target user, default to executer if not given const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { @@ -160,8 +160,6 @@ export async function userTop(interaction: ChatInputCommandInteraction) { await interaction.reply(MEMBERNOTFOUND); return; } - // get filter - const filter = interaction.options.getString("filter", true); // load sorted list based on filter let games: TrackerSublog[] | undefined = []; @@ -170,7 +168,6 @@ export async function userTop(interaction: ChatInputCommandInteraction) { } else if (filter == "playtime") { games = sortGamesPlaytime(member.id); } else { - await interaction.reply(INVALIDFILTER); return; } From 973db52f65d3e3b5c0ee460023e488c853927e70 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:04:01 +0200 Subject: [PATCH 21/92] adaptive autocomplete (a bit to many nested if's, but works) --- .../autocompleters/TrackerAutocomple.ts | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index ffeee65..f0854b7 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -1,16 +1,63 @@ import { Autocompleter } from "../interactionClasses"; import { AutocompleteInteraction } from "discord.js"; -import { trackerGames } from "../../db"; - +import { trackerBlacklist, trackerGames, trackerUsers } from "../../db"; class Tracker extends Autocompleter { constructor() { super("tracker"); } async execute(interaction: AutocompleteInteraction): Promise { - const games = Array.from(trackerGames.keys()); + const sub = interaction.options.getSubcommand(); const focus = interaction.options.getFocused().toLowerCase() as string; - let filteredOptions = games.filter((g) => g.toLowerCase().startsWith(focus)); + const option = interaction.options.getFocused(true).name; + const user = + typeof interaction.options.get("user")?.value == "string" + ? interaction.options.get("user")?.value + : interaction.user.id; + const game = interaction.options.get("game")?.value; + const action = + typeof interaction.options.get("action")?.value == "string" + ? interaction.options.get("action")?.value + : "add"; + const options: string[] = []; + + if (sub == "user") { + if (option == "game") { + if (typeof user == "string") { + if (trackerUsers.get(user)?.games) { + trackerUsers.get(user)?.games.forEach((g) => options.push(g.id)); + } else { + options.push("USER HAS NOT PLAYED ANY GAMES YET"); + } + } else { + options.push(...trackerGames.keyArray()); + } + } else if (option == "statistic") { + if (typeof game == "string") { + options.push(...["playtime", "logs"]); + } else { + options.push( + ...["general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"] + ); + } + } + } else if (sub == "game") { + trackerGames.keyArray().forEach((e) => options.push(e)); + } else if (sub == "blacklist") { + if (action == "add") { + options.push( + ...trackerGames.keyArray().filter((e) => !trackerBlacklist.get("")?.find((e2) => e2 == e)) + ); + } else if (action == "rem") { + options.push( + ...trackerGames.keyArray().filter((e) => !!trackerBlacklist.get("")?.find((e2) => e2 == e)) + ); + } else { + options.push(...trackerGames.keyArray()); + } + } + + let filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)); if (filteredOptions.length > 25) filteredOptions = filteredOptions.slice(0, 25); const map = filteredOptions.map((c) => ({ name: c, value: c })); From 8f7e56609f8cff848846cba617c7afd17c24a055 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:04:45 +0200 Subject: [PATCH 22/92] default to executer.id and small line break fix --- src/util/tracker/subCommands.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index c29632a..efd9c8a 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -59,7 +59,9 @@ export async function playtime(interaction: ChatInputCommandInteraction) { } export async function logs(interaction: ChatInputCommandInteraction) { // get target user and game - const targetUser = interaction.options.getUser("user"); + const targetUser = interaction.options.getUser("user") + ? interaction.options.getUser("user") + : interaction.user; const targetGame = interaction.options.getString("game"); let text = ""; // used later in the final embed @@ -203,12 +205,12 @@ export async function stats(interaction: ChatInputCommandInteraction) { Math.round(totalPlaytime / (range / 2628000000)) )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nuser: ${makeTimeString( Math.round(totalPlaytime / users) - )}log: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; + )}\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; // temporary sorted list based on playtime/log const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; // make string from temporary list const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString( - tmp.playtime / tmp.logs + Math.round(tmp.playtime / tmp.logs) )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; const embed = new EmbedBuilder() From 02206c98e7703c8efaa98ed227be0dd9ce4ff7fa Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:05:27 +0200 Subject: [PATCH 23/92] invalid filter is not needed anymore --- src/util/tracker/messages.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index c1b52d9..4e9c55f 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -7,8 +7,6 @@ export const MEMBERNOTFOUND = errorEmbed( "member not found", "Cant get guild member, user may not be on this server anymore." ); -/** Used when the filter option is not "playtime" or "logs" */ -export const INVALIDFILTER = errorEmbed("invalid filter", 'Given filter needs to be "logs" or "playtime"'); /** Used when a given user is not found in the tracker database */ export const USERNOENTRY = errorEmbed("no database entry", "Given user has no entries in the database."); /** Used when a given game is not found in users tracker database entry */ @@ -23,7 +21,7 @@ export const ADMINONLY = errorEmbed("admin only", "This command can only be exec /** Used when there is an attempt to add a game to the blacklist while its already on there */ export const GAMEONBLACKLIST = errorEmbed("already on blacklist", "Given game is already on the blacklist."); /** User when there is an attempt to remove a game from the blacklist whole its not on there */ -export const GAMENOTBLACKLIST = errorEmbed("no on blacklist", "Given game is not on the blacklist."); +export const GAMENOTBLACKLIST = errorEmbed("not on blacklist", "Given game is not on the blacklist."); /** Used to confirm the adding of a given game to the blacklist */ export const GAMEADDED = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); /** Used to confirm the removal of a given game from the blacklist */ From 4aab6d902316023848085d9093c347ddbedba499 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:06:10 +0200 Subject: [PATCH 24/92] color fix --- src/util/tracker/helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index d40aaad..428d27f 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -24,7 +24,7 @@ export function errorEmbed(title: string, info: string): InteractionReplyOptions new EmbedBuilder() .setTitle(`ERROR: ${title}`) .setDescription(`further info:\n\`\`\`${info}\`\`\``) - .setColor([0, 255, 0]), + .setColor([255, 0, 0]), ], ephemeral: true, }; From 5261f05dada2167dd2ad6eba40a059bd6d021809 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:06:38 +0200 Subject: [PATCH 25/92] formatting --- src/commands/TrackerCommands.ts | 152 ++++++++++++++------------------ 1 file changed, 66 insertions(+), 86 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 37369d2..42f8072 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -15,77 +15,82 @@ class Tracker extends Command { } async execute(interaction: ChatInputCommandInteraction): Promise { - const sub = interaction.options.getSubcommand() - const gam = interaction.options.getString('game') - const sta = interaction.options.getString('statistic') ? interaction.options.getString('statistic') : "stats" - const act = interaction.options.getString("action") - let exectued = false + const sub = interaction.options.getSubcommand(); + const gam = interaction.options.getString("game"); + const sta = interaction.options.getString("statistic") + ? interaction.options.getString("statistic") + : "stats"; + const act = interaction.options.getString("action"); + let exectued = false; switch (sub) { case "user": if (sta == "playtime" && gam) { - await playtime(interaction) - exectued = true + await playtime(interaction); + exectued = true; } else if (sta == "logs" && gam) { - await logs(interaction) - exectued = true + await logs(interaction); + exectued = true; } else if (sta == "general statistics") { - await userStats(interaction) - exectued = true + await userStats(interaction); + exectued = true; } else if (sta == "top 5 most played games") { - await userTop(interaction, "playtime") - exectued = true + await userTop(interaction, "playtime"); + exectued = true; } else if (sta == "top 5 most logged games") { - await userTop(interaction, "logs") - exectued = true + await userTop(interaction, "logs"); + exectued = true; } else if (sta == "Latest 5 logs") { - await userLast(interaction) - exectued = true + await userLast(interaction); + exectued = true; } - return + return; case "game": if (sta == "general statistics") { - await gameStats(interaction) - exectued = true + await gameStats(interaction); + exectued = true; } else if (sta == "top 5 most played games") { - await gameTop(interaction, "playtime") - exectued = true + await gameTop(interaction, "playtime"); + exectued = true; } else if (sta == "top 5 most logged games") { - await gameTop(interaction, "logs") - exectued = true + await gameTop(interaction, "logs"); + exectued = true; } else if (sta == "Latest 5 logs") { - await gameLast(interaction) - exectued = true + await gameLast(interaction); + exectued = true; } - return + return; case "blacklist": if (act == "add") { - await addBlacklist(interaction) - exectued = true + await addBlacklist(interaction); + exectued = true; } else if (act == "rem") { - await remBlacklist(interaction) - exectued = true + await remBlacklist(interaction); + exectued = true; } - return + return; case "latest": - await latest(interaction) - exectued = true - return + await latest(interaction); + exectued = true; + return; case "statistics": - await stats(interaction) - exectued = true - return + await stats(interaction); + exectued = true; + return; } if (!exectued) { - await interaction.reply({embeds: [ - new EmbedBuilder() - .setTitle("invalid options") - .setDescription("this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.") - .setColor([255, 255, 0]) - ]}) + await interaction.reply({ + embeds: [ + new EmbedBuilder() + .setTitle("invalid options") + .setDescription( + "this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer." + ) + .setColor([255, 255, 0]), + ], + }); } - } register(): SlashCommandSubcommandsOnlyBuilder { @@ -95,68 +100,43 @@ class Tracker extends Command { .addSubcommand((sub) => sub .setName("user") - .setDescription("Get tracking stats about a user") - .addUserOption((opt) => - opt - .setName("user") - .setDescription("the user") - ) - .addStringOption((opt) => - opt - .setName("game") - .setDescription("the game") - .setAutocomplete(true) - ) - .addStringOption((opt) => - opt - .setName("statistic") - .setDescription("ye") - .setAutocomplete(true) - ) + .setDescription("Get tracking stats about a user") + .addUserOption((opt) => opt.setName("user").setDescription("the user")) + .addStringOption((opt) => opt.setName("game").setDescription("the game").setAutocomplete(true)) + .addStringOption((opt) => opt.setName("statistic").setDescription("ye").setAutocomplete(true)) ) .addSubcommand((sub) => sub .setName("game") - .setDescription("Get tracking stats about a game") + .setDescription("Get tracking stats about a game") .addStringOption((opt) => - opt - .setName("game") - .setDescription("the game") - .setRequired(true) - .setAutocomplete(true) + opt.setName("game").setDescription("the game").setRequired(true).setAutocomplete(true) ) - .addStringOption((opt) => + .addStringOption((opt) => opt .setName("statistic") .setDescription("ye") .addChoices( - {name:"general statistics", value:"general statistics"}, - {name:"top 5 most played games", value:"top 5 most played games"}, - {name:"top 5 most logged games", value:"top 5 most logged games"}, - {name:"Latest 5 logs", value:"Latest 5 logs"} + { name: "general statistics", value: "general statistics" }, + { name: "top 5 most played games", value: "top 5 most played games" }, + { name: "top 5 most logged games", value: "top 5 most logged games" }, + { name: "Latest 5 logs", value: "Latest 5 logs" } ) ) ) - .addSubcommand((sub) => + .addSubcommand((sub) => sub .setName("blacklist") .setDescription("ADMIN ONLY") - .addStringOption((opt) => + .addStringOption((opt) => opt .setName("action") .setDescription("yo") - .addChoices( - {name:"add", value:"add"}, - {name:"remove", value:"rem"} - ) + .addChoices({ name: "add", value: "add" }, { name: "remove", value: "rem" }) .setRequired(true) ) - .addStringOption((opt) => - opt - .setName("game") - .setDescription("the game") - .setRequired(true) - .setAutocomplete(true) + .addStringOption((opt) => + opt.setName("game").setDescription("the game").setRequired(true).setAutocomplete(true) ) ) .addSubcommand((sub) => From a436f8961b26c6e9d0f9854f985e63ced69bccc8 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:19:37 +0200 Subject: [PATCH 26/92] tracking can now be disabled. --- src/commands/TrackerCommands.ts | 15 +++++++++++++++ src/config.ts | 3 +++ src/events/presenceUpdate.ts | 3 +++ src/util/config/configInterface.ts | 4 ++++ 4 files changed, 25 insertions(+) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 42f8072..2d856dc 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -9,12 +9,24 @@ import { latest, logs, playtime, stats } from "../util/tracker/subCommands"; import { userLast, userStats, userTop } from "../util/tracker/userCommands"; import { gameLast, gameStats, gameTop } from "../util/tracker/gameCommands"; import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; +import {config } from "../config" + class Tracker extends Command { constructor() { super("tracker"); } async execute(interaction: ChatInputCommandInteraction): Promise { + if (!config.tracking) { + await interaction.reply({embeds:[ + new EmbedBuilder() + .setTitle("tracking disabled") + .setDescription("Tracking is disabled. No game activity's will be logged and tracking commands are disabled.") + .setColor([255, 0, 0]) + + ], ephemeral:true}) + return + } const sub = interaction.options.getSubcommand(); const gam = interaction.options.getString("game"); const sta = interaction.options.getString("statistic") @@ -94,6 +106,9 @@ class Tracker extends Command { } register(): SlashCommandSubcommandsOnlyBuilder { + if (!config.tracking) return new SlashCommandBuilder() + .setName("tracker") + .setDescription("Tracking is disabled. No game activity's will be logged and tracking commands are disabled.") return new SlashCommandBuilder() .setName("tracker") .setDescription("The gateway to some cool stats about here being users") diff --git a/src/config.ts b/src/config.ts index a59f368..d10d3ba 100644 --- a/src/config.ts +++ b/src/config.ts @@ -51,4 +51,7 @@ export const config: BotConfig = { // --- Birthday feature --- birthdayNotificationAt: 9, + + // --- Tracker --- + tracking: false }; diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index ef2bdfc..860bc9e 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -1,9 +1,12 @@ import { Presence } from "discord.js"; import { getChangedActivities, isBlacklisted, logIt } from "../util/tracker/presence"; +import { config } from "../config" + const userCache = new Map>(); export default async function presenceUpdate(oldPresence: Presence | null, newPresence: Presence) { + if (!config.tracking) return if (newPresence.user?.bot) return; const { started, stopped } = await getChangedActivities(oldPresence, newPresence); diff --git a/src/util/config/configInterface.ts b/src/util/config/configInterface.ts index cb85724..28e8ad1 100644 --- a/src/util/config/configInterface.ts +++ b/src/util/config/configInterface.ts @@ -67,4 +67,8 @@ export interface BotConfig { | 21 | 22 | 23; + + // tracker + /** Enable or Disable the tracking feature (if disabled, tracking-commands and logging wont be available anymore, but tracking-Database wont be cleared!) */ + tracking:boolean; } From f21336cf341cb131d44afc31365ee4247caec868 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:20:06 +0200 Subject: [PATCH 27/92] formatting --- .env_sample | 5 ----- src/commands/TrackerCommands.ts | 31 ++++++++++++++++++------------ src/config.ts | 2 +- src/events/presenceUpdate.ts | 5 ++--- src/util/config/configInterface.ts | 4 ++-- 5 files changed, 24 insertions(+), 23 deletions(-) delete mode 100644 .env_sample diff --git a/.env_sample b/.env_sample deleted file mode 100644 index fa2139c..0000000 --- a/.env_sample +++ /dev/null @@ -1,5 +0,0 @@ -TOKEN=[ENTER_YOUR_TOKEN_HERE] -DEFAULT_GUILD=[GUILD_ID] - -# "1" or "0" (true or false). In debug mode no config validation will be made. -DEBUG_MODE=0 \ No newline at end of file diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 2d856dc..1206258 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -9,7 +9,7 @@ import { latest, logs, playtime, stats } from "../util/tracker/subCommands"; import { userLast, userStats, userTop } from "../util/tracker/userCommands"; import { gameLast, gameStats, gameTop } from "../util/tracker/gameCommands"; import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; -import {config } from "../config" +import { config } from "../config"; class Tracker extends Command { constructor() { @@ -18,14 +18,18 @@ class Tracker extends Command { async execute(interaction: ChatInputCommandInteraction): Promise { if (!config.tracking) { - await interaction.reply({embeds:[ - new EmbedBuilder() - .setTitle("tracking disabled") - .setDescription("Tracking is disabled. No game activity's will be logged and tracking commands are disabled.") - .setColor([255, 0, 0]) - - ], ephemeral:true}) - return + await interaction.reply({ + embeds: [ + new EmbedBuilder() + .setTitle("tracking disabled") + .setDescription( + "Tracking is disabled. No game activity's will be logged and tracking commands are disabled." + ) + .setColor([255, 0, 0]), + ], + ephemeral: true, + }); + return; } const sub = interaction.options.getSubcommand(); const gam = interaction.options.getString("game"); @@ -106,9 +110,12 @@ class Tracker extends Command { } register(): SlashCommandSubcommandsOnlyBuilder { - if (!config.tracking) return new SlashCommandBuilder() - .setName("tracker") - .setDescription("Tracking is disabled. No game activity's will be logged and tracking commands are disabled.") + if (!config.tracking) + return new SlashCommandBuilder() + .setName("tracker") + .setDescription( + "Tracking is disabled. No game activity's will be logged and tracking commands are disabled." + ); return new SlashCommandBuilder() .setName("tracker") .setDescription("The gateway to some cool stats about here being users") diff --git a/src/config.ts b/src/config.ts index d10d3ba..af23ebf 100644 --- a/src/config.ts +++ b/src/config.ts @@ -53,5 +53,5 @@ export const config: BotConfig = { birthdayNotificationAt: 9, // --- Tracker --- - tracking: false + tracking: false, }; diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index 860bc9e..b87ce92 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -1,12 +1,11 @@ import { Presence } from "discord.js"; import { getChangedActivities, isBlacklisted, logIt } from "../util/tracker/presence"; -import { config } from "../config" - +import { config } from "../config"; const userCache = new Map>(); export default async function presenceUpdate(oldPresence: Presence | null, newPresence: Presence) { - if (!config.tracking) return + if (!config.tracking) return; if (newPresence.user?.bot) return; const { started, stopped } = await getChangedActivities(oldPresence, newPresence); diff --git a/src/util/config/configInterface.ts b/src/util/config/configInterface.ts index 28e8ad1..c9915df 100644 --- a/src/util/config/configInterface.ts +++ b/src/util/config/configInterface.ts @@ -67,8 +67,8 @@ export interface BotConfig { | 21 | 22 | 23; - + // tracker /** Enable or Disable the tracking feature (if disabled, tracking-commands and logging wont be available anymore, but tracking-Database wont be cleared!) */ - tracking:boolean; + tracking: boolean; } From abef62a0a1cde4706c40d194b888ed20d057dbf8 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:22:07 +0200 Subject: [PATCH 28/92] bring back .env_sample :/ --- .env_sample | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .env_sample diff --git a/.env_sample b/.env_sample new file mode 100644 index 0000000..fa2139c --- /dev/null +++ b/.env_sample @@ -0,0 +1,5 @@ +TOKEN=[ENTER_YOUR_TOKEN_HERE] +DEFAULT_GUILD=[GUILD_ID] + +# "1" or "0" (true or false). In debug mode no config validation will be made. +DEBUG_MODE=0 \ No newline at end of file From 6866d36c2b222e531ee3fa1b1b45a501f9e9a5d7 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 23 Apr 2023 14:36:42 +0200 Subject: [PATCH 29/92] hotfix --- src/commands/TrackerCommands.ts | 29 ++++++----------------------- src/config.ts | 2 +- src/util/tracker/subCommands.ts | 12 +++++++----- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 1206258..2c175da 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -32,70 +32,53 @@ class Tracker extends Command { return; } const sub = interaction.options.getSubcommand(); - const gam = interaction.options.getString("game"); const sta = interaction.options.getString("statistic") ? interaction.options.getString("statistic") : "stats"; const act = interaction.options.getString("action"); - let exectued = false; - switch (sub) { case "user": - if (sta == "playtime" && gam) { + if (sta == "playtime") { await playtime(interaction); - exectued = true; - } else if (sta == "logs" && gam) { + } else if (sta == "logs") { await logs(interaction); - exectued = true; } else if (sta == "general statistics") { await userStats(interaction); - exectued = true; } else if (sta == "top 5 most played games") { await userTop(interaction, "playtime"); - exectued = true; } else if (sta == "top 5 most logged games") { await userTop(interaction, "logs"); - exectued = true; - } else if (sta == "Latest 5 logs") { + } else if (sta == "latest 5 logs") { await userLast(interaction); - exectued = true; } return; case "game": if (sta == "general statistics") { await gameStats(interaction); - exectued = true; } else if (sta == "top 5 most played games") { await gameTop(interaction, "playtime"); - exectued = true; } else if (sta == "top 5 most logged games") { await gameTop(interaction, "logs"); - exectued = true; - } else if (sta == "Latest 5 logs") { + } else if (sta == "latest 5 logs") { await gameLast(interaction); - exectued = true; } return; case "blacklist": if (act == "add") { await addBlacklist(interaction); - exectued = true; } else if (act == "rem") { await remBlacklist(interaction); - exectued = true; } return; case "latest": await latest(interaction); - exectued = true; return; case "statistics": await stats(interaction); - exectued = true; return; } - if (!exectued) { + if (!interaction.replied) { await interaction.reply({ embeds: [ new EmbedBuilder() @@ -142,7 +125,7 @@ class Tracker extends Command { { name: "general statistics", value: "general statistics" }, { name: "top 5 most played games", value: "top 5 most played games" }, { name: "top 5 most logged games", value: "top 5 most logged games" }, - { name: "Latest 5 logs", value: "Latest 5 logs" } + { name: "latest 5 logs", value: "latest 5 logs" } ) ) ) diff --git a/src/config.ts b/src/config.ts index af23ebf..0c564f6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -53,5 +53,5 @@ export const config: BotConfig = { birthdayNotificationAt: 9, // --- Tracker --- - tracking: false, + tracking: true, }; diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index efd9c8a..0cf29e6 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -114,17 +114,19 @@ export async function latest(interaction: ChatInputCommandInteraction) { const fields: APIEmbedField[] = []; // latest system logs - const logs = trackerLogs.array().slice(0, 5).reverse(); + const logs = trackerLogs + .array() + .sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()) + .slice(0, 5); + // make embed for each log logs.forEach((log) => fields.push({ inline: true, name: log.gameName, - value: `user: <@${log.userid}>\ntime: \n\nplayed time: ${makeTimeString( - log.playtime - )}`, + )}:t>\n${makeTimeString(log.playtime)}`, }) ); From 6b58cba4e34887a04bc8d5fc138e1590a5d12d5f Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:28:51 +0200 Subject: [PATCH 30/92] no embed --- src/commands/TrackerCommands.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 2c175da..b3ccf91 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -19,14 +19,7 @@ class Tracker extends Command { async execute(interaction: ChatInputCommandInteraction): Promise { if (!config.tracking) { await interaction.reply({ - embeds: [ - new EmbedBuilder() - .setTitle("tracking disabled") - .setDescription( - "Tracking is disabled. No game activity's will be logged and tracking commands are disabled." - ) - .setColor([255, 0, 0]), - ], + content: "Tracking is disabled. No game activity's will be logged and tracking commands are disabled.", ephemeral: true, }); return; From 851c0cef16428e420752724c12fced3da6d0beaa Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:31:59 +0200 Subject: [PATCH 31/92] variable names --- src/commands/TrackerCommands.ts | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index b3ccf91..756a8b0 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -19,47 +19,48 @@ class Tracker extends Command { async execute(interaction: ChatInputCommandInteraction): Promise { if (!config.tracking) { await interaction.reply({ - content: "Tracking is disabled. No game activity's will be logged and tracking commands are disabled.", + content: + "Tracking is disabled. No game activity's will be logged and tracking commands are disabled.", ephemeral: true, }); return; } - const sub = interaction.options.getSubcommand(); - const sta = interaction.options.getString("statistic") + const subCommand = interaction.options.getSubcommand(); + const statistics = interaction.options.getString("statistic") ? interaction.options.getString("statistic") : "stats"; - const act = interaction.options.getString("action"); - switch (sub) { + const action = interaction.options.getString("action"); + switch (subCommand) { case "user": - if (sta == "playtime") { + if (statistics == "playtime") { await playtime(interaction); - } else if (sta == "logs") { + } else if (statistics == "logs") { await logs(interaction); - } else if (sta == "general statistics") { + } else if (statistics == "general statistics") { await userStats(interaction); - } else if (sta == "top 5 most played games") { + } else if (statistics == "top 5 most played games") { await userTop(interaction, "playtime"); - } else if (sta == "top 5 most logged games") { + } else if (statistics == "top 5 most logged games") { await userTop(interaction, "logs"); - } else if (sta == "latest 5 logs") { + } else if (statistics == "latest 5 logs") { await userLast(interaction); } return; case "game": - if (sta == "general statistics") { + if (statistics == "general statistics") { await gameStats(interaction); - } else if (sta == "top 5 most played games") { + } else if (statistics == "top 5 most played games") { await gameTop(interaction, "playtime"); - } else if (sta == "top 5 most logged games") { + } else if (statistics == "top 5 most logged games") { await gameTop(interaction, "logs"); - } else if (sta == "latest 5 logs") { + } else if (statistics == "latest 5 logs") { await gameLast(interaction); } return; case "blacklist": - if (act == "add") { + if (action == "add") { await addBlacklist(interaction); - } else if (act == "rem") { + } else if (action == "rem") { await remBlacklist(interaction); } return; From cb1ac674f7588fb6d61554d3172082c9d328a1ef Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:39:35 +0200 Subject: [PATCH 32/92] reworked command descriptions --- src/commands/TrackerCommands.ts | 41 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 756a8b0..793cf71 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -74,15 +74,10 @@ class Tracker extends Command { if (!interaction.replied) { await interaction.reply({ - embeds: [ - new EmbedBuilder() - .setTitle("invalid options") - .setDescription( - "this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer." - ) - .setColor([255, 255, 0]), - ], - }); + content: + "this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", + ephemeral: true + }); } } @@ -91,30 +86,30 @@ class Tracker extends Command { return new SlashCommandBuilder() .setName("tracker") .setDescription( - "Tracking is disabled. No game activity's will be logged and tracking commands are disabled." + "The tracking system is disabled which means commands are removed and tracking is paused." ); return new SlashCommandBuilder() .setName("tracker") - .setDescription("The gateway to some cool stats about here being users") + .setDescription("The gateway to some cool stats about here being users.") .addSubcommand((sub) => sub .setName("user") - .setDescription("Get tracking stats about a user") - .addUserOption((opt) => opt.setName("user").setDescription("the user")) - .addStringOption((opt) => opt.setName("game").setDescription("the game").setAutocomplete(true)) - .addStringOption((opt) => opt.setName("statistic").setDescription("ye").setAutocomplete(true)) + .setDescription("Get tracking stats about a user.") + .addUserOption((opt) => opt.setName("user").setDescription("the target user, if not given defaults to you.")) + .addStringOption((opt) => opt.setName("game").setDescription("the game of which to get playtime/logs from.").setAutocomplete(true)) + .addStringOption((opt) => opt.setName("statistic").setDescription("select what statistics should get shown. (Options depend on if a game is given or not)").setAutocomplete(true)) ) .addSubcommand((sub) => sub .setName("game") - .setDescription("Get tracking stats about a game") + .setDescription("Get tracking stats about a game.") .addStringOption((opt) => - opt.setName("game").setDescription("the game").setRequired(true).setAutocomplete(true) + opt.setName("game").setDescription("the game of which to get statistics from.").setRequired(true).setAutocomplete(true) ) .addStringOption((opt) => opt .setName("statistic") - .setDescription("ye") + .setDescription("select what statistics should get shown.") .addChoices( { name: "general statistics", value: "general statistics" }, { name: "top 5 most played games", value: "top 5 most played games" }, @@ -126,22 +121,22 @@ class Tracker extends Command { .addSubcommand((sub) => sub .setName("blacklist") - .setDescription("ADMIN ONLY") + .setDescription("[ADMIN ONLY] edit the tracking blacklist to allow or block certain games.") .addStringOption((opt) => opt .setName("action") - .setDescription("yo") + .setDescription("select which action should get executed.") .addChoices({ name: "add", value: "add" }, { name: "remove", value: "rem" }) .setRequired(true) ) .addStringOption((opt) => - opt.setName("game").setDescription("the game").setRequired(true).setAutocomplete(true) + opt.setName("game").setDescription("the game on which the action should be performed.").setRequired(true).setAutocomplete(true) ) ) .addSubcommand((sub) => - sub.setName("latest").setDescription("See the latest logs across the whole system") + sub.setName("latest").setDescription("See the latest logs across the whole system.") ) - .addSubcommand((sub) => sub.setName("statistics").setDescription("See some system stats")); + .addSubcommand((sub) => sub.setName("statistics").setDescription("See general statistics across the whole system.")); } } From 61d3004c26a7bc8146761d92713ea35470f5810c Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:41:55 +0200 Subject: [PATCH 33/92] formatting --- src/commands/TrackerCommands.ts | 42 ++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 793cf71..655a69a 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -75,9 +75,9 @@ class Tracker extends Command { if (!interaction.replied) { await interaction.reply({ content: - "this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", - ephemeral: true - }); + "this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", + ephemeral: true, + }); } } @@ -95,16 +95,34 @@ class Tracker extends Command { sub .setName("user") .setDescription("Get tracking stats about a user.") - .addUserOption((opt) => opt.setName("user").setDescription("the target user, if not given defaults to you.")) - .addStringOption((opt) => opt.setName("game").setDescription("the game of which to get playtime/logs from.").setAutocomplete(true)) - .addStringOption((opt) => opt.setName("statistic").setDescription("select what statistics should get shown. (Options depend on if a game is given or not)").setAutocomplete(true)) + .addUserOption((opt) => + opt.setName("user").setDescription("the target user, if not given defaults to you.") + ) + .addStringOption((opt) => + opt + .setName("game") + .setDescription("the game of which to get playtime/logs from.") + .setAutocomplete(true) + ) + .addStringOption((opt) => + opt + .setName("statistic") + .setDescription( + "select what statistics should get shown. (Options depend on if a game is given or not)" + ) + .setAutocomplete(true) + ) ) .addSubcommand((sub) => sub .setName("game") .setDescription("Get tracking stats about a game.") .addStringOption((opt) => - opt.setName("game").setDescription("the game of which to get statistics from.").setRequired(true).setAutocomplete(true) + opt + .setName("game") + .setDescription("the game of which to get statistics from.") + .setRequired(true) + .setAutocomplete(true) ) .addStringOption((opt) => opt @@ -130,13 +148,19 @@ class Tracker extends Command { .setRequired(true) ) .addStringOption((opt) => - opt.setName("game").setDescription("the game on which the action should be performed.").setRequired(true).setAutocomplete(true) + opt + .setName("game") + .setDescription("the game on which the action should be performed.") + .setRequired(true) + .setAutocomplete(true) ) ) .addSubcommand((sub) => sub.setName("latest").setDescription("See the latest logs across the whole system.") ) - .addSubcommand((sub) => sub.setName("statistics").setDescription("See general statistics across the whole system.")); + .addSubcommand((sub) => + sub.setName("statistics").setDescription("See general statistics across the whole system.") + ); } } From ac0d4bc15817cab7bb93a2be0cfb0bc56332b3fb Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:42:33 +0200 Subject: [PATCH 34/92] lowercase --- src/interactions/autocompleters/TrackerAutocomple.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index f0854b7..855fb22 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -27,7 +27,7 @@ class Tracker extends Autocompleter { if (trackerUsers.get(user)?.games) { trackerUsers.get(user)?.games.forEach((g) => options.push(g.id)); } else { - options.push("USER HAS NOT PLAYED ANY GAMES YET"); + options.push("[user has not played any games yet]"); } } else { options.push(...trackerGames.keyArray()); From cb2d74aa8745f0f867d00334cb790f4521c72014 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:58:38 +0200 Subject: [PATCH 35/92] export nested ifs and comments --- .../autocompleters/TrackerAutocomple.ts | 52 ++++++------------- src/util/tracker/autocompleteHelper.ts | 45 ++++++++++++++++ 2 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 src/util/tracker/autocompleteHelper.ts diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 855fb22..8c7b04c 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -1,6 +1,8 @@ import { Autocompleter } from "../interactionClasses"; import { AutocompleteInteraction } from "discord.js"; import { trackerBlacklist, trackerGames, trackerUsers } from "../../db"; +import { blacklist, user } from "../../util/tracker/autocompleteHelper"; + class Tracker extends Autocompleter { constructor() { super("tracker"); @@ -10,59 +12,37 @@ class Tracker extends Autocompleter { const sub = interaction.options.getSubcommand(); const focus = interaction.options.getFocused().toLowerCase() as string; const option = interaction.options.getFocused(true).name; - const user = + + // if user-option is given, use it, else default to executer + const userId = typeof interaction.options.get("user")?.value == "string" ? interaction.options.get("user")?.value : interaction.user.id; const game = interaction.options.get("game")?.value; + + // if action-option is given, use it, else default to add const action = typeof interaction.options.get("action")?.value == "string" ? interaction.options.get("action")?.value : "add"; - const options: string[] = []; + + // make ts happy... + if (typeof action != "string") return + + let options: string[] = []; if (sub == "user") { - if (option == "game") { - if (typeof user == "string") { - if (trackerUsers.get(user)?.games) { - trackerUsers.get(user)?.games.forEach((g) => options.push(g.id)); - } else { - options.push("[user has not played any games yet]"); - } - } else { - options.push(...trackerGames.keyArray()); - } - } else if (option == "statistic") { - if (typeof game == "string") { - options.push(...["playtime", "logs"]); - } else { - options.push( - ...["general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"] - ); - } - } + options = user(option, userId, game); } else if (sub == "game") { trackerGames.keyArray().forEach((e) => options.push(e)); } else if (sub == "blacklist") { - if (action == "add") { - options.push( - ...trackerGames.keyArray().filter((e) => !trackerBlacklist.get("")?.find((e2) => e2 == e)) - ); - } else if (action == "rem") { - options.push( - ...trackerGames.keyArray().filter((e) => !!trackerBlacklist.get("")?.find((e2) => e2 == e)) - ); - } else { - options.push(...trackerGames.keyArray()); - } + options = blacklist(action) } let filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)); - if (filteredOptions.length > 25) filteredOptions = filteredOptions.slice(0, 25); - - const map = filteredOptions.map((c) => ({ name: c, value: c })); + filteredOptions = filteredOptions.slice(0, 25); - await interaction.respond(map); + await interaction.respond(filteredOptions.map((c) => ({ name: c, value: c }))); } } diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts new file mode 100644 index 0000000..49b7b32 --- /dev/null +++ b/src/util/tracker/autocompleteHelper.ts @@ -0,0 +1,45 @@ +import { trackerBlacklist, trackerGames, trackerUsers } from "../../db"; + + +export function user(option:string, userid:string | number | boolean | undefined, game:string | number | boolean | undefined) { + + let results = [] + + if (option == "game") { + if (typeof userid == "string") { + if (trackerUsers.get(userid)?.games) { + // list games a user has played (if user has played anything) + trackerUsers.get(userid)?.games.forEach((g) => results.push(g.id)); + } else { + // show this when user didnt play any games + results.push("[user has not played any games yet]"); + } + } else { + // list all games in case user hasnt filled in the user-option yet + results.push(...trackerGames.keyArray()); + } + } else if (option == "statistic") { + // only show playtime and logs if a game is given, else show standard options + results.push(...(typeof game == "string" ? ["playtime", "logs"] : ["general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"])); + } + + return results + +} + +export function blacklist(action:string) { + + let results = [] + + if (action == "add") { + // only show not blacklisted games when "add" is chosen as the action, else only show blacklisted games + results.push( + ...trackerGames.keyArray().filter((e) => (action=="add" ? !trackerBlacklist.get("")?.find((e2) => e2 == e) : !!trackerBlacklist.get("")?.find((e2) => e2 == e))) + ) + } else { + // show all games in case action-option isnt filled in yet + results.push(...trackerGames.keyArray()); + } + + return results +} \ No newline at end of file From 45014a528bcf389a6a7d8fdf60f95f0e430dc5b9 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 20:59:18 +0200 Subject: [PATCH 36/92] formatting --- .../autocompleters/TrackerAutocomple.ts | 6 +- src/util/tracker/autocompleteHelper.ts | 92 ++++++++++--------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 8c7b04c..e91a414 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -12,7 +12,7 @@ class Tracker extends Autocompleter { const sub = interaction.options.getSubcommand(); const focus = interaction.options.getFocused().toLowerCase() as string; const option = interaction.options.getFocused(true).name; - + // if user-option is given, use it, else default to executer const userId = typeof interaction.options.get("user")?.value == "string" @@ -27,7 +27,7 @@ class Tracker extends Autocompleter { : "add"; // make ts happy... - if (typeof action != "string") return + if (typeof action != "string") return; let options: string[] = []; @@ -36,7 +36,7 @@ class Tracker extends Autocompleter { } else if (sub == "game") { trackerGames.keyArray().forEach((e) => options.push(e)); } else if (sub == "blacklist") { - options = blacklist(action) + options = blacklist(action); } let filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)); diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts index 49b7b32..7e32bb2 100644 --- a/src/util/tracker/autocompleteHelper.ts +++ b/src/util/tracker/autocompleteHelper.ts @@ -1,45 +1,55 @@ import { trackerBlacklist, trackerGames, trackerUsers } from "../../db"; - -export function user(option:string, userid:string | number | boolean | undefined, game:string | number | boolean | undefined) { - - let results = [] - - if (option == "game") { - if (typeof userid == "string") { - if (trackerUsers.get(userid)?.games) { - // list games a user has played (if user has played anything) - trackerUsers.get(userid)?.games.forEach((g) => results.push(g.id)); - } else { - // show this when user didnt play any games - results.push("[user has not played any games yet]"); - } - } else { - // list all games in case user hasnt filled in the user-option yet - results.push(...trackerGames.keyArray()); - } - } else if (option == "statistic") { - // only show playtime and logs if a game is given, else show standard options - results.push(...(typeof game == "string" ? ["playtime", "logs"] : ["general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"])); - } - - return results - +export function user( + option: string, + userid: string | number | boolean | undefined, + game: string | number | boolean | undefined +) { + const results = []; + + if (option == "game") { + if (typeof userid == "string") { + if (trackerUsers.get(userid)?.games) { + // list games a user has played (if user has played anything) + trackerUsers.get(userid)?.games.forEach((g) => results.push(g.id)); + } else { + // show this when user didnt play any games + results.push("[user has not played any games yet]"); + } + } else { + // list all games in case user hasnt filled in the user-option yet + results.push(...trackerGames.keyArray()); + } + } else if (option == "statistic") { + // only show playtime and logs if a game is given, else show standard options + results.push( + ...(typeof game == "string" + ? ["playtime", "logs"] + : ["general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"]) + ); + } + + return results; } -export function blacklist(action:string) { - - let results = [] - - if (action == "add") { - // only show not blacklisted games when "add" is chosen as the action, else only show blacklisted games - results.push( - ...trackerGames.keyArray().filter((e) => (action=="add" ? !trackerBlacklist.get("")?.find((e2) => e2 == e) : !!trackerBlacklist.get("")?.find((e2) => e2 == e))) - ) - } else { - // show all games in case action-option isnt filled in yet - results.push(...trackerGames.keyArray()); - } - - return results -} \ No newline at end of file +export function blacklist(action: string) { + const results = []; + + if (action == "add") { + // only show not blacklisted games when "add" is chosen as the action, else only show blacklisted games + results.push( + ...trackerGames + .keyArray() + .filter((e) => + action == "add" + ? !trackerBlacklist.get("")?.find((e2) => e2 == e) + : !!trackerBlacklist.get("")?.find((e2) => e2 == e) + ) + ); + } else { + // show all games in case action-option isnt filled in yet + results.push(...trackerGames.keyArray()); + } + + return results; +} From 6437499e83dcef5060e64c0a95c81bc2bb569168 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 21:00:44 +0200 Subject: [PATCH 37/92] intent updates depending on tracking on/off --- src/start.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/start.ts b/src/start.ts index 24dad6f..aea243e 100644 --- a/src/start.ts +++ b/src/start.ts @@ -11,10 +11,11 @@ import { Client } from "discord.js"; import { ctx } from "./ctx"; import { logger } from "./logger"; import { Settings } from "luxon"; +import { config } from "./config" logger.debug("Creating client..."); const client = new Client({ - intents: ["Guilds", "GuildMembers", "GuildPresences", "0"], + intents: ["Guilds", "GuildMembers", config.tracking ? "GuildPresences" : "0"], }); function shutdown(info: unknown) { From b318d25ce988d478afd8cbf7ae00de683b34629a Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 21:01:27 +0200 Subject: [PATCH 38/92] formatting --- src/start.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/start.ts b/src/start.ts index aea243e..cb97793 100644 --- a/src/start.ts +++ b/src/start.ts @@ -11,7 +11,7 @@ import { Client } from "discord.js"; import { ctx } from "./ctx"; import { logger } from "./logger"; import { Settings } from "luxon"; -import { config } from "./config" +import { config } from "./config"; logger.debug("Creating client..."); const client = new Client({ From 154ec30afe37c83355fe416ab48e91b833b4df7e Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 21:25:22 +0200 Subject: [PATCH 39/92] message names and numbers --- src/util/tracker/blacklistCommands.ts | 14 +++++++------- src/util/tracker/gameCommands.ts | 24 +++++++++++++++++------- src/util/tracker/messages.ts | 20 ++++++++++---------- src/util/tracker/subCommands.ts | 14 +++++++------- src/util/tracker/userCommands.ts | 20 ++++++++++---------- 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts index 7df5153..1cb38c4 100644 --- a/src/util/tracker/blacklistCommands.ts +++ b/src/util/tracker/blacklistCommands.ts @@ -1,12 +1,12 @@ import { ChatInputCommandInteraction } from "discord.js"; import { trackerBlacklist } from "../../db"; import { hasAdminPerms } from "../misc/permissions"; -import { ADMINONLY, GAMEADDED, GAMENOTBLACKLIST, GAMEONBLACKLIST, GAMEREMOVED } from "./messages"; +import { adminOnly, GameAdded, gameNotOnBlacklist, gameOnBlacklist, GameRemoved } from "./messages"; export async function addBlacklist(interaction: ChatInputCommandInteraction) { // check for admin permissions if (!(await hasAdminPerms(interaction))) { - interaction.reply(ADMINONLY); + interaction.reply(adminOnly); return; } @@ -14,17 +14,17 @@ export async function addBlacklist(interaction: ChatInputCommandInteraction) { const game = interaction.options.getString("game", true); // check if game is already on blacklist if (trackerBlacklist.get("")?.find((g) => g.toLowerCase() == game.toLowerCase())) { - interaction.reply(GAMEONBLACKLIST); + interaction.reply(gameOnBlacklist); return; } // add game to blacklist trackerBlacklist.push("", game.toLowerCase()); - interaction.reply(GAMEADDED(game)); + interaction.reply(GameAdded(game)); } export async function remBlacklist(interaction: ChatInputCommandInteraction) { // check for admin permissions if (!(await hasAdminPerms(interaction))) { - interaction.reply(ADMINONLY); + interaction.reply(adminOnly); return; } @@ -38,9 +38,9 @@ export async function remBlacklist(interaction: ChatInputCommandInteraction) { "", db.filter((g) => g.toLowerCase() != game.toLowerCase()) ); - interaction.reply(GAMEREMOVED(game)); + interaction.reply(GameRemoved(game)); return; } // send error - interaction.reply(GAMENOTBLACKLIST); + interaction.reply(gameNotOnBlacklist); } diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 011cb73..f3b749b 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -3,7 +3,17 @@ import { config } from "../../config"; import { trackerGames, trackerLogs } from "../../db"; import { discordTimestamp } from "../misc/time"; import { makeTimeString } from "./helper"; -import { GAMENOENTRY } from "./messages"; +import { gameNoEntry } from "./messages"; + + +// 60seconds * 60 minutes * 24 hours = One day +const dayInSeconds = 60*60*24 +// seconds of day to milliseconds +const dayInMillis = dayInSeconds * 1000 +// 7 times a day = week +const weekInMillis = dayInMillis*7 +// 4 times a week + 2.4 days = (average) month +const monthInMillis = weekInMillis*4 + (dayInMillis*2.4167) export async function gameStats(interaction: ChatInputCommandInteraction) { // get game option @@ -12,7 +22,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { // load tracker Games db const db = trackerGames.get(targetGame.toLocaleLowerCase()); if (!db) { - await interaction.reply(GAMENOENTRY); + await interaction.reply(gameNoEntry); return; } @@ -40,9 +50,9 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const range = Date.now() - firstSeen; // calculate daily/weekly/monthly and per-log average playtime const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / (86400 * 1000))) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / 2628000000)) + Math.round(totalPlaytime / (range / dayInMillis)) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / monthInMillis)) )}\nuser: ${makeTimeString(Math.round(totalPlaytime / users))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) )}`; @@ -90,7 +100,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { // load games db const db = trackerGames.get(targetGame); if (!db) { - await interaction.reply(GAMENOENTRY); + await interaction.reply(gameNoEntry); return; } @@ -140,7 +150,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction, filter: // load games db const db = trackerGames.get(targetGame); if (!db) { - await interaction.reply(GAMENOENTRY); + await interaction.reply(gameNoEntry); return; } diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index 4e9c55f..be84664 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -1,28 +1,28 @@ import { confirmEmbed, errorEmbed } from "./helper"; /** Used when a user cant be fetched by `interaction.options.getUser("user")` or `interaction.user` */ -export const USERNOTFOUND = errorEmbed("user not found", "Cant get user option or interaction user."); +export const userNotFound = errorEmbed("user not found", "Cant get user option or interaction user."); /** Used when `interaction.guild.members.get(userID)` does work with given user */ -export const MEMBERNOTFOUND = errorEmbed( +export const memberNotFound = errorEmbed( "member not found", "Cant get guild member, user may not be on this server anymore." ); /** Used when a given user is not found in the tracker database */ -export const USERNOENTRY = errorEmbed("no database entry", "Given user has no entries in the database."); +export const userNoEntry = errorEmbed("no database entry", "Given user has no entries in the database."); /** Used when a given game is not found in users tracker database entry */ -export const USERNOGAMEENTRY = errorEmbed( +export const userNoGameEntry = errorEmbed( "no database entry", "Given user has no entrys in the database associated with given the game." ); //** Used when a given game is not found in the tracker database */ -export const GAMENOENTRY = errorEmbed("no database entry", "Given game has no entry in the database."); +export const gameNoEntry = errorEmbed("no database entry", "Given game has no entry in the database."); /** Used when a non admin user tryes to use a admin only command */ -export const ADMINONLY = errorEmbed("admin only", "This command can only be executed by an admin."); +export const adminOnly = errorEmbed("admin only", "This command can only be executed by an admin."); /** Used when there is an attempt to add a game to the blacklist while its already on there */ -export const GAMEONBLACKLIST = errorEmbed("already on blacklist", "Given game is already on the blacklist."); +export const gameOnBlacklist = errorEmbed("already on blacklist", "Given game is already on the blacklist."); /** User when there is an attempt to remove a game from the blacklist whole its not on there */ -export const GAMENOTBLACKLIST = errorEmbed("not on blacklist", "Given game is not on the blacklist."); +export const gameNotOnBlacklist = errorEmbed("not on blacklist", "Given game is not on the blacklist."); /** Used to confirm the adding of a given game to the blacklist */ -export const GAMEADDED = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); +export const GameAdded = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); /** Used to confirm the removal of a given game from the blacklist */ -export const GAMEREMOVED = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); +export const GameRemoved = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 0cf29e6..3ca3b98 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -4,7 +4,7 @@ import { discordTimestamp } from "../misc/time"; import { trackerGames, trackerLogs, trackerUsers } from "../../db"; import { config } from "../../config"; import { makeTimeString } from "./helper"; -import { GAMENOENTRY, USERNOENTRY, USERNOGAMEENTRY } from "./messages"; +import { gameNoEntry, userNoEntry, userNoGameEntry } from "./messages"; export async function playtime(interaction: ChatInputCommandInteraction) { // get target user and game @@ -24,7 +24,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { // load db and get target game const db = trackerGames.get(targetGame.toLowerCase()); if (!db) { - await interaction.reply(GAMENOENTRY); + await interaction.reply(gameNoEntry); return; } text = `${targetGame} has ${makeTimeString(db.playtime)} of playtime`; @@ -33,7 +33,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { // load db and get target user const db = trackerUsers.get(targetUser.id); if (!db) { - await interaction.reply(USERNOENTRY); + await interaction.reply(userNoEntry); return; } text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime`; @@ -44,7 +44,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { .get(targetUser.id) ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); if (!db) { - await interaction.reply(USERNOGAMEENTRY); + await interaction.reply(userNoGameEntry); return; } text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime in ${targetGame}`; @@ -77,7 +77,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { // get target db game entry const db = trackerGames.get(targetGame.toLowerCase()); if (!db) { - await interaction.reply(GAMENOENTRY); + await interaction.reply(gameNoEntry); return; } text = `${targetGame} was played ${db.logs} times`; @@ -86,7 +86,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { // get target db user entry const db = trackerUsers.get(targetUser.id); if (!db) { - await interaction.reply(USERNOENTRY); + await interaction.reply(userNoEntry); return; } text = `${targetUser.username} was logged ${db.logs} times`; @@ -97,7 +97,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { .get(targetUser.id) ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); if (!db) { - await interaction.reply(USERNOGAMEENTRY); + await interaction.reply(userNoGameEntry); return; } text = `${targetUser.username} has played ${targetGame} ${db.logs} times`; diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 1554d7c..003362c 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -3,25 +3,25 @@ import { config } from "../../config"; import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; import { discordTimestamp } from "../misc/time"; import { makeTimeString, sortGamesLogs, sortGamesPlaytime } from "./helper"; -import { MEMBERNOTFOUND, USERNOENTRY, USERNOTFOUND } from "./messages"; +import { memberNotFound, userNoEntry, userNotFound } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { // get target user and default to command executer if not given const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { - await interaction.reply(USERNOTFOUND); + await interaction.reply(userNotFound); return; } // get the member (to use their display name and color) const member = await interaction.guild?.members.fetch(target.id); if (!member) { - await interaction.reply(MEMBERNOTFOUND); + await interaction.reply(memberNotFound); return; } // load db and get target user const db = trackerUsers.get(target.id); if (!db) { - await interaction.reply(USERNOENTRY); + await interaction.reply(userNoEntry); return; } @@ -98,19 +98,19 @@ export async function userLast(interaction: ChatInputCommandInteraction) { // get target user, if not given default to executer const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { - await interaction.reply(USERNOTFOUND); + await interaction.reply(userNotFound); return; } // get member from target const member = await interaction.guild?.members.fetch(target.id); if (member == undefined) { - await interaction.reply(MEMBERNOTFOUND); + await interaction.reply(memberNotFound); return; } // get targets db entry const db = trackerUsers.get(member.id); if (!db) { - await interaction.reply(USERNOENTRY); + await interaction.reply(userNoEntry); return; } // get latest logs @@ -151,13 +151,13 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: // get target user, default to executer if not given const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; if (!target) { - await interaction.reply(USERNOTFOUND); + await interaction.reply(userNotFound); return; } // get the member from target const member = await interaction.guild?.members.fetch(target.id); if (member == undefined) { - await interaction.reply(MEMBERNOTFOUND); + await interaction.reply(memberNotFound); return; } @@ -173,7 +173,7 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: // return if no games are being listed if (!games) { - await interaction.reply(USERNOENTRY); + await interaction.reply(userNoEntry); return; } From 2460d5ba92d0ea7450f8cfff6c2322a6865da314 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 21:27:43 +0200 Subject: [PATCH 40/92] formatting --- src/util/tracker/gameCommands.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index f3b749b..9ad9eab 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -5,15 +5,14 @@ import { discordTimestamp } from "../misc/time"; import { makeTimeString } from "./helper"; import { gameNoEntry } from "./messages"; - // 60seconds * 60 minutes * 24 hours = One day -const dayInSeconds = 60*60*24 +const dayInSeconds = 60 * 60 * 24; // seconds of day to milliseconds -const dayInMillis = dayInSeconds * 1000 +const dayInMillis = dayInSeconds * 1000; // 7 times a day = week -const weekInMillis = dayInMillis*7 +const weekInMillis = dayInMillis * 7; // 4 times a week + 2.4 days = (average) month -const monthInMillis = weekInMillis*4 + (dayInMillis*2.4167) +const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; export async function gameStats(interaction: ChatInputCommandInteraction) { // get game option From b96179c6bf0d112153b9dba22747094cd534297d Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 21:56:20 +0200 Subject: [PATCH 41/92] helper functions --- src/util/tracker/gameCommands.ts | 16 +++++--------- src/util/tracker/helper.ts | 20 ++++++++++++++++- src/util/tracker/subCommands.ts | 38 ++++++++------------------------ src/util/tracker/userCommands.ts | 16 +++++--------- 4 files changed, 38 insertions(+), 52 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 9ad9eab..98ffda7 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -2,7 +2,7 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discor import { config } from "../../config"; import { trackerGames, trackerLogs } from "../../db"; import { discordTimestamp } from "../misc/time"; -import { makeTimeString } from "./helper"; +import { makeTimeString, sortDbEntrysToString } from "./helper"; import { gameNoEntry } from "./messages"; // 60seconds * 60 minutes * 24 hours = One day @@ -26,17 +26,11 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { } // get top 5 played games and make a string - const mostPlayed = db.users - .sort((a, b) => b.playtime - a.playtime) - .slice(0, 5) - .map((user) => `<@${user.id}>: ${makeTimeString(user.playtime)}`) - .join("\n"); + const mostPlayed = sortDbEntrysToString(db.users, (a,b)=>b.playtime-a.playtime, (user)=>`<@${user.id}>: ${makeTimeString(user.playtime)}`) + // get top 5 logged games and make a string - const mostLogged = db.users - .sort((a, b) => b.logs - a.logs) - .slice(0, 5) - .map((user) => `<@${user.id}>: ${user.logs} logs`) - .join("\n"); + const mostLogged = sortDbEntrysToString(db.users, (a,b)=>b.logs-a.logs, (user)=>`<@${user.id}>: ${user.logs} logs`) + // format latest logs into a string const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); // get total playtime, logs and users diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 428d27f..637f96a 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -1,6 +1,6 @@ import { EmbedBuilder, InteractionReplyOptions } from "discord.js"; import { Duration } from "luxon"; -import { trackerUsers } from "../../db"; +import { TrackerGame, TrackerSublog, TrackerUser, trackerUsers } from "../../db"; import { durationToReadable } from "../misc/time"; /** Uses `durationToReadable` from src/util/misc/time.ts but without the need to parse a Duration object */ @@ -36,3 +36,21 @@ export function confirmEmbed(title: string): InteractionReplyOptions { ephemeral: true, }; } +/** Sort database entrys, given a sorting and maping callback */ +export const sortDbEntrysToString = (db:TrackerSublog[], sortFn:(a:TrackerSublog, b:TrackerSublog) => number, mapFn:(log: TrackerSublog) => any) => db + .sort((a, b) => sortFn(a, b)) + .slice(0, 5) + .map((log) => mapFn(log)) + .join("\n") +/** Sort database games, given a sorting and maping callback */ +export const sortDbGamesToString = (db:TrackerGame[], sortFn:(a:TrackerGame, b:TrackerGame) => number, mapFn:(log: TrackerGame) => any) => db + .sort((a, b) => sortFn(a, b)) + .slice(0, 5) + .map((log) => mapFn(log)) + .join("\n") +/** Sort database users, given a sorting and maping callback */ +export const sortDbUsersToString = (db:TrackerUser[], sortFn:(a:TrackerUser, b:TrackerUser) => number, mapFn:(log: TrackerUser) => any) => db + .sort((a, b) => sortFn(a, b)) + .slice(0, 5) + .map((log) => mapFn(log)) + .join("\n") \ No newline at end of file diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 3ca3b98..eaad38a 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -3,7 +3,7 @@ import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; import { discordTimestamp } from "../misc/time"; import { trackerGames, trackerLogs, trackerUsers } from "../../db"; import { config } from "../../config"; -import { makeTimeString } from "./helper"; +import { makeTimeString, sortDbEntrysToString, sortDbGamesToString, sortDbUsersToString } from "./helper"; import { gameNoEntry, userNoEntry, userNoGameEntry } from "./messages"; export async function playtime(interaction: ChatInputCommandInteraction) { @@ -142,37 +142,17 @@ export async function latest(interaction: ChatInputCommandInteraction) { } export async function stats(interaction: ChatInputCommandInteraction) { // get 5 most logged games and make string - const mostLoggedGame = trackerGames - .array() - .sort((a, b) => a.logs - b.logs) - .reverse() - .slice(0, 5) - .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) - .join("\n"); + const mostLoggedGame = sortDbGamesToString(trackerGames.array(), (a,b)=>b.logs-a.logs, (game)=>`${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) + // get 5 most played games and make string - const mostPlayedGame = trackerGames - .array() - .sort((a, b) => a.playtime - b.playtime) - .reverse() - .slice(0, 5) - .map((game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) - .join("\n"); + const mostPlayedGame = sortDbGamesToString(trackerGames.array(), (a,b)=>b.playtime-a.playtime, (game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) + // get 5 most logged users and make string - const mostLoggedUser = trackerUsers - .array() - .sort((a, b) => a.logs - b.logs) - .reverse() - .splice(0, 5) - .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) - .join("\n"); + const mostLoggedUser = sortDbUsersToString(trackerUsers.array(), (a,b)=>b.logs-a.logs, (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) + // get 5 most playtime users and make string - const mostPlayedUser = trackerUsers - .array() - .sort((a, b) => a.playtime - b.playtime) - .reverse() - .splice(0, 5) - .map((user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) - .join("\n"); + const mostPlayedUser = sortDbUsersToString(trackerUsers.array(), (a,b)=>b.playtime-a.playtime, (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) + // get latest system wide logs and make string const latestLogs = trackerLogs .array() diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 003362c..2cb1a4b 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -2,7 +2,7 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discor import { config } from "../../config"; import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; import { discordTimestamp } from "../misc/time"; -import { makeTimeString, sortGamesLogs, sortGamesPlaytime } from "./helper"; +import { makeTimeString, sortDbEntrysToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { memberNotFound, userNoEntry, userNotFound } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { @@ -26,17 +26,11 @@ export async function userStats(interaction: ChatInputCommandInteraction) { } // make sorted list of most played games and make string - const mostPlayed = db.games - .sort((a, b) => b.playtime - a.playtime) - .slice(0, 5) - .map((game) => `${game.id}: ${makeTimeString(game.playtime)}`) - .join("\n"); + const mostPlayed = sortDbEntrysToString(db.games, (a,b)=>b.playtime-a.playtime, (game)=>`${game.id}: ${makeTimeString(game.playtime)}`) + // make sorted list of most logged games and make string - const mostLogged = db.games - .sort((a, b) => b.logs - a.logs) - .slice(0, 5) - .map((game) => `${game.id}: ${game.logs} logs`) - .join("\n"); + const mostLogged = sortDbEntrysToString(db.games, (a,b)=>b.logs-a.logs, (game) => `${game.id}: ${game.logs} logs`) + // get latest logs and make string const latestLogs = db.lastlogs.map((log) => `${trackerLogs.get(log)?.gameName}`).join("\n"); // get total users playtime, logs and games From 3b83d680d4b3a0834a95acc4c9040736b6a2a288 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 21:59:56 +0200 Subject: [PATCH 42/92] formatting --- src/util/tracker/gameCommands.ts | 14 +++++++++++--- src/util/tracker/helper.ts | 27 +++++++++++++++++++++------ src/util/tracker/subCommands.ts | 24 ++++++++++++++++++++---- src/util/tracker/userCommands.ts | 12 ++++++++++-- 4 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 98ffda7..44bf385 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -26,11 +26,19 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { } // get top 5 played games and make a string - const mostPlayed = sortDbEntrysToString(db.users, (a,b)=>b.playtime-a.playtime, (user)=>`<@${user.id}>: ${makeTimeString(user.playtime)}`) + const mostPlayed = sortDbEntrysToString( + db.users, + (a, b) => b.playtime - a.playtime, + (user) => `<@${user.id}>: ${makeTimeString(user.playtime)}` + ); // get top 5 logged games and make a string - const mostLogged = sortDbEntrysToString(db.users, (a,b)=>b.logs-a.logs, (user)=>`<@${user.id}>: ${user.logs} logs`) - + const mostLogged = sortDbEntrysToString( + db.users, + (a, b) => b.logs - a.logs, + (user) => `<@${user.id}>: ${user.logs} logs` + ); + // format latest logs into a string const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); // get total playtime, logs and users diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 637f96a..ced6784 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -37,20 +37,35 @@ export function confirmEmbed(title: string): InteractionReplyOptions { }; } /** Sort database entrys, given a sorting and maping callback */ -export const sortDbEntrysToString = (db:TrackerSublog[], sortFn:(a:TrackerSublog, b:TrackerSublog) => number, mapFn:(log: TrackerSublog) => any) => db +export const sortDbEntrysToString = ( + db: TrackerSublog[], + sortFn: (a: TrackerSublog, b: TrackerSublog) => number, + mapFn: (log: TrackerSublog) => any +) => + db .sort((a, b) => sortFn(a, b)) .slice(0, 5) .map((log) => mapFn(log)) - .join("\n") + .join("\n"); /** Sort database games, given a sorting and maping callback */ -export const sortDbGamesToString = (db:TrackerGame[], sortFn:(a:TrackerGame, b:TrackerGame) => number, mapFn:(log: TrackerGame) => any) => db +export const sortDbGamesToString = ( + db: TrackerGame[], + sortFn: (a: TrackerGame, b: TrackerGame) => number, + mapFn: (log: TrackerGame) => any +) => + db .sort((a, b) => sortFn(a, b)) .slice(0, 5) .map((log) => mapFn(log)) - .join("\n") + .join("\n"); /** Sort database users, given a sorting and maping callback */ -export const sortDbUsersToString = (db:TrackerUser[], sortFn:(a:TrackerUser, b:TrackerUser) => number, mapFn:(log: TrackerUser) => any) => db +export const sortDbUsersToString = ( + db: TrackerUser[], + sortFn: (a: TrackerUser, b: TrackerUser) => number, + mapFn: (log: TrackerUser) => any +) => + db .sort((a, b) => sortFn(a, b)) .slice(0, 5) .map((log) => mapFn(log)) - .join("\n") \ No newline at end of file + .join("\n"); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index eaad38a..2e3e8b0 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -142,16 +142,32 @@ export async function latest(interaction: ChatInputCommandInteraction) { } export async function stats(interaction: ChatInputCommandInteraction) { // get 5 most logged games and make string - const mostLoggedGame = sortDbGamesToString(trackerGames.array(), (a,b)=>b.logs-a.logs, (game)=>`${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}`) + const mostLoggedGame = sortDbGamesToString( + trackerGames.array(), + (a, b) => b.logs - a.logs, + (game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}` + ); // get 5 most played games and make string - const mostPlayedGame = sortDbGamesToString(trackerGames.array(), (a,b)=>b.playtime-a.playtime, (game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}`) + const mostPlayedGame = sortDbGamesToString( + trackerGames.array(), + (a, b) => b.playtime - a.playtime, + (game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}` + ); // get 5 most logged users and make string - const mostLoggedUser = sortDbUsersToString(trackerUsers.array(), (a,b)=>b.logs-a.logs, (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs`) + const mostLoggedUser = sortDbUsersToString( + trackerUsers.array(), + (a, b) => b.logs - a.logs, + (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs` + ); // get 5 most playtime users and make string - const mostPlayedUser = sortDbUsersToString(trackerUsers.array(), (a,b)=>b.playtime-a.playtime, (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}`) + const mostPlayedUser = sortDbUsersToString( + trackerUsers.array(), + (a, b) => b.playtime - a.playtime, + (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}` + ); // get latest system wide logs and make string const latestLogs = trackerLogs diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 2cb1a4b..ceefee0 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -26,10 +26,18 @@ export async function userStats(interaction: ChatInputCommandInteraction) { } // make sorted list of most played games and make string - const mostPlayed = sortDbEntrysToString(db.games, (a,b)=>b.playtime-a.playtime, (game)=>`${game.id}: ${makeTimeString(game.playtime)}`) + const mostPlayed = sortDbEntrysToString( + db.games, + (a, b) => b.playtime - a.playtime, + (game) => `${game.id}: ${makeTimeString(game.playtime)}` + ); // make sorted list of most logged games and make string - const mostLogged = sortDbEntrysToString(db.games, (a,b)=>b.logs-a.logs, (game) => `${game.id}: ${game.logs} logs`) + const mostLogged = sortDbEntrysToString( + db.games, + (a, b) => b.logs - a.logs, + (game) => `${game.id}: ${game.logs} logs` + ); // get latest logs and make string const latestLogs = db.lastlogs.map((log) => `${trackerLogs.get(log)?.gameName}`).join("\n"); From 5f05bf8d5e5b695f288fea6327d82e72be1f2c72 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 22:00:43 +0200 Subject: [PATCH 43/92] I just always run `npm run prettier`, just makes my life easier lol --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2663dac..f7ba202 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "type": "module", "scripts": { "prettify": "eslint ./src/ --fix && npx prettier -w .", + "prettier": "eslint ./src/ --fix && npx prettier -w .", "build": "tsc -p tsconfig.json", "run": "node --experimental-specifier-resolution=node .", "start": "npm run build && npm run run", From 760e3931e29e3c80272689ee5ec8d0bd672bf861 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 22:02:38 +0200 Subject: [PATCH 44/92] tsc errors fixed (unused imports) --- src/commands/TrackerCommands.ts | 1 - src/interactions/autocompleters/TrackerAutocomple.ts | 2 +- src/util/tracker/subCommands.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 655a69a..36bf30c 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -1,7 +1,6 @@ import { Command } from "../interactions/interactionClasses"; import { ChatInputCommandInteraction, - EmbedBuilder, SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, } from "discord.js"; diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index e91a414..65b3bdd 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -1,6 +1,6 @@ import { Autocompleter } from "../interactionClasses"; import { AutocompleteInteraction } from "discord.js"; -import { trackerBlacklist, trackerGames, trackerUsers } from "../../db"; +import { trackerGames } from "../../db"; import { blacklist, user } from "../../util/tracker/autocompleteHelper"; class Tracker extends Autocompleter { diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 2e3e8b0..a6a4851 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -3,7 +3,7 @@ import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; import { discordTimestamp } from "../misc/time"; import { trackerGames, trackerLogs, trackerUsers } from "../../db"; import { config } from "../../config"; -import { makeTimeString, sortDbEntrysToString, sortDbGamesToString, sortDbUsersToString } from "./helper"; +import { makeTimeString, sortDbGamesToString, sortDbUsersToString } from "./helper"; import { gameNoEntry, userNoEntry, userNoGameEntry } from "./messages"; export async function playtime(interaction: ChatInputCommandInteraction) { From a0703ec0fedf292af009a23afa57d72220c52aef Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 22:09:53 +0200 Subject: [PATCH 45/92] fixed defaulting value --- src/commands/TrackerCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 36bf30c..c024f21 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -27,7 +27,7 @@ class Tracker extends Command { const subCommand = interaction.options.getSubcommand(); const statistics = interaction.options.getString("statistic") ? interaction.options.getString("statistic") - : "stats"; + : "general statistics"; const action = interaction.options.getString("action"); switch (subCommand) { case "user": From a723ea268f8690ae14a30dfdef99b3a7784224c2 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 22:16:21 +0200 Subject: [PATCH 46/92] fixed blacklist autocomplete showing all games --- src/util/tracker/autocompleteHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts index 7e32bb2..18b38a4 100644 --- a/src/util/tracker/autocompleteHelper.ts +++ b/src/util/tracker/autocompleteHelper.ts @@ -35,7 +35,7 @@ export function user( export function blacklist(action: string) { const results = []; - if (action == "add") { + if (action == "add" || action == "rem") { // only show not blacklisted games when "add" is chosen as the action, else only show blacklisted games results.push( ...trackerGames From 030e088813b2beec535e487a9d63180c0ec021e2 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 26 Apr 2023 22:17:57 +0200 Subject: [PATCH 47/92] types and very smart moment in package.json --- package.json | 2 +- src/util/tracker/helper.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f7ba202..466bfb2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "type": "module", "scripts": { "prettify": "eslint ./src/ --fix && npx prettier -w .", - "prettier": "eslint ./src/ --fix && npx prettier -w .", + "prettier": "npm run prettify", "build": "tsc -p tsconfig.json", "run": "node --experimental-specifier-resolution=node .", "start": "npm run build && npm run run", diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index ced6784..1f0d0a8 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -40,7 +40,7 @@ export function confirmEmbed(title: string): InteractionReplyOptions { export const sortDbEntrysToString = ( db: TrackerSublog[], sortFn: (a: TrackerSublog, b: TrackerSublog) => number, - mapFn: (log: TrackerSublog) => any + mapFn: (log: TrackerSublog) => string ) => db .sort((a, b) => sortFn(a, b)) @@ -51,7 +51,7 @@ export const sortDbEntrysToString = ( export const sortDbGamesToString = ( db: TrackerGame[], sortFn: (a: TrackerGame, b: TrackerGame) => number, - mapFn: (log: TrackerGame) => any + mapFn: (log: TrackerGame) => string ) => db .sort((a, b) => sortFn(a, b)) @@ -62,7 +62,7 @@ export const sortDbGamesToString = ( export const sortDbUsersToString = ( db: TrackerUser[], sortFn: (a: TrackerUser, b: TrackerUser) => number, - mapFn: (log: TrackerUser) => any + mapFn: (log: TrackerUser) => string ) => db .sort((a, b) => sortFn(a, b)) From a289347e1d0bbf2ffba52cf915ad402a247bbd1a Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:36:59 +0200 Subject: [PATCH 48/92] clearer function name --- src/util/tracker/blacklistCommands.ts | 6 +++--- src/util/tracker/messages.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts index 1cb38c4..aa5c84c 100644 --- a/src/util/tracker/blacklistCommands.ts +++ b/src/util/tracker/blacklistCommands.ts @@ -1,7 +1,7 @@ import { ChatInputCommandInteraction } from "discord.js"; import { trackerBlacklist } from "../../db"; import { hasAdminPerms } from "../misc/permissions"; -import { adminOnly, GameAdded, gameNotOnBlacklist, gameOnBlacklist, GameRemoved } from "./messages"; +import { adminOnly, makeGameAddedEmbed, gameNotOnBlacklist, gameOnBlacklist, makeGameRemovedEmbed } from "./messages"; export async function addBlacklist(interaction: ChatInputCommandInteraction) { // check for admin permissions @@ -19,7 +19,7 @@ export async function addBlacklist(interaction: ChatInputCommandInteraction) { } // add game to blacklist trackerBlacklist.push("", game.toLowerCase()); - interaction.reply(GameAdded(game)); + interaction.reply(makeGameAddedEmbed(game)); } export async function remBlacklist(interaction: ChatInputCommandInteraction) { // check for admin permissions @@ -38,7 +38,7 @@ export async function remBlacklist(interaction: ChatInputCommandInteraction) { "", db.filter((g) => g.toLowerCase() != game.toLowerCase()) ); - interaction.reply(GameRemoved(game)); + interaction.reply(makeGameRemovedEmbed(game)); return; } // send error diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index be84664..3125baa 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -23,6 +23,6 @@ export const gameOnBlacklist = errorEmbed("already on blacklist", "Given game is /** User when there is an attempt to remove a game from the blacklist whole its not on there */ export const gameNotOnBlacklist = errorEmbed("not on blacklist", "Given game is not on the blacklist."); /** Used to confirm the adding of a given game to the blacklist */ -export const GameAdded = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); +export const makeGameAddedEmbed = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); /** Used to confirm the removal of a given game from the blacklist */ -export const GameRemoved = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); +export const makeGameRemovedEmbed = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); From 194474548783ccd786ecb6fbb671dfaf58082f39 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:39:54 +0200 Subject: [PATCH 49/92] use ?? instead of inline-if --- src/commands/TrackerCommands.ts | 4 +--- src/util/tracker/blacklistCommands.ts | 8 +++++++- src/util/tracker/messages.ts | 6 ++++-- src/util/tracker/subCommands.ts | 4 +--- src/util/tracker/userCommands.ts | 6 +++--- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index c024f21..3bb5cad 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -25,9 +25,7 @@ class Tracker extends Command { return; } const subCommand = interaction.options.getSubcommand(); - const statistics = interaction.options.getString("statistic") - ? interaction.options.getString("statistic") - : "general statistics"; + const statistics = interaction.options.getString("statistic") ?? "general statistics"; const action = interaction.options.getString("action"); switch (subCommand) { case "user": diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts index aa5c84c..e994c26 100644 --- a/src/util/tracker/blacklistCommands.ts +++ b/src/util/tracker/blacklistCommands.ts @@ -1,7 +1,13 @@ import { ChatInputCommandInteraction } from "discord.js"; import { trackerBlacklist } from "../../db"; import { hasAdminPerms } from "../misc/permissions"; -import { adminOnly, makeGameAddedEmbed, gameNotOnBlacklist, gameOnBlacklist, makeGameRemovedEmbed } from "./messages"; +import { + adminOnly, + gameNotOnBlacklist, + gameOnBlacklist, + makeGameAddedEmbed, + makeGameRemovedEmbed, +} from "./messages"; export async function addBlacklist(interaction: ChatInputCommandInteraction) { // check for admin permissions diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index 3125baa..c41535e 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -23,6 +23,8 @@ export const gameOnBlacklist = errorEmbed("already on blacklist", "Given game is /** User when there is an attempt to remove a game from the blacklist whole its not on there */ export const gameNotOnBlacklist = errorEmbed("not on blacklist", "Given game is not on the blacklist."); /** Used to confirm the adding of a given game to the blacklist */ -export const makeGameAddedEmbed = (game: string) => confirmEmbed(`"${game}" has been added to the blacklist!`); +export const makeGameAddedEmbed = (game: string) => + confirmEmbed(`"${game}" has been added to the blacklist!`); /** Used to confirm the removal of a given game from the blacklist */ -export const makeGameRemovedEmbed = (game: string) => confirmEmbed(`"${game}" has been removed from the blacklist!`); +export const makeGameRemovedEmbed = (game: string) => + confirmEmbed(`"${game}" has been removed from the blacklist!`); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index a6a4851..2f0d72f 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -59,9 +59,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { } export async function logs(interaction: ChatInputCommandInteraction) { // get target user and game - const targetUser = interaction.options.getUser("user") - ? interaction.options.getUser("user") - : interaction.user; + const targetUser = interaction.options.getUser("user") ?? interaction.user; const targetGame = interaction.options.getString("game"); let text = ""; // used later in the final embed diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index ceefee0..3521773 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -7,7 +7,7 @@ import { memberNotFound, userNoEntry, userNotFound } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { // get target user and default to command executer if not given - const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + const target = interaction.options.getUser("user") ?? interaction.user; if (!target) { await interaction.reply(userNotFound); return; @@ -98,7 +98,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { } export async function userLast(interaction: ChatInputCommandInteraction) { // get target user, if not given default to executer - const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + const target = interaction.options.getUser("user") ?? interaction.user; if (!target) { await interaction.reply(userNotFound); return; @@ -151,7 +151,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { } export async function userTop(interaction: ChatInputCommandInteraction, filter: string) { // get target user, default to executer if not given - const target = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.user; + const target = interaction.options.getUser("user") ?? interaction.user; if (!target) { await interaction.reply(userNotFound); return; From e5038b497e1c2132cdc89c14ddcd855d3c96c6f0 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:47:53 +0200 Subject: [PATCH 50/92] dont => don't --- src/commands/TrackerCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 3bb5cad..e45b6cf 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -72,7 +72,7 @@ class Tracker extends Command { if (!interaction.replied) { await interaction.reply({ content: - "this can happen when you dont follow the order of the given options. Sadly thats a bug by discord (options dont get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", + "this can happen when you don't follow the order of the given options. Sadly thats a bug by discord (options don't get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", ephemeral: true, }); } From 9ee2e1bc3a691ff4cb98ac1f02df47efdd6e77df Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:48:28 +0200 Subject: [PATCH 51/92] two ifs into one or --- src/events/presenceUpdate.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index b87ce92..c68af5d 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -5,8 +5,7 @@ import { config } from "../config"; const userCache = new Map>(); export default async function presenceUpdate(oldPresence: Presence | null, newPresence: Presence) { - if (!config.tracking) return; - if (newPresence.user?.bot) return; + if (!config.tracking || newPresence.user?.bot) return; const { started, stopped } = await getChangedActivities(oldPresence, newPresence); if (!started.length && !stopped.length) return; From 4342bdc552eba7517aaa1ee30f2f6caee0fcf087 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:49:55 +0200 Subject: [PATCH 52/92] compact down and make it const --- src/interactions/autocompleters/TrackerAutocomple.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 65b3bdd..5824162 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -39,8 +39,7 @@ class Tracker extends Autocompleter { options = blacklist(action); } - let filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)); - filteredOptions = filteredOptions.slice(0, 25); + const filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)).slice(0, 25); await interaction.respond(filteredOptions.map((c) => ({ name: c, value: c }))); } From 1b1f3f45ea47dc33ca27b965f4fcf79b369b697e Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:51:52 +0200 Subject: [PATCH 53/92] moved to time --- src/util/misc/time.ts | 10 ++++++++++ src/util/tracker/gameCommands.ts | 11 +---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/util/misc/time.ts b/src/util/misc/time.ts index 7669486..00e3258 100644 --- a/src/util/misc/time.ts +++ b/src/util/misc/time.ts @@ -56,3 +56,13 @@ export async function checkDuration( return null; } else return iso; } + + +// 60seconds * 60 minutes * 24 hours = One day +export const dayInSeconds = 60 * 60 * 24; +// seconds of day to milliseconds +export const dayInMillis = dayInSeconds * 1000; +// 7 times a day = week +export const weekInMillis = dayInMillis * 7; +// 4 times a week + 2.4 days = (average) month +export const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; \ No newline at end of file diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 44bf385..aeeacbc 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -1,19 +1,10 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; import { config } from "../../config"; import { trackerGames, trackerLogs } from "../../db"; -import { discordTimestamp } from "../misc/time"; +import { dayInMillis, discordTimestamp, monthInMillis, weekInMillis } from "../misc/time"; import { makeTimeString, sortDbEntrysToString } from "./helper"; import { gameNoEntry } from "./messages"; -// 60seconds * 60 minutes * 24 hours = One day -const dayInSeconds = 60 * 60 * 24; -// seconds of day to milliseconds -const dayInMillis = dayInSeconds * 1000; -// 7 times a day = week -const weekInMillis = dayInMillis * 7; -// 4 times a week + 2.4 days = (average) month -const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; - export async function gameStats(interaction: ChatInputCommandInteraction) { // get game option const targetGame = interaction.options.getString("game", true); From b4a59a9a76c139073add823a44889d3f003320f2 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 16:52:59 +0200 Subject: [PATCH 54/92] entrys => entries --- src/util/misc/time.ts | 3 +-- src/util/tracker/gameCommands.ts | 6 +++--- src/util/tracker/helper.ts | 4 ++-- src/util/tracker/userCommands.ts | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/util/misc/time.ts b/src/util/misc/time.ts index 00e3258..62f9094 100644 --- a/src/util/misc/time.ts +++ b/src/util/misc/time.ts @@ -57,7 +57,6 @@ export async function checkDuration( } else return iso; } - // 60seconds * 60 minutes * 24 hours = One day export const dayInSeconds = 60 * 60 * 24; // seconds of day to milliseconds @@ -65,4 +64,4 @@ export const dayInMillis = dayInSeconds * 1000; // 7 times a day = week export const weekInMillis = dayInMillis * 7; // 4 times a week + 2.4 days = (average) month -export const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; \ No newline at end of file +export const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index aeeacbc..b0ac180 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -2,7 +2,7 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discor import { config } from "../../config"; import { trackerGames, trackerLogs } from "../../db"; import { dayInMillis, discordTimestamp, monthInMillis, weekInMillis } from "../misc/time"; -import { makeTimeString, sortDbEntrysToString } from "./helper"; +import { makeTimeString, sortDbEntriesToString } from "./helper"; import { gameNoEntry } from "./messages"; export async function gameStats(interaction: ChatInputCommandInteraction) { @@ -17,14 +17,14 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { } // get top 5 played games and make a string - const mostPlayed = sortDbEntrysToString( + const mostPlayed = sortDbEntriesToString( db.users, (a, b) => b.playtime - a.playtime, (user) => `<@${user.id}>: ${makeTimeString(user.playtime)}` ); // get top 5 logged games and make a string - const mostLogged = sortDbEntrysToString( + const mostLogged = sortDbEntriesToString( db.users, (a, b) => b.logs - a.logs, (user) => `<@${user.id}>: ${user.logs} logs` diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 1f0d0a8..553e0a3 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -36,8 +36,8 @@ export function confirmEmbed(title: string): InteractionReplyOptions { ephemeral: true, }; } -/** Sort database entrys, given a sorting and maping callback */ -export const sortDbEntrysToString = ( +/** Sort database entries, given a sorting and maping callback */ +export const sortDbEntriesToString = ( db: TrackerSublog[], sortFn: (a: TrackerSublog, b: TrackerSublog) => number, mapFn: (log: TrackerSublog) => string diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 3521773..2853a06 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -2,7 +2,7 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discor import { config } from "../../config"; import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; import { discordTimestamp } from "../misc/time"; -import { makeTimeString, sortDbEntrysToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; +import { makeTimeString, sortDbEntriesToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { memberNotFound, userNoEntry, userNotFound } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { @@ -26,14 +26,14 @@ export async function userStats(interaction: ChatInputCommandInteraction) { } // make sorted list of most played games and make string - const mostPlayed = sortDbEntrysToString( + const mostPlayed = sortDbEntriesToString( db.games, (a, b) => b.playtime - a.playtime, (game) => `${game.id}: ${makeTimeString(game.playtime)}` ); // make sorted list of most logged games and make string - const mostLogged = sortDbEntrysToString( + const mostLogged = sortDbEntriesToString( db.games, (a, b) => b.logs - a.logs, (game) => `${game.id}: ${game.logs} logs` From c15376c58368787d9db6da845b0ac97f27d3739c Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 17:08:29 +0200 Subject: [PATCH 55/92] no need to check for string --- src/interactions/autocompleters/TrackerAutocomple.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 5824162..653307b 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -15,16 +15,14 @@ class Tracker extends Autocompleter { // if user-option is given, use it, else default to executer const userId = - typeof interaction.options.get("user")?.value == "string" - ? interaction.options.get("user")?.value - : interaction.user.id; + interaction.options.get("user")?.value ?? + interaction.user.id; const game = interaction.options.get("game")?.value; // if action-option is given, use it, else default to add const action = - typeof interaction.options.get("action")?.value == "string" - ? interaction.options.get("action")?.value - : "add"; + interaction.options.get("action")?.value ?? + "add"; // make ts happy... if (typeof action != "string") return; From fdc4054924b1c6563eee69e88d51de9d36d69604 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 27 Apr 2023 17:09:35 +0200 Subject: [PATCH 56/92] formatting --- src/interactions/autocompleters/TrackerAutocomple.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 653307b..50bd787 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -14,15 +14,11 @@ class Tracker extends Autocompleter { const option = interaction.options.getFocused(true).name; // if user-option is given, use it, else default to executer - const userId = - interaction.options.get("user")?.value ?? - interaction.user.id; + const userId = interaction.options.get("user")?.value ?? interaction.user.id; const game = interaction.options.get("game")?.value; // if action-option is given, use it, else default to add - const action = - interaction.options.get("action")?.value ?? - "add"; + const action = interaction.options.get("action")?.value ?? "add"; // make ts happy... if (typeof action != "string") return; From 012ad89a094e7fbd967f81c55e6da996acc9a53c Mon Sep 17 00:00:00 2001 From: Murphy Date: Thu, 27 Apr 2023 20:54:00 +0200 Subject: [PATCH 57/92] small styling --- src/db.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db.ts b/src/db.ts index a7ca2cd..686562c 100644 --- a/src/db.ts +++ b/src/db.ts @@ -15,9 +15,9 @@ export type proposalID = string; // key of proposalDb export type userID = string; // Discord user id /* ------------------ --LaTeX Database - ------------------ +------------------ +- LaTeX Database - +------------------ */ export const latexDb = new Enmap("latex"); // key: reply id From 6176403038acae5688d05df0a6ed19092abf64b0 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 28 Apr 2023 15:40:58 +0200 Subject: [PATCH 58/92] tims stamp functions --- src/util/misc/time.ts | 61 +++++++++++++++++++++++++++++--- src/util/tracker/subCommands.ts | 10 ++---- src/util/tracker/userCommands.ts | 12 +++---- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/util/misc/time.ts b/src/util/misc/time.ts index 62f9094..e788146 100644 --- a/src/util/misc/time.ts +++ b/src/util/misc/time.ts @@ -1,13 +1,65 @@ import { ChatInputCommandInteraction, CommandInteraction, ModalSubmitInteraction } from "discord.js"; import { DateTime, Duration } from "luxon"; -export function discordTimestamp(datetime: DateTime | number): string { - return ``; +function makeTimestamp(datetime: DateTime | number, end?: string): string { + return ``; } +/** + * Make a discord Timestamp which displays like this: `1 January 1970 01:00` \ + * @param datetime DateTime object or number in Seconds */ +export function discordTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime); +} +/** + * Make a discord Timestamp which displays like this: `53 years ago` \ + * @param datetime DateTime object or number in Seconds */ export function discordRelativeTimestamp(datetime: DateTime | number): string { - return ``; + return makeTimestamp(datetime, "R"); +} +/** + * Make a discord Timestamp which displays like this: `01:00` \ + * @param datetime DateTime object or number in Seconds */ +export function discordShortTimeTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime, "t"); +} +/** + * Make a discord Timestamp which displays like this: `01:00:00` \ + * @param datetime DateTime object or number in Seconds */ +export function discordLongTimeTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime, "T"); +} +/** + * Make a discord Timestamp which displays like this: `01/01/1970` \ + * @param datetime DateTime object or number as Seconds */ +export function discordShortDateTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime, "d"); } +/** + * Make a discord Timestamp which displays like this: `1 January 1970` \ + * @param datetime DateTime object or number in Seconds */ +export function discordLongDateTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime, "D"); +} +/** + * Make a discord Timestamp which displays like this: `1 January 1970 01:00` \ + * @param datetime DateTime object or number in Seconds */ +export function discordLongDateWithShortTimeTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime, "f"); +} +/** + * Make a discord Timestamp which displays like this: `Thursday, 1 January 1970 01:00` \ + * @param datetime DateTime object or number in Seconds */ +export function discordLongDateWithDateOfWeekAndShortTimeTimestamp(datetime: DateTime | number): string { + return makeTimestamp(datetime, "F"); +} +/** + * Make a custom Timestamp which displays like this: `01/01/1970 01:00` \ + * @param datetime DateTime object or number in Seconds */ +export function shortDateAndShortTimeTimestamp(datetime: DateTime | number): string { + return discordShortDateTimestamp(datetime) + discordShortTimeTimestamp(datetime) +} + export function durationToReadable(duration: Duration, short = false): string { if (short) { @@ -59,9 +111,10 @@ export async function checkDuration( // 60seconds * 60 minutes * 24 hours = One day export const dayInSeconds = 60 * 60 * 24; -// seconds of day to milliseconds export const dayInMillis = dayInSeconds * 1000; // 7 times a day = week +export const weekInSeconds = dayInSeconds * 7; export const weekInMillis = dayInMillis * 7; // 4 times a week + 2.4 days = (average) month +export const monthInSeconds = weekInSeconds * 4 + dayInSeconds * 2.4167 export const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 2f0d72f..391eb10 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -1,6 +1,6 @@ import { EmbedBuilder } from "discord.js"; import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; -import { discordTimestamp } from "../misc/time"; +import { discordTimestamp, shortDateAndShortTimeTimestamp } from "../misc/time"; import { trackerGames, trackerLogs, trackerUsers } from "../../db"; import { config } from "../../config"; import { makeTimeString, sortDbGamesToString, sortDbUsersToString } from "./helper"; @@ -122,9 +122,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `<@${log.userid}>\n\n${makeTimeString(log.playtime)}`, + value: `<@${log.userid}>\n${shortDateAndShortTimeTimestamp(new Date(log.time).getTime() / 1000)}\n${makeTimeString(log.playtime)}`, }) ); @@ -174,9 +172,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map( (log) => - ` <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` + `${shortDateAndShortTimeTimestamp(new Date(log.time).getTime() / 1000)} <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` ) .join("\n"); // get total playtime of all games diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 2853a06..396d46f 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -1,7 +1,7 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; import { config } from "../../config"; import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; -import { discordTimestamp } from "../misc/time"; +import { dayInMillis, discordTimestamp, monthInMillis, shortDateAndShortTimeTimestamp, weekInMillis } from "../misc/time"; import { makeTimeString, sortDbEntriesToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { memberNotFound, userNoEntry, userNotFound } from "./messages"; @@ -51,9 +51,9 @@ export async function userStats(interaction: ChatInputCommandInteraction) { const range = Date.now() - firstSeen; // calculate average paytime per day/week/month/user/log const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / (86400 * 1000))) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / 2628000000)) + Math.round(totalPlaytime / (range / dayInMillis)) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / weekInMillis))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / monthInMillis)) )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) )}`; @@ -128,9 +128,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: entry.gameName, - value: `\n${makeTimeString(entry.playtime)}`, + value: `${shortDateAndShortTimeTimestamp(new Date(entry.time).getTime() / 1000)}\n${makeTimeString(entry.playtime)}`, }); }); From fa10720f3aa94a9cc2e8cce27e7cef23e16196c7 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 28 Apr 2023 16:54:26 +0200 Subject: [PATCH 59/92] formatting --- src/util/misc/time.ts | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/util/misc/time.ts b/src/util/misc/time.ts index e788146..a03acaa 100644 --- a/src/util/misc/time.ts +++ b/src/util/misc/time.ts @@ -2,65 +2,66 @@ import { ChatInputCommandInteraction, CommandInteraction, ModalSubmitInteraction import { DateTime, Duration } from "luxon"; function makeTimestamp(datetime: DateTime | number, end?: string): string { - return ``; + return ``; } -/** +/** * Make a discord Timestamp which displays like this: `1 January 1970 01:00` \ * @param datetime DateTime object or number in Seconds */ export function discordTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime); } -/** +/** * Make a discord Timestamp which displays like this: `53 years ago` \ * @param datetime DateTime object or number in Seconds */ export function discordRelativeTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "R"); } -/** +/** * Make a discord Timestamp which displays like this: `01:00` \ * @param datetime DateTime object or number in Seconds */ export function discordShortTimeTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "t"); } -/** +/** * Make a discord Timestamp which displays like this: `01:00:00` \ * @param datetime DateTime object or number in Seconds */ export function discordLongTimeTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "T"); } -/** +/** * Make a discord Timestamp which displays like this: `01/01/1970` \ * @param datetime DateTime object or number as Seconds */ export function discordShortDateTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "d"); } -/** +/** * Make a discord Timestamp which displays like this: `1 January 1970` \ * @param datetime DateTime object or number in Seconds */ export function discordLongDateTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "D"); } -/** +/** * Make a discord Timestamp which displays like this: `1 January 1970 01:00` \ * @param datetime DateTime object or number in Seconds */ export function discordLongDateWithShortTimeTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "f"); } -/** +/** * Make a discord Timestamp which displays like this: `Thursday, 1 January 1970 01:00` \ * @param datetime DateTime object or number in Seconds */ export function discordLongDateWithDateOfWeekAndShortTimeTimestamp(datetime: DateTime | number): string { return makeTimestamp(datetime, "F"); } -/** +/** * Make a custom Timestamp which displays like this: `01/01/1970 01:00` \ * @param datetime DateTime object or number in Seconds */ export function shortDateAndShortTimeTimestamp(datetime: DateTime | number): string { - return discordShortDateTimestamp(datetime) + discordShortTimeTimestamp(datetime) + return discordShortDateTimestamp(datetime) + discordShortTimeTimestamp(datetime); } - export function durationToReadable(duration: Duration, short = false): string { if (short) { return duration.normalize().rescale().toHuman({ listStyle: "short", unitDisplay: "narrow" }); @@ -116,5 +117,5 @@ export const dayInMillis = dayInSeconds * 1000; export const weekInSeconds = dayInSeconds * 7; export const weekInMillis = dayInMillis * 7; // 4 times a week + 2.4 days = (average) month -export const monthInSeconds = weekInSeconds * 4 + dayInSeconds * 2.4167 +export const monthInSeconds = weekInSeconds * 4 + dayInSeconds * 2.4167; export const monthInMillis = weekInMillis * 4 + dayInMillis * 2.4167; From fc7f9a7960714f4c8a6717d8cb4456e7008df9b4 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 28 Apr 2023 16:56:59 +0200 Subject: [PATCH 60/92] =?UTF-8?q?introducing:=20=F0=9F=8C=9Fserializing?= =?UTF-8?q?=F0=9F=8C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db.ts | 110 +++++++++++++----- .../autocompleters/TrackerAutocomple.ts | 2 +- src/util/tracker/autocompleteHelper.ts | 2 +- src/util/tracker/gameCommands.ts | 29 ++--- src/util/tracker/presence.ts | 36 +++--- src/util/tracker/subCommands.ts | 26 +++-- src/util/tracker/userCommands.ts | 30 ++--- 7 files changed, 153 insertions(+), 82 deletions(-) diff --git a/src/db.ts b/src/db.ts index 686562c..e8378a1 100644 --- a/src/db.ts +++ b/src/db.ts @@ -52,66 +52,124 @@ interface InternalReminder { } /* ------------------------------ -- Activity Tracker Database - ------------------------------ +-------------------- +- Tracker Database - +-------------------- */ /** Interface for tracker logs */ export interface TrackerLog { + /** id of the log */ + id: string; /** When a log got logged */ - time: ISODate; - /** How long a game got played */ + date: Date; + /** How long a game got played in ms*/ playtime: number; /** User id of the User who just got logged */ - userid: userID; - /** Game id of the game which just got logged */ + userID: userID; + /** Game name of the game which just got logged */ gameName: string; } /** Interface for stripped down tracking logs */ export interface TrackerSublog { /** User- or Game- ID */ - id: userID; + name: userID; /** How often a User or Game got logged */ logs: number; - /** How long a User played a certain game - * (Or the other way around) - */ + /** How long a User played a certain game in ms + * (Or the other way around) */ playtime: number; } /** Interface for tracking users */ export interface TrackerUser { - /** How much a user played */ + /** How long a user played a game in ms*/ playtime: number; - /** Reference to the first log assosiated with the user */ - firstlog: string; + /** ID of the first log associated with the user */ + firstlog: TrackerLog; /** How often a user got logged */ logs: number; - /** List of lastest logs */ - lastlogs: string[]; - /** Stripped down logs about how often a game got logged and how long it was played */ + /** List of the latest 5 logs in form of the log ID*/ + lastlogs: TrackerLog[]; + /** Little infos about the game which the user played (id, log amount, time played in total)*/ games: TrackerSublog[]; } /** Interface for tracking games */ export interface TrackerGame { - /** How much a game got played */ + /** How long a game got played in ms*/ playtime: number; - /** Reference to the first log assosiated with the game */ - firstlog: string; + /** ID of the first log associated with the game */ + firstlog: TrackerLog; /** How often a game got logged */ logs: number; - /** List of latest logs */ + //** List of the latest 5 logs in form of the log ID*/ + lastlogs: TrackerLog[]; + /** Little infos about the users who played the game (id, log amount, time played in total)*/ + users: TrackerSublog[]; +} + +interface internalTrackerLog { + id: string; + date: ISODate; + playtime: number; + userID: userID; + gameName: string; +} +interface internalTrackerGameAndUser { + playtime: number; + firstlog: string; + logs: number; lastlogs: string[]; - /** Stripped down logs about how often a user got logged and how long they played */ + games: TrackerSublog[]; users: TrackerSublog[]; } -export const trackerLogs = new Enmap({ name: "trackerLogs" }); -export const trackerUsers = new Enmap({ name: "trackerUsers" }); -export const trackerGames = new Enmap({ name: "trackerGames" }); +// just in case a referenced log wasnt found. +// Although there really isnt any reason why there should accrue such a case. +// Better safe than sorry and to make ts happy of course! +const unknownTrackerLog: TrackerLog = { + id: "-1", + date: new Date(2000, 4, 0, 4), + playtime: -1, + userID: "0000000000000000000", + gameName: "[unknown log]", +}; + +export const trackerLogs = new Enmap({ + name: "trackerLogs", + serializer: (data) => ({ ...data, date: data.date.toISOString() }), + deserializer: (data) => ({ ...data, date: new Date(data.date) }), +}); +export const trackerUsers = new Enmap({ + name: "trackerUsers", + serializer: (data) => ({ + ...data, + firstlog: data.firstlog.id, + lastlogs: data.lastlogs.map((log) => log.id), + users: [], + }), + deserializer: (data) => ({ + ...data, + firstlog: trackerLogs.get(data.firstlog) ?? unknownTrackerLog, + lastlogs: data.lastlogs.map((logID) => trackerLogs.get(logID) ?? unknownTrackerLog), + }), +}); +export const trackerGames = new Enmap({ + name: "trackerGames", + serializer: (data) => ({ + ...data, + firstlog: data.firstlog.id, + lastlogs: data.lastlogs.map((log) => log.id), + games: [], + }), + deserializer: (data) => ({ + ...data, + firstlog: trackerLogs.get(data.firstlog) ?? unknownTrackerLog, + lastlogs: data.lastlogs.map((logID) => trackerLogs.get(logID) ?? unknownTrackerLog), + }), +}); export const trackerBlacklist = new Enmap({ name: "trackerBlacklist" }); trackerBlacklist.ensure("", []); @@ -231,7 +289,7 @@ interface InternalPollEvent { /* ---------------- - Jam Database - ------..--------- +---------------- */ // key: unique id diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 50bd787..895b22a 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -10,7 +10,7 @@ class Tracker extends Autocompleter { async execute(interaction: AutocompleteInteraction): Promise { const sub = interaction.options.getSubcommand(); - const focus = interaction.options.getFocused().toLowerCase() as string; + const focus = (interaction.options.getFocused() as string).toLowerCase(); const option = interaction.options.getFocused(true).name; // if user-option is given, use it, else default to executer diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts index 18b38a4..abcd0ba 100644 --- a/src/util/tracker/autocompleteHelper.ts +++ b/src/util/tracker/autocompleteHelper.ts @@ -11,7 +11,7 @@ export function user( if (typeof userid == "string") { if (trackerUsers.get(userid)?.games) { // list games a user has played (if user has played anything) - trackerUsers.get(userid)?.games.forEach((g) => results.push(g.id)); + trackerUsers.get(userid)?.games.forEach((g) => results.push(g.name)); } else { // show this when user didnt play any games results.push("[user has not played any games yet]"); diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index b0ac180..4416110 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -1,7 +1,13 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; import { config } from "../../config"; import { trackerGames, trackerLogs } from "../../db"; -import { dayInMillis, discordTimestamp, monthInMillis, weekInMillis } from "../misc/time"; +import { + dayInMillis, + discordTimestamp, + monthInMillis, + shortDateAndShortTimeTimestamp, + weekInMillis, +} from "../misc/time"; import { makeTimeString, sortDbEntriesToString } from "./helper"; import { gameNoEntry } from "./messages"; @@ -20,24 +26,24 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const mostPlayed = sortDbEntriesToString( db.users, (a, b) => b.playtime - a.playtime, - (user) => `<@${user.id}>: ${makeTimeString(user.playtime)}` + (user) => `<@${user.name}>: ${makeTimeString(user.playtime)}` ); // get top 5 logged games and make a string const mostLogged = sortDbEntriesToString( db.users, (a, b) => b.logs - a.logs, - (user) => `<@${user.id}>: ${user.logs} logs` + (user) => `<@${user.name}>: ${user.logs} logs` ); // format latest logs into a string - const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log)?.userid}>`).join("\n"); + const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log.id)?.userID}>`).join("\n"); // get total playtime, logs and users const totalPlaytime = db.playtime; const totalLogs = db.logs; const users = db.users.length; // get first log of game (first log is iso string) - const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + const firstSeen = db.firstlog.date.getTime(); // calculate the range from first log to now const range = Date.now() - firstSeen; // calculate daily/weekly/monthly and per-log average playtime @@ -51,7 +57,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { // temporary sorted list of most playtime/log (users) const tmp = db.users.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; // make most playtime/log string - const mostPlaytime = `<@${tmp.id}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ + const mostPlaytime = `<@${tmp.name}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; @@ -104,10 +110,9 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { // make embed field for every log await logs.forEach(async (log) => { // get and validate log (db.lastlogs is a list of numbers as strings, not the actual log) - const entry = trackerLogs.get(log); - if (!entry) return; + if (!log) return; // get user who owns the log - const user = await interaction.client.users.fetch(entry.userid); + const user = await interaction.client.users.fetch(log.userID); // skip if user doesnt exist anymore if (!user) return; @@ -115,9 +120,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: user.username, - value: `\n${makeTimeString(entry.playtime)}`, + value: `${shortDateAndShortTimeTimestamp(log.date.getTime() / 1000)}\n${makeTimeString(log.playtime)}`, }); }); @@ -158,7 +161,7 @@ export async function gameTop(interaction: ChatInputCommandInteraction, filter: await users.forEach(async (user) => { fields.push({ inline: true, - name: (await interaction.client.users.fetch(user.id)).username, + name: (await interaction.client.users.fetch(user.name)).username, value: filter == "playtime" ? makeTimeString(user.playtime) + "\n" + user.logs + " logs" diff --git a/src/util/tracker/presence.ts b/src/util/tracker/presence.ts index 84cd7a2..f507ad0 100644 --- a/src/util/tracker/presence.ts +++ b/src/util/tracker/presence.ts @@ -39,17 +39,17 @@ export async function logIt(gameName: string, userID: string, timePlayed: number if (isBlacklisted(gameName)) return; const logID = (trackerLogs.count + 1).toString(); // make new a unique ID for the new log - ensure(gameName, userID, logID); // make sure user and game exist - updateUser(gameName, userID, timePlayed, logID); // update users db entry - updateGame(gameName, userID, timePlayed, logID); // update games db entry - addLog(gameName, userID, timePlayed, logID); // make a db entry for this new log + const log = addLog(gameName, userID, timePlayed, logID); // make a db entry for this new log + ensure(gameName, userID, log); // make sure user and game exist + updateUser(gameName, userID, timePlayed, log); // update users db entry + updateGame(gameName, userID, timePlayed, log); // update games db entry } /** Ensures that there is a entry in the user and game db before continuing*/ -function ensure(gameName: string, userID: string, logID: string) { +function ensure(gameName: string, userID: string, log: TrackerLog) { if (!trackerGames.has(gameName.toLowerCase())) { const data: TrackerGame = { playtime: 0, - firstlog: logID, + firstlog: log, logs: 0, lastlogs: [], users: [], @@ -59,7 +59,7 @@ function ensure(gameName: string, userID: string, logID: string) { if (!trackerUsers.has(userID)) { const data: TrackerUser = { playtime: 0, - firstlog: logID, + firstlog: log, logs: 0, lastlogs: [], games: [], @@ -68,7 +68,7 @@ function ensure(gameName: string, userID: string, logID: string) { } } /** updates the latest logs, logs, game and playtime of a user */ -function updateUser(gameName: string, userID: string, timePlayed: number, logID: string) { +function updateUser(gameName: string, userID: string, timePlayed: number, log: TrackerLog) { gameName = gameName.toLowerCase(); const data: TrackerUser | undefined = trackerUsers.get(userID); @@ -77,14 +77,14 @@ function updateUser(gameName: string, userID: string, timePlayed: number, logID: // remove oldest log from the latest log history if (data.lastlogs.length >= 5) data.lastlogs.shift(); - data.lastlogs.push(logID); // add newest log to the log history + data.lastlogs.push(log); // add newest log to the log history data.logs += 1; data.playtime += timePlayed; - let gamelog = data.games.find((e) => e.id == gameName); + let gamelog = data.games.find((g) => g.name == gameName); if (!gamelog) { - gamelog = data.games[data.games.push({ id: gameName, logs: 0, playtime: 0 }) - 1]; + gamelog = data.games[data.games.push({ name: gameName, logs: 0, playtime: 0 }) - 1]; } gamelog.logs += 1; gamelog.playtime += timePlayed; @@ -92,7 +92,7 @@ function updateUser(gameName: string, userID: string, timePlayed: number, logID: trackerUsers.set(userID, data); } /** updates the latest logs, logs, users and playtime of a game*/ -function updateGame(gameName: string, userID: string, timePlayed: number, logID: string) { +function updateGame(gameName: string, userID: string, timePlayed: number, log: TrackerLog) { gameName = gameName.toLowerCase(); const data: TrackerGame | undefined = trackerGames.get(gameName); @@ -100,14 +100,14 @@ function updateGame(gameName: string, userID: string, timePlayed: number, logID: // remove oldest log from the latest log history if (data.lastlogs.length >= 5) data.lastlogs.shift(); - data.lastlogs.push(logID); // add newest log to the log history + data.lastlogs.push(log); // add newest log to the log history data.logs += 1; data.playtime += timePlayed; - let gamelog = data.users.find((e) => e.id == userID); + let gamelog = data.users.find((e) => e.name == userID); if (!gamelog) { - gamelog = data.users[data.users.push({ id: userID, logs: 0, playtime: 0 }) - 1]; + gamelog = data.users[data.users.push({ name: userID, logs: 0, playtime: 0 }) - 1]; } gamelog.logs += 1; gamelog.playtime += timePlayed; @@ -117,10 +117,12 @@ function updateGame(gameName: string, userID: string, timePlayed: number, logID: /** Make a new log */ function addLog(gameName: string, userID: string, timePlayed: number, logID: string) { const data: TrackerLog = { + id: logID, gameName: gameName, - userid: userID, - time: new Date().toISOString(), + userID: userID, + date: new Date(), playtime: timePlayed, }; trackerLogs.set(logID, data); + return data; } diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 391eb10..e2d861b 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -42,7 +42,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { // load db and get user.games and find target game in there const db = trackerUsers .get(targetUser.id) - ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); + ?.games.find((g) => g.name.toLowerCase() == targetGame.toLowerCase()); if (!db) { await interaction.reply(userNoGameEntry); return; @@ -93,7 +93,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { // load db and get user.games and find target game in there const db = trackerUsers .get(targetUser.id) - ?.games.find((g) => g.id.toLowerCase() == targetGame.toLowerCase()); + ?.games.find((g) => g.name.toLowerCase() == targetGame.toLowerCase()); if (!db) { await interaction.reply(userNoGameEntry); return; @@ -114,7 +114,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { // latest system logs const logs = trackerLogs .array() - .sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()) + .sort((a, b) => b.date.getTime() - a.date.getTime()) .slice(0, 5); // make embed for each log @@ -122,7 +122,9 @@ export async function latest(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `<@${log.userid}>\n${shortDateAndShortTimeTimestamp(new Date(log.time).getTime() / 1000)}\n${makeTimeString(log.playtime)}`, + value: `<@${log.userID}>\n${shortDateAndShortTimeTimestamp( + log.date.getTime() / 1000 + )}\n${makeTimeString(log.playtime)}`, }) ); @@ -141,28 +143,28 @@ export async function stats(interaction: ChatInputCommandInteraction) { const mostLoggedGame = sortDbGamesToString( trackerGames.array(), (a, b) => b.logs - a.logs, - (game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${game.logs}` + (game) => `${game.lastlogs[0].gameName}: ${game.logs}` ); // get 5 most played games and make string const mostPlayedGame = sortDbGamesToString( trackerGames.array(), (a, b) => b.playtime - a.playtime, - (game) => `${trackerLogs.get(game.lastlogs[0])?.gameName}: ${makeTimeString(game.playtime)}` + (game) => `${game.lastlogs[0].gameName}: ${makeTimeString(game.playtime)}` ); // get 5 most logged users and make string const mostLoggedUser = sortDbUsersToString( trackerUsers.array(), (a, b) => b.logs - a.logs, - (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${user.logs} logs` + (user) => `<@${user.lastlogs[0].userID}>: ${user.logs} logs` ); // get 5 most playtime users and make string const mostPlayedUser = sortDbUsersToString( trackerUsers.array(), (a, b) => b.playtime - a.playtime, - (user) => `<@${trackerLogs.get(user.lastlogs[0])?.userid}>: ${makeTimeString(user.playtime)}` + (user) => `<@${user.lastlogs[0].userID}>: ${makeTimeString(user.playtime)}` ); // get latest system wide logs and make string @@ -172,7 +174,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map( (log) => - `${shortDateAndShortTimeTimestamp(new Date(log.time).getTime() / 1000)} <@${log.userid}> ${log.gameName}: ${makeTimeString(log.playtime)}` + `${shortDateAndShortTimeTimestamp(log.date.getTime() / 1000)} <@${log.userID}> ${ + log.gameName + }: ${makeTimeString(log.playtime)}` ) .join("\n"); // get total playtime of all games @@ -187,7 +191,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { // get amount of users const users = trackerUsers.count; // get first log (time is iso string) - const firstSeen = new Date(trackerLogs.array()[0].time).getTime(); + const firstSeen = trackerLogs.array()[0].date.getTime(); // make range from first log to now const range = Date.now() - firstSeen; // calculate average playtime per day/week/month/game/log @@ -201,7 +205,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { // temporary sorted list based on playtime/log const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; // make string from temporary list - const mostPlaytime = `${trackerLogs.get(tmp.lastlogs[0])?.gameName}: ${makeTimeString( + const mostPlaytime = `${tmp.lastlogs[0].gameName}: ${makeTimeString( Math.round(tmp.playtime / tmp.logs) )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 396d46f..bc7aef9 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -1,7 +1,13 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; import { config } from "../../config"; -import { trackerLogs, TrackerSublog, trackerUsers } from "../../db"; -import { dayInMillis, discordTimestamp, monthInMillis, shortDateAndShortTimeTimestamp, weekInMillis } from "../misc/time"; +import { TrackerSublog, trackerUsers } from "../../db"; +import { + dayInMillis, + discordTimestamp, + monthInMillis, + shortDateAndShortTimeTimestamp, + weekInMillis, +} from "../misc/time"; import { makeTimeString, sortDbEntriesToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { memberNotFound, userNoEntry, userNotFound } from "./messages"; @@ -29,24 +35,24 @@ export async function userStats(interaction: ChatInputCommandInteraction) { const mostPlayed = sortDbEntriesToString( db.games, (a, b) => b.playtime - a.playtime, - (game) => `${game.id}: ${makeTimeString(game.playtime)}` + (game) => `${game.name}: ${makeTimeString(game.playtime)}` ); // make sorted list of most logged games and make string const mostLogged = sortDbEntriesToString( db.games, (a, b) => b.logs - a.logs, - (game) => `${game.id}: ${game.logs} logs` + (game) => `${game.name}: ${game.logs} logs` ); // get latest logs and make string - const latestLogs = db.lastlogs.map((log) => `${trackerLogs.get(log)?.gameName}`).join("\n"); + const latestLogs = db.lastlogs.map((log) => `${log.gameName}`).join("\n"); // get total users playtime, logs and games const totalPlaytime = db.playtime; const games = db.games.length; const totalLogs = db.logs; // get first log (time is iso string) - const firstSeen = new Date(trackerLogs.get(db.firstlog)?.time as string).getTime(); + const firstSeen = db.firstlog.date.getTime(); // make range from first log to now const range = Date.now() - firstSeen; // calculate average paytime per day/week/month/user/log @@ -60,7 +66,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { // temporary sorted list of games playtime/log const tmp = db.games.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; // make string from first element of temporary list - const mostPlaytime = `${tmp.id}: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ + const mostPlaytime = `${tmp.name}: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; @@ -122,13 +128,11 @@ export async function userLast(interaction: ChatInputCommandInteraction) { // make field for every log logs.forEach((log) => { - // get log entry (latest logs is list of number as string, not the actual log data) - const entry = trackerLogs.get(log); - if (!entry) return; + if (!log) return; fields.push({ inline: true, - name: entry.gameName, - value: `${shortDateAndShortTimeTimestamp(new Date(entry.time).getTime() / 1000)}\n${makeTimeString(entry.playtime)}`, + name: log.gameName, + value: `${shortDateAndShortTimeTimestamp(log.date.getTime() / 1000)}\n${makeTimeString(log.playtime)}`, }); }); @@ -186,7 +190,7 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: games.forEach((game) => { fields.push({ inline: true, - name: game.id, + name: game.name, value: filter == "playtime" ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` From c069fe2091e12cfa1e0ea86d89408320b833c676 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 28 Apr 2023 17:09:20 +0200 Subject: [PATCH 61/92] weird double slash comment "fixed" --- src/db.ts | 2 +- src/util/tracker/messages.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db.ts b/src/db.ts index e8378a1..dd38940 100644 --- a/src/db.ts +++ b/src/db.ts @@ -104,7 +104,7 @@ export interface TrackerGame { firstlog: TrackerLog; /** How often a game got logged */ logs: number; - //** List of the latest 5 logs in form of the log ID*/ + /** List of the latest 5 logs in form of the log ID*/ lastlogs: TrackerLog[]; /** Little infos about the users who played the game (id, log amount, time played in total)*/ users: TrackerSublog[]; diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index c41535e..23f53df 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -14,7 +14,7 @@ export const userNoGameEntry = errorEmbed( "no database entry", "Given user has no entrys in the database associated with given the game." ); -//** Used when a given game is not found in the tracker database */ +/** Used when a given game is not found in the tracker database */ export const gameNoEntry = errorEmbed("no database entry", "Given game has no entry in the database."); /** Used when a non admin user tryes to use a admin only command */ export const adminOnly = errorEmbed("admin only", "This command can only be executed by an admin."); From 7a1248e9f227382ad4439e586272004d31890226 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 28 Apr 2023 17:11:46 +0200 Subject: [PATCH 62/92] note --- src/util/tracker/note.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/util/tracker/note.txt diff --git a/src/util/tracker/note.txt b/src/util/tracker/note.txt new file mode 100644 index 0000000..572d855 --- /dev/null +++ b/src/util/tracker/note.txt @@ -0,0 +1,4 @@ +I didnt test the serializing stuff because the values in the interfaces changed and the new obviously code doesnt work on them anymore. +There are at least no problems starting the bot but there might still be issues when executing commands. +Ill let the bot run for a bit and then test. +Once tested this note will be removed. \ No newline at end of file From 80a3c625a24a3e1c1a94d823f42dcf40a5d028ab Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 14:00:21 +0200 Subject: [PATCH 63/92] T typed function (and serializing works :D) --- src/util/tracker/gameCommands.ts | 8 +++---- src/util/tracker/helper.ts | 38 ++++++++------------------------ src/util/tracker/note.txt | 4 ---- src/util/tracker/subCommands.ts | 12 +++++----- src/util/tracker/userCommands.ts | 6 ++--- 5 files changed, 22 insertions(+), 46 deletions(-) delete mode 100644 src/util/tracker/note.txt diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 4416110..8305f24 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -1,6 +1,6 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; import { config } from "../../config"; -import { trackerGames, trackerLogs } from "../../db"; +import { trackerGames, trackerLogs, TrackerSublog } from "../../db"; import { dayInMillis, discordTimestamp, @@ -8,7 +8,7 @@ import { shortDateAndShortTimeTimestamp, weekInMillis, } from "../misc/time"; -import { makeTimeString, sortDbEntriesToString } from "./helper"; +import { makeTimeString, sortDbToString } from "./helper"; import { gameNoEntry } from "./messages"; export async function gameStats(interaction: ChatInputCommandInteraction) { @@ -23,14 +23,14 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { } // get top 5 played games and make a string - const mostPlayed = sortDbEntriesToString( + const mostPlayed = sortDbToString( db.users, (a, b) => b.playtime - a.playtime, (user) => `<@${user.name}>: ${makeTimeString(user.playtime)}` ); // get top 5 logged games and make a string - const mostLogged = sortDbEntriesToString( + const mostLogged = sortDbToString( db.users, (a, b) => b.logs - a.logs, (user) => `<@${user.name}>: ${user.logs} logs` diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 553e0a3..196d5b9 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -1,6 +1,6 @@ import { EmbedBuilder, InteractionReplyOptions } from "discord.js"; import { Duration } from "luxon"; -import { TrackerGame, TrackerSublog, TrackerUser, trackerUsers } from "../../db"; +import { trackerUsers } from "../../db"; import { durationToReadable } from "../misc/time"; /** Uses `durationToReadable` from src/util/misc/time.ts but without the need to parse a Duration object */ @@ -36,36 +36,16 @@ export function confirmEmbed(title: string): InteractionReplyOptions { ephemeral: true, }; } + /** Sort database entries, given a sorting and maping callback */ -export const sortDbEntriesToString = ( - db: TrackerSublog[], - sortFn: (a: TrackerSublog, b: TrackerSublog) => number, - mapFn: (log: TrackerSublog) => string -) => - db - .sort((a, b) => sortFn(a, b)) - .slice(0, 5) - .map((log) => mapFn(log)) - .join("\n"); -/** Sort database games, given a sorting and maping callback */ -export const sortDbGamesToString = ( - db: TrackerGame[], - sortFn: (a: TrackerGame, b: TrackerGame) => number, - mapFn: (log: TrackerGame) => string -) => - db - .sort((a, b) => sortFn(a, b)) - .slice(0, 5) - .map((log) => mapFn(log)) - .join("\n"); -/** Sort database users, given a sorting and maping callback */ -export const sortDbUsersToString = ( - db: TrackerUser[], - sortFn: (a: TrackerUser, b: TrackerUser) => number, - mapFn: (log: TrackerUser) => string -) => - db +export function sortDbToString ( + db: T[], + sortFn: (a: T, b:T) => number, + mapFn: (log: T) => string +) { + return db .sort((a, b) => sortFn(a, b)) .slice(0, 5) .map((log) => mapFn(log)) .join("\n"); +} \ No newline at end of file diff --git a/src/util/tracker/note.txt b/src/util/tracker/note.txt deleted file mode 100644 index 572d855..0000000 --- a/src/util/tracker/note.txt +++ /dev/null @@ -1,4 +0,0 @@ -I didnt test the serializing stuff because the values in the interfaces changed and the new obviously code doesnt work on them anymore. -There are at least no problems starting the bot but there might still be issues when executing commands. -Ill let the bot run for a bit and then test. -Once tested this note will be removed. \ No newline at end of file diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index e2d861b..d4db617 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -1,9 +1,9 @@ import { EmbedBuilder } from "discord.js"; import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; import { discordTimestamp, shortDateAndShortTimeTimestamp } from "../misc/time"; -import { trackerGames, trackerLogs, trackerUsers } from "../../db"; +import { TrackerGame, trackerGames, trackerLogs, TrackerUser, trackerUsers } from "../../db"; import { config } from "../../config"; -import { makeTimeString, sortDbGamesToString, sortDbUsersToString } from "./helper"; +import { makeTimeString, sortDbToString } from "./helper"; import { gameNoEntry, userNoEntry, userNoGameEntry } from "./messages"; export async function playtime(interaction: ChatInputCommandInteraction) { @@ -140,28 +140,28 @@ export async function latest(interaction: ChatInputCommandInteraction) { } export async function stats(interaction: ChatInputCommandInteraction) { // get 5 most logged games and make string - const mostLoggedGame = sortDbGamesToString( + const mostLoggedGame = sortDbToString( trackerGames.array(), (a, b) => b.logs - a.logs, (game) => `${game.lastlogs[0].gameName}: ${game.logs}` ); // get 5 most played games and make string - const mostPlayedGame = sortDbGamesToString( + const mostPlayedGame = sortDbToString( trackerGames.array(), (a, b) => b.playtime - a.playtime, (game) => `${game.lastlogs[0].gameName}: ${makeTimeString(game.playtime)}` ); // get 5 most logged users and make string - const mostLoggedUser = sortDbUsersToString( + const mostLoggedUser = sortDbToString( trackerUsers.array(), (a, b) => b.logs - a.logs, (user) => `<@${user.lastlogs[0].userID}>: ${user.logs} logs` ); // get 5 most playtime users and make string - const mostPlayedUser = sortDbUsersToString( + const mostPlayedUser = sortDbToString( trackerUsers.array(), (a, b) => b.playtime - a.playtime, (user) => `<@${user.lastlogs[0].userID}>: ${makeTimeString(user.playtime)}` diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index bc7aef9..e60b19b 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -8,7 +8,7 @@ import { shortDateAndShortTimeTimestamp, weekInMillis, } from "../misc/time"; -import { makeTimeString, sortDbEntriesToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; +import { makeTimeString, sortDbToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { memberNotFound, userNoEntry, userNotFound } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { @@ -32,14 +32,14 @@ export async function userStats(interaction: ChatInputCommandInteraction) { } // make sorted list of most played games and make string - const mostPlayed = sortDbEntriesToString( + const mostPlayed = sortDbToString( db.games, (a, b) => b.playtime - a.playtime, (game) => `${game.name}: ${makeTimeString(game.playtime)}` ); // make sorted list of most logged games and make string - const mostLogged = sortDbEntriesToString( + const mostLogged = sortDbToString( db.games, (a, b) => b.logs - a.logs, (game) => `${game.name}: ${game.logs} logs` From 1745863cd195d066976852ffdc132b2406f5c942 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 14:00:56 +0200 Subject: [PATCH 64/92] formatting --- src/util/tracker/helper.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 196d5b9..ed59486 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -38,14 +38,10 @@ export function confirmEmbed(title: string): InteractionReplyOptions { } /** Sort database entries, given a sorting and maping callback */ -export function sortDbToString ( - db: T[], - sortFn: (a: T, b:T) => number, - mapFn: (log: T) => string -) { +export function sortDbToString(db: T[], sortFn: (a: T, b: T) => number, mapFn: (log: T) => string) { return db .sort((a, b) => sortFn(a, b)) .slice(0, 5) .map((log) => mapFn(log)) .join("\n"); -} \ No newline at end of file +} From df5f978664e17b86eb1a211ee5e25972133d1e4f Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 14:01:29 +0200 Subject: [PATCH 65/92] round time for timestamps --- src/util/misc/time.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/misc/time.ts b/src/util/misc/time.ts index a03acaa..2726e2c 100644 --- a/src/util/misc/time.ts +++ b/src/util/misc/time.ts @@ -2,7 +2,7 @@ import { ChatInputCommandInteraction, CommandInteraction, ModalSubmitInteraction import { DateTime, Duration } from "luxon"; function makeTimestamp(datetime: DateTime | number, end?: string): string { - return ``; } From bc491feb4bc50e44ed6a5e566de591a15b497be6 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 14:02:29 +0200 Subject: [PATCH 66/92] big T --- src/commands/TrackerCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index e45b6cf..023b8cc 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -72,7 +72,7 @@ class Tracker extends Command { if (!interaction.replied) { await interaction.reply({ content: - "this can happen when you don't follow the order of the given options. Sadly thats a bug by discord (options don't get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", + "This can happen when you don't follow the order of the given options. Sadly thats a bug by discord (options don't get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", ephemeral: true, }); } From 6e6141307a9391e94972924a718531e7eda1ec88 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 21:47:54 +0200 Subject: [PATCH 67/92] npm runs --- .husky/pre-commit | 2 +- package.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index c46dfe1..fa5c531 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,3 +1,3 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm run prettify +npm run tidy diff --git a/package.json b/package.json index 466bfb2..24f6f53 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "private": "true", "type": "module", "scripts": { - "prettify": "eslint ./src/ --fix && npx prettier -w .", - "prettier": "npm run prettify", + "lint": "eslint ./src/", + "format": "npx prettier -w .", + "tidy": "npm run lint && npm run format", "build": "tsc -p tsconfig.json", "run": "node --experimental-specifier-resolution=node .", "start": "npm run build && npm run run", From 0d4a7472312ed757bd3ae9d69094fcf44bd1107c Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 22:05:40 +0200 Subject: [PATCH 68/92] removed brackets and update descriptions --- src/commands/TrackerCommands.ts | 44 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 023b8cc..6b60196 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -29,37 +29,35 @@ class Tracker extends Command { const action = interaction.options.getString("action"); switch (subCommand) { case "user": - if (statistics == "playtime") { + if (statistics == "playtime") await playtime(interaction); - } else if (statistics == "logs") { + else if (statistics == "logs") await logs(interaction); - } else if (statistics == "general statistics") { + else if (statistics == "general statistics") await userStats(interaction); - } else if (statistics == "top 5 most played games") { + else if (statistics == "top 5 most played games") await userTop(interaction, "playtime"); - } else if (statistics == "top 5 most logged games") { + else if (statistics == "top 5 most logged games") await userTop(interaction, "logs"); - } else if (statistics == "latest 5 logs") { + else if (statistics == "latest 5 logs") await userLast(interaction); - } return; case "game": - if (statistics == "general statistics") { + if (statistics == "general statistics") await gameStats(interaction); - } else if (statistics == "top 5 most played games") { + else if (statistics == "top 5 most played games") await gameTop(interaction, "playtime"); - } else if (statistics == "top 5 most logged games") { + else if (statistics == "top 5 most logged games") await gameTop(interaction, "logs"); - } else if (statistics == "latest 5 logs") { + else if (statistics == "latest 5 logs") await gameLast(interaction); - } + return; case "blacklist": - if (action == "add") { + if (action == "add") await addBlacklist(interaction); - } else if (action == "rem") { + else if (action == "rem") await remBlacklist(interaction); - } return; case "latest": await latest(interaction); @@ -87,25 +85,25 @@ class Tracker extends Command { ); return new SlashCommandBuilder() .setName("tracker") - .setDescription("The gateway to some cool stats about here being users.") + .setDescription("The gateway to some cool stats about the people on this server.") .addSubcommand((sub) => sub .setName("user") .setDescription("Get tracking stats about a user.") .addUserOption((opt) => - opt.setName("user").setDescription("the target user, if not given defaults to you.") + opt.setName("user").setDescription("The target user, if not given defaults to you.") ) .addStringOption((opt) => opt .setName("game") - .setDescription("the game of which to get playtime/logs from.") + .setDescription("The game of which to get playtime/logs from.") .setAutocomplete(true) ) .addStringOption((opt) => opt .setName("statistic") .setDescription( - "select what statistics should get shown. (Options depend on if a game is given or not)" + "Select what statistics should get shown. (Options depend on if a game is given or not)" ) .setAutocomplete(true) ) @@ -117,14 +115,14 @@ class Tracker extends Command { .addStringOption((opt) => opt .setName("game") - .setDescription("the game of which to get statistics from.") + .setDescription("The game of which to get statistics from.") .setRequired(true) .setAutocomplete(true) ) .addStringOption((opt) => opt .setName("statistic") - .setDescription("select what statistics should get shown.") + .setDescription("Select what statistics should get shown.") .addChoices( { name: "general statistics", value: "general statistics" }, { name: "top 5 most played games", value: "top 5 most played games" }, @@ -140,14 +138,14 @@ class Tracker extends Command { .addStringOption((opt) => opt .setName("action") - .setDescription("select which action should get executed.") + .setDescription("Select which action should get executed.") .addChoices({ name: "add", value: "add" }, { name: "remove", value: "rem" }) .setRequired(true) ) .addStringOption((opt) => opt .setName("game") - .setDescription("the game on which the action should be performed.") + .setDescription("The game on which the action should be performed.") .setRequired(true) .setAutocomplete(true) ) From 17cc9957204075eb1982c2ee12227acaed753d82 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 22:07:41 +0200 Subject: [PATCH 69/92] formatting --- src/commands/TrackerCommands.ts | 38 +++++++++++---------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 6b60196..a26c07f 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -29,35 +29,23 @@ class Tracker extends Command { const action = interaction.options.getString("action"); switch (subCommand) { case "user": - if (statistics == "playtime") - await playtime(interaction); - else if (statistics == "logs") - await logs(interaction); - else if (statistics == "general statistics") - await userStats(interaction); - else if (statistics == "top 5 most played games") - await userTop(interaction, "playtime"); - else if (statistics == "top 5 most logged games") - await userTop(interaction, "logs"); - else if (statistics == "latest 5 logs") - await userLast(interaction); + if (statistics == "playtime") await playtime(interaction); + else if (statistics == "logs") await logs(interaction); + else if (statistics == "general statistics") await userStats(interaction); + else if (statistics == "top 5 most played games") await userTop(interaction, "playtime"); + else if (statistics == "top 5 most logged games") await userTop(interaction, "logs"); + else if (statistics == "latest 5 logs") await userLast(interaction); return; case "game": - if (statistics == "general statistics") - await gameStats(interaction); - else if (statistics == "top 5 most played games") - await gameTop(interaction, "playtime"); - else if (statistics == "top 5 most logged games") - await gameTop(interaction, "logs"); - else if (statistics == "latest 5 logs") - await gameLast(interaction); - + if (statistics == "general statistics") await gameStats(interaction); + else if (statistics == "top 5 most played games") await gameTop(interaction, "playtime"); + else if (statistics == "top 5 most logged games") await gameTop(interaction, "logs"); + else if (statistics == "latest 5 logs") await gameLast(interaction); + return; case "blacklist": - if (action == "add") - await addBlacklist(interaction); - else if (action == "rem") - await remBlacklist(interaction); + if (action == "add") await addBlacklist(interaction); + else if (action == "rem") await remBlacklist(interaction); return; case "latest": await latest(interaction); From 7647d9997979c272b9a36f4a6c14a1119f419b84 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 29 Apr 2023 22:09:42 +0200 Subject: [PATCH 70/92] match other comments --- src/util/config/configInterface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/config/configInterface.ts b/src/util/config/configInterface.ts index c9915df..3f137d3 100644 --- a/src/util/config/configInterface.ts +++ b/src/util/config/configInterface.ts @@ -68,7 +68,7 @@ export interface BotConfig { | 22 | 23; - // tracker + // --- Tracker --- /** Enable or Disable the tracking feature (if disabled, tracking-commands and logging wont be available anymore, but tracking-Database wont be cleared!) */ tracking: boolean; } From e0707ee87bf6489e9dfe82b8b7392db9b0bef2e7 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 30 Apr 2023 00:10:56 +0200 Subject: [PATCH 71/92] A bunch of things, forgot to commit per comment --- src/util/misc/embeds.ts | 4 +- src/util/tracker/blacklistCommands.ts | 8 ++-- src/util/tracker/gameCommands.ts | 29 +++++-------- src/util/tracker/helper.ts | 13 ++---- src/util/tracker/messages.ts | 26 ++++++------ src/util/tracker/subCommands.ts | 60 ++++++++++++++------------- src/util/tracker/userCommands.ts | 32 ++++---------- 7 files changed, 72 insertions(+), 100 deletions(-) diff --git a/src/util/misc/embeds.ts b/src/util/misc/embeds.ts index d3fa4a7..a4f0195 100644 --- a/src/util/misc/embeds.ts +++ b/src/util/misc/embeds.ts @@ -1,8 +1,8 @@ import { EmbedBuilder } from "discord.js"; import { config } from "../../config"; -export function addEmbedFooter(embed: EmbedBuilder, timestamp = false): EmbedBuilder { - embed = embed.setColor(config.color).setAuthor({ +export function addEmbedFooter(embed: EmbedBuilder, timestamp = false, color = config.color): EmbedBuilder { + embed = embed.setColor(color).setAuthor({ name: `Made by me, ${config.botName} :)`, iconURL: config.iconURL, url: config.githubURL, diff --git a/src/util/tracker/blacklistCommands.ts b/src/util/tracker/blacklistCommands.ts index e994c26..ee919f3 100644 --- a/src/util/tracker/blacklistCommands.ts +++ b/src/util/tracker/blacklistCommands.ts @@ -5,8 +5,8 @@ import { adminOnly, gameNotOnBlacklist, gameOnBlacklist, - makeGameAddedEmbed, - makeGameRemovedEmbed, + makeGameAddedMessage, + makeGameRemovedMessage, } from "./messages"; export async function addBlacklist(interaction: ChatInputCommandInteraction) { @@ -25,7 +25,7 @@ export async function addBlacklist(interaction: ChatInputCommandInteraction) { } // add game to blacklist trackerBlacklist.push("", game.toLowerCase()); - interaction.reply(makeGameAddedEmbed(game)); + interaction.reply(makeGameAddedMessage(game)); } export async function remBlacklist(interaction: ChatInputCommandInteraction) { // check for admin permissions @@ -44,7 +44,7 @@ export async function remBlacklist(interaction: ChatInputCommandInteraction) { "", db.filter((g) => g.toLowerCase() != game.toLowerCase()) ); - interaction.reply(makeGameRemovedEmbed(game)); + interaction.reply(makeGameRemovedMessage(game)); return; } // send error diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 8305f24..43f39be 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -1,6 +1,7 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; import { config } from "../../config"; import { trackerGames, trackerLogs, TrackerSublog } from "../../db"; +import { addEmbedFooter } from "../misc/embeds"; import { dayInMillis, discordTimestamp, @@ -127,15 +128,11 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { // add a last field so embed looks better formatted fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = new EmbedBuilder() - .setColor(config.color) - .setAuthor({ - name: config.botName, - url: config.githubURL, - iconURL: config.iconURL, - }) - .setTitle(`Latest logs of ${targetGame}`) - .addFields(...fields); + const embed = addEmbedFooter( + new EmbedBuilder() + .setTitle(`Latest logs of ${targetGame}`) + .addFields(...fields) + ) await interaction.reply({ embeds: [embed] }); } @@ -172,15 +169,11 @@ export async function gameTop(interaction: ChatInputCommandInteraction, filter: // add one final field for formatting purposes fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = new EmbedBuilder() - .setColor(config.color) - .setAuthor({ - name: config.botName, - url: config.githubURL, - iconURL: config.iconURL, - }) - .setTitle(`Top user (${filter}) by ${targetGame}`) - .addFields(...fields); + const embed = addEmbedFooter( + new EmbedBuilder() + .setTitle(`Top user (${filter}) by ${targetGame}`) + .addFields(...fields) + ) await interaction.reply({ embeds: [embed] }); } diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index ed59486..3c750ad 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -18,21 +18,16 @@ export function sortGamesLogs(userId: string) { return data?.games.sort((a, b) => b.logs - a.logs); } /** Make an error embed */ -export function errorEmbed(title: string, info: string): InteractionReplyOptions { +export function errorMessage(title: string, info: string): InteractionReplyOptions { return { - embeds: [ - new EmbedBuilder() - .setTitle(`ERROR: ${title}`) - .setDescription(`further info:\n\`\`\`${info}\`\`\``) - .setColor([255, 0, 0]), - ], + content: `${title}: ${info}`, ephemeral: true, }; } /** Make an success embed */ -export function confirmEmbed(title: string): InteractionReplyOptions { +export function confirmMessage(title: string): InteractionReplyOptions { return { - embeds: [new EmbedBuilder().setTitle(title).setColor([0, 255, 0])], + content: title, ephemeral: true, }; } diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index 23f53df..14de440 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -1,30 +1,30 @@ -import { confirmEmbed, errorEmbed } from "./helper"; +import { confirmMessage, errorMessage } from "./helper"; /** Used when a user cant be fetched by `interaction.options.getUser("user")` or `interaction.user` */ -export const userNotFound = errorEmbed("user not found", "Cant get user option or interaction user."); +export const userNotFound = errorMessage("user not found", "Cant get user option or interaction user."); /** Used when `interaction.guild.members.get(userID)` does work with given user */ -export const memberNotFound = errorEmbed( +export const memberNotFound = errorMessage( "member not found", "Cant get guild member, user may not be on this server anymore." ); /** Used when a given user is not found in the tracker database */ -export const userNoEntry = errorEmbed("no database entry", "Given user has no entries in the database."); +export const userNoEntry = errorMessage("no database entry", "Given user has no entries in the database."); /** Used when a given game is not found in users tracker database entry */ -export const userNoGameEntry = errorEmbed( +export const userNoGameEntry = errorMessage( "no database entry", "Given user has no entrys in the database associated with given the game." ); /** Used when a given game is not found in the tracker database */ -export const gameNoEntry = errorEmbed("no database entry", "Given game has no entry in the database."); +export const gameNoEntry = errorMessage("no database entry", "Given game has no entry in the database."); /** Used when a non admin user tryes to use a admin only command */ -export const adminOnly = errorEmbed("admin only", "This command can only be executed by an admin."); +export const adminOnly = errorMessage("admin only", "This command can only be executed by an admin."); /** Used when there is an attempt to add a game to the blacklist while its already on there */ -export const gameOnBlacklist = errorEmbed("already on blacklist", "Given game is already on the blacklist."); +export const gameOnBlacklist = errorMessage("already on blacklist", "Given game is already on the blacklist."); /** User when there is an attempt to remove a game from the blacklist whole its not on there */ -export const gameNotOnBlacklist = errorEmbed("not on blacklist", "Given game is not on the blacklist."); +export const gameNotOnBlacklist = errorMessage("not on blacklist", "Given game is not on the blacklist."); /** Used to confirm the adding of a given game to the blacklist */ -export const makeGameAddedEmbed = (game: string) => - confirmEmbed(`"${game}" has been added to the blacklist!`); +export const makeGameAddedMessage = (game: string) => + confirmMessage(`"${game}" has been added to the blacklist!`); /** Used to confirm the removal of a given game from the blacklist */ -export const makeGameRemovedEmbed = (game: string) => - confirmEmbed(`"${game}" has been removed from the blacklist!`); +export const makeGameRemovedMessage = (game: string) => + confirmMessage(`"${game}" has been removed from the blacklist!`); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index d4db617..6d06b2f 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -5,6 +5,7 @@ import { TrackerGame, trackerGames, trackerLogs, TrackerUser, trackerUsers } fro import { config } from "../../config"; import { makeTimeString, sortDbToString } from "./helper"; import { gameNoEntry, userNoEntry, userNoGameEntry } from "./messages"; +import { addEmbedFooter } from "../misc/embeds"; export async function playtime(interaction: ChatInputCommandInteraction) { // get target user and game @@ -209,35 +210,36 @@ export async function stats(interaction: ChatInputCommandInteraction) { Math.round(tmp.playtime / tmp.logs) )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; - const embed = new EmbedBuilder() - .setTitle("System stats") - .setColor(config.color) - .addFields( - { inline: true, name: "Most logged games", value: mostLoggedGame }, - { inline: true, name: "Most played games", value: mostPlayedGame }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Most logged users", value: mostLoggedUser }, - { inline: true, name: "Users with most playtime", value: mostPlayedUser }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total games", value: games.toString() }, - { inline: true, name: "Total users", value: users.toString() } - ); + const embed = addEmbedFooter( + new EmbedBuilder() + .setTitle("System stats") + .addFields( + { inline: true, name: "Most logged games", value: mostLoggedGame }, + { inline: true, name: "Most played games", value: mostPlayedGame }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Most logged users", value: mostLoggedUser }, + { inline: true, name: "Users with most playtime", value: mostPlayedUser }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total games", value: games.toString() }, + { inline: true, name: "Total users", value: users.toString() } + ) + ) await interaction.reply({ embeds: [embed] }); } diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index e60b19b..73d01da 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -1,6 +1,6 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; -import { config } from "../../config"; import { TrackerSublog, trackerUsers } from "../../db"; +import { addEmbedFooter } from "../misc/embeds"; import { dayInMillis, discordTimestamp, @@ -70,13 +70,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; - const embed = new EmbedBuilder() - .setAuthor({ - name: config.botName, - url: config.githubURL, - iconURL: config.iconURL, - }) - .setColor(member.displayColor) + const embed = addEmbedFooter(new EmbedBuilder() .setTitle(`Tracking stats about ${member.displayName}`) .addFields( { inline: true, name: "Most logged game", value: mostLogged }, @@ -98,7 +92,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Total logs", value: totalLogs.toString() }, { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } - ); + )) await interaction.reply({ embeds: [embed] }); } @@ -139,15 +133,9 @@ export async function userLast(interaction: ChatInputCommandInteraction) { // add extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = new EmbedBuilder() - .setColor(member.displayColor) - .setAuthor({ - name: config.botName, - url: config.githubURL, - iconURL: config.iconURL, - }) + const embed = addEmbedFooter(new EmbedBuilder() .setTitle(`Latest logs by ${member?.displayName}`) - .addFields(...fields); + .addFields(...fields)) await interaction.reply({ embeds: [embed] }); } @@ -201,15 +189,9 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: // add one extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = new EmbedBuilder() - .setColor(member.displayColor) - .setAuthor({ - name: config.botName, - url: config.githubURL, - iconURL: config.iconURL, - }) + const embed = addEmbedFooter(new EmbedBuilder() .setTitle(`Top games (${filter}) by ${member?.displayName}`) - .addFields(...fields); + .addFields(...fields)) await interaction.reply({ embeds: [embed] }); } From 116550c2f8437f775f7fb9c95f7f14beb2af1075 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 30 Apr 2023 00:11:29 +0200 Subject: [PATCH 72/92] formatting --- src/util/tracker/gameCommands.ts | 12 +++---- src/util/tracker/messages.ts | 5 ++- src/util/tracker/subCommands.ts | 56 +++++++++++++++----------------- src/util/tracker/userCommands.ts | 20 ++++++------ 4 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 43f39be..59c80dc 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -129,10 +129,8 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = addEmbedFooter( - new EmbedBuilder() - .setTitle(`Latest logs of ${targetGame}`) - .addFields(...fields) - ) + new EmbedBuilder().setTitle(`Latest logs of ${targetGame}`).addFields(...fields) + ); await interaction.reply({ embeds: [embed] }); } @@ -170,10 +168,8 @@ export async function gameTop(interaction: ChatInputCommandInteraction, filter: fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = addEmbedFooter( - new EmbedBuilder() - .setTitle(`Top user (${filter}) by ${targetGame}`) - .addFields(...fields) - ) + new EmbedBuilder().setTitle(`Top user (${filter}) by ${targetGame}`).addFields(...fields) + ); await interaction.reply({ embeds: [embed] }); } diff --git a/src/util/tracker/messages.ts b/src/util/tracker/messages.ts index 14de440..3b03077 100644 --- a/src/util/tracker/messages.ts +++ b/src/util/tracker/messages.ts @@ -19,7 +19,10 @@ export const gameNoEntry = errorMessage("no database entry", "Given game has no /** Used when a non admin user tryes to use a admin only command */ export const adminOnly = errorMessage("admin only", "This command can only be executed by an admin."); /** Used when there is an attempt to add a game to the blacklist while its already on there */ -export const gameOnBlacklist = errorMessage("already on blacklist", "Given game is already on the blacklist."); +export const gameOnBlacklist = errorMessage( + "already on blacklist", + "Given game is already on the blacklist." +); /** User when there is an attempt to remove a game from the blacklist whole its not on there */ export const gameNotOnBlacklist = errorMessage("not on blacklist", "Given game is not on the blacklist."); /** Used to confirm the adding of a given game to the blacklist */ diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 6d06b2f..4fda544 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -211,35 +211,33 @@ export async function stats(interaction: ChatInputCommandInteraction) { )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; const embed = addEmbedFooter( - new EmbedBuilder() - .setTitle("System stats") - .addFields( - { inline: true, name: "Most logged games", value: mostLoggedGame }, - { inline: true, name: "Most played games", value: mostPlayedGame }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Most logged users", value: mostLoggedUser }, - { inline: true, name: "Users with most playtime", value: mostPlayedUser }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total games", value: games.toString() }, - { inline: true, name: "Total users", value: users.toString() } - ) - ) + new EmbedBuilder().setTitle("System stats").addFields( + { inline: true, name: "Most logged games", value: mostLoggedGame }, + { inline: true, name: "Most played games", value: mostPlayedGame }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Most logged users", value: mostLoggedUser }, + { inline: true, name: "Users with most playtime", value: mostPlayedUser }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total logs", value: totalLogs.toString() }, + { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Total games", value: games.toString() }, + { inline: true, name: "Total users", value: users.toString() } + ) + ); await interaction.reply({ embeds: [embed] }); } diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 73d01da..18004d0 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -70,9 +70,8 @@ export async function userStats(interaction: ChatInputCommandInteraction) { tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; - const embed = addEmbedFooter(new EmbedBuilder() - .setTitle(`Tracking stats about ${member.displayName}`) - .addFields( + const embed = addEmbedFooter( + new EmbedBuilder().setTitle(`Tracking stats about ${member.displayName}`).addFields( { inline: true, name: "Most logged game", value: mostLogged }, { inline: true, name: "Game with most playtime", value: mostPlayed }, { inline: false, name: "_ _", value: "_ _" }, @@ -92,7 +91,8 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Total logs", value: totalLogs.toString() }, { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } - )) + ) + ); await interaction.reply({ embeds: [embed] }); } @@ -133,9 +133,9 @@ export async function userLast(interaction: ChatInputCommandInteraction) { // add extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = addEmbedFooter(new EmbedBuilder() - .setTitle(`Latest logs by ${member?.displayName}`) - .addFields(...fields)) + const embed = addEmbedFooter( + new EmbedBuilder().setTitle(`Latest logs by ${member?.displayName}`).addFields(...fields) + ); await interaction.reply({ embeds: [embed] }); } @@ -189,9 +189,9 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: // add one extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = addEmbedFooter(new EmbedBuilder() - .setTitle(`Top games (${filter}) by ${member?.displayName}`) - .addFields(...fields)) + const embed = addEmbedFooter( + new EmbedBuilder().setTitle(`Top games (${filter}) by ${member?.displayName}`).addFields(...fields) + ); await interaction.reply({ embeds: [embed] }); } From ca6e903cf123829c72b8ee94cce3c7b21310f966 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 30 Apr 2023 00:20:10 +0200 Subject: [PATCH 73/92] unused import --- src/util/tracker/helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 3c750ad..6a873b3 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -1,4 +1,4 @@ -import { EmbedBuilder, InteractionReplyOptions } from "discord.js"; +import { InteractionReplyOptions } from "discord.js"; import { Duration } from "luxon"; import { trackerUsers } from "../../db"; import { durationToReadable } from "../misc/time"; From 53e203f5206d37460f90c871bb0792f47cb7381c Mon Sep 17 00:00:00 2001 From: flloschy Date: Sun, 30 Apr 2023 18:38:23 +0200 Subject: [PATCH 74/92] small tweaks --- src/db.ts | 12 ++++++------ src/util/tracker/gameCommands.ts | 4 ++-- src/util/tracker/presence.ts | 2 +- src/util/tracker/subCommands.ts | 26 ++++++++++++++++---------- src/util/tracker/userCommands.ts | 10 +++++----- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/db.ts b/src/db.ts index dd38940..7cfd270 100644 --- a/src/db.ts +++ b/src/db.ts @@ -61,8 +61,8 @@ interface InternalReminder { export interface TrackerLog { /** id of the log */ id: string; - /** When a log got logged */ - date: Date; + /** When a log got logged (Date.now()) */ + date: number; /** How long a game got played in ms*/ playtime: number; /** User id of the User who just got logged */ @@ -126,12 +126,12 @@ interface internalTrackerGameAndUser { users: TrackerSublog[]; } -// just in case a referenced log wasnt found. +// Just in case a referenced log wasnt found. // Although there really isnt any reason why there should accrue such a case. // Better safe than sorry and to make ts happy of course! const unknownTrackerLog: TrackerLog = { id: "-1", - date: new Date(2000, 4, 0, 4), + date: 404, playtime: -1, userID: "0000000000000000000", gameName: "[unknown log]", @@ -139,8 +139,8 @@ const unknownTrackerLog: TrackerLog = { export const trackerLogs = new Enmap({ name: "trackerLogs", - serializer: (data) => ({ ...data, date: data.date.toISOString() }), - deserializer: (data) => ({ ...data, date: new Date(data.date) }), + serializer: (data) => ({ ...data, date: new Date(data.date).toISOString() }), + deserializer: (data) => ({ ...data, date: new Date(data.date).getTime() }), }); export const trackerUsers = new Enmap({ name: "trackerUsers", diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 59c80dc..ed04ed7 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -44,7 +44,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const totalLogs = db.logs; const users = db.users.length; // get first log of game (first log is iso string) - const firstSeen = db.firstlog.date.getTime(); + const firstSeen = db.firstlog.date; // calculate the range from first log to now const range = Date.now() - firstSeen; // calculate daily/weekly/monthly and per-log average playtime @@ -121,7 +121,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: user.username, - value: `${shortDateAndShortTimeTimestamp(log.date.getTime() / 1000)}\n${makeTimeString(log.playtime)}`, + value: `${shortDateAndShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, }); }); diff --git a/src/util/tracker/presence.ts b/src/util/tracker/presence.ts index f507ad0..d92ba57 100644 --- a/src/util/tracker/presence.ts +++ b/src/util/tracker/presence.ts @@ -120,7 +120,7 @@ function addLog(gameName: string, userID: string, timePlayed: number, logID: str id: logID, gameName: gameName, userID: userID, - date: new Date(), + date: Date.now(), playtime: timePlayed, }; trackerLogs.set(logID, data); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 4fda544..4c545c9 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -1,6 +1,12 @@ import { EmbedBuilder } from "discord.js"; import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; -import { discordTimestamp, shortDateAndShortTimeTimestamp } from "../misc/time"; +import { + dayInMillis, + discordTimestamp, + monthInMillis, + shortDateAndShortTimeTimestamp, + weekInMillis, +} from "../misc/time"; import { TrackerGame, trackerGames, trackerLogs, TrackerUser, trackerUsers } from "../../db"; import { config } from "../../config"; import { makeTimeString, sortDbToString } from "./helper"; @@ -115,7 +121,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { // latest system logs const logs = trackerLogs .array() - .sort((a, b) => b.date.getTime() - a.date.getTime()) + .sort((a, b) => b.date - a.date) .slice(0, 5); // make embed for each log @@ -123,9 +129,9 @@ export async function latest(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `<@${log.userID}>\n${shortDateAndShortTimeTimestamp( - log.date.getTime() / 1000 - )}\n${makeTimeString(log.playtime)}`, + value: `<@${log.userID}>\n${shortDateAndShortTimeTimestamp(log.date / 1000)}\n${makeTimeString( + log.playtime + )}`, }) ); @@ -175,7 +181,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map( (log) => - `${shortDateAndShortTimeTimestamp(log.date.getTime() / 1000)} <@${log.userID}> ${ + `${shortDateAndShortTimeTimestamp(log.date / 1000)} <@${log.userID}> ${ log.gameName }: ${makeTimeString(log.playtime)}` ) @@ -192,14 +198,14 @@ export async function stats(interaction: ChatInputCommandInteraction) { // get amount of users const users = trackerUsers.count; // get first log (time is iso string) - const firstSeen = trackerLogs.array()[0].date.getTime(); + const firstSeen = trackerLogs.array()[0].date; // make range from first log to now const range = Date.now() - firstSeen; // calculate average playtime per day/week/month/game/log const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / (86400 * 1000))) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / 604800000)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / 2628000000)) + Math.round(totalPlaytime / (range / dayInMillis)) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / monthInMillis)) )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nuser: ${makeTimeString( Math.round(totalPlaytime / users) )}\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 18004d0..8286511 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -52,13 +52,13 @@ export async function userStats(interaction: ChatInputCommandInteraction) { const games = db.games.length; const totalLogs = db.logs; // get first log (time is iso string) - const firstSeen = db.firstlog.date.getTime(); + const firstSeen = db.firstlog.date; // make range from first log to now const range = Date.now() - firstSeen; // calculate average paytime per day/week/month/user/log const playtimePer = `day: ${makeTimeString( Math.round(totalPlaytime / (range / dayInMillis)) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / weekInMillis))}\nmonth: ${makeTimeString( + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( Math.round(totalPlaytime / (range / monthInMillis)) )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( Math.round(totalPlaytime / totalLogs) @@ -126,7 +126,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `${shortDateAndShortTimeTimestamp(log.date.getTime() / 1000)}\n${makeTimeString(log.playtime)}`, + value: `${shortDateAndShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, }); }); @@ -181,8 +181,8 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: name: game.name, value: filter == "playtime" - ? `${makeTimeString(new Date(game.playtime).getTime())}\n${game.logs} logs` - : `${game.logs} logs\n${makeTimeString(new Date(game.playtime).getTime())}`, + ? `${makeTimeString(game.playtime)}\n${game.logs} logs` + : `${game.logs} logs\n${makeTimeString(game.playtime)}`, }); }); From 6aa67c1469712880b1f93f39306be0903c2d5b2f Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 10 Jun 2023 14:29:05 +0200 Subject: [PATCH 75/92] use function for author --- src/util/tracker/gameCommands.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index ed04ed7..fddc18e 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -63,11 +63,6 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; const embed = new EmbedBuilder() - .setAuthor({ - name: config.botName, - url: config.githubURL, - iconURL: config.iconURL, - }) .setColor(config.color) .setTitle(`Tracking stats about ${targetGame}`) .addFields( @@ -91,7 +86,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } ); - await interaction.reply({ embeds: [embed] }); + await interaction.reply({ embeds: [addEmbedFooter(embed)] }); } export async function gameLast(interaction: ChatInputCommandInteraction) { // get the target game From bf5a16376c00405adfb99f766bb7927572596848 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 10 Jun 2023 14:30:14 +0200 Subject: [PATCH 76/92] using seconds for logs now --- src/db.ts | 8 ++++---- src/events/presenceUpdate.ts | 4 ++-- src/util/tracker/helper.ts | 4 ++-- src/util/tracker/subCommands.ts | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/db.ts b/src/db.ts index 7cfd270..27bdf8f 100644 --- a/src/db.ts +++ b/src/db.ts @@ -63,7 +63,7 @@ export interface TrackerLog { id: string; /** When a log got logged (Date.now()) */ date: number; - /** How long a game got played in ms*/ + /** How long a game got played in seconds*/ playtime: number; /** User id of the User who just got logged */ userID: userID; @@ -77,14 +77,14 @@ export interface TrackerSublog { name: userID; /** How often a User or Game got logged */ logs: number; - /** How long a User played a certain game in ms + /** How long a User played a certain game in seconds * (Or the other way around) */ playtime: number; } /** Interface for tracking users */ export interface TrackerUser { - /** How long a user played a game in ms*/ + /** How long a user played a game in seconds */ playtime: number; /** ID of the first log associated with the user */ firstlog: TrackerLog; @@ -98,7 +98,7 @@ export interface TrackerUser { /** Interface for tracking games */ export interface TrackerGame { - /** How long a game got played in ms*/ + /** How long a game got played in seconds */ playtime: number; /** ID of the first log associated with the game */ firstlog: TrackerLog; diff --git a/src/events/presenceUpdate.ts b/src/events/presenceUpdate.ts index c68af5d..1d2a45c 100644 --- a/src/events/presenceUpdate.ts +++ b/src/events/presenceUpdate.ts @@ -23,8 +23,8 @@ export default async function presenceUpdate(oldPresence: Presence | null, newPr if (!start) return; delete cachedUser[activity.name]; - // Played time in secods ("~~" makes float to int) - const timePlayed = ~~(Date.now() - start); + // Played time in seconds ("~~" makes float to int) + const timePlayed = ~~((Date.now() - start) / 1000); await logIt(activity.name, userID, timePlayed); }); diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 6a873b3..660f1c8 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -4,8 +4,8 @@ import { trackerUsers } from "../../db"; import { durationToReadable } from "../misc/time"; /** Uses `durationToReadable` from src/util/misc/time.ts but without the need to parse a Duration object */ -export function makeTimeString(timeMS: number) { - return durationToReadable(Duration.fromMillis(timeMS), true); +export function makeTimeString(timeSeconds: number) { + return durationToReadable(Duration.fromMillis(timeSeconds * 1000), true); } /** Sorts users games after playtime of a user (most playtime at index 0)*/ export function sortGamesPlaytime(userId: string) { diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 4c545c9..a21e746 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -1,11 +1,11 @@ import { EmbedBuilder } from "discord.js"; import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; import { - dayInMillis, + dayInSeconds, discordTimestamp, - monthInMillis, + monthInSeconds, shortDateAndShortTimeTimestamp, - weekInMillis, + weekInSeconds, } from "../misc/time"; import { TrackerGame, trackerGames, trackerLogs, TrackerUser, trackerUsers } from "../../db"; import { config } from "../../config"; @@ -203,9 +203,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { const range = Date.now() - firstSeen; // calculate average playtime per day/week/month/game/log const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / dayInMillis)) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / monthInMillis)) + Math.round(totalPlaytime / (range / dayInSeconds)) + )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInSeconds)))}\nmonth: ${makeTimeString( + Math.round(totalPlaytime / (range / monthInSeconds)) )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nuser: ${makeTimeString( Math.round(totalPlaytime / users) )}\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; From 4b45531acfb9b534a22b0fe1b60df522690d024e Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 10 Jun 2023 14:36:52 +0200 Subject: [PATCH 77/92] explain what defaults are used --- src/commands/TrackerCommands.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index a26c07f..2c39122 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -79,19 +79,19 @@ class Tracker extends Command { .setName("user") .setDescription("Get tracking stats about a user.") .addUserOption((opt) => - opt.setName("user").setDescription("The target user, if not given defaults to you.") + opt.setName("user").setDescription("The target user. default: you") ) .addStringOption((opt) => opt .setName("game") - .setDescription("The game of which to get playtime/logs from.") + .setDescription("The game of which to get playtime/logs from. default: every game") .setAutocomplete(true) ) .addStringOption((opt) => opt .setName("statistic") .setDescription( - "Select what statistics should get shown. (Options depend on if a game is given or not)" + "Select what statistics should get shown. default: general statistics" ) .setAutocomplete(true) ) @@ -110,7 +110,7 @@ class Tracker extends Command { .addStringOption((opt) => opt .setName("statistic") - .setDescription("Select what statistics should get shown.") + .setDescription("Select what statistics should get shown. default: general statistics") .addChoices( { name: "general statistics", value: "general statistics" }, { name: "top 5 most played games", value: "top 5 most played games" }, From c16cec2294fb9db11435b236bf44a22133e8e6f1 Mon Sep 17 00:00:00 2001 From: flloschy Date: Tue, 13 Jun 2023 07:25:55 +0200 Subject: [PATCH 78/92] added command for clearer use, doesnt work though --- package-lock.json | 14 ++--- package.json | 3 +- src/commands/TrackerCommands.ts | 51 +++++++++++++++---- .../autocompleters/TrackerAutocomple.ts | 11 +++- src/util/tracker/autocompleteHelper.ts | 10 ++-- src/util/tracker/subCommands.ts | 17 +++---- 6 files changed, 71 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c18d44..f391f41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2676,14 +2676,14 @@ } }, "node_modules/undici": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.15.0.tgz", - "integrity": "sha512-wCAZJDyjw9Myv+Ay62LAoB+hZLPW9SmKbQkbHIhMw/acKSlpn7WohdMUc/Vd4j1iSMBO0hWwU8mjB7a5p5bl8g==", + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz", + "integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==", "dependencies": { "busboy": "^1.6.0" }, "engines": { - "node": ">=12.18" + "node": ">=14.0" } }, "node_modules/uri-js": { @@ -4627,9 +4627,9 @@ "dev": true }, "undici": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.15.0.tgz", - "integrity": "sha512-wCAZJDyjw9Myv+Ay62LAoB+hZLPW9SmKbQkbHIhMw/acKSlpn7WohdMUc/Vd4j1iSMBO0hWwU8mjB7a5p5bl8g==", + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz", + "integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==", "requires": { "busboy": "^1.6.0" } diff --git a/package.json b/package.json index 24f6f53..97e696e 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,13 @@ "private": "true", "type": "module", "scripts": { + "rmdist": "rm ./dist -r", "lint": "eslint ./src/", "format": "npx prettier -w .", "tidy": "npm run lint && npm run format", "build": "tsc -p tsconfig.json", "run": "node --experimental-specifier-resolution=node .", - "start": "npm run build && npm run run", + "start": "npm run rmdist && npm run build && npm run run", "prepare": "husky install" }, "repository": { diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 2c39122..820af7c 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -10,12 +10,14 @@ import { gameLast, gameStats, gameTop } from "../util/tracker/gameCommands"; import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; import { config } from "../config"; + class Tracker extends Command { constructor() { super("tracker"); } async execute(interaction: ChatInputCommandInteraction): Promise { + await interaction.deferReply() if (!config.tracking) { await interaction.reply({ content: @@ -27,11 +29,11 @@ class Tracker extends Command { const subCommand = interaction.options.getSubcommand(); const statistics = interaction.options.getString("statistic") ?? "general statistics"; const action = interaction.options.getString("action"); + + switch (subCommand) { case "user": - if (statistics == "playtime") await playtime(interaction); - else if (statistics == "logs") await logs(interaction); - else if (statistics == "general statistics") await userStats(interaction); + if (statistics == "general statistics") await userStats(interaction); else if (statistics == "top 5 most played games") await userTop(interaction, "playtime"); else if (statistics == "top 5 most logged games") await userTop(interaction, "logs"); else if (statistics == "latest 5 logs") await userLast(interaction); @@ -41,7 +43,10 @@ class Tracker extends Command { else if (statistics == "top 5 most played games") await gameTop(interaction, "playtime"); else if (statistics == "top 5 most logged games") await gameTop(interaction, "logs"); else if (statistics == "latest 5 logs") await gameLast(interaction); - + return; + case "general": + if (statistics == "playtime") await playtime(interaction); + else if (statistics == "logs") await logs(interaction); return; case "blacklist": if (action == "add") await addBlacklist(interaction); @@ -54,7 +59,7 @@ class Tracker extends Command { await stats(interaction); return; } - + if (!interaction.replied) { await interaction.reply({ content: @@ -78,9 +83,7 @@ class Tracker extends Command { sub .setName("user") .setDescription("Get tracking stats about a user.") - .addUserOption((opt) => - opt.setName("user").setDescription("The target user. default: you") - ) + .addUserOption((opt) => opt.setName("user").setDescription("The target user. default: you")) .addStringOption((opt) => opt .setName("game") @@ -90,9 +93,7 @@ class Tracker extends Command { .addStringOption((opt) => opt .setName("statistic") - .setDescription( - "Select what statistics should get shown. default: general statistics" - ) + .setDescription("Select what statistics should get shown. default: general statistics") .setAutocomplete(true) ) ) @@ -119,6 +120,34 @@ class Tracker extends Command { ) ) ) + .addSubcommand((sub) => + sub + .setName("general") + .setDescription("overall statistics without the need to specify a user or game") + .addStringOption((opt) => + opt + .setName("statistic") + .setDescription("Choose the statistic which should get shown.") + .addChoices( + {name:"playtime", value:"playtime"}, + {name:"logs", value:"logs"} + ) + .setRequired(true) + ) + .addUserOption((opt) => + opt + .setName("user") + .setDescription("The Target User") + ) + .addStringOption((opt) => + opt + .setName("game") + .setDescription("The target Game") + .setAutocomplete(true) + ) + ) + + .addSubcommand((sub) => sub .setName("blacklist") diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 895b22a..14f263b 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -1,6 +1,6 @@ import { Autocompleter } from "../interactionClasses"; import { AutocompleteInteraction } from "discord.js"; -import { trackerGames } from "../../db"; +import { trackerGames, trackerUsers } from "../../db"; import { blacklist, user } from "../../util/tracker/autocompleteHelper"; class Tracker extends Autocompleter { @@ -15,6 +15,7 @@ class Tracker extends Autocompleter { // if user-option is given, use it, else default to executer const userId = interaction.options.get("user")?.value ?? interaction.user.id; + if (typeof userId !== "string") return const game = interaction.options.get("game")?.value; // if action-option is given, use it, else default to add @@ -28,9 +29,15 @@ class Tracker extends Autocompleter { if (sub == "user") { options = user(option, userId, game); } else if (sub == "game") { - trackerGames.keyArray().forEach((e) => options.push(e)); + options = trackerGames.keyArray().map(g => g) } else if (sub == "blacklist") { options = blacklist(action); + } else if (sub == "general") { + if (interaction.options.get("user")) { + options = trackerUsers.get(userId)?.games.map(g => g.name) ?? ["[no games played yet]"] + } else { + options = trackerGames.keyArray().map(g => g) + } } const filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)).slice(0, 25); diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts index abcd0ba..3a653c5 100644 --- a/src/util/tracker/autocompleteHelper.ts +++ b/src/util/tracker/autocompleteHelper.ts @@ -5,7 +5,7 @@ export function user( userid: string | number | boolean | undefined, game: string | number | boolean | undefined ) { - const results = []; + const results:string[] = []; if (option == "game") { if (typeof userid == "string") { @@ -23,10 +23,10 @@ export function user( } else if (option == "statistic") { // only show playtime and logs if a game is given, else show standard options results.push( - ...(typeof game == "string" - ? ["playtime", "logs"] - : ["general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"]) - ); + "general statistics", + "top 5 most played games", + "top 5 most logged games", + "latest 5 logs"); } return results; diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index a21e746..c577562 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -43,7 +43,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { await interaction.reply(userNoEntry); return; } - text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime`; + text = `${targetUser} has ${makeTimeString(db.playtime)} of playtime`; } else if (targetUser && targetGame) { // user, game // load db and get user.games and find target game in there @@ -54,19 +54,18 @@ export async function playtime(interaction: ChatInputCommandInteraction) { await interaction.reply(userNoGameEntry); return; } - text = `${targetUser.username} has ${makeTimeString(db.playtime)} of playtime in ${targetGame}`; + text = `${targetUser} has ${makeTimeString(db.playtime)} of playtime in ${targetGame}`; } else { - // shouldn't happen but just in case a return - return; + text = "error"; } - await interaction.reply({ - embeds: [new EmbedBuilder().setTitle(text).setColor(config.color)], + await interaction.editReply({ + embeds: [new EmbedBuilder().setTitle("playtime").setDescription(text).setColor(config.color)], }); } export async function logs(interaction: ChatInputCommandInteraction) { // get target user and game - const targetUser = interaction.options.getUser("user") ?? interaction.user; + const targetUser = interaction.options.getUser("user"); const targetGame = interaction.options.getString("game"); let text = ""; // used later in the final embed @@ -107,11 +106,11 @@ export async function logs(interaction: ChatInputCommandInteraction) { } text = `${targetUser.username} has played ${targetGame} ${db.logs} times`; } else { - return; + text = "error" } await interaction.reply({ - embeds: [new EmbedBuilder().setTitle(text).setColor(config.color)], + embeds: [new EmbedBuilder().setTitle("logs").setDescription(text).setColor(config.color)], }); } export async function latest(interaction: ChatInputCommandInteraction) { From 45a123af6917395a31cdadd2d6c4c72fa1115595 Mon Sep 17 00:00:00 2001 From: flloschy Date: Thu, 15 Jun 2023 17:07:13 +0200 Subject: [PATCH 79/92] new command, but unknow interaction, idk how fix --- src/commands/TrackerCommands.ts | 25 +++++-------------- .../autocompleters/TrackerAutocomple.ts | 8 +++--- src/util/tracker/autocompleteHelper.ts | 8 ++---- src/util/tracker/subCommands.ts | 2 +- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 820af7c..a25373e 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -10,14 +10,13 @@ import { gameLast, gameStats, gameTop } from "../util/tracker/gameCommands"; import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; import { config } from "../config"; - class Tracker extends Command { constructor() { super("tracker"); } async execute(interaction: ChatInputCommandInteraction): Promise { - await interaction.deferReply() + await interaction.deferReply(); if (!config.tracking) { await interaction.reply({ content: @@ -30,7 +29,6 @@ class Tracker extends Command { const statistics = interaction.options.getString("statistic") ?? "general statistics"; const action = interaction.options.getString("action"); - switch (subCommand) { case "user": if (statistics == "general statistics") await userStats(interaction); @@ -59,7 +57,7 @@ class Tracker extends Command { await stats(interaction); return; } - + if (!interaction.replied) { await interaction.reply({ content: @@ -120,7 +118,7 @@ class Tracker extends Command { ) ) ) - .addSubcommand((sub) => + .addSubcommand((sub) => sub .setName("general") .setDescription("overall statistics without the need to specify a user or game") @@ -128,26 +126,15 @@ class Tracker extends Command { opt .setName("statistic") .setDescription("Choose the statistic which should get shown.") - .addChoices( - {name:"playtime", value:"playtime"}, - {name:"logs", value:"logs"} - ) + .addChoices({ name: "playtime", value: "playtime" }, { name: "logs", value: "logs" }) .setRequired(true) ) - .addUserOption((opt) => - opt - .setName("user") - .setDescription("The Target User") - ) + .addUserOption((opt) => opt.setName("user").setDescription("The Target User")) .addStringOption((opt) => - opt - .setName("game") - .setDescription("The target Game") - .setAutocomplete(true) + opt.setName("game").setDescription("The target Game").setAutocomplete(true) ) ) - .addSubcommand((sub) => sub .setName("blacklist") diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 14f263b..02deaf2 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -15,7 +15,7 @@ class Tracker extends Autocompleter { // if user-option is given, use it, else default to executer const userId = interaction.options.get("user")?.value ?? interaction.user.id; - if (typeof userId !== "string") return + if (typeof userId !== "string") return; const game = interaction.options.get("game")?.value; // if action-option is given, use it, else default to add @@ -29,14 +29,14 @@ class Tracker extends Autocompleter { if (sub == "user") { options = user(option, userId, game); } else if (sub == "game") { - options = trackerGames.keyArray().map(g => g) + options = trackerGames.keyArray().map((g) => g); } else if (sub == "blacklist") { options = blacklist(action); } else if (sub == "general") { if (interaction.options.get("user")) { - options = trackerUsers.get(userId)?.games.map(g => g.name) ?? ["[no games played yet]"] + options = trackerUsers.get(userId)?.games.map((g) => g.name) ?? ["[no games played yet]"]; } else { - options = trackerGames.keyArray().map(g => g) + options = trackerGames.keyArray().map((g) => g); } } diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts index 3a653c5..cc8fdd2 100644 --- a/src/util/tracker/autocompleteHelper.ts +++ b/src/util/tracker/autocompleteHelper.ts @@ -5,7 +5,7 @@ export function user( userid: string | number | boolean | undefined, game: string | number | boolean | undefined ) { - const results:string[] = []; + const results: string[] = []; if (option == "game") { if (typeof userid == "string") { @@ -22,11 +22,7 @@ export function user( } } else if (option == "statistic") { // only show playtime and logs if a game is given, else show standard options - results.push( - "general statistics", - "top 5 most played games", - "top 5 most logged games", - "latest 5 logs"); + results.push("general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"); } return results; diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index c577562..d16d08c 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -106,7 +106,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { } text = `${targetUser.username} has played ${targetGame} ${db.logs} times`; } else { - text = "error" + text = "error"; } await interaction.reply({ From cbc3ab0e68ae996099ae282abf89149d1a0198d6 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 5 Jul 2023 15:33:15 +0200 Subject: [PATCH 80/92] discordjs upgrade --- package-lock.json | 370 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 223 insertions(+), 149 deletions(-) diff --git a/package-lock.json b/package-lock.json index f391f41..bf3ad8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "SEE LICENSE IN LICENSE", "dependencies": { "@napi-rs/canvas": "^0.1.34", - "discord.js": "^14.7.1", + "discord.js": "^14.11.0", "dotenv": "^16.0.3", "enmap": "github:Cowoding-Jams/enmap", "luxon": "^3.2.1", @@ -31,6 +31,15 @@ "typescript": "^4.9.4" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -50,51 +59,82 @@ } }, "node_modules/@discordjs/builders": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", - "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.3.tgz", + "integrity": "sha512-CTCh8NqED3iecTNuiz49mwSsrc2iQb4d0MjMdmS/8pb69Y4IlzJ/DIy/p5GFlgOrFbNO2WzMHkWKQSiJ3VNXaw==", "dependencies": { - "@discordjs/util": "^0.1.0", - "@sapphire/shapeshift": "^3.7.1", - "discord-api-types": "^0.37.20", + "@discordjs/formatters": "^0.3.1", + "@discordjs/util": "^0.3.1", + "@sapphire/shapeshift": "^3.8.2", + "discord-api-types": "^0.37.41", "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.2", - "tslib": "^2.4.1" + "ts-mixer": "^6.0.3", + "tslib": "^2.5.0" }, "engines": { "node": ">=16.9.0" } }, "node_modules/@discordjs/collection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", - "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.1.tgz", + "integrity": "sha512-aWEc9DCf3TMDe9iaJoOnO2+JVAjeRNuRxPZQA6GVvBf+Z3gqUuWYBy2NWh4+5CLYq5uoc3MOvUQ5H5m8CJBqOA==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.1.tgz", + "integrity": "sha512-M7X4IGiSeh4znwcRGcs+49B5tBkNDn4k5bmhxJDAUhRxRHTiFAOTVUNQ6yAKySu5jZTnCbSvTYHW3w0rAzV1MA==", + "dependencies": { + "discord-api-types": "^0.37.41" + }, "engines": { "node": ">=16.9.0" } }, "node_modules/@discordjs/rest": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", - "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.7.1.tgz", + "integrity": "sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ==", "dependencies": { - "@discordjs/collection": "^1.3.0", - "@discordjs/util": "^0.1.0", + "@discordjs/collection": "^1.5.1", + "@discordjs/util": "^0.3.0", "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.2.2", - "discord-api-types": "^0.37.23", - "file-type": "^18.0.0", - "tslib": "^2.4.1", - "undici": "^5.13.0" + "@sapphire/snowflake": "^3.4.2", + "discord-api-types": "^0.37.41", + "file-type": "^18.3.0", + "tslib": "^2.5.0", + "undici": "^5.22.0" }, "engines": { "node": ">=16.9.0" } }, "node_modules/@discordjs/util": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", - "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.3.1.tgz", + "integrity": "sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/ws": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-0.8.3.tgz", + "integrity": "sha512-hcYtppanjHecbdNyCKQNH2I4RP9UrphDgmRgLYrATEQF1oo4sYSve7ZmGsBEXSzH72MO2tBPdWSThunbxUVk0g==", + "dependencies": { + "@discordjs/collection": "^1.5.1", + "@discordjs/rest": "^1.7.1", + "@discordjs/util": "^0.3.1", + "@sapphire/async-queue": "^1.5.0", + "@types/ws": "^8.5.4", + "@vladfrangu/async_event_emitter": "^2.2.1", + "discord-api-types": "^0.37.41", + "tslib": "^2.5.0", + "ws": "^8.13.0" + }, "engines": { "node": ">=16.9.0" } @@ -358,9 +398,9 @@ } }, "node_modules/@sapphire/shapeshift": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", - "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz", + "integrity": "sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==", "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" @@ -371,9 +411,9 @@ } }, "node_modules/@sapphire/snowflake": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", - "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", + "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" @@ -608,6 +648,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz", + "integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/acorn": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", @@ -979,27 +1028,29 @@ } }, "node_modules/discord-api-types": { - "version": "0.37.28", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.28.tgz", - "integrity": "sha512-K0fw7m7km9th3dCQ2AR90q/FwX3uAj+OLc+Zuo39VY9vCn0Ux/iObM4y1zJYIH3vTc+QlrksVErUvyeONjOKMQ==" + "version": "0.37.45", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.45.tgz", + "integrity": "sha512-r9m/g+YQfo7XWMrl645jvMlYoWF8lvns/ch4NCxsz/FbingrECu97LFSD2zKOvgHaSc90BHP8wgshaMcA2/c6Q==" }, "node_modules/discord.js": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", - "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", + "version": "14.11.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.11.0.tgz", + "integrity": "sha512-CkueWYFQ28U38YPR8HgsBR/QT35oPpMbEsTNM30Fs8loBIhnA4s70AwQEoy6JvLcpWWJO7GY0y2BUzZmuBMepQ==", "dependencies": { - "@discordjs/builders": "^1.4.0", - "@discordjs/collection": "^1.3.0", - "@discordjs/rest": "^1.4.0", - "@discordjs/util": "^0.1.0", - "@sapphire/snowflake": "^3.2.2", - "@types/ws": "^8.5.3", - "discord-api-types": "^0.37.20", + "@discordjs/builders": "^1.6.3", + "@discordjs/collection": "^1.5.1", + "@discordjs/formatters": "^0.3.1", + "@discordjs/rest": "^1.7.1", + "@discordjs/util": "^0.3.1", + "@discordjs/ws": "^0.8.3", + "@sapphire/snowflake": "^3.4.2", + "@types/ws": "^8.5.4", + "discord-api-types": "^0.37.41", "fast-deep-equal": "^3.1.3", "lodash.snakecase": "^4.1.1", - "tslib": "^2.4.1", - "undici": "^5.13.0", - "ws": "^8.11.0" + "tslib": "^2.5.0", + "undici": "^5.22.0", + "ws": "^8.13.0" }, "engines": { "node": ">=16.9.0" @@ -1379,9 +1430,9 @@ } }, "node_modules/file-type": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.0.tgz", - "integrity": "sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==", + "version": "18.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.5.0.tgz", + "integrity": "sha512-yvpl5U868+V6PqXHMmsESpg6unQ5GfnPssl4dxdJudBrr9qy7Fddt7EVX1VLlddFfe8Gj9N7goCZH22FXuSQXQ==", "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", @@ -1988,17 +2039,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -2348,9 +2399,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -2597,14 +2648,14 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "node_modules/ts-mixer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", - "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" }, "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -2765,24 +2816,15 @@ "node": ">= 6.4.0" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", - "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "engines": { "node": ">=10.0.0" }, @@ -2818,6 +2860,12 @@ } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2834,42 +2882,67 @@ } }, "@discordjs/builders": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", - "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.3.tgz", + "integrity": "sha512-CTCh8NqED3iecTNuiz49mwSsrc2iQb4d0MjMdmS/8pb69Y4IlzJ/DIy/p5GFlgOrFbNO2WzMHkWKQSiJ3VNXaw==", "requires": { - "@discordjs/util": "^0.1.0", - "@sapphire/shapeshift": "^3.7.1", - "discord-api-types": "^0.37.20", + "@discordjs/formatters": "^0.3.1", + "@discordjs/util": "^0.3.1", + "@sapphire/shapeshift": "^3.8.2", + "discord-api-types": "^0.37.41", "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.2", - "tslib": "^2.4.1" + "ts-mixer": "^6.0.3", + "tslib": "^2.5.0" } }, "@discordjs/collection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", - "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.1.tgz", + "integrity": "sha512-aWEc9DCf3TMDe9iaJoOnO2+JVAjeRNuRxPZQA6GVvBf+Z3gqUuWYBy2NWh4+5CLYq5uoc3MOvUQ5H5m8CJBqOA==" + }, + "@discordjs/formatters": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.1.tgz", + "integrity": "sha512-M7X4IGiSeh4znwcRGcs+49B5tBkNDn4k5bmhxJDAUhRxRHTiFAOTVUNQ6yAKySu5jZTnCbSvTYHW3w0rAzV1MA==", + "requires": { + "discord-api-types": "^0.37.41" + } }, "@discordjs/rest": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", - "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.7.1.tgz", + "integrity": "sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ==", "requires": { - "@discordjs/collection": "^1.3.0", - "@discordjs/util": "^0.1.0", + "@discordjs/collection": "^1.5.1", + "@discordjs/util": "^0.3.0", "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.2.2", - "discord-api-types": "^0.37.23", - "file-type": "^18.0.0", - "tslib": "^2.4.1", - "undici": "^5.13.0" + "@sapphire/snowflake": "^3.4.2", + "discord-api-types": "^0.37.41", + "file-type": "^18.3.0", + "tslib": "^2.5.0", + "undici": "^5.22.0" } }, "@discordjs/util": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", - "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.3.1.tgz", + "integrity": "sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==" + }, + "@discordjs/ws": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-0.8.3.tgz", + "integrity": "sha512-hcYtppanjHecbdNyCKQNH2I4RP9UrphDgmRgLYrATEQF1oo4sYSve7ZmGsBEXSzH72MO2tBPdWSThunbxUVk0g==", + "requires": { + "@discordjs/collection": "^1.5.1", + "@discordjs/rest": "^1.7.1", + "@discordjs/util": "^0.3.1", + "@sapphire/async-queue": "^1.5.0", + "@types/ws": "^8.5.4", + "@vladfrangu/async_event_emitter": "^2.2.1", + "discord-api-types": "^0.37.41", + "tslib": "^2.5.0", + "ws": "^8.13.0" + } }, "@eslint/eslintrc": { "version": "1.4.1", @@ -3013,18 +3086,18 @@ "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==" }, "@sapphire/shapeshift": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", - "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz", + "integrity": "sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==", "requires": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" } }, "@sapphire/snowflake": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", - "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", + "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==" }, "@tokenizer/token": { "version": "0.3.0", @@ -3166,6 +3239,11 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@vladfrangu/async_event_emitter": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz", + "integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==" + }, "acorn": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", @@ -3439,27 +3517,29 @@ } }, "discord-api-types": { - "version": "0.37.28", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.28.tgz", - "integrity": "sha512-K0fw7m7km9th3dCQ2AR90q/FwX3uAj+OLc+Zuo39VY9vCn0Ux/iObM4y1zJYIH3vTc+QlrksVErUvyeONjOKMQ==" + "version": "0.37.45", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.45.tgz", + "integrity": "sha512-r9m/g+YQfo7XWMrl645jvMlYoWF8lvns/ch4NCxsz/FbingrECu97LFSD2zKOvgHaSc90BHP8wgshaMcA2/c6Q==" }, "discord.js": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", - "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", + "version": "14.11.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.11.0.tgz", + "integrity": "sha512-CkueWYFQ28U38YPR8HgsBR/QT35oPpMbEsTNM30Fs8loBIhnA4s70AwQEoy6JvLcpWWJO7GY0y2BUzZmuBMepQ==", "requires": { - "@discordjs/builders": "^1.4.0", - "@discordjs/collection": "^1.3.0", - "@discordjs/rest": "^1.4.0", - "@discordjs/util": "^0.1.0", - "@sapphire/snowflake": "^3.2.2", - "@types/ws": "^8.5.3", - "discord-api-types": "^0.37.20", + "@discordjs/builders": "^1.6.3", + "@discordjs/collection": "^1.5.1", + "@discordjs/formatters": "^0.3.1", + "@discordjs/rest": "^1.7.1", + "@discordjs/util": "^0.3.1", + "@discordjs/ws": "^0.8.3", + "@sapphire/snowflake": "^3.4.2", + "@types/ws": "^8.5.4", + "discord-api-types": "^0.37.41", "fast-deep-equal": "^3.1.3", "lodash.snakecase": "^4.1.1", - "tslib": "^2.4.1", - "undici": "^5.13.0", - "ws": "^8.11.0" + "tslib": "^2.5.0", + "undici": "^5.22.0", + "ws": "^8.13.0" } }, "doctrine": { @@ -3746,9 +3826,9 @@ } }, "file-type": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.0.tgz", - "integrity": "sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==", + "version": "18.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.5.0.tgz", + "integrity": "sha512-yvpl5U868+V6PqXHMmsESpg6unQ5GfnPssl4dxdJudBrr9qy7Fddt7EVX1VLlddFfe8Gj9N7goCZH22FXuSQXQ==", "requires": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", @@ -4185,17 +4265,17 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, "p-limit": { @@ -4403,9 +4483,9 @@ "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==" }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "requires": { "lru-cache": "^6.0.0" } @@ -4571,14 +4651,14 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "ts-mixer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", - "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" }, "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, "tsutils": { "version": "3.21.0", @@ -4695,21 +4775,15 @@ "triple-beam": "^1.3.0" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", - "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "requires": {} }, "yallist": { diff --git a/package.json b/package.json index 97e696e..5356c87 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ }, "dependencies": { "@napi-rs/canvas": "^0.1.34", - "discord.js": "^14.7.1", + "discord.js": "^14.11.0", "dotenv": "^16.0.3", "enmap": "github:Cowoding-Jams/enmap", "luxon": "^3.2.1", From 4318223ae8ed73bd2c11f0a8b3000915fb2ac3fc Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 5 Jul 2023 15:34:38 +0200 Subject: [PATCH 81/92] somehow fixed it idk --- src/commands/TrackerCommands.ts | 1 - src/util/tracker/subCommands.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index a25373e..ea2078e 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -16,7 +16,6 @@ class Tracker extends Command { } async execute(interaction: ChatInputCommandInteraction): Promise { - await interaction.deferReply(); if (!config.tracking) { await interaction.reply({ content: diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index d16d08c..8566624 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -59,7 +59,7 @@ export async function playtime(interaction: ChatInputCommandInteraction) { text = "error"; } - await interaction.editReply({ + await interaction.reply({ embeds: [new EmbedBuilder().setTitle("playtime").setDescription(text).setColor(config.color)], }); } @@ -71,7 +71,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { // make matching text for each case if (!targetUser && !targetGame) { - // no u ser, no game + // no user, no game let logs = 0; // count all logs of all games together trackerGames.array().forEach((game) => (logs += game.logs)); From 88129ef65c27fe06abb9e4c1647411feb8fefab3 Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 5 Jul 2023 17:02:08 +0200 Subject: [PATCH 82/92] done all points --- package.json | 5 +- src/commands/TrackerCommands.ts | 44 +++---- .../autocompleters/TrackerAutocomple.ts | 5 +- src/util/tracker/autocompleteHelper.ts | 8 +- src/util/tracker/gameCommands.ts | 25 ++-- src/util/tracker/subCommands.ts | 92 +++++++++------ src/util/tracker/userCommands.ts | 111 +++++++++++------- 7 files changed, 163 insertions(+), 127 deletions(-) diff --git a/package.json b/package.json index 5356c87..908b39b 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,14 @@ "private": "true", "type": "module", "scripts": { - "rmdist": "rm ./dist -r", + "rmDist": "rm ./dist -r", "lint": "eslint ./src/", "format": "npx prettier -w .", "tidy": "npm run lint && npm run format", "build": "tsc -p tsconfig.json", "run": "node --experimental-specifier-resolution=node .", - "start": "npm run rmdist && npm run build && npm run run", + "start": "npm run build && npm run run", + "freshStart": "npm run rmDist && mpm run start", "prepare": "husky install" }, "repository": { diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index ea2078e..ab5e46a 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -5,8 +5,8 @@ import { SlashCommandSubcommandsOnlyBuilder, } from "discord.js"; import { latest, logs, playtime, stats } from "../util/tracker/subCommands"; -import { userLast, userStats, userTop } from "../util/tracker/userCommands"; -import { gameLast, gameStats, gameTop } from "../util/tracker/gameCommands"; +import { userStats } from "../util/tracker/userCommands"; +import { gameStats } from "../util/tracker/gameCommands"; import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; import { config } from "../config"; @@ -30,16 +30,10 @@ class Tracker extends Command { switch (subCommand) { case "user": - if (statistics == "general statistics") await userStats(interaction); - else if (statistics == "top 5 most played games") await userTop(interaction, "playtime"); - else if (statistics == "top 5 most logged games") await userTop(interaction, "logs"); - else if (statistics == "latest 5 logs") await userLast(interaction); + await userStats(interaction); return; case "game": - if (statistics == "general statistics") await gameStats(interaction); - else if (statistics == "top 5 most played games") await gameTop(interaction, "playtime"); - else if (statistics == "top 5 most logged games") await gameTop(interaction, "logs"); - else if (statistics == "latest 5 logs") await gameLast(interaction); + await gameStats(interaction); return; case "general": if (statistics == "playtime") await playtime(interaction); @@ -87,12 +81,6 @@ class Tracker extends Command { .setDescription("The game of which to get playtime/logs from. default: every game") .setAutocomplete(true) ) - .addStringOption((opt) => - opt - .setName("statistic") - .setDescription("Select what statistics should get shown. default: general statistics") - .setAutocomplete(true) - ) ) .addSubcommand((sub) => sub @@ -105,17 +93,6 @@ class Tracker extends Command { .setRequired(true) .setAutocomplete(true) ) - .addStringOption((opt) => - opt - .setName("statistic") - .setDescription("Select what statistics should get shown. default: general statistics") - .addChoices( - { name: "general statistics", value: "general statistics" }, - { name: "top 5 most played games", value: "top 5 most played games" }, - { name: "top 5 most logged games", value: "top 5 most logged games" }, - { name: "latest 5 logs", value: "latest 5 logs" } - ) - ) ) .addSubcommand((sub) => sub @@ -154,7 +131,18 @@ class Tracker extends Command { ) ) .addSubcommand((sub) => - sub.setName("latest").setDescription("See the latest logs across the whole system.") + sub + .setName("latest") + .setDescription("See the latest logs across the whole system.") + .addStringOption((opt) => + opt + .setName("game") + .setDescription("The game of which the latest logs should get shown.") // bad wording and it sounds bad, ik + .setAutocomplete(true) + ) + .addUserOption( + (opt) => opt.setName("user").setDescription("The user who's latest logs should get show.") // bad wording and it sounds bad, ik + ) ) .addSubcommand((sub) => sub.setName("statistics").setDescription("See general statistics across the whole system.") diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 02deaf2..029fe09 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -16,7 +16,6 @@ class Tracker extends Autocompleter { // if user-option is given, use it, else default to executer const userId = interaction.options.get("user")?.value ?? interaction.user.id; if (typeof userId !== "string") return; - const game = interaction.options.get("game")?.value; // if action-option is given, use it, else default to add const action = interaction.options.get("action")?.value ?? "add"; @@ -26,8 +25,8 @@ class Tracker extends Autocompleter { let options: string[] = []; - if (sub == "user") { - options = user(option, userId, game); + if (sub == "user" || option == "game") { + options = user(option, userId); } else if (sub == "game") { options = trackerGames.keyArray().map((g) => g); } else if (sub == "blacklist") { diff --git a/src/util/tracker/autocompleteHelper.ts b/src/util/tracker/autocompleteHelper.ts index cc8fdd2..98f24e3 100644 --- a/src/util/tracker/autocompleteHelper.ts +++ b/src/util/tracker/autocompleteHelper.ts @@ -1,10 +1,6 @@ import { trackerBlacklist, trackerGames, trackerUsers } from "../../db"; -export function user( - option: string, - userid: string | number | boolean | undefined, - game: string | number | boolean | undefined -) { +export function user(option: string, userid: string | number | boolean | undefined) { const results: string[] = []; if (option == "game") { @@ -22,7 +18,7 @@ export function user( } } else if (option == "statistic") { // only show playtime and logs if a game is given, else show standard options - results.push("general statistics", "top 5 most played games", "top 5 most logged games", "latest 5 logs"); + results.push("general statistics", "top 5 most played games", "top 5 most logged games"); } return results; diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index fddc18e..09acacc 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -4,9 +4,9 @@ import { trackerGames, trackerLogs, TrackerSublog } from "../../db"; import { addEmbedFooter } from "../misc/embeds"; import { dayInMillis, + discordLongDateWithShortTimeTimestamp, discordTimestamp, monthInMillis, - shortDateAndShortTimeTimestamp, weekInMillis, } from "../misc/time"; import { makeTimeString, sortDbToString } from "./helper"; @@ -38,7 +38,9 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { ); // format latest logs into a string - const latestLogs = db.lastlogs.map((log) => `<@${trackerLogs.get(log.id)?.userID}>`).join("\n"); + const latestLogs = db.lastlogs + .map((log) => `${discordLongDateWithShortTimeTimestamp(log.date)} <@${trackerLogs.get(log.id)?.userID}>`) + .join("\n"); // get total playtime, logs and users const totalPlaytime = db.playtime; const totalLogs = db.logs; @@ -66,13 +68,17 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { .setColor(config.color) .setTitle(`Tracking stats about ${targetGame}`) .addFields( - { inline: true, name: "Most logged users", value: mostLogged }, - { inline: true, name: "User with most playtime", value: mostPlayed }, + { inline: true, name: "Most playtime", value: mostPlayed }, + { inline: true, name: "Most logs", value: mostLogged }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, + { + inline: true, + name: "Total...", + value: "Playtime: " + makeTimeString(totalPlaytime) + "\nlogs: " + totalLogs.toString(), + }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: true, name: "(Average) playtime/log", value: mostPlaytime }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, @@ -82,8 +88,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } + { inline: true, name: "Latest logs", value: latestLogs } ); await interaction.reply({ embeds: [addEmbedFooter(embed)] }); @@ -116,7 +121,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: user.username, - value: `${shortDateAndShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, + value: `${discordLongDateWithShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, }); }); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 8566624..3519a16 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -2,6 +2,7 @@ import { EmbedBuilder } from "discord.js"; import { APIEmbedField, ChatInputCommandInteraction } from "discord.js"; import { dayInSeconds, + discordLongDateWithShortTimeTimestamp, discordTimestamp, monthInSeconds, shortDateAndShortTimeTimestamp, @@ -12,6 +13,8 @@ import { config } from "../../config"; import { makeTimeString, sortDbToString } from "./helper"; import { gameNoEntry, userNoEntry, userNoGameEntry } from "./messages"; import { addEmbedFooter } from "../misc/embeds"; +import { userLast } from "./userCommands"; +import { gameLast } from "./gameCommands"; export async function playtime(interaction: ChatInputCommandInteraction) { // get target user and game @@ -84,7 +87,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { await interaction.reply(gameNoEntry); return; } - text = `${targetGame} was played ${db.logs} times`; + text = `${targetGame} was logged ${db.logs} times`; } else if (targetUser && !targetGame) { // user, no game // get target db user entry @@ -93,7 +96,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { await interaction.reply(userNoEntry); return; } - text = `${targetUser.username} was logged ${db.logs} times`; + text = `${targetUser.toString()} was logged ${db.logs} times`; } else if (targetUser && targetGame) { // user, game // load db and get user.games and find target game in there @@ -104,7 +107,7 @@ export async function logs(interaction: ChatInputCommandInteraction) { await interaction.reply(userNoGameEntry); return; } - text = `${targetUser.username} has played ${targetGame} ${db.logs} times`; + text = `${targetUser.toString()} has played ${targetGame} ${db.logs} times`; } else { text = "error"; } @@ -114,35 +117,42 @@ export async function logs(interaction: ChatInputCommandInteraction) { }); } export async function latest(interaction: ChatInputCommandInteraction) { - // future embed fields - const fields: APIEmbedField[] = []; + const user = interaction.options.getUser("user"); + const game = interaction.options.getString("game"); - // latest system logs - const logs = trackerLogs - .array() - .sort((a, b) => b.date - a.date) - .slice(0, 5); - - // make embed for each log - logs.forEach((log) => - fields.push({ - inline: true, - name: log.gameName, - value: `<@${log.userID}>\n${shortDateAndShortTimeTimestamp(log.date / 1000)}\n${makeTimeString( - log.playtime - )}`, - }) - ); + if (user) { + await userLast(interaction); + } else if (game) { + await gameLast(interaction); + } else { + // future embed fields + const fields: APIEmbedField[] = []; + // latest system logs + const logs = trackerLogs + .array() + .sort((a, b) => b.date - a.date) + .slice(0, 5); - // add empty field for better formatting - fields.push({ inline: true, name: "_ _", value: "_ _" }); + // make embed for each log + logs.forEach((log) => + fields.push({ + inline: true, + name: log.gameName, + value: `<@${log.userID}>\n${discordLongDateWithShortTimeTimestamp(log.date / 1000)}\n${makeTimeString( + log.playtime + )}`, + }) + ); + // add empty field for better formatting + fields.push({ inline: true, name: "_ _", value: "_ _" }); - const embed = new EmbedBuilder() - .setTitle("Latest logs") - .setColor(config.color) - .addFields(...fields); + const embed = new EmbedBuilder() + .setTitle("Latest logs") + .setColor(config.color) + .addFields(...fields); - await interaction.reply({ embeds: [embed] }); + await interaction.reply({ embeds: [embed] }); + } } export async function stats(interaction: ChatInputCommandInteraction) { // get 5 most logged games and make string @@ -217,16 +227,16 @@ export async function stats(interaction: ChatInputCommandInteraction) { const embed = addEmbedFooter( new EmbedBuilder().setTitle("System stats").addFields( - { inline: true, name: "Most logged games", value: mostLoggedGame }, { inline: true, name: "Most played games", value: mostPlayedGame }, + { inline: true, name: "Most logged games", value: mostLoggedGame }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Most logged users", value: mostLoggedUser }, - { inline: true, name: "Users with most playtime", value: mostPlayedUser }, + { inline: true, name: "Most playtime", value: mostPlayedUser }, + { inline: true, name: "Most logs", value: mostLoggedUser }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Latest logs", value: latestLogs }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, + { inline: true, name: "(Average) playtime/log", value: mostPlaytime }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, @@ -236,11 +246,19 @@ export async function stats(interaction: ChatInputCommandInteraction) { )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total games", value: games.toString() }, - { inline: true, name: "Total users", value: users.toString() } + { + inline: true, + name: "Total...", + value: + "logs: " + + totalLogs.toString() + + "\nplaytime: " + + makeTimeString(totalPlaytime) + + "\ngames: " + + games.toString() + + "\nusers: " + + users.toString(), + } ) ); diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 8286511..6ad51b6 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -3,9 +3,9 @@ import { TrackerSublog, trackerUsers } from "../../db"; import { addEmbedFooter } from "../misc/embeds"; import { dayInMillis, + discordLongDateWithShortTimeTimestamp, discordTimestamp, monthInMillis, - shortDateAndShortTimeTimestamp, weekInMillis, } from "../misc/time"; import { makeTimeString, sortDbToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; @@ -14,55 +14,68 @@ import { memberNotFound, userNoEntry, userNotFound } from "./messages"; export async function userStats(interaction: ChatInputCommandInteraction) { // get target user and default to command executer if not given const target = interaction.options.getUser("user") ?? interaction.user; + const targetGame = interaction.options.getString("game"); if (!target) { await interaction.reply(userNotFound); return; } - // get the member (to use their display name and color) - const member = await interaction.guild?.members.fetch(target.id); - if (!member) { - await interaction.reply(memberNotFound); - return; - } // load db and get target user const db = trackerUsers.get(target.id); if (!db) { await interaction.reply(userNoEntry); return; } - + // get the member (to use their display name and color) + const member = await interaction.guild?.members.fetch(target.id); + if (!member) { + await interaction.reply(memberNotFound); + return; + } // make sorted list of most played games and make string const mostPlayed = sortDbToString( - db.games, + db.games.filter((v) => (targetGame ? v.name == targetGame : true)), (a, b) => b.playtime - a.playtime, (game) => `${game.name}: ${makeTimeString(game.playtime)}` ); // make sorted list of most logged games and make string const mostLogged = sortDbToString( - db.games, + db.games.filter((v) => (targetGame ? v.name == targetGame : true)), (a, b) => b.logs - a.logs, (game) => `${game.name}: ${game.logs} logs` ); // get latest logs and make string - const latestLogs = db.lastlogs.map((log) => `${log.gameName}`).join("\n"); + const latestLogs = db.lastlogs + .reverse() + .map((log) => `${discordLongDateWithShortTimeTimestamp(log.date)} ${log.gameName}`) + .join("\n"); // get total users playtime, logs and games - const totalPlaytime = db.playtime; + const totalPlaytime = targetGame + ? db.games + .filter((v) => v.name == targetGame) + .map((v) => v.playtime) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + : db.playtime; const games = db.games.length; - const totalLogs = db.logs; - // get first log (time is iso string) + const totalLogs = targetGame + ? db.games + .filter((v) => v.name == targetGame) + .map((v) => v.logs) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + : db.logs; + // get first log const firstSeen = db.firstlog.date; // make range from first log to now const range = Date.now() - firstSeen; - // calculate average paytime per day/week/month/user/log + // calculate average playtime per day/week/month/user/log const playtimePer = `day: ${makeTimeString( Math.round(totalPlaytime / (range / dayInMillis)) )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( Math.round(totalPlaytime / (range / monthInMillis)) - )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nlog: ${makeTimeString( - Math.round(totalPlaytime / totalLogs) - )}`; + )}${ + targetGame ? "" : "\ngame: " + makeTimeString(Math.round(totalPlaytime / games)) + }\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; // temporary sorted list of games playtime/log const tmp = db.games.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; // make string from first element of temporary list @@ -70,29 +83,45 @@ export async function userStats(interaction: ChatInputCommandInteraction) { tmp.logs } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; - const embed = addEmbedFooter( - new EmbedBuilder().setTitle(`Tracking stats about ${member.displayName}`).addFields( - { inline: true, name: "Most logged game", value: mostLogged }, - { inline: true, name: "Game with most playtime", value: mostPlayed }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, - { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "(Average) most playtime/log", value: mostPlaytime }, - { inline: true, name: "Total played games", value: games.toString() }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Total logs", value: totalLogs.toString() }, - { inline: true, name: "Total playtime", value: makeTimeString(totalPlaytime) } - ) + const embed = new EmbedBuilder().setTitle( + targetGame + ? `${member.displayName}'s tracking stats about ${targetGame}` + : `Tracking stats about ${member.displayName}` ); + if (!targetGame) + embed.addFields( + { inline: true, name: "Most playtime", value: mostPlayed }, + { inline: true, name: "Most logs", value: mostLogged }, + { inline: false, name: "_ _", value: "_ _" } + ); + embed.addFields( + { + inline: true, + name: "Total...", + value: + "playtime: " + + makeTimeString(totalPlaytime) + + "\nlogs: " + + totalLogs.toString() + + (targetGame ? "" : "\ngames: " + games.toString()), + }, + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "(Average) playtime per", value: playtimePer }, + { inline: true, name: "(Average) playtime/log", value: mostPlaytime }, + { inline: false, name: "_ _", value: "_ _" }, + { + inline: true, + name: "Record range", + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + Math.floor(Date.now() / 1000) + )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + } + ); + if (!targetGame) + embed.addFields( + { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "Latest logs", value: latestLogs } + ); await interaction.reply({ embeds: [embed] }); } @@ -126,7 +155,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `${shortDateAndShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, + value: `${discordLongDateWithShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, }); }); From 04dad793c96549694fe598f2e178e71f954e94ea Mon Sep 17 00:00:00 2001 From: flloschy Date: Wed, 5 Jul 2023 17:03:27 +0200 Subject: [PATCH 83/92] typo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 908b39b..ce28b00 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build": "tsc -p tsconfig.json", "run": "node --experimental-specifier-resolution=node .", "start": "npm run build && npm run run", - "freshStart": "npm run rmDist && mpm run start", + "freshStart": "npm run rmDist && npm run start", "prepare": "husky install" }, "repository": { From c25973b72a54c2119cd6bdf9cb5129e2391f4628 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 7 Jul 2023 18:24:37 +0200 Subject: [PATCH 84/92] time/date/timestamp fixes --- src/db.ts | 4 ++-- src/interactions/autocompleters/TrackerAutocomple.ts | 8 +++++--- src/util/tracker/gameCommands.ts | 8 ++++---- src/util/tracker/subCommands.ts | 8 ++++---- src/util/tracker/userCommands.ts | 6 +++--- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/db.ts b/src/db.ts index 27bdf8f..ec24bc2 100644 --- a/src/db.ts +++ b/src/db.ts @@ -61,7 +61,7 @@ interface InternalReminder { export interface TrackerLog { /** id of the log */ id: string; - /** When a log got logged (Date.now()) */ + /** When a log gets logged (Date.now())*/ date: number; /** How long a game got played in seconds*/ playtime: number; @@ -140,7 +140,7 @@ const unknownTrackerLog: TrackerLog = { export const trackerLogs = new Enmap({ name: "trackerLogs", serializer: (data) => ({ ...data, date: new Date(data.date).toISOString() }), - deserializer: (data) => ({ ...data, date: new Date(data.date).getTime() }), + deserializer: (data) => ({ ...data, date: new Date(data.date).getTime() / 1000 }), }); export const trackerUsers = new Enmap({ name: "trackerUsers", diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 029fe09..562d2f2 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -25,18 +25,20 @@ class Tracker extends Autocompleter { let options: string[] = []; - if (sub == "user" || option == "game") { + if (sub == "user") { options = user(option, userId); } else if (sub == "game") { - options = trackerGames.keyArray().map((g) => g); + options = trackerGames.keyArray(); } else if (sub == "blacklist") { options = blacklist(action); } else if (sub == "general") { if (interaction.options.get("user")) { options = trackerUsers.get(userId)?.games.map((g) => g.name) ?? ["[no games played yet]"]; } else { - options = trackerGames.keyArray().map((g) => g); + options = trackerGames.keyArray(); } + } else if (option == "game") { + options = user(option, false) } const filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)).slice(0, 25); diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 09acacc..cc60d91 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -45,7 +45,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const totalPlaytime = db.playtime; const totalLogs = db.logs; const users = db.users.length; - // get first log of game (first log is iso string) + // get first log of game const firstSeen = db.firstlog.date; // calculate the range from first log to now const range = Date.now() - firstSeen; @@ -83,9 +83,9 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + )}(now)\n${makeTimeString(Math.floor(Date.now()/1000 - firstSeen))}`, }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Latest logs", value: latestLogs } @@ -121,7 +121,7 @@ export async function gameLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: user.username, - value: `${discordLongDateWithShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, + value: `${discordLongDateWithShortTimeTimestamp(log.date)}\n${makeTimeString(log.playtime)}`, }); }); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 3519a16..8f56a14 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -138,7 +138,7 @@ export async function latest(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `<@${log.userID}>\n${discordLongDateWithShortTimeTimestamp(log.date / 1000)}\n${makeTimeString( + value: `<@${log.userID}>\n${discordLongDateWithShortTimeTimestamp(log.date)}\n${makeTimeString( log.playtime )}`, }) @@ -190,7 +190,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map( (log) => - `${shortDateAndShortTimeTimestamp(log.date / 1000)} <@${log.userID}> ${ + `${shortDateAndShortTimeTimestamp(log.date)} <@${log.userID}> ${ log.gameName }: ${makeTimeString(log.playtime)}` ) @@ -241,9 +241,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + )}(now)\n${makeTimeString(Math.floor(Date.now() / 1000 - firstSeen))}`, }, { inline: false, name: "_ _", value: "_ _" }, { diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 6ad51b6..3300d20 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -112,9 +112,9 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Date.now() - firstSeen)}`, + )}(now)\n${makeTimeString(Math.floor(Date.now()/1000 - firstSeen))}`, } ); if (!targetGame) @@ -155,7 +155,7 @@ export async function userLast(interaction: ChatInputCommandInteraction) { fields.push({ inline: true, name: log.gameName, - value: `${discordLongDateWithShortTimeTimestamp(log.date / 1000)}\n${makeTimeString(log.playtime)}`, + value: `${discordLongDateWithShortTimeTimestamp(log.date)}\n${makeTimeString(log.playtime)}`, }); }); From f18cae6eeb1ab68ba75fec60c70e8ce55e80c1d7 Mon Sep 17 00:00:00 2001 From: flloschy Date: Fri, 7 Jul 2023 18:25:46 +0200 Subject: [PATCH 85/92] formatting --- src/interactions/autocompleters/TrackerAutocomple.ts | 2 +- src/util/tracker/gameCommands.ts | 2 +- src/util/tracker/helper.ts | 3 ++- src/util/tracker/subCommands.ts | 8 ++++---- src/util/tracker/userCommands.ts | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/interactions/autocompleters/TrackerAutocomple.ts b/src/interactions/autocompleters/TrackerAutocomple.ts index 562d2f2..1c25202 100644 --- a/src/interactions/autocompleters/TrackerAutocomple.ts +++ b/src/interactions/autocompleters/TrackerAutocomple.ts @@ -38,7 +38,7 @@ class Tracker extends Autocompleter { options = trackerGames.keyArray(); } } else if (option == "game") { - options = user(option, false) + options = user(option, false); } const filteredOptions = options.filter((o) => o.toLowerCase().startsWith(focus)).slice(0, 25); diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index cc60d91..204e471 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -85,7 +85,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { name: "Record range", value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor(Date.now()/1000 - firstSeen))}`, + )}(now)\n${makeTimeString(Math.floor(Date.now() / 1000 - firstSeen))}`, }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Latest logs", value: latestLogs } diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 660f1c8..2ee498c 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -5,7 +5,8 @@ import { durationToReadable } from "../misc/time"; /** Uses `durationToReadable` from src/util/misc/time.ts but without the need to parse a Duration object */ export function makeTimeString(timeSeconds: number) { - return durationToReadable(Duration.fromMillis(timeSeconds * 1000), true); + const str = durationToReadable(Duration.fromMillis(timeSeconds * 1000), true); + return str.length >= 1 ? str : "0s" } /** Sorts users games after playtime of a user (most playtime at index 0)*/ export function sortGamesPlaytime(userId: string) { diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 8f56a14..7b5558c 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -190,9 +190,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { .slice(0, 5) .map( (log) => - `${shortDateAndShortTimeTimestamp(log.date)} <@${log.userID}> ${ - log.gameName - }: ${makeTimeString(log.playtime)}` + `${shortDateAndShortTimeTimestamp(log.date)} <@${log.userID}> ${log.gameName}: ${makeTimeString( + log.playtime + )}` ) .join("\n"); // get total playtime of all games @@ -206,7 +206,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { const games = trackerGames.count; // get amount of users const users = trackerUsers.count; - // get first log (time is iso string) + // get first log const firstSeen = trackerLogs.array()[0].date; // make range from first log to now const range = Date.now() - firstSeen; diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 3300d20..269351c 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -114,7 +114,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { name: "Record range", value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor(Date.now()/1000 - firstSeen))}`, + )}(now)\n${makeTimeString(Math.floor(Date.now() / 1000 - firstSeen))}`, } ); if (!targetGame) From 0f82baed7d52557bae8152f9b90c8bf624c39dc7 Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 19 Aug 2023 17:12:41 +0200 Subject: [PATCH 86/92] some fixes related to the ms->s change and a bit cleaner code --- src/commands/TrackerCommands.ts | 51 +++++++++++----------- src/db.ts | 2 +- src/util/misc/time.ts | 9 ++-- src/util/tracker/gameCommands.ts | 56 ++++++++++++------------ src/util/tracker/helper.ts | 4 +- src/util/tracker/subCommands.ts | 39 +++++++++-------- src/util/tracker/userCommands.ts | 74 +++++++++++++++++--------------- 7 files changed, 126 insertions(+), 109 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index ab5e46a..36398bc 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -27,34 +27,37 @@ class Tracker extends Command { const subCommand = interaction.options.getSubcommand(); const statistics = interaction.options.getString("statistic") ?? "general statistics"; const action = interaction.options.getString("action"); + let err = false; - switch (subCommand) { - case "user": - await userStats(interaction); - return; - case "game": - await gameStats(interaction); - return; - case "general": - if (statistics == "playtime") await playtime(interaction); - else if (statistics == "logs") await logs(interaction); - return; - case "blacklist": - if (action == "add") await addBlacklist(interaction); - else if (action == "rem") await remBlacklist(interaction); - return; - case "latest": - await latest(interaction); - return; - case "statistics": - await stats(interaction); - return; - } + try { + switch (subCommand) { + case "user": + await userStats(interaction); + return; + case "game": + await gameStats(interaction); + return; + case "general": + if (statistics == "playtime") await playtime(interaction); + else if (statistics == "logs") await logs(interaction); + return; + case "blacklist": + if (action == "add") await addBlacklist(interaction); + else if (action == "rem") await remBlacklist(interaction); + return; + case "latest": + await latest(interaction); + return; + case "statistics": + await stats(interaction); + return; + } + } catch (e) {err = true} - if (!interaction.replied) { + if (!interaction.replied || err) { await interaction.reply({ content: - "This can happen when you don't follow the order of the given options. Sadly thats a bug by discord (options don't get updated correctly when not in order)\nJust execute the command again in the right order and everything should work!\nIf not, please get in touch with a developer.", + "The command you where trying to executed failed... This can happen because there are no logs fitting the given criteria, or you used the wrong order of inputs, which might result in options being available although they shouldn't. Sadly, this is an issue on discords side.", ephemeral: true, }); } diff --git a/src/db.ts b/src/db.ts index ec24bc2..869cedd 100644 --- a/src/db.ts +++ b/src/db.ts @@ -140,7 +140,7 @@ const unknownTrackerLog: TrackerLog = { export const trackerLogs = new Enmap({ name: "trackerLogs", serializer: (data) => ({ ...data, date: new Date(data.date).toISOString() }), - deserializer: (data) => ({ ...data, date: new Date(data.date).getTime() / 1000 }), + deserializer: (data) => ({ ...data, date: new Date(data.date).getTime() }), }); export const trackerUsers = new Enmap({ name: "trackerUsers", diff --git a/src/util/misc/time.ts b/src/util/misc/time.ts index 2726e2c..7c1b796 100644 --- a/src/util/misc/time.ts +++ b/src/util/misc/time.ts @@ -110,9 +110,12 @@ export async function checkDuration( } else return iso; } -// 60seconds * 60 minutes * 24 hours = One day -export const dayInSeconds = 60 * 60 * 24; -export const dayInMillis = dayInSeconds * 1000; +// 60 seconds * 60minutes +export const hourInSeconds = 60 * 60; +export const hourInMillis = hourInSeconds * 1000; +// 1 hour * 24 hours = One day +export const dayInSeconds = hourInSeconds * 24; +export const dayInMillis = hourInMillis * 24; // 7 times a day = week export const weekInSeconds = dayInSeconds * 7; export const weekInMillis = dayInMillis * 7; diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 204e471..9fd8c29 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -3,11 +3,12 @@ import { config } from "../../config"; import { trackerGames, trackerLogs, TrackerSublog } from "../../db"; import { addEmbedFooter } from "../misc/embeds"; import { - dayInMillis, + dayInSeconds, discordLongDateWithShortTimeTimestamp, discordTimestamp, - monthInMillis, - weekInMillis, + hourInSeconds, + monthInSeconds, + weekInSeconds, } from "../misc/time"; import { makeTimeString, sortDbToString } from "./helper"; import { gameNoEntry } from "./messages"; @@ -48,21 +49,22 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { // get first log of game const firstSeen = db.firstlog.date; // calculate the range from first log to now - const range = Date.now() - firstSeen; - // calculate daily/weekly/monthly and per-log average playtime - const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / dayInMillis)) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / monthInMillis)) - )}\nuser: ${makeTimeString(Math.round(totalPlaytime / users))}\nlog: ${makeTimeString( - Math.round(totalPlaytime / totalLogs) - )}`; - // temporary sorted list of most playtime/log (users) - const tmp = db.users.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; - // make most playtime/log string - const mostPlaytime = `<@${tmp.name}>: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ - tmp.logs - } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + const range = ~~((Date.now() - firstSeen) / 1000); + + const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)) + const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)) + const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)) + const playtimePerUser = makeTimeString(totalPlaytime / users) + const playtimePerLog = makeTimeString(totalPlaytime / totalLogs) + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}` + + const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); + const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); + const logsPerMonth = Math.round(totalLogs / (range / monthInSeconds)); + const logsPerUser = Math.round(totalLogs / users); + const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\nuser: ${logsPerUser}\nhour: ${logsPerHour}` + const embed = new EmbedBuilder() .setColor(config.color) @@ -71,24 +73,24 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Most playtime", value: mostPlayed }, { inline: true, name: "Most logs", value: mostLogged }, { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Total...", - value: "Playtime: " + makeTimeString(totalPlaytime) + "\nlogs: " + totalLogs.toString(), - }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) playtime/log", value: mostPlaytime }, + { inline: true, name: "(Average) logs per", value: logsPer }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor(Date.now() / 1000 - firstSeen))}`, + )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen)/1000))}`, }, { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs } + { inline: true, name: "Latest logs", value: latestLogs }, + { + inline: false, + name: "Total...", + value: "Playtime: " + makeTimeString(totalPlaytime) + "\nlogs: " + totalLogs.toString(), + }, ); await interaction.reply({ embeds: [addEmbedFooter(embed)] }); diff --git a/src/util/tracker/helper.ts b/src/util/tracker/helper.ts index 2ee498c..d0da45a 100644 --- a/src/util/tracker/helper.ts +++ b/src/util/tracker/helper.ts @@ -5,8 +5,8 @@ import { durationToReadable } from "../misc/time"; /** Uses `durationToReadable` from src/util/misc/time.ts but without the need to parse a Duration object */ export function makeTimeString(timeSeconds: number) { - const str = durationToReadable(Duration.fromMillis(timeSeconds * 1000), true); - return str.length >= 1 ? str : "0s" + const str = durationToReadable(Duration.fromMillis(Math.round(timeSeconds) * 1000), true); + return str.length >= 1 ? str : "0s"; } /** Sorts users games after playtime of a user (most playtime at index 0)*/ export function sortGamesPlaytime(userId: string) { diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index 7b5558c..deea152 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -4,6 +4,7 @@ import { dayInSeconds, discordLongDateWithShortTimeTimestamp, discordTimestamp, + hourInSeconds, monthInSeconds, shortDateAndShortTimeTimestamp, weekInSeconds, @@ -209,21 +210,23 @@ export async function stats(interaction: ChatInputCommandInteraction) { // get first log const firstSeen = trackerLogs.array()[0].date; // make range from first log to now - const range = Date.now() - firstSeen; - // calculate average playtime per day/week/month/game/log - const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / dayInSeconds)) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInSeconds)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / monthInSeconds)) - )}\ngame: ${makeTimeString(Math.round(totalPlaytime / games))}\nuser: ${makeTimeString( - Math.round(totalPlaytime / users) - )}\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; - // temporary sorted list based on playtime/log - const tmp = trackerGames.array().sort((a, b) => b.playtime / b.logs - a.playtime / b.logs)[0]; - // make string from temporary list - const mostPlaytime = `${tmp.lastlogs[0].gameName}: ${makeTimeString( - Math.round(tmp.playtime / tmp.logs) - )} - ${tmp.logs} logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + const range = ~~((Date.now() - firstSeen) / 1000); + + const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)) + const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)) + const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)) + const playtimePerGame = makeTimeString(totalPlaytime / games) + const playtimePerUser = makeTimeString(totalPlaytime / users) + const playtimePerLog = makeTimeString(totalPlaytime / totalLogs) + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}` + + const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); + const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); + const logsPerMonth = Math.round(totalLogs / (range / monthInSeconds)); + const logsPerUser = Math.round(totalLogs / users); + const logsPerGame = Math.round(totalLogs / games); + const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\ngame: ${logsPerGame}\nuser: ${logsPerUser}\nhour: ${logsPerHour}` const embed = addEmbedFooter( new EmbedBuilder().setTitle("System stats").addFields( @@ -236,14 +239,14 @@ export async function stats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Latest logs", value: latestLogs }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) playtime/log", value: mostPlaytime }, + { inline: true, name: "(Average) logs per", value: logsPer }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor(Date.now() / 1000 - firstSeen))}`, + )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen)/100))}`, }, { inline: false, name: "_ _", value: "_ _" }, { diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 269351c..77f857c 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -1,12 +1,14 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; +import { config } from "../../config"; import { TrackerSublog, trackerUsers } from "../../db"; import { addEmbedFooter } from "../misc/embeds"; import { - dayInMillis, + dayInSeconds, discordLongDateWithShortTimeTimestamp, discordTimestamp, - monthInMillis, - weekInMillis, + hourInSeconds, + monthInSeconds, + weekInSeconds, } from "../misc/time"; import { makeTimeString, sortDbToString, sortGamesLogs, sortGamesPlaytime } from "./helper"; import { memberNotFound, userNoEntry, userNotFound } from "./messages"; @@ -67,27 +69,28 @@ export async function userStats(interaction: ChatInputCommandInteraction) { // get first log const firstSeen = db.firstlog.date; // make range from first log to now - const range = Date.now() - firstSeen; - // calculate average playtime per day/week/month/user/log - const playtimePer = `day: ${makeTimeString( - Math.round(totalPlaytime / (range / dayInMillis)) - )}\nweek: ${makeTimeString(Math.round(totalPlaytime / (range / weekInMillis)))}\nmonth: ${makeTimeString( - Math.round(totalPlaytime / (range / monthInMillis)) - )}${ - targetGame ? "" : "\ngame: " + makeTimeString(Math.round(totalPlaytime / games)) - }\nlog: ${makeTimeString(Math.round(totalPlaytime / totalLogs))}`; - // temporary sorted list of games playtime/log - const tmp = db.games.sort((a, b) => b.playtime / b.logs - a.playtime / a.logs)[0]; - // make string from first element of temporary list - const mostPlaytime = `${tmp.name}: ${makeTimeString(tmp.playtime / tmp.logs)} - ${ - tmp.logs - } logs\nTotal playtime: ${makeTimeString(tmp.playtime)}`; + const range = ~~((Date.now() - firstSeen)/1000); + + const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)) + const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)) + const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)) + const playtimePerGame = makeTimeString(totalPlaytime / games) + const playtimePerLog = makeTimeString(totalPlaytime / totalLogs) + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nhour: ${playtimePerLog}` + + const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); + const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); + const logsPerMonth = Math.round(totalLogs / (range / monthInSeconds)); + const logsPerGame = Math.round(totalLogs / games); + const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\ngame: ${logsPerGame}\nhour: ${logsPerHour}` const embed = new EmbedBuilder().setTitle( targetGame ? `${member.displayName}'s tracking stats about ${targetGame}` : `Tracking stats about ${member.displayName}` - ); + ).setColor(config.color) + ; if (!targetGame) embed.addFields( { inline: true, name: "Most playtime", value: mostPlayed }, @@ -95,26 +98,16 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: false, name: "_ _", value: "_ _" } ); embed.addFields( - { - inline: true, - name: "Total...", - value: - "playtime: " + - makeTimeString(totalPlaytime) + - "\nlogs: " + - totalLogs.toString() + - (targetGame ? "" : "\ngames: " + games.toString()), - }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, - { inline: true, name: "(Average) playtime/log", value: mostPlaytime }, + { inline: true, name: "(Average) logs per", value: logsPer }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor(Date.now() / 1000 - firstSeen))}`, + )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen) / 1000))}`, } ); if (!targetGame) @@ -123,6 +116,19 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Latest logs", value: latestLogs } ); + embed.addFields( + { + inline: false, + name: "Total...", + value: + "playtime: " + + makeTimeString(totalPlaytime) + + "\nlogs: " + + totalLogs.toString() + + (targetGame ? "" : "\ngames: " + games.toString()), + } + ) + await interaction.reply({ embeds: [embed] }); } export async function userLast(interaction: ChatInputCommandInteraction) { @@ -203,6 +209,8 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: // future embed fields const fields: APIEmbedField[] = []; + + // add one extra field for better formatting // make field for every game based on filter games.forEach((game) => { fields.push({ @@ -214,8 +222,6 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: : `${game.logs} logs\n${makeTimeString(game.playtime)}`, }); }); - - // add one extra field for better formatting fields.push({ inline: true, name: "_ _", value: "_ _" }); const embed = addEmbedFooter( From 2014f9ff38c4c8eb7a550e761ad0bbb71decf24d Mon Sep 17 00:00:00 2001 From: flloschy Date: Sat, 19 Aug 2023 17:14:02 +0200 Subject: [PATCH 87/92] formatting --- src/commands/TrackerCommands.ts | 4 ++- src/util/tracker/gameCommands.ts | 19 +++++------ src/util/tracker/subCommands.ts | 20 +++++------ src/util/tracker/userCommands.ts | 57 ++++++++++++++++---------------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 36398bc..54c4ef7 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -52,7 +52,9 @@ class Tracker extends Command { await stats(interaction); return; } - } catch (e) {err = true} + } catch (e) { + err = true; + } if (!interaction.replied || err) { await interaction.reply({ diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 9fd8c29..cc911d7 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -51,20 +51,19 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { // calculate the range from first log to now const range = ~~((Date.now() - firstSeen) / 1000); - const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)) - const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)) - const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)) - const playtimePerUser = makeTimeString(totalPlaytime / users) - const playtimePerLog = makeTimeString(totalPlaytime / totalLogs) - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}` + const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)); + const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)); + const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)); + const playtimePerUser = makeTimeString(totalPlaytime / users); + const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); const logsPerMonth = Math.round(totalLogs / (range / monthInSeconds)); const logsPerUser = Math.round(totalLogs / users); const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); - const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\nuser: ${logsPerUser}\nhour: ${logsPerHour}` - + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\nuser: ${logsPerUser}\nhour: ${logsPerHour}`; const embed = new EmbedBuilder() .setColor(config.color) @@ -82,7 +81,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { name: "Record range", value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen)/1000))}`, + )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen) / 1000))}`, }, { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "Latest logs", value: latestLogs }, @@ -90,7 +89,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { inline: false, name: "Total...", value: "Playtime: " + makeTimeString(totalPlaytime) + "\nlogs: " + totalLogs.toString(), - }, + } ); await interaction.reply({ embeds: [addEmbedFooter(embed)] }); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index deea152..d58e13c 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -212,13 +212,13 @@ export async function stats(interaction: ChatInputCommandInteraction) { // make range from first log to now const range = ~~((Date.now() - firstSeen) / 1000); - const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)) - const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)) - const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)) - const playtimePerGame = makeTimeString(totalPlaytime / games) - const playtimePerUser = makeTimeString(totalPlaytime / users) - const playtimePerLog = makeTimeString(totalPlaytime / totalLogs) - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}` + const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)); + const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)); + const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)); + const playtimePerGame = makeTimeString(totalPlaytime / games); + const playtimePerUser = makeTimeString(totalPlaytime / users); + const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); @@ -226,7 +226,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { const logsPerUser = Math.round(totalLogs / users); const logsPerGame = Math.round(totalLogs / games); const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); - const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\ngame: ${logsPerGame}\nuser: ${logsPerUser}\nhour: ${logsPerHour}` + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\ngame: ${logsPerGame}\nuser: ${logsPerUser}\nhour: ${logsPerHour}`; const embed = addEmbedFooter( new EmbedBuilder().setTitle("System stats").addFields( @@ -244,9 +244,9 @@ export async function stats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen/1000))} -> ${discordTimestamp( + value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen)/100))}`, + )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen) / 100))}`, }, { inline: false, name: "_ _", value: "_ _" }, { diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 77f857c..63937fe 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -69,28 +69,29 @@ export async function userStats(interaction: ChatInputCommandInteraction) { // get first log const firstSeen = db.firstlog.date; // make range from first log to now - const range = ~~((Date.now() - firstSeen)/1000); + const range = ~~((Date.now() - firstSeen) / 1000); - const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)) - const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)) - const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)) - const playtimePerGame = makeTimeString(totalPlaytime / games) - const playtimePerLog = makeTimeString(totalPlaytime / totalLogs) - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nhour: ${playtimePerLog}` + const playtimePerDay = makeTimeString(totalPlaytime / (range / dayInSeconds)); + const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)); + const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)); + const playtimePerGame = makeTimeString(totalPlaytime / games); + const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nhour: ${playtimePerLog}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); const logsPerMonth = Math.round(totalLogs / (range / monthInSeconds)); const logsPerGame = Math.round(totalLogs / games); const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); - const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\ngame: ${logsPerGame}\nhour: ${logsPerHour}` - - const embed = new EmbedBuilder().setTitle( - targetGame - ? `${member.displayName}'s tracking stats about ${targetGame}` - : `Tracking stats about ${member.displayName}` - ).setColor(config.color) - ; + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\ngame: ${logsPerGame}\nhour: ${logsPerHour}`; + + const embed = new EmbedBuilder() + .setTitle( + targetGame + ? `${member.displayName}'s tracking stats about ${targetGame}` + : `Tracking stats about ${member.displayName}` + ) + .setColor(config.color); if (!targetGame) embed.addFields( { inline: true, name: "Most playtime", value: mostPlayed }, @@ -107,7 +108,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { name: "Record range", value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen) / 1000))}`, + )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen) / 1000))}`, } ); if (!targetGame) @@ -116,18 +117,16 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Latest logs", value: latestLogs } ); - embed.addFields( - { - inline: false, - name: "Total...", - value: - "playtime: " + - makeTimeString(totalPlaytime) + - "\nlogs: " + - totalLogs.toString() + - (targetGame ? "" : "\ngames: " + games.toString()), - } - ) + embed.addFields({ + inline: false, + name: "Total...", + value: + "playtime: " + + makeTimeString(totalPlaytime) + + "\nlogs: " + + totalLogs.toString() + + (targetGame ? "" : "\ngames: " + games.toString()), + }); await interaction.reply({ embeds: [embed] }); } @@ -209,7 +208,7 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: // future embed fields const fields: APIEmbedField[] = []; - + // add one extra field for better formatting // make field for every game based on filter games.forEach((game) => { From f761f677383c6a2209453717e5826698b52aaf28 Mon Sep 17 00:00:00 2001 From: flloschy Date: Tue, 10 Oct 2023 16:28:11 +0200 Subject: [PATCH 88/92] fix double field --- src/util/tracker/userCommands.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 63937fe..39951f4 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -99,7 +99,6 @@ export async function userStats(interaction: ChatInputCommandInteraction) { { inline: false, name: "_ _", value: "_ _" } ); embed.addFields( - { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, { inline: true, name: "(Average) logs per", value: logsPer }, { inline: false, name: "_ _", value: "_ _" }, From c2e4bb5d43dc8aa72f5dc8d4aa3da66f842acb75 Mon Sep 17 00:00:00 2001 From: flloschy Date: Tue, 10 Oct 2023 16:28:49 +0200 Subject: [PATCH 89/92] ms to s fix --- src/util/tracker/gameCommands.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index cc911d7..cab5f89 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -40,7 +40,10 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { // format latest logs into a string const latestLogs = db.lastlogs - .map((log) => `${discordLongDateWithShortTimeTimestamp(log.date)} <@${trackerLogs.get(log.id)?.userID}>`) + .map( + (log) => + `${discordLongDateWithShortTimeTimestamp(log.date / 1000)} <@${trackerLogs.get(log.id)?.userID}>` + ) .join("\n"); // get total playtime, logs and users const totalPlaytime = db.playtime; @@ -72,7 +75,6 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { { inline: true, name: "Most playtime", value: mostPlayed }, { inline: true, name: "Most logs", value: mostLogged }, { inline: false, name: "_ _", value: "_ _" }, - { inline: false, name: "_ _", value: "_ _" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, { inline: true, name: "(Average) logs per", value: logsPer }, { inline: false, name: "_ _", value: "_ _" }, From 3661198cddc12205132853c56a3e34b95f6d3e06 Mon Sep 17 00:00:00 2001 From: flloschy Date: Tue, 10 Oct 2023 16:36:56 +0200 Subject: [PATCH 90/92] wrongly named value fix --- src/util/tracker/gameCommands.ts | 2 +- src/util/tracker/subCommands.ts | 2 +- src/util/tracker/userCommands.ts | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index cab5f89..289c13b 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -59,7 +59,7 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)); const playtimePerUser = makeTimeString(totalPlaytime / users); const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}`; + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}\nlog: ${playtimePerLog}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); diff --git a/src/util/tracker/subCommands.ts b/src/util/tracker/subCommands.ts index d58e13c..44a3a59 100644 --- a/src/util/tracker/subCommands.ts +++ b/src/util/tracker/subCommands.ts @@ -218,7 +218,7 @@ export async function stats(interaction: ChatInputCommandInteraction) { const playtimePerGame = makeTimeString(totalPlaytime / games); const playtimePerUser = makeTimeString(totalPlaytime / users); const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nuser: ${playtimePerUser}\nhour: ${playtimePerLog}`; + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nuser: ${playtimePerUser}\nlog: ${playtimePerLog}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 39951f4..7115191 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -55,16 +55,16 @@ export async function userStats(interaction: ChatInputCommandInteraction) { // get total users playtime, logs and games const totalPlaytime = targetGame ? db.games - .filter((v) => v.name == targetGame) - .map((v) => v.playtime) - .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + .filter((v) => v.name == targetGame) + .map((v) => v.playtime) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) : db.playtime; const games = db.games.length; const totalLogs = targetGame ? db.games - .filter((v) => v.name == targetGame) - .map((v) => v.logs) - .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + .filter((v) => v.name == targetGame) + .map((v) => v.logs) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) : db.logs; // get first log const firstSeen = db.firstlog.date; @@ -76,7 +76,7 @@ export async function userStats(interaction: ChatInputCommandInteraction) { const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)); const playtimePerGame = makeTimeString(totalPlaytime / games); const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nhour: ${playtimePerLog}`; + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\ngame: ${playtimePerGame}\nlog: ${playtimePerLog}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); @@ -227,4 +227,4 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: ); await interaction.reply({ embeds: [embed] }); -} +} \ No newline at end of file From e53a0b31578eabfb3558b5b8dbc03bc9960f7309 Mon Sep 17 00:00:00 2001 From: flloschy Date: Tue, 10 Oct 2023 16:37:29 +0200 Subject: [PATCH 91/92] formatting --- src/util/tracker/userCommands.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util/tracker/userCommands.ts b/src/util/tracker/userCommands.ts index 7115191..72f3752 100644 --- a/src/util/tracker/userCommands.ts +++ b/src/util/tracker/userCommands.ts @@ -55,16 +55,16 @@ export async function userStats(interaction: ChatInputCommandInteraction) { // get total users playtime, logs and games const totalPlaytime = targetGame ? db.games - .filter((v) => v.name == targetGame) - .map((v) => v.playtime) - .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + .filter((v) => v.name == targetGame) + .map((v) => v.playtime) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) : db.playtime; const games = db.games.length; const totalLogs = targetGame ? db.games - .filter((v) => v.name == targetGame) - .map((v) => v.logs) - .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + .filter((v) => v.name == targetGame) + .map((v) => v.logs) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) : db.logs; // get first log const firstSeen = db.firstlog.date; @@ -227,4 +227,4 @@ export async function userTop(interaction: ChatInputCommandInteraction, filter: ); await interaction.reply({ embeds: [embed] }); -} \ No newline at end of file +} From dd850fe58e1d6f78cc4d22e5daca8d69c8f22ae9 Mon Sep 17 00:00:00 2001 From: Murphy Date: Tue, 10 Oct 2023 17:30:32 +0200 Subject: [PATCH 92/92] tweak style of game command --- src/commands/TrackerCommands.ts | 2 ++ src/util/tracker/gameCommands.ts | 40 +++++++++++++------------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/commands/TrackerCommands.ts b/src/commands/TrackerCommands.ts index 54c4ef7..1dca492 100644 --- a/src/commands/TrackerCommands.ts +++ b/src/commands/TrackerCommands.ts @@ -9,6 +9,7 @@ import { userStats } from "../util/tracker/userCommands"; import { gameStats } from "../util/tracker/gameCommands"; import { addBlacklist, remBlacklist } from "../util/tracker/blacklistCommands"; import { config } from "../config"; +import { logger } from "../logger"; class Tracker extends Command { constructor() { @@ -53,6 +54,7 @@ class Tracker extends Command { return; } } catch (e) { + logger.error(e); err = true; } diff --git a/src/util/tracker/gameCommands.ts b/src/util/tracker/gameCommands.ts index 289c13b..5d859d8 100644 --- a/src/util/tracker/gameCommands.ts +++ b/src/util/tracker/gameCommands.ts @@ -4,9 +4,9 @@ import { trackerGames, trackerLogs, TrackerSublog } from "../../db"; import { addEmbedFooter } from "../misc/embeds"; import { dayInSeconds, + discordLongDateTimestamp, discordLongDateWithShortTimeTimestamp, - discordTimestamp, - hourInSeconds, + discordShortDateTimestamp, monthInSeconds, weekInSeconds, } from "../misc/time"; @@ -40,10 +40,8 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { // format latest logs into a string const latestLogs = db.lastlogs - .map( - (log) => - `${discordLongDateWithShortTimeTimestamp(log.date / 1000)} <@${trackerLogs.get(log.id)?.userID}>` - ) + .reverse() + .map((log) => `${discordShortDateTimestamp(log.date / 1000)} <@${trackerLogs.get(log.id)?.userID}>`) .join("\n"); // get total playtime, logs and users const totalPlaytime = db.playtime; @@ -58,15 +56,13 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { const playtimePerWeek = makeTimeString(totalPlaytime / (range / weekInSeconds)); const playtimePerMonth = makeTimeString(totalPlaytime / (range / monthInSeconds)); const playtimePerUser = makeTimeString(totalPlaytime / users); - const playtimePerLog = makeTimeString(totalPlaytime / totalLogs); - const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}\nlog: ${playtimePerLog}`; + const playtimePer = `day: ${playtimePerDay}\nweek: ${playtimePerWeek}\nmonth: ${playtimePerMonth}\nuser: ${playtimePerUser}`; const logsPerDay = Math.round(totalLogs / (range / dayInSeconds)); const logsPerWeek = Math.round(totalLogs / (range / weekInSeconds)); const logsPerMonth = Math.round(totalLogs / (range / monthInSeconds)); const logsPerUser = Math.round(totalLogs / users); - const logsPerHour = Math.round(totalLogs / (totalPlaytime / hourInSeconds)); - const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\nuser: ${logsPerUser}\nhour: ${logsPerHour}`; + const logsPer = `day: ${logsPerDay}\nweek: ${logsPerWeek}\nmonth: ${logsPerMonth}\nuser: ${logsPerUser}`; const embed = new EmbedBuilder() .setColor(config.color) @@ -74,24 +70,20 @@ export async function gameStats(interaction: ChatInputCommandInteraction) { .addFields( { inline: true, name: "Most playtime", value: mostPlayed }, { inline: true, name: "Most logs", value: mostLogged }, - { inline: false, name: "_ _", value: "_ _" }, + { inline: true, name: "\u200B", value: "\u200B" }, { inline: true, name: "(Average) playtime per", value: playtimePer }, { inline: true, name: "(Average) logs per", value: logsPer }, - { inline: false, name: "_ _", value: "_ _" }, - { - inline: true, - name: "Record range", - value: `${discordTimestamp(Math.floor(firstSeen / 1000))} -> ${discordTimestamp( - Math.floor(Date.now() / 1000) - )}(now)\n${makeTimeString(Math.floor((Date.now() - firstSeen) / 1000))}`, - }, - { inline: false, name: "_ _", value: "_ _" }, - { inline: true, name: "Latest logs", value: latestLogs }, + { inline: true, name: "\u200B", value: "\u200B" }, { inline: false, - name: "Total...", - value: "Playtime: " + makeTimeString(totalPlaytime) + "\nlogs: " + totalLogs.toString(), - } + name: "In total", + value: `Time range: ${discordLongDateTimestamp( + Math.floor(firstSeen / 1000) + )} -> ${discordLongDateTimestamp(db.lastlogs.reverse()[0].date / 1000)}\nPlaytime: ${makeTimeString( + totalPlaytime + )}\nLogs: ${totalLogs}, Users: ${users}`, + }, + { inline: false, name: "Latest logs", value: latestLogs } ); await interaction.reply({ embeds: [addEmbedFooter(embed)] });