Skip to content

Commit 868f4aa

Browse files
committed
Added booster fixes & db cleanups
1 parent 113c88e commit 868f4aa

6 files changed

Lines changed: 193 additions & 34 deletions

File tree

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
/node_modules
22
/dist
33
.env
4-
src/generated/
4+
src/generated/
5+
6+
src/commands/*.make.ts
7+
src/events/*.make.ts
8+
# ^ For commands/events that are being made - not ready for commits

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "boostify",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "",
55
"main": "dist/index.js",
66
"type": "module",

src/commands/booster.ts

Lines changed: 122 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import {
55
ContainerBuilder,
66
TextDisplayBuilder,
77
MessageFlags,
8+
SeparatorSpacingSize,
9+
ButtonBuilder,
10+
ButtonStyle,
811
} from "discord.js";
912
import {
1013
getBooster,
@@ -14,54 +17,59 @@ import {
1417
getActiveBoosters,
1518
getTotalBoosts,
1619
registerBoost,
20+
removeBoost,
1721
} from "../services/boosterService.js";
1822
import { Command } from "../base/classes/command.js";
23+
import { logger } from "../libs/logger.js";
1924

2025
export default new Command({
2126
info: new SlashCommandBuilder()
22-
.setName("booster")
27+
.setName("booster")
2328
.setDescription("Booster management commands")
2429
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
2530
.addSubcommand((sub) =>
2631
sub
2732
.setName("check")
2833
.setDescription("Check booster info for a user")
2934
.addUserOption((opt) =>
30-
opt.setName("user").setDescription("The user to check").setRequired(true)
31-
)
35+
opt
36+
.setName("user")
37+
.setDescription("The user to check")
38+
.setRequired(true),
39+
),
3240
)
3341
.addSubcommand((sub) =>
3442
sub
3543
.setName("add")
3644
.setDescription("Add boost count to a user")
3745
.addUserOption((opt) =>
38-
opt.setName("user").setDescription("The user").setRequired(true)
46+
opt.setName("user").setDescription("The user").setRequired(true),
3947
)
4048
.addIntegerOption((opt) =>
4149
opt
4250
.setName("amount")
4351
.setDescription("Amount to add")
4452
.setRequired(true)
45-
.setMinValue(1)
46-
)
53+
.setMinValue(1),
54+
),
4755
)
4856
.addSubcommand((sub) =>
4957
sub
5058
.setName("remove")
5159
.setDescription("Remove boost count from a user")
5260
.addUserOption((opt) =>
53-
opt.setName("user").setDescription("The user").setRequired(true)
61+
opt.setName("user").setDescription("The user").setRequired(true),
5462
)
5563
.addIntegerOption((opt) =>
5664
opt
5765
.setName("amount")
5866
.setDescription("Amount to remove")
5967
.setRequired(true)
60-
.setMinValue(1)
61-
)
68+
.setMinValue(1),
69+
),
6270
)
6371
.addSubcommand((sub) =>
64-
sub.setName("stats").setDescription("View server boost statistics")
72+
sub.setName("stats").setDescription("View server boost statistics"),
6573
),
6674
async execute(interaction) {
6775
const sub = interaction.options.getSubcommand();
@@ -80,13 +88,18 @@ export default new Command({
8088
if (sub === "check") {
8189
const user = interaction.options.getUser("user", true);
8290
const result = await getBooster(user.id, interaction);
83-
const member = await discordGuild.members.fetch(user.id).catch(() => null);
91+
const member = await discordGuild.members
92+
.fetch(user.id)
93+
.catch(() => null);
8494
const isBoostingServer = member?.premiumSince !== null;
8595
const avatarUrl =
86-
member?.displayAvatarURL({ size: 256 }) ?? user.displayAvatarURL({ size: 256 });
96+
member?.displayAvatarURL({ size: 256 }) ??
97+
user.displayAvatarURL({ size: 256 });
8798

8899
if (!result?.success) {
89-
await interaction.editReply({ content: "Could not load booster info for this server." });
100+
await interaction.editReply({
101+
content: "Could not load booster info for this server.",
102+
});
90103
return;
91104
}
92105

@@ -98,10 +111,14 @@ export default new Command({
98111
.setTitle(`Here is the Booster Information for ${user.id}!`)
99112
.setThumbnail(avatarUrl)
100113
.addFields(
101-
{ name: "Status", value: "🟢 Active (Nitro Boost)", inline: true },
114+
{
115+
name: "Status",
116+
value: "🟢 Active (Nitro Boost)",
117+
inline: true,
118+
},
102119
{ name: "Discord boost", value: discordBoost, inline: true },
103120
{ name: "Custom Role", value: "None", inline: true },
104-
{ name: "User Ping", value: `<@${user.id}>`}
121+
{ name: "User Ping", value: `<@${user.id}>` },
105122
)
106123
.setTimestamp();
107124

@@ -113,7 +130,9 @@ export default new Command({
113130
.setAccentColor(0xe642a4)
114131
.addTextDisplayComponents(
115132
new TextDisplayBuilder().setContent("**Uh oh!**"),
116-
new TextDisplayBuilder().setContent(`It looks like ${user} is not a Booster.`)
133+
new TextDisplayBuilder().setContent(
134+
`It looks like ${user} is not a Booster.`,
135+
),
117136
);
118137

119138
await interaction.editReply({
@@ -125,19 +144,69 @@ export default new Command({
125144

126145
const booster = result.data;
127146

147+
if (booster.boostCounts == 0) {
148+
try {
149+
removeBoost(booster.userId, interaction.guild.id);
150+
} catch (err) {
151+
logger.error(
152+
`An error occured while removing ${booster.userId}'s data: ${err}`,
153+
);
154+
155+
const supportButton = new ButtonBuilder()
156+
.setStyle(ButtonStyle.Link)
157+
.setLabel("Our Support Server")
158+
.setURL("https://discord.gg/NUtyKs7hA6");
159+
160+
const container = new ContainerBuilder()
161+
.setAccentColor(0xe642a4)
162+
.addTextDisplayComponents(
163+
new TextDisplayBuilder().setContent("**Uh oh!**"),
164+
new TextDisplayBuilder().setContent(
165+
`It looks like we ran into an issue\n-# If this issue is persistent, please consult your console logs if you're using a self-hosted version of Boostify, and create an issue on our [Repository](https://github.com/teamboostify/boostify/issues), or use our support server!.`,
166+
),
167+
)
168+
.addSeparatorComponents((sep) =>
169+
sep.setDivider(true).setSpacing(SeparatorSpacingSize.Small),
170+
)
171+
.addActionRowComponents((actrow) =>
172+
actrow.addComponents(supportButton),
173+
);
174+
175+
await interaction.editReply({
176+
flags: MessageFlags.Ephemeral | MessageFlags.IsComponentsV2,
177+
components: [container],
178+
});
179+
return;
180+
}
181+
}
182+
128183
const embed = new EmbedBuilder()
129184
.setColor(booster.active ? 0xf47fff : 0x99aab5)
130185
.setTitle(`Booster Info: ${user.username}`)
131186
.setThumbnail(avatarUrl)
132187
.addFields(
133-
{ name: "Status", value: booster.active ? "🟢 Active" : "🔴 Inactive", inline: true },
188+
{
189+
name: "Status",
190+
value: booster.active && booster.boostCounts > 0 ? "🟢 Active" : "🔴 Inactive",
191+
inline: true,
192+
},
134193
{
135194
name: "Custom Role",
136-
value: booster.customRole ? `<@&${booster.customRole.discordRoleId}>` : "None",
195+
value: booster.customRole
196+
? `<@&${booster.customRole.discordRoleId}>`
197+
: "None",
198+
inline: true,
199+
},
200+
{
201+
name: "Boosting since",
202+
value: `<t:${Math.floor(member!.premiumSince!.getTime() / 1000)}:D>`,
203+
inline: true,
204+
},
205+
{
206+
name: "Boosts Counts",
207+
value: booster.boostCounts.toString(),
137208
inline: true,
138209
},
139-
{ name: "Boosting since", value: `<t:${Math.floor(member!.premiumSince!.getTime() / 1000)}:D>`, inline: true},
140-
{ name: "Boosts Counts", value: booster.boostCounts.toString(), inline: true}
141210
)
142211
.setTimestamp();
143212

@@ -149,32 +218,39 @@ export default new Command({
149218
const user = interaction.options.getUser("user", true);
150219
const amount = interaction.options.getInteger("amount", true);
151220

152-
const targetMember = await discordGuild.members.fetch(user.id).catch(() => null);
221+
const targetMember = await discordGuild.members
222+
.fetch(user.id)
223+
.catch(() => null);
153224
if (!targetMember?.premiumSince) {
154225
await interaction.editReply({
155226
content: `${user} is not currently boosting this server with Nitro, so boosts can't be added for them.`,
156227
});
157228
return;
158229
}
159230

160-
await registerBoost(user.id, discordGuild.id, discordGuild.name, discordGuild.iconURL());
231+
await registerBoost(
232+
user.id,
233+
discordGuild.id,
234+
discordGuild.name,
235+
discordGuild.iconURL(),
236+
);
161237

162238
const updated = await addBoostCount(user.id, discordGuild.id, amount);
163239
if (!updated) {
164-
await interaction.editReply({ content: "Failed to update boost count." });
240+
await interaction.editReply({
241+
content: "Failed to update boost count.",
242+
});
165243
return;
166244
}
167245

168246
const boostWord = amount === 1 ? "boost" : "boosts";
169247
const container = new ContainerBuilder()
170248
.setAccentColor(0xe642a4)
171249
.addTextDisplayComponents(
250+
new TextDisplayBuilder().setContent(`**Boost successfully added!**`),
172251
new TextDisplayBuilder().setContent(
173-
`**Boost successfully added!**`
252+
`We've successfully added ${amount} ${boostWord} to ${user}'s profile.`,
174253
),
175-
new TextDisplayBuilder().setContent(
176-
`We've successfully added ${amount} ${boostWord} to ${user}'s profile.`
177-
)
178254
);
179255

180256
await interaction.editReply({
@@ -190,7 +266,9 @@ export default new Command({
190266

191267
const updated = await removeBoostCount(user.id, discordGuild.id, amount);
192268
if (!updated) {
193-
await interaction.editReply({ content: `No booster record found for ${user.tag}.` });
269+
await interaction.editReply({
270+
content: `No booster record found for ${user.tag}.`,
271+
});
194272
return;
195273
}
196274

@@ -211,14 +289,26 @@ export default new Command({
211289
.setColor(0xf47fff)
212290
.setTitle("Server Boost Statistics")
213291
.addFields(
214-
{ name: "Current Boosters", value: String(activeBoosters.length), inline: true },
215-
{ name: "Total Boosts (All Time)", value: String(totalBoosts), inline: true },
216-
{ name: "Unique Boosters (All Time)", value: String(allBoosters.length), inline: true }
292+
{
293+
name: "Current Boosters",
294+
value: String(activeBoosters.length),
295+
inline: true,
296+
},
297+
{
298+
name: "Total Boosts (All Time)",
299+
value: String(totalBoosts),
300+
inline: true,
301+
},
302+
{
303+
name: "Unique Boosters (All Time)",
304+
value: String(allBoosters.length),
305+
inline: true,
306+
},
217307
)
218308
.setTimestamp();
219309

220310
await interaction.editReply({ embeds: [embed] });
221311
return;
222312
}
223313
},
224-
})
314+
});

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import fs from "fs";
66
import { fileURLToPath, pathToFileURL } from "url";
77
import { logger } from "./libs/logger.js";
88
import chalk from "chalk";
9+
import { loadLoops } from "./libs/loops.js";
910

1011
const __dirname = fileURLToPath(new URL(".", import.meta.url));
1112

@@ -99,6 +100,7 @@ const eventFiles = fs
99100
}
100101

101102
await loadCommands();
103+
await loadLoops();
102104

103105
try {
104106
client.login(process.env.BOT_TOKEN);

src/libs/loops.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
import { fileURLToPath, pathToFileURL } from "url";
4+
import { logger } from "./logger.js";
5+
6+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
7+
8+
export async function loadLoops() {
9+
const loops = path.join(__dirname, "..", "loops");
10+
if (!fs.existsSync(loops)) {
11+
logger.warn(
12+
"Loops folder wasn't found — this may cause the system to be slow, and not lightweight!",
13+
);
14+
return;
15+
}
16+
17+
const loopFiles = fs
18+
.readdirSync(loops)
19+
.filter((file) => file.endsWith(".ts") || file.endsWith(".js"));
20+
21+
if (loopFiles.length === 0) {
22+
logger.warn("No command files found — nothing to register.");
23+
return;
24+
}
25+
26+
for (const file of loopFiles) {
27+
const filePath = path.join(loops, file);
28+
const fileInfo = (await import(pathToFileURL(filePath).href)).default;
29+
console.log(file);
30+
31+
if (!fileInfo?.runEvery || !fileInfo?.execute) {
32+
logger.warn(`Skipping ${file} - missing runEvery or execute`);
33+
continue;
34+
}
35+
36+
const run = async () => {
37+
try {
38+
await fileInfo.execute();
39+
} catch (err) {
40+
logger.error(`Loop "${file}" failed: ${err}`);
41+
}
42+
};
43+
44+
run();
45+
setInterval(run, fileInfo.runEvery * 1000);
46+
47+
logger.success(`Loaded loop "${file}"`);
48+
}
49+
}

src/loops/boosterCleanup.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { prisma } from "../libs/database.js"
2+
3+
export default {
4+
runEvery: 60, // seconds
5+
async execute() {
6+
await prisma.booster.deleteMany({
7+
where: {
8+
boostCounts: {
9+
lt: 1 // delete all boosters' data in which boost counts is 0
10+
}
11+
}
12+
})
13+
}
14+
}

0 commit comments

Comments
 (0)