Skip to content

Commit 6df7af6

Browse files
committed
add bumps to the leaderboard
1 parent f258fc9 commit 6df7af6

File tree

1 file changed

+117
-112
lines changed

1 file changed

+117
-112
lines changed

src/modules/xp/leaderboard.command.ts

+117-112
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,140 @@
1-
import { APIApplicationCommandOptionChoice, GuildMember } from 'discord.js'
1+
import {APIApplicationCommandOptionChoice, GuildMember} from 'discord.js'
22

3-
import { DDUser } from '../../store/models/DDUser.js'
4-
import { Command } from 'djs-slash-helper'
5-
import { ApplicationCommandOptionType, ApplicationCommandType } from 'discord-api-types/v10'
6-
import { createStandardEmbed } from '../../util/embeds.js'
7-
import { branding } from '../../util/branding.js'
8-
import { actualMention } from '../../util/users.js'
9-
import { getActualDailyStreak } from './dailyReward.command.js'
10-
import { wrapInTransaction } from '../../sentry.js'
3+
import {DDUser} from '../../store/models/DDUser.js'
4+
import {Command} from 'djs-slash-helper'
5+
import {ApplicationCommandOptionType, ApplicationCommandType} from 'discord-api-types/v10'
6+
import {createStandardEmbed} from '../../util/embeds.js'
7+
import {branding} from '../../util/branding.js'
8+
import {actualMention} from '../../util/users.js'
9+
import {getActualDailyStreak} from './dailyReward.command.js'
10+
import {wrapInTransaction} from '../../sentry.js'
1111

1212
type KeysMatching<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]
1313

1414
interface LeaderboardType extends APIApplicationCommandOptionChoice<string> {
15-
calculate?: (user: DDUser) => Promise<number>
16-
value: KeysMatching<DDUser, number | bigint>
15+
calculate?: (user: DDUser) => Promise<number>
16+
value: KeysMatching<DDUser, number | bigint>
1717

18-
format: (value: number | bigint) => string
18+
format: (value: number | bigint) => string
1919
}
2020

2121
const info: LeaderboardType[] = [
22-
{
23-
value: 'xp',
24-
name: 'XP',
25-
format: (value) => `${value.toLocaleString()} XP`
26-
},
27-
{
28-
value: 'level',
29-
name: 'Level',
30-
format: (value) => `Level ${value}`
31-
},
32-
{
33-
value: 'currentDailyStreak',
34-
calculate: async (user) => await getActualDailyStreak(user),
35-
name: 'Current Daily Streak',
36-
format: (s) => `${formatDays(s)}`
37-
},
38-
{
39-
value: 'highestDailyStreak',
40-
name: 'Highest Daily Streak',
41-
format: (s) => `${formatDays(s)}`
42-
}
22+
{
23+
value: 'xp',
24+
name: 'XP',
25+
format: (value) => `${value.toLocaleString()} XP`
26+
},
27+
{
28+
value: 'level',
29+
name: 'Level',
30+
format: (value) => `Level ${value}`
31+
},
32+
{
33+
value: 'currentDailyStreak',
34+
calculate: async (user) => await getActualDailyStreak(user),
35+
name: 'Current Daily Streak',
36+
format: (s) => `${formatDays(s)}`
37+
},
38+
{
39+
value: 'highestDailyStreak',
40+
name: 'Highest Daily Streak',
41+
format: (s) => `${formatDays(s)}`
42+
},
43+
{
44+
value: 'bumps',
45+
name: 'Disboard Bumps',
46+
format: (value) => value == 1 ? "1 Bump" : `${value.toLocaleString()} Bumps`
47+
}
4348
]
4449

4550
export const LeaderboardCommand: Command<ApplicationCommandType.ChatInput> = {
46-
type: ApplicationCommandType.ChatInput,
47-
name: 'leaderboard',
48-
description: 'Show the top 10 users based on XP, Level, or Daily Streak',
49-
options: [
50-
{
51-
type: ApplicationCommandOptionType.String,
52-
name: 'type',
53-
description: 'The type of leaderboard to show',
54-
required: true,
55-
choices: info
56-
}
57-
],
51+
type: ApplicationCommandType.ChatInput,
52+
name: 'leaderboard',
53+
description: 'Show the top 10 users based on XP, Level, or Daily Streak',
54+
options: [
55+
{
56+
type: ApplicationCommandOptionType.String,
57+
name: 'type',
58+
description: 'The type of leaderboard to show',
59+
required: true,
60+
choices: info
61+
}
62+
],
5863

59-
handle: wrapInTransaction('leaderboard', async (span, interaction) => {
60-
await interaction.deferReply()
61-
const guild = interaction.guild
62-
if (guild == null) {
63-
await interaction.followUp('This command can only be used in a server')
64-
return
65-
}
66-
const option = interaction.options.get('type', true).value as string
67-
const traitInfo = info.find((it) => it.value === option)
68-
if (traitInfo == null) {
69-
await interaction.followUp('Invalid leaderboard type')
70-
return
71-
}
72-
if (traitInfo.value === 'currentDailyStreak') {
73-
// manually refresh all the dailies. this is not very efficient
74-
const all = await DDUser.findAll()
75-
await Promise.all(all.map(getActualDailyStreak))
76-
}
77-
const {
78-
format,
79-
value,
80-
name
81-
} = traitInfo
64+
handle: wrapInTransaction('leaderboard', async (span, interaction) => {
65+
await interaction.deferReply()
66+
const guild = interaction.guild
67+
if (guild == null) {
68+
await interaction.followUp('This command can only be used in a server')
69+
return
70+
}
71+
const option = interaction.options.get('type', true).value as string
72+
const traitInfo = info.find((it) => it.value === option)
73+
if (traitInfo == null) {
74+
await interaction.followUp('Invalid leaderboard type')
75+
return
76+
}
77+
if (traitInfo.value === 'currentDailyStreak') {
78+
// manually refresh all the dailies. this is not very efficient
79+
const all = await DDUser.findAll()
80+
await Promise.all(all.map(getActualDailyStreak))
81+
}
82+
const {
83+
format,
84+
value,
85+
name
86+
} = traitInfo
8287

83-
const calculate: (user: DDUser) => Promise<number | bigint> = traitInfo.calculate ??
84-
(async (user: DDUser) => user[value])
88+
const calculate: (user: DDUser) => Promise<number | bigint> = traitInfo.calculate ??
89+
(async (user: DDUser) => user[value])
8590

86-
const users = await DDUser.findAll({
87-
order: [[value, 'DESC']],
88-
limit: 10
89-
}).then((users) => users.filter(async (it) => await calculate(it) > 0))
91+
const users = await DDUser.findAll({
92+
order: [[value, 'DESC']],
93+
limit: 10
94+
}).then((users) => users.filter(async (it) => await calculate(it) > 0))
9095

91-
if (users.length === 0) {
92-
await interaction.followUp('No applicable users')
93-
return
94-
}
95-
const embed = {
96-
...createStandardEmbed(interaction.member as GuildMember),
97-
title: `${branding.name} Leaderboard`,
98-
description: `The top ${users.length} users based on ${name}`,
99-
fields: await Promise.all(users.map(async (user, index) => {
100-
const discordUser = await guild.client.users.fetch(user.id.toString())
101-
.catch(() => null)
96+
if (users.length === 0) {
97+
await interaction.followUp('No applicable users')
98+
return
99+
}
100+
const embed = {
101+
...createStandardEmbed(interaction.member as GuildMember),
102+
title: `${branding.name} Leaderboard`,
103+
description: `The top ${users.length} users based on ${name}`,
104+
fields: await Promise.all(users.map(async (user, index) => {
105+
const discordUser = await guild.client.users.fetch(user.id.toString())
106+
.catch(() => null)
102107

103-
return {
104-
name: `${medal(index)} #${index + 1} - ${format(await calculate(user))}`.trimStart(),
105-
value: discordUser == null
106-
? 'Unknown User'
107-
: actualMention(
108-
discordUser
109-
)
108+
return {
109+
name: `${medal(index)} #${index + 1} - ${format(await calculate(user))}`.trimStart(),
110+
value: discordUser == null
111+
? 'Unknown User'
112+
: actualMention(
113+
discordUser
114+
)
115+
}
116+
}))
110117
}
111-
}))
112-
}
113-
await interaction.followUp({ embeds: [embed] })
114-
})
118+
await interaction.followUp({embeds: [embed]})
119+
})
115120
}
116121

117-
function medal (index: number): string {
118-
switch (index) {
119-
case 0:
120-
return '🥇'
121-
case 1:
122-
return '🥈'
123-
case 2:
124-
return '🥉'
125-
default:
126-
return ''
127-
}
122+
function medal(index: number): string {
123+
switch (index) {
124+
case 0:
125+
return '🥇'
126+
case 1:
127+
return '🥈'
128+
case 2:
129+
return '🥉'
130+
default:
131+
return ''
132+
}
128133
}
129134

130-
function formatDays (days: number | bigint) {
131-
if (days === 1) {
132-
return '1 day'
133-
}
134-
return `${days} days`
135+
function formatDays(days: number | bigint) {
136+
if (days === 1) {
137+
return '1 day'
138+
}
139+
return `${days} days`
135140
}

0 commit comments

Comments
 (0)