first commit
This commit is contained in:
36
src/handlers/registerCommands.ts
Normal file
36
src/handlers/registerCommands.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Client, Events, REST, Routes } from "discord.js";
|
||||
import { getAllCommands, getCommand } from "../lib/commandRegistry";
|
||||
import { GUILD_ID } from "../config";
|
||||
|
||||
export default async function (client: Client) {
|
||||
const token = process.env.DISCORD_TOKEN!;
|
||||
const clientId = client.user!.id; // use the ready client's ID
|
||||
|
||||
const rest = new REST().setToken(token);
|
||||
|
||||
const commandData = getAllCommands().map((cmd) => cmd.data.toJSON());
|
||||
|
||||
await rest.put(Routes.applicationGuildCommands(clientId, GUILD_ID), {
|
||||
body: commandData,
|
||||
});
|
||||
|
||||
console.log(`Registered ${commandData.length} slash command(s).`);
|
||||
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const command = getCommand(interaction.commandName);
|
||||
if (!command) return;
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (interaction.replied || interaction.deferred) {
|
||||
await interaction.followUp({ content: "An error occurred.", ephemeral: true });
|
||||
} else {
|
||||
await interaction.reply({ content: "An error occurred.", ephemeral: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
155
src/handlers/startup/checkRules.ts
Normal file
155
src/handlers/startup/checkRules.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import {
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
Events,
|
||||
GuildMember,
|
||||
Partials,
|
||||
TextChannel,
|
||||
} from "discord.js";
|
||||
import { CHANNELS, GUILD_ID, ROLES } from "../../config";
|
||||
|
||||
const RULES_EMOJI = "✅";
|
||||
|
||||
export default async function checkRules(client: Client): Promise<void> {
|
||||
const guild = await client.guilds.fetch(GUILD_ID);
|
||||
const channel = (await guild.channels.fetch(
|
||||
CHANNELS["RULES"],
|
||||
)) as TextChannel;
|
||||
|
||||
if (!channel || !channel.isTextBased()) {
|
||||
console.error(
|
||||
"[checkRules] Rules channel not found or is not a text channel.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for an existing rules message posted by the bot
|
||||
const messages = await channel.messages.fetch({ limit: 50 });
|
||||
const existingMessage = messages.find((m) => m.author.id === client.user!.id);
|
||||
|
||||
if (!existingMessage) {
|
||||
const embed1 = new EmbedBuilder()
|
||||
.setTitle("📜 Server Rules — Part 1: General Conduct")
|
||||
.setColor(0x5865f2)
|
||||
.setDescription(
|
||||
"Please read and follow all rules to keep this server a safe and welcoming place for everyone.",
|
||||
)
|
||||
.addFields(
|
||||
{
|
||||
name: "1. Be Respectful",
|
||||
value:
|
||||
"Treat all members with respect. Harassment, hate speech, slurs, and discrimination of any kind will not be tolerated.",
|
||||
},
|
||||
{
|
||||
name: "2. No Spam",
|
||||
value:
|
||||
"Do not spam messages, emojis, or mentions. Keep conversations relevant to the channel topic.",
|
||||
},
|
||||
{
|
||||
name: "3. No NSFW Content",
|
||||
value:
|
||||
"Explicit, graphic, or otherwise inappropriate content is strictly prohibited.",
|
||||
},
|
||||
{
|
||||
name: "4. No Self-Promotion",
|
||||
value:
|
||||
"Do not advertise other servers, social media accounts, or services without prior approval from staff.",
|
||||
},
|
||||
{
|
||||
name: "5. Follow Discord ToS",
|
||||
value:
|
||||
"All members must comply with [Discord's Terms of Service](https://discord.com/terms) and [Community Guidelines](https://discord.com/guidelines).",
|
||||
},
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
const embed2 = new EmbedBuilder()
|
||||
.setTitle("📋 Server Rules — Part 2: Channels & Community")
|
||||
.setColor(0x57f287)
|
||||
.addFields(
|
||||
{
|
||||
name: "6. Use the Right Channels",
|
||||
value:
|
||||
"Keep discussions in their appropriate channels. Off-topic conversations belong in the designated channel.",
|
||||
},
|
||||
{
|
||||
name: "7. No Doxxing",
|
||||
value:
|
||||
"Sharing personal or private information of others without their explicit consent is strictly forbidden.",
|
||||
},
|
||||
{
|
||||
name: "8. English in Main Channels",
|
||||
value:
|
||||
"Please communicate in English in main channels so all members and staff can participate.",
|
||||
},
|
||||
{
|
||||
name: "9. Listen to Staff",
|
||||
value:
|
||||
"Follow the instructions of moderators and admins. If you disagree with a decision, open a support ticket calmly.",
|
||||
},
|
||||
{
|
||||
name: "10. Have Fun!",
|
||||
value:
|
||||
"This is a community — be kind, stay positive, and enjoy your time here. 🎉",
|
||||
},
|
||||
)
|
||||
.setFooter({
|
||||
text: "React with ✅ below to accept the rules and gain access to the server.",
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
const rulesMessage = await channel.send({ embeds: [embed1, embed2] });
|
||||
await rulesMessage.react(RULES_EMOJI);
|
||||
console.log("[checkRules] Rules message posted successfully.");
|
||||
} else {
|
||||
console.log("[checkRules] Rules message already exists, skipping.");
|
||||
}
|
||||
|
||||
// Grant role when a member reacts with ✅ in the rules channel
|
||||
client.on(Events.MessageReactionAdd, async (reaction, user) => {
|
||||
if (user.bot) return;
|
||||
if (reaction.message.channelId !== CHANNELS.RULES) return;
|
||||
if (reaction.emoji.name !== RULES_EMOJI) return;
|
||||
|
||||
// Resolve partials if needed
|
||||
try {
|
||||
if (reaction.partial) await reaction.fetch();
|
||||
if (user.partial) await user.fetch();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const member = await guild.members.fetch(user.id);
|
||||
await member.roles.add(ROLES.RULES);
|
||||
console.log(`[checkRules] Granted rules role to ${user.tag ?? user.id}`);
|
||||
} catch (err) {
|
||||
console.error("[checkRules] Failed to add rules role:", err);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove role when the reaction is removed
|
||||
client.on(Events.MessageReactionRemove, async (reaction, user) => {
|
||||
if (user.bot) return;
|
||||
if (reaction.message.channelId !== CHANNELS.RULES) return;
|
||||
if (reaction.emoji.name !== RULES_EMOJI) return;
|
||||
|
||||
try {
|
||||
if (reaction.partial) await reaction.fetch();
|
||||
if (user.partial) await user.fetch();
|
||||
} catch (err) {
|
||||
console.error("[checkRules] Failed to fetch reaction or user:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const member = await guild.members.fetch(user.id);
|
||||
await member.roles.remove(ROLES.RULES);
|
||||
console.log(
|
||||
`[checkRules] Removed rules role from ${user.tag ?? user.id}`,
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("[checkRules] Failed to remove rules role:", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
18
src/handlers/startup/rotatingActivity.ts
Normal file
18
src/handlers/startup/rotatingActivity.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ActivityType, Client } from "discord.js";
|
||||
import { ROTATE_ACTIVITIES } from "../../config";
|
||||
|
||||
export default async function rotatingActivity(client: Client): Promise<void> {
|
||||
if (!client.user) {
|
||||
console.error("[rotatingActivity] Client is not ready yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
client.user.setActivity("Fixing Bugs", { type: ActivityType.Custom });
|
||||
let index = 0;
|
||||
setInterval(() => {
|
||||
const activity = ROTATE_ACTIVITIES[index];
|
||||
client.user!.setActivity(activity.content, { type: activity.type });
|
||||
index = (index + 1) % ROTATE_ACTIVITIES.length;
|
||||
console.log(`[rotatingActivity] Updated activity to: ${activity.content} (${activity.type})`);
|
||||
}, 60000); // Rotate every 60 seconds
|
||||
}
|
||||
Reference in New Issue
Block a user