Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.

Commit 844670f

Browse files
authored
feat: /profile command to view own or someone else's profile (#246)
* feat: basic `/profile` command * remove LevelingService from custom client props * add makeshift badge
1 parent 4d6383f commit 844670f

File tree

9 files changed

+309
-234
lines changed

9 files changed

+309
-234
lines changed

prisma/schema.prisma

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,16 @@ model UserData {
169169
banReason String?
170170
mentionOnReply Boolean @default(true)
171171
acceptedRules Boolean @default(false)
172-
updatedAt DateTime @updatedAt
173-
xp Int @default(0)
174-
level Int @default(0)
175172
messageCount Int @default(0)
176173
lastMessageAt DateTime @default(now())
177174
modPositions HubModerator[]
178175
ownedHubs Hub[]
179176
infractions Infraction[] @relation("infractions")
180177
issuedInfractions Infraction[] @relation("issuedInfractions")
181178
inboxLastReadDate DateTime? @default(now())
179+
createdAt DateTime @default(now())
180+
updatedAt DateTime @updatedAt
182181
183-
@@index([xp])
184-
@@index([level])
185182
@@index([messageCount])
186183
}
187184

src/commands/Information/rank.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ export default class RankCommand extends BaseCommand {
9191
await ctx.deferReply();
9292

9393
try {
94-
const targetUser = await ctx.options.getUser('user') ?? ctx.user;
95-
const stats = await ctx.client.userLevels.getStats(
96-
targetUser.id,
97-
targetUser.username,
98-
);
99-
const rankCard = await this.createRankCard(targetUser, stats);
100-
101-
await ctx.editReply({ files: [rankCard] });
94+
// const targetUser = await ctx.options.getUser('user') ?? ctx.user;
95+
// const stats = await ctx.client.userLevels.getStats(
96+
// targetUser.id,
97+
// targetUser.username,
98+
// );
99+
// const rankCard = await this.createRankCard(targetUser, stats);
100+
101+
// await ctx.editReply({ files: [rankCard] });
102102
}
103103
catch (error) {
104104
handleError(error, {

src/commands/profile.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import BaseCommand from '#src/core/BaseCommand.js';
2+
import Context from '#src/core/CommandContext/Context.js';
3+
import Constants from '#src/utils/Constants.js';
4+
import db from '#src/utils/Db.js';
5+
import { getUserLeaderboardRank } from '#src/utils/Leaderboard.js';
6+
import { checkIfStaff, fetchUserData } from '#src/utils/Utils.js';
7+
import { ApplicationCommandOptionType, EmbedBuilder, time } from 'discord.js';
8+
export default class ProfileCommand extends BaseCommand {
9+
constructor() {
10+
super({
11+
name: 'profile',
12+
description: 'View your profile or someone else\'s InterChat profile.',
13+
types: { slash: true, prefix: true },
14+
options: [
15+
{
16+
type: ApplicationCommandOptionType.User,
17+
name: 'user',
18+
description: 'The user to view the profile of.',
19+
required: false,
20+
},
21+
],
22+
});
23+
}
24+
async execute(ctx: Context) {
25+
const user = (await ctx.options.getUser('user')) ?? ctx.user;
26+
const userData = await fetchUserData(user.id);
27+
28+
if (!userData) {
29+
await ctx.reply('User not found.');
30+
return;
31+
}
32+
33+
const embed = new EmbedBuilder()
34+
.setDescription(`### @${user.username} ${checkIfStaff(user.id) ? ctx.getEmoji('staff_badge') : ''}`)
35+
.addFields([
36+
{
37+
name: 'Leaderboard Rank',
38+
value: `#${(await getUserLeaderboardRank(user.id)) ?? 'Unranked.'}`,
39+
inline: true,
40+
},
41+
{
42+
name: 'Total Messages',
43+
value: `${userData.messageCount}`,
44+
inline: true,
45+
},
46+
{
47+
name: 'User Since',
48+
value: `${time(Math.round(userData.createdAt.getTime() / 1000), 'D')}`,
49+
inline: true,
50+
},
51+
{
52+
name: 'Hubs Owned',
53+
value: `${(await db.hub.findMany({ where: { ownerId: user.id, private: false } })).map((h) => h.name).join(', ')}`,
54+
inline: true,
55+
},
56+
{
57+
name: 'User ID',
58+
value: user.id,
59+
inline: true,
60+
},
61+
])
62+
.setColor(Constants.Colors.invisible)
63+
.setThumbnail(user.displayAvatarURL());
64+
65+
await ctx.reply({ embeds: [embed] });
66+
}
67+
}

src/core/BaseClient.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import type { InteractionFunction } from '#src/decorators/RegisterInteractionHan
2020
import AntiSpamManager from '#src/managers/AntiSpamManager.js';
2121
import EventLoader from '#src/modules/Loaders/EventLoader.js';
2222
import CooldownService from '#src/services/CooldownService.js';
23-
import { LevelingService } from '#src/services/LevelingService.js';
2423
import Scheduler from '#src/services/SchedulerService.js';
2524
import { loadInteractions } from '#src/utils/CommandUtils.js';
2625
import { loadCommands } from '#src/utils/Loaders.js';
@@ -59,8 +58,6 @@ export default class InterChatClient extends Client {
5958
spamCountExpirySecs: 60,
6059
});
6160

62-
public readonly userLevels: LevelingService = new LevelingService();
63-
6461
constructor() {
6562
super({
6663
shards: getInfo().SHARD_LIST, // An array of shards that will get spawned

0 commit comments

Comments
 (0)