Skip to content

Commit dd9ebd4

Browse files
Statistics command
1 parent b7e3822 commit dd9ebd4

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

src/commands/general/stats.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
2+
import { Command } from '../../structs/Command.ts';
3+
import { PermissionLevel } from '../../modules/helpers/Utils.ts';
4+
import * as schema from '../../modules/database/Schema.ts';
5+
import { eq } from 'drizzle-orm';
6+
7+
export class StatsCommand extends Command {
8+
constructor(client: any) {
9+
super(client, {
10+
name: 'stats',
11+
description: 'Displays IEEE @ UCF chapter statistics.',
12+
usage: 'stats',
13+
category: 'general',
14+
permissionLevel: PermissionLevel.GUEST,
15+
guildOnly: false,
16+
cooldown: 10,
17+
});
18+
}
19+
20+
command(): SlashCommandBuilder {
21+
return new SlashCommandBuilder()
22+
.setName(this.name)
23+
.setDescription(this.description);
24+
}
25+
26+
/**
27+
* Get academic year start date (August of previous year or current year)
28+
*/
29+
getAcademicYearStart(): Date {
30+
const now = new Date();
31+
const currentYear = now.getFullYear();
32+
const currentMonth = now.getMonth(); // 0-indexed (0 = January)
33+
34+
// If we're before August (month 7), academic year started last August
35+
// If we're August or later, academic year started this August
36+
const academicYearStartYear = currentMonth < 7 ? currentYear - 1 : currentYear;
37+
38+
return new Date(`${academicYearStartYear}-08-01T00:00:00Z`);
39+
}
40+
41+
async run(interaction: ChatInputCommandInteraction): Promise<void> {
42+
await interaction.deferReply();
43+
44+
try {
45+
const academicYearStart = this.getAcademicYearStart();
46+
47+
// Get all active members
48+
const allMembers = await this.client.database.getDB()
49+
.select()
50+
.from(schema.members)
51+
.where(eq(schema.members.active, true));
52+
53+
const totalMembers = allMembers.length;
54+
55+
// Count new members this academic year
56+
const newMembers = allMembers.filter((m: any) => {
57+
const joinDate = new Date(m.createdAt);
58+
return joinDate >= academicYearStart;
59+
});
60+
const newMemberCount = newMembers.length;
61+
62+
// Get all active committees
63+
const committees = await this.client.database.getDB()
64+
.select()
65+
.from(schema.committees)
66+
.where(eq(schema.committees.active, true));
67+
68+
// Get committee member counts
69+
const committeeStats = await Promise.all(
70+
committees.map(async (committee: any) => {
71+
const members = await this.client.database.getDB()
72+
.select()
73+
.from(schema.committeeMembers)
74+
.where(eq(schema.committeeMembers.committeeId, committee.id));
75+
76+
return {
77+
title: committee.title,
78+
memberCount: members.length,
79+
};
80+
})
81+
);
82+
83+
// Sort by member count (largest first)
84+
committeeStats.sort((a, b) => b.memberCount - a.memberCount);
85+
86+
// Get all active projects
87+
const projects = await this.client.database.getDB()
88+
.select()
89+
.from(schema.projects)
90+
.where(eq(schema.projects.active, true));
91+
92+
// Get project member counts
93+
const projectStats = await Promise.all(
94+
projects.map(async (project: any) => {
95+
const members = await this.client.database.getDB()
96+
.select()
97+
.from(schema.projectMembers)
98+
.where(eq(schema.projectMembers.projectId, project.id));
99+
100+
return {
101+
title: project.title,
102+
memberCount: members.length,
103+
};
104+
})
105+
);
106+
107+
// Sort by member count (largest first)
108+
projectStats.sort((a, b) => b.memberCount - a.memberCount);
109+
110+
// Build embed
111+
const embed = this.client.createEmbed()
112+
.setTitle('IEEE @ UCF Chapter Statistics')
113+
.setTimestamp();
114+
115+
// Overview section
116+
embed.addFields({
117+
name: 'Overview:',
118+
value: `👥 **\u00A0\Total Active Members:** ${totalMembers}\n🆕\u00A0\** New Members This Year:** ${newMemberCount}`,
119+
inline: false,
120+
});
121+
122+
// Committees section
123+
if (committeeStats.length > 0) {
124+
const committeeList = committeeStats
125+
.map((c) => `${c.title}: **${c.memberCount}** member${c.memberCount !== 1 ? 's' : ''}`)
126+
.join('\n');
127+
128+
embed.addFields({
129+
name: 'Committees:',
130+
value: committeeList,
131+
inline: false,
132+
});
133+
} else {
134+
embed.addFields({
135+
name: 'Committees:',
136+
value: 'No active committees 😡',
137+
inline: false,
138+
});
139+
}
140+
141+
// Projects section
142+
if (projectStats.length > 0) {
143+
const projectList = projectStats
144+
.map((p) => `${p.title}: **${p.memberCount}** member${p.memberCount !== 1 ? 's' : ''}`)
145+
.join('\n');
146+
147+
embed.addFields({
148+
name: 'Projects:',
149+
value: projectList,
150+
inline: false,
151+
});
152+
} else {
153+
embed.addFields({
154+
name: 'Projects:',
155+
value: 'No active projects 😡',
156+
inline: false,
157+
});
158+
}
159+
160+
await interaction.editReply({ embeds: [embed] });
161+
} catch (error) {
162+
this.client.logger.fail(`Error fetching stats: ${error}`);
163+
console.error(error);
164+
await interaction.editReply('An error occurred while fetching statistics.');
165+
}
166+
}
167+
}

src/config.example.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface Config {
99
calendar: string;
1010
reminders: string;
1111
assistance: string;
12+
general: string;
1213
};
1314
eventReminders: {
1415
enabled: boolean;
@@ -57,6 +58,7 @@ const config: Config = {
5758
calendar: '',
5859
reminders: '',
5960
assistance: '',
61+
general: '',
6062
},
6163
eventReminders: {
6264
enabled: false,

0 commit comments

Comments
 (0)