-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
232 lines (207 loc) · 9.04 KB
/
Copy pathindex.js
File metadata and controls
232 lines (207 loc) · 9.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
require("dotenv/config");
const { Client, GatewayIntentBits, Collection, Options, Events, PermissionsBitField, EmbedBuilder, MessageFlags } = require("discord.js");
const logger = require("./utils/logger");
const loadEvents = require("./handlers/eventHandler");
const loadComponents = require("./handlers/componentHandler");
const config = require("./config/config");
const checkPermissions = require("./utils/checkPermissions");
const { connectToMongo } = require("./utils/mongo");
const { LogError } = require("./utils/LogError");
const { postStats } = require("./postStats");
const userblacklist = require("./schema/blacklist_user");
const Blacklistserver = require("./schema/blacklist_server");
const { botowner } = require("./config/config");
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessages],
allowedMentions: { parse: ["users", "roles"], repliedUser: false }, //* If you need @everyone ping (not suggested) you can edit allowedMentions in .send object on TextChannel
makeCache: Options.cacheWithLimits({ PresenceManager: 0, ReactionManager: 0, ReactionUserManager: 0 }),
});
const { error_emote, warning_emote, success_emote } = require("./utils/emotes");
const { supportinvite } = require("./utils/support-invite");
client.config = config;
client.logger = logger;
client.events = new Map();
client.commands = { slash: new Collection(), prefix: new Collection(), context: new Collection() };
client.components = { buttons: new Collection(), selectMenus: new Collection(), modals: new Collection() };
client.cooldowns = new Map();
client.helpers = { checkPermissions };
module.exports = client;
async function InteractionHandler(interaction, type) {
// Resolve the collection where this component/command should live
const id = interaction.customId ?? interaction.commandName;
let collection = null;
// Directly use client[type] if it's a Collection
if (client[type] && typeof client[type].get === "function") {
collection = client[type];
} else {
// Fallback mappings for common types
if (type === "commands") collection = client.commands?.slash;
else if (type === "buttons") collection = client.components?.buttons;
else if (type === "selectMenus") collection = client.components?.selectMenus;
else if (type === "modals") collection = client.components?.modals;
}
const component = collection?.get?.(id);
if (!component) return;
// Developer-only and blacklist pre-checks for "commands" type
if (type === "commands") {
try {
if (component.settings?.isDeveloperOnly) {
const devs = Array.isArray(config.developers) ? config.developers : [];
if (!devs.includes(interaction.user.id)) {
return await interaction.reply({
content: `${warning_emote} Only **DEVELOPERS** can use this command`,
flags: MessageFlags.Ephemeral,
});
}
}
const blacklistData = await userblacklist.findOne({ User: interaction.user.id });
if (blacklistData) {
return await interaction.reply({
content: `${warning_emote} You have been **BLACKLISTED** from using this bot!\nTo appeal, join ${supportinvite}.`,
flags: MessageFlags.Ephemeral,
});
}
} catch (error) {
logger.error("Error checking dev/blacklist preconditions:", error);
}
}
try {
// Safer, robust logging that won't throw if values are missing.
const userTag = interaction.user?.tag ?? interaction.user?.username ?? interaction.user?.id ?? "Unknown User";
const location = interaction.guild ? interaction.guild.name : "DMs";
const triggerId = id ?? "unknown-id";
const triggerType = type ?? "unknown-type";
if (client.logger && typeof client.logger.info === "function") {
client.logger.info(`[INTERACTION] ${userTag} in ${location} triggered ${triggerType} ${triggerId}`, {
event: "interaction",
type: triggerType,
id: triggerId,
userId: interaction.user?.id,
guildId: interaction.guild?.id,
});
} else {
logger.info(`[INTERACTION] ${userTag} in ${location} triggered ${triggerType} ${triggerId}`);
}
// Owner-only check (hard-coded id from your snippet)
if (component.owner) {
if (!client.config.botowner.includes(interaction.user.id)) {
return await interaction.reply({ content: `${warning_emote} Only bot owners can use this command!`, flags: MessageFlags.Ephemeral });
}
}
if (await client.helpers.checkPermissions("interaction", interaction, component, client)) return;
// Route autocomplete interactions correctly while keeping centralized permission checks.
if (interaction.isAutocomplete?.() && typeof component.autocomplete === "function") {
await component.autocomplete(interaction, client);
return;
}
await component.execute(interaction, client);
} catch (error) {
// Log locally
logger.error(error);
// Try to notify user (safely). Ignore known Discord races (10062, 40060).
try {
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch(() => {});
await interaction
.editReply({
content: `${error_emote} There was an error while executing this command!`,
embeds: [],
components: [],
files: [],
})
.catch(() => {});
} catch (replyErr) {
const code = replyErr && replyErr.code;
if (code === 10062 || code === 40060) {
logger.debug?.("Ignored expected Discord API error while replying to interaction error", { code, replyErr });
const APIErrorAlert = new EmbedBuilder()
.setTitle("⚠️ Alert: Interaction Error Notification Failed")
.setDescription(`${error_emote} An error occurred while trying to notify a user about an interaction error, but it was an expected Discord API error (code ${code}). This likely means the user deleted their message or left the channel before we could reply.\n\nOriginal Interaction: ${type} ${id}\nUser: ${interaction.user?.tag ?? interaction.user?.id}\nGuild: ${interaction.guild?.name ?? "DMs"}`)
.setColor("Yellow")
.setTimestamp();
await interaction.reply({ embeds: [APIErrorAlert], flags: MessageFlags.Ephemeral });
logger.warn(APIErrorAlert.data.description);
} else {
logger.error("Failed to notify user about command error:", replyErr);
}
}
try {
LogError(error, client, `${type} ${id}`);
} catch (e) {
logger.error("LogError failed while reporting execution error", e);
}
}
}
client.helpers.InteractionHandler = InteractionHandler;
(async () => {
try {
await Promise.all([connectToMongo(), loadEvents(client), loadComponents(client)]);
const token = process.env.DISCORD_TOKEN;
if (!token) {
logger.error("DISCORD_TOKEN environment variable is not set. Aborting startup.");
process.exit(1);
}
await client.login(token);
logger.info(`[Startup] Logged in as ${client.user?.tag ?? "unknown user"}, please wait until terminal says Java Lava Standard is ready.`);
client.on("clientReady", () => {
logger.info(`Bot is ready as ${client.user?.tag ?? "unknown user"}, Posting initial stats to Top.gg and DBL...`);
postStats(client);
setInterval(
() => {
// logger.info("Posting stats to Top.gg and DBL...");
postStats(client);
},
30 * 60 * 1000,
); // 30 minutes
postStats(client);
// logger.info("Initial stats posted to Top.gg and DBL");
});
try {
} catch (_) {}
} catch (error) {
logger.error("An error occurred during startup/login", error);
}
})();
process.on("unhandledRejection", (reason, promise) => {
const code = reason && reason.code;
if (code === 10062 || code === 40060 || code === 50035) {
logger.debug?.("Ignored expected Discord API rejection", { code, reason });
return;
}
logger.error("Unhandled Promise Rejection", {
reason: reason instanceof Error ? reason.stack || reason.message : reason,
promise,
});
try {
LogError(reason, client, "unhandledRejection");
} catch (e) {
logger.warn("LogError failed while reporting unhandledRejection", e);
}
});
process.on("uncaughtException", (error) => {
if (error && error.code === "ENOMEM") {
logger.error("Out of memory detected.", error);
} else {
logger.error("Uncaught Exception", error);
}
try {
LogError(error, client, "uncaughtException");
} catch (e) {
logger.warn("LogError failed while reporting uncaughtException", e);
}
});
process.on("uncaughtExceptionMonitor", (error) => {
logger.warn("Uncaught Exception Monitor", error);
});
process.on("warning", (warning) => {
logger.warn("Node.js Warning", warning);
});
client.on(Events.GuildCreate, async (guild) => {
try {
const data = await Blacklistserver.findOne({ Guild: guild.id });
if (data) {
await guild.leave();
}
} catch (err) {
logger.error("Error handling GuildCreate blacklist check", { error: err, guildId: guild.id });
}
});