From 4b4faa0860bd1fbad4a130693d18abbd43f08146 Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sat, 6 Aug 2022 10:56:02 -0400 Subject: [PATCH 1/8] initial implementation of leaderboard --- src/commandDetails/coin/leaderboard.ts | 62 ++++++++++++++++++++++++++ src/commands/coin/coin.ts | 4 +- src/components/coin.ts | 22 ++++++++- 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 src/commandDetails/coin/leaderboard.ts diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts new file mode 100644 index 00000000..fd479b6f --- /dev/null +++ b/src/commandDetails/coin/leaderboard.ts @@ -0,0 +1,62 @@ +import { container, SapphireClient } from '@sapphire/framework'; +import { Client, MessageEmbed } from 'discord.js'; +import { + CodeyCommandDetails, + CodeyCommandResponseType, + getUserIdFromMessage, + SapphireMessageExecuteType, + SapphireMessageResponse +} from '../../codeyCommand'; +import { getCurrentCoinLeaderboard, UserCoinEntry } from '../../components/coin'; +import { EMBED_COLOUR } from '../../utils/embeds'; + +const getCurrentCoinLeaderboardEmbed = async ( + client: SapphireClient, + leaderboard: UserCoinEntry[], + currentUserId: string, + limit = 10 +): Promise => { + const currentLeaderboardEmbed = new MessageEmbed().setColor(EMBED_COLOUR).setTitle('Current CodeyCoin Leaderboard'); + + let currentLeaderboardText = ''; + for (let i = 0; i < limit; i++) { + if (leaderboard.length <= i) break; + const userCoinEntry = leaderboard[i]; + const user = await client.users.cache.get(userCoinEntry.user_id); + const userCoinEntryText = `${i + 1}. ${user?.username ?? ''} - ${userCoinEntry.balance} 🪙.\n`; + currentLeaderboardText += userCoinEntryText; + } + currentLeaderboardEmbed.addFields({ + name: 'Current Leaderboard', + value: currentLeaderboardText + }); + + return currentLeaderboardEmbed; +}; + +const coinCurrentLeaderboardExecuteCommand: SapphireMessageExecuteType = async ( + client, + messageFromUser, + _args +): Promise => { + const userid = getUserIdFromMessage(messageFromUser); + const leaderboard = await getCurrentCoinLeaderboard(); + return getCurrentCoinLeaderboardEmbed(client, leaderboard, userid); +}; + +export const coinCurrentLeaderboardCommandDetails: CodeyCommandDetails = { + name: 'leaderboard', + aliases: ['lb'], + description: 'Get the current coin leaderboard.', + detailedDescription: `**Examples:** + \`${container.botPrefix}coin lb\` + \`${container.botPrefix}coin leaderboard\``, + + isCommandResponseEphemeral: false, + messageWhenExecutingCommand: 'Getting the current coin leaderboard...', + executeCommand: coinCurrentLeaderboardExecuteCommand, + codeyCommandResponseType: CodeyCommandResponseType.EMBED, + + options: [], + subcommandDetails: {} +}; diff --git a/src/commands/coin/coin.ts b/src/commands/coin/coin.ts index 31921ce5..46c1b5cc 100644 --- a/src/commands/coin/coin.ts +++ b/src/commands/coin/coin.ts @@ -5,6 +5,7 @@ import { coinBalanceCommandDetails } from '../../commandDetails/coin/balance'; import { coinCheckCommandDetails } from '../../commandDetails/coin/check'; import { coinInfoCommandDetails } from '../../commandDetails/coin/info'; import { coinUpdateCommandDetails } from '../../commandDetails/coin/update'; +import { coinCurrentLeaderboardCommandDetails } from '../../commandDetails/coin/leaderboard'; const coinCommandDetails: CodeyCommandDetails = { name: 'coin', @@ -28,7 +29,8 @@ const coinCommandDetails: CodeyCommandDetails = { balance: coinBalanceCommandDetails, check: coinCheckCommandDetails, info: coinInfoCommandDetails, - update: coinUpdateCommandDetails + update: coinUpdateCommandDetails, + leaderboard: coinCurrentLeaderboardCommandDetails }, defaultSubcommandDetails: coinBalanceCommandDetails }; diff --git a/src/components/coin.ts b/src/components/coin.ts index 5301f778..7485c969 100644 --- a/src/components/coin.ts +++ b/src/components/coin.ts @@ -61,6 +61,11 @@ export const coinBonusMap = new Map([ ] ]); +export interface UserCoinEntry { + user_id: string; + balance: number; +} + export interface UserCoinBonus { id: string; user_id: string; @@ -135,6 +140,21 @@ export const changeDbCoinBalanceByUserId = async ( await createCoinLedgerEntry(userId, oldBalance, newBalance, event, reason, adminId); }; +/* + Get the leaderboard for the current coin amounts. +*/ +export const getCurrentCoinLeaderboard = async (): Promise => { + const db = await openDB(); + const res = await db.all( + ` + SELECT user_id, balance + FROM user_coin + ORDER BY balance DESC + ` + ); + return res; +}; + /* Adds an entry to the Codey coin ledger due to a change in a user's coin balance. reason is only applicable for admin commands and is optional. @@ -149,7 +169,7 @@ export const createCoinLedgerEntry = async ( adminId: string | null ): Promise => { const db = await openDB(); - await db.run( + const res = await db.get( 'INSERT INTO user_coin_ledger (user_id, amount, new_balance, event, reason, admin_id) VALUES (?, ?, ?, ?, ?, ?)', userId, newBalance - oldBalance, From 3720b589078f02c20ad1943baec0c7bc8916355c Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sat, 6 Aug 2022 11:47:46 -0400 Subject: [PATCH 2/8] more changes --- src/commandDetails/coin/leaderboard.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index fd479b6f..b5b70435 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -9,6 +9,7 @@ import { } from '../../codeyCommand'; import { getCurrentCoinLeaderboard, UserCoinEntry } from '../../components/coin'; import { EMBED_COLOUR } from '../../utils/embeds'; +import { Alignment, generateTextTable, TableHeader } from '../../utils/textTable'; const getCurrentCoinLeaderboardEmbed = async ( client: SapphireClient, @@ -22,8 +23,9 @@ const getCurrentCoinLeaderboardEmbed = async ( for (let i = 0; i < limit; i++) { if (leaderboard.length <= i) break; const userCoinEntry = leaderboard[i]; - const user = await client.users.cache.get(userCoinEntry.user_id); - const userCoinEntryText = `${i + 1}. ${user?.username ?? ''} - ${userCoinEntry.balance} 🪙.\n`; + const user = await client.users.fetch(userCoinEntry.user_id); + const userTag = user?.tag ?? ''; + const userCoinEntryText = `${i + 1}. ${userTag} - ${userCoinEntry.balance} 🪙.\n`; currentLeaderboardText += userCoinEntryText; } currentLeaderboardEmbed.addFields({ From 5b250bde98a58a324fed453859775a65b3f81209 Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sat, 6 Aug 2022 11:54:10 -0400 Subject: [PATCH 3/8] added current position to leaderboard --- src/commandDetails/coin/leaderboard.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index b5b70435..abfb4846 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -9,7 +9,6 @@ import { } from '../../codeyCommand'; import { getCurrentCoinLeaderboard, UserCoinEntry } from '../../components/coin'; import { EMBED_COLOUR } from '../../utils/embeds'; -import { Alignment, generateTextTable, TableHeader } from '../../utils/textTable'; const getCurrentCoinLeaderboardEmbed = async ( client: SapphireClient, @@ -18,6 +17,7 @@ const getCurrentCoinLeaderboardEmbed = async ( limit = 10 ): Promise => { const currentLeaderboardEmbed = new MessageEmbed().setColor(EMBED_COLOUR).setTitle('Current CodeyCoin Leaderboard'); + const currentPosition = leaderboard.findIndex((userCoinEntry) => userCoinEntry.user_id === currentUserId) + 1; let currentLeaderboardText = ''; for (let i = 0; i < limit; i++) { @@ -28,10 +28,19 @@ const getCurrentCoinLeaderboardEmbed = async ( const userCoinEntryText = `${i + 1}. ${userTag} - ${userCoinEntry.balance} 🪙.\n`; currentLeaderboardText += userCoinEntryText; } - currentLeaderboardEmbed.addFields({ - name: 'Current Leaderboard', - value: currentLeaderboardText - }); + currentLeaderboardEmbed.addFields( + { + name: 'Current Leaderboard', + value: currentLeaderboardText + }, + { + name: 'Current Position', + value: + currentPosition === 0 + ? `You are currently unranked in the leaderboard: type \`.coin bal\` to get started!` + : `You are currently **#${currentPosition}** in the leaderboard.` + } + ); return currentLeaderboardEmbed; }; From 6a826e23f667e742c2a3af87078a8a376e12d866 Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sat, 6 Aug 2022 11:59:44 -0400 Subject: [PATCH 4/8] Added user balance --- src/commandDetails/coin/leaderboard.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index abfb4846..c796679e 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -7,7 +7,7 @@ import { SapphireMessageExecuteType, SapphireMessageResponse } from '../../codeyCommand'; -import { getCurrentCoinLeaderboard, UserCoinEntry } from '../../components/coin'; +import { getCurrentCoinLeaderboard, getCoinBalanceByUserId, UserCoinEntry } from '../../components/coin'; import { EMBED_COLOUR } from '../../utils/embeds'; const getCurrentCoinLeaderboardEmbed = async ( @@ -17,6 +17,8 @@ const getCurrentCoinLeaderboardEmbed = async ( limit = 10 ): Promise => { const currentLeaderboardEmbed = new MessageEmbed().setColor(EMBED_COLOUR).setTitle('Current CodeyCoin Leaderboard'); + // Initialise user's coin balance if they have not already + const userBalance = await getCoinBalanceByUserId(currentUserId); const currentPosition = leaderboard.findIndex((userCoinEntry) => userCoinEntry.user_id === currentUserId) + 1; let currentLeaderboardText = ''; @@ -35,10 +37,7 @@ const getCurrentCoinLeaderboardEmbed = async ( }, { name: 'Current Position', - value: - currentPosition === 0 - ? `You are currently unranked in the leaderboard: type \`.coin bal\` to get started!` - : `You are currently **#${currentPosition}** in the leaderboard.` + value: `You are currently **#${currentPosition}** in the leaderboard with ${userBalance} 🪙` } ); From 5462416b067590cab002c4f93cf443293d433346 Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sat, 6 Aug 2022 21:48:21 -0400 Subject: [PATCH 5/8] Addressed comments --- src/commandDetails/coin/leaderboard.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index c796679e..c5671e74 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -16,7 +16,6 @@ const getCurrentCoinLeaderboardEmbed = async ( currentUserId: string, limit = 10 ): Promise => { - const currentLeaderboardEmbed = new MessageEmbed().setColor(EMBED_COLOUR).setTitle('Current CodeyCoin Leaderboard'); // Initialise user's coin balance if they have not already const userBalance = await getCoinBalanceByUserId(currentUserId); const currentPosition = leaderboard.findIndex((userCoinEntry) => userCoinEntry.user_id === currentUserId) + 1; @@ -30,16 +29,15 @@ const getCurrentCoinLeaderboardEmbed = async ( const userCoinEntryText = `${i + 1}. ${userTag} - ${userCoinEntry.balance} 🪙.\n`; currentLeaderboardText += userCoinEntryText; } - currentLeaderboardEmbed.addFields( - { - name: 'Current Leaderboard', - value: currentLeaderboardText - }, - { - name: 'Current Position', - value: `You are currently **#${currentPosition}** in the leaderboard with ${userBalance} 🪙` - } - ); + const currentLeaderboardEmbed = new MessageEmbed() + .setColor(EMBED_COLOUR) + .setTitle('CodeyCoin Leaderboard') + .setDescription(currentLeaderboardText); + + currentLeaderboardEmbed.addFields({ + name: 'Your Position', + value: `You are currently **#${currentPosition}** in the leaderboard with ${userBalance} 🪙` + }); return currentLeaderboardEmbed; }; From 914d3a2c6c137b0bb91d823e741991d9a8b7a812 Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sat, 6 Aug 2022 21:49:48 -0400 Subject: [PATCH 6/8] Fixed linter --- src/commandDetails/coin/leaderboard.ts | 6 +++--- src/components/coin.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index c5671e74..622767b0 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -1,5 +1,5 @@ import { container, SapphireClient } from '@sapphire/framework'; -import { Client, MessageEmbed } from 'discord.js'; +import { MessageEmbed } from 'discord.js'; import { CodeyCommandDetails, CodeyCommandResponseType, @@ -26,7 +26,7 @@ const getCurrentCoinLeaderboardEmbed = async ( const userCoinEntry = leaderboard[i]; const user = await client.users.fetch(userCoinEntry.user_id); const userTag = user?.tag ?? ''; - const userCoinEntryText = `${i + 1}. ${userTag} - ${userCoinEntry.balance} 🪙.\n`; + const userCoinEntryText = `${i + 1}. ${userTag} - ${userCoinEntry.balance} 🪙\n`; currentLeaderboardText += userCoinEntryText; } const currentLeaderboardEmbed = new MessageEmbed() @@ -36,7 +36,7 @@ const getCurrentCoinLeaderboardEmbed = async ( currentLeaderboardEmbed.addFields({ name: 'Your Position', - value: `You are currently **#${currentPosition}** in the leaderboard with ${userBalance} 🪙` + value: `You are currently **#${currentPosition}** in the leaderboard with ${userBalance} 🪙.` }); return currentLeaderboardEmbed; diff --git a/src/components/coin.ts b/src/components/coin.ts index 7485c969..056ddbe1 100644 --- a/src/components/coin.ts +++ b/src/components/coin.ts @@ -169,7 +169,7 @@ export const createCoinLedgerEntry = async ( adminId: string | null ): Promise => { const db = await openDB(); - const res = await db.get( + await db.run( 'INSERT INTO user_coin_ledger (user_id, amount, new_balance, event, reason, admin_id) VALUES (?, ?, ?, ?, ?, ?)', userId, newBalance - oldBalance, From d1a34351e7ae57cf6fa1e3d13e6d6359b9aad3b6 Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sun, 7 Aug 2022 10:51:57 -0400 Subject: [PATCH 7/8] Addressed comments --- src/commandDetails/coin/leaderboard.ts | 17 +++++++++++------ src/components/coin.ts | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index 622767b0..053e697b 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -7,7 +7,12 @@ import { SapphireMessageExecuteType, SapphireMessageResponse } from '../../codeyCommand'; -import { getCurrentCoinLeaderboard, getCoinBalanceByUserId, UserCoinEntry } from '../../components/coin'; +import { + getCurrentCoinLeaderboard, + getCoinBalanceByUserId, + UserCoinEntry, + getUserIdCurrentCoinPosition +} from '../../components/coin'; import { EMBED_COLOUR } from '../../utils/embeds'; const getCurrentCoinLeaderboardEmbed = async ( @@ -18,7 +23,7 @@ const getCurrentCoinLeaderboardEmbed = async ( ): Promise => { // Initialise user's coin balance if they have not already const userBalance = await getCoinBalanceByUserId(currentUserId); - const currentPosition = leaderboard.findIndex((userCoinEntry) => userCoinEntry.user_id === currentUserId) + 1; + const currentPosition = await getUserIdCurrentCoinPosition(currentUserId); let currentLeaderboardText = ''; for (let i = 0; i < limit; i++) { @@ -47,9 +52,9 @@ const coinCurrentLeaderboardExecuteCommand: SapphireMessageExecuteType = async ( messageFromUser, _args ): Promise => { - const userid = getUserIdFromMessage(messageFromUser); + const userId = getUserIdFromMessage(messageFromUser); const leaderboard = await getCurrentCoinLeaderboard(); - return getCurrentCoinLeaderboardEmbed(client, leaderboard, userid); + return getCurrentCoinLeaderboardEmbed(client, leaderboard, userId); }; export const coinCurrentLeaderboardCommandDetails: CodeyCommandDetails = { @@ -57,8 +62,8 @@ export const coinCurrentLeaderboardCommandDetails: CodeyCommandDetails = { aliases: ['lb'], description: 'Get the current coin leaderboard.', detailedDescription: `**Examples:** - \`${container.botPrefix}coin lb\` - \`${container.botPrefix}coin leaderboard\``, +\`${container.botPrefix}coin lb\` +\`${container.botPrefix}coin leaderboard\``, isCommandResponseEphemeral: false, messageWhenExecutingCommand: 'Getting the current coin leaderboard...', diff --git a/src/components/coin.ts b/src/components/coin.ts index 056ddbe1..e65e773e 100644 --- a/src/components/coin.ts +++ b/src/components/coin.ts @@ -143,18 +143,34 @@ export const changeDbCoinBalanceByUserId = async ( /* Get the leaderboard for the current coin amounts. */ -export const getCurrentCoinLeaderboard = async (): Promise => { +export const getCurrentCoinLeaderboard = async (limit = 10): Promise => { const db = await openDB(); const res = await db.all( ` SELECT user_id, balance FROM user_coin ORDER BY balance DESC + LIMIT ${limit} ` ); return res; }; +/* + Get the position for a user id's balance +*/ +export const getUserIdCurrentCoinPosition = async (userId: string): Promise => { + const db = await openDB(); + const res = await db.get( + ` + SELECT COUNT(user_id) AS count + FROM user_coin + WHERE balance >= (SELECT balance FROM user_coin WHERE user_id = '${userId}') + ` + ); + return _.get(res, 'count', 0); +}; + /* Adds an entry to the Codey coin ledger due to a change in a user's coin balance. reason is only applicable for admin commands and is optional. From 91771d7d25794afa3b7889038d33209aad29948c Mon Sep 17 00:00:00 2001 From: mcpenguin Date: Sun, 7 Aug 2022 15:33:29 -0400 Subject: [PATCH 8/8] Addressed comments again --- src/commandDetails/coin/leaderboard.ts | 6 ++++-- src/components/coin.ts | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/commandDetails/coin/leaderboard.ts b/src/commandDetails/coin/leaderboard.ts index 053e697b..d8251395 100644 --- a/src/commandDetails/coin/leaderboard.ts +++ b/src/commandDetails/coin/leaderboard.ts @@ -15,11 +15,13 @@ import { } from '../../components/coin'; import { EMBED_COLOUR } from '../../utils/embeds'; +// How many people are shown on the leaderboard +const limit = 10; + const getCurrentCoinLeaderboardEmbed = async ( client: SapphireClient, leaderboard: UserCoinEntry[], - currentUserId: string, - limit = 10 + currentUserId: string ): Promise => { // Initialise user's coin balance if they have not already const userBalance = await getCoinBalanceByUserId(currentUserId); diff --git a/src/components/coin.ts b/src/components/coin.ts index e65e773e..262f6c7b 100644 --- a/src/components/coin.ts +++ b/src/components/coin.ts @@ -150,8 +150,9 @@ export const getCurrentCoinLeaderboard = async (limit = 10): Promise= (SELECT balance FROM user_coin WHERE user_id = '${userId}') - ` + WHERE balance >= ? + `, + await getCoinBalanceByUserId(userId) ); return _.get(res, 'count', 0); };