From a4f9767f8da8c1793ac0ad2f1c5d7aaab96ed120 Mon Sep 17 00:00:00 2001 From: qwerinope Date: Sat, 13 Sep 2025 17:07:44 +0200 Subject: [PATCH] #4 done. added qbucks, monthly and alltime leaderboards --- README.md | 15 +++++++++- src/commands/alltimekdleaderboard.ts | 43 ++++++++++++++++++++++++++++ src/commands/monthlykdleaderboard.ts | 43 ++++++++++++++++++++++++++++ src/commands/qbucksleaderboard.ts | 25 ++++++++++++++++ src/db/dbUser.ts | 12 ++++++++ 5 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/commands/alltimekdleaderboard.ts create mode 100644 src/commands/monthlykdleaderboard.ts create mode 100644 src/commands/qbucksleaderboard.ts diff --git a/README.md b/README.md index 4b2aa20..115d123 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,22 @@ Not all Commands can be disabled, the `DISABLEABLE` field below shows if they ca A full list of Commands can be found [here](#commands-1) -### Timeouts and whispering messages +### Timeouts and ghost whispers If you've been timed out, you can ghost whisper a message to the chatterbot and it will relay your message to the chat. You can only send one message every 10 minutes. Try to bargain for your release with the chatter that shot you, or just call them names. +### Leaderboards + +There are 3 types of leaderboards: monthlyKD, alltimeKD and qbucks. +The monthlyKD leaderboard (command: `monthlykdleaderboard`) gives you the leaderboard of the top 5 user Kill/Death ratios for the current month. +The alltimeKD leaderboard (command: `alltimekdleaderboard`) gives you the leaderboard of the top 5 user Kill/Death ratios of all time in the channel. +the qbucks leaderboard (command: `qbucksleaderboard`) gives you the current leaderboard of the top 10 qbucks havers. + +To appear on the KD leaderboards you need to have been timed out 5 times, in the specified timeframe. +Blasters, Grenade explosions and TNT explosions all count for the KD, Silver bullets do not. + ### Items and Itemlock Items are commands that can only be used when the chatter has them in their inventory. @@ -106,6 +116,9 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE `inventory [target]`|Get inventory contents of target or self|anyone|`inventory` `inv` `pocket`|:white_check_mark: `give {target} {item} {amount}`|Give targeted user amount of items|anyone|`give`|:white_check_mark: `use {item} ...`|Use item. More info at [The items section](#items)|anyone|`use`|:x: +`monthlyleaderboard`|Get the K/D leaderboard for this month [(info)](#leaderboards)|anyone|`monthlyleaderboard` `kdleaderboard` `leaderboard`|:white_check_mark: +`alltimeleaderboard`|Get the K/D leaderboard of all time [(info)](#leaderboards)|anyone|`alltimeleaderboard` `alltimekdleaderboard`|:white_check_mark: +`qbucksleaderboard`|Get the current qbucks leaderboard [(info)](#leaderboards)|anyone|`qbucksleaderboard` `moneyleaderboard` `baltop`|:white_check_mark: `admindonate {target} {amount}`|Gives the targeted user amount of qweribucks|admins|`admindonate`|:white_check_mark: `admingive {target} {item} {amount}`|Give targeted user amount of new items|admins|`admingive`|:white_check_mark: diff --git a/src/commands/alltimekdleaderboard.ts b/src/commands/alltimekdleaderboard.ts new file mode 100644 index 0000000..6fa5d7d --- /dev/null +++ b/src/commands/alltimekdleaderboard.ts @@ -0,0 +1,43 @@ +import { Command, sendMessage } from "commands"; +import { getAllUserRecords } from "db/dbUser"; +import { getTimeoutStats } from "lib/getStats"; +import User from "user"; + +type KD = { user: User; kd: number; }; + +export default new Command({ + name: 'alltimekdleaderboard', + aliases: ['alltimeleaderboard', 'alltimekdleaderboard'], + usertype: 'chatter', + execution: async msg => { + const users = await getAllUserRecords(); + if (!users) return; + + const userKDs: KD[] = []; + await Promise.all(users.map(async userRecord => { + const user = await User.initUserId(userRecord.id); + if (!user) return; + const data = await getTimeoutStats(user, false); + if (!data) return; + if (data.hit.blaster < 5) return; + + let kd = data.shot.blaster / data.hit.blaster; + if (isNaN(kd)) kd = 0; + userKDs.push({ user, kd }); + })); + + if (userKDs.length === 0) { + await sendMessage(`No users on leaderboard yet!`, msg.messageId); + return; + }; + + userKDs.sort((a, b) => b.kd - a.kd); + + const txt: string[] = []; + for (let i = 0; i < (userKDs.length < 5 ? userKDs.length : 5); i++) { + txt.push(`${i + 1}. ${userKDs[i]?.user.displayName}: ${userKDs[i]?.kd.toFixed(2)}`); + }; + + await sendMessage(`Alltime leaderboard: ${txt.join(' | ')}`, msg.messageId); + } +}); diff --git a/src/commands/monthlykdleaderboard.ts b/src/commands/monthlykdleaderboard.ts new file mode 100644 index 0000000..fc65439 --- /dev/null +++ b/src/commands/monthlykdleaderboard.ts @@ -0,0 +1,43 @@ +import { Command, sendMessage } from "commands"; +import { getAllUserRecords } from "db/dbUser"; +import { getTimeoutStats } from "lib/getStats"; +import User from "user"; + +type KD = { user: User; kd: number; }; + +export default new Command({ + name: 'monthlykdleaderboard', + aliases: ['monthlyleaderboard', 'kdleaderboard', 'leaderboard'], + usertype: 'chatter', + execution: async msg => { + const users = await getAllUserRecords(); + if (!users) return; + + const userKDs: KD[] = []; + await Promise.all(users.map(async userRecord => { + const user = await User.initUserId(userRecord.id); + if (!user) return; + const data = await getTimeoutStats(user, true); + if (!data) return; + if (data.hit.blaster < 5) return; + + let kd = data.shot.blaster / data.hit.blaster; + if (isNaN(kd)) kd = 0; + userKDs.push({ user, kd }); + })); + + if (userKDs.length === 0) { + await sendMessage(`No users on leaderboard yet!`, msg.messageId); + return; + }; + + userKDs.sort((a, b) => b.kd - a.kd); + + const txt: string[] = []; + for (let i = 0; i < (userKDs.length < 5 ? userKDs.length : 5); i++) { + txt.push(`${i + 1}. ${userKDs[i]?.user.displayName}: ${userKDs[i]?.kd.toFixed(2)}`); + }; + + await sendMessage(`Monthly leaderboard: ${txt.join(' | ')}`, msg.messageId); + } +}); diff --git a/src/commands/qbucksleaderboard.ts b/src/commands/qbucksleaderboard.ts new file mode 100644 index 0000000..ecc6f32 --- /dev/null +++ b/src/commands/qbucksleaderboard.ts @@ -0,0 +1,25 @@ +import { Command, sendMessage } from "commands"; +import { getBalanceLeaderboard } from "db/dbUser"; +import User from "user"; + +export default new Command({ + name: 'qbucksleaderboard', + aliases: ['qbucksleaderboard', 'baltop', 'moneyleaderboard'], + usertype: 'chatter', + execution: async msg => { + const data = await getBalanceLeaderboard(); + if (!data) return; + + let index = 1; + const txt: string[] = []; + for (const userRecord of data) { + if (userRecord.balance === 0) continue; + const user = await User.initUserId(userRecord.id); + if (!user) continue; + txt.push(`${index}. ${user.displayName}: ${userRecord.balance}`); + index++; + }; + + await sendMessage(`Balance leaderboard: ${txt.join(' | ')}`, msg.messageId); + } +}); diff --git a/src/db/dbUser.ts b/src/db/dbUser.ts index 5db01c8..e472a9f 100644 --- a/src/db/dbUser.ts +++ b/src/db/dbUser.ts @@ -23,6 +23,10 @@ export async function getUserRecord(user: User): Promise { }; }; +export async function getAllUserRecords(): Promise { + return await pb.getFullList(); +}; + async function createUserRecord(user: User): Promise { const data = await pb.create({ id: user.id, @@ -44,3 +48,11 @@ export async function updateUserRecord(user: User, newData: userRecord): Promise return false; }; }; + +export async function getBalanceLeaderboard() { + try { + return await pb.getList(1, 10, { sort: '-balance,id' }).then(a => a.items); + } catch (err) { + logger.err(err as string); + }; +};