Skip to content

Commit 7e98da8

Browse files
authored
Create honeypot.js
1 parent 50e5598 commit 7e98da8

1 file changed

Lines changed: 161 additions & 0 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { SlashCommandBuilder, MessageFlags, PermissionFlagsBits } from 'discord.js';
2+
import { InteractionHelper } from '../../utils/interactionHelper.js';
3+
import { handleInteractionError, TitanBotError, ErrorTypes } from '../../utils/errorHandler.js';
4+
import { createEmbed } from '../../utils/embeds.js';
5+
import { logger } from '../../utils/logger.js';
6+
import { getHoneypotConfig, setHoneypotConfig, buildHoneypotWarningEmbed, buildHoneypotComponents, refreshHoneypotMessage } from '../../services/honeypotService.js';
7+
8+
export default {
9+
category: 'Moderation',
10+
11+
data: new SlashCommandBuilder()
12+
.setName('honeypot')
13+
.setDescription('Manage the honeypot channel that auto-bans/kicks anyone who sends a message in it')
14+
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
15+
16+
.addSubcommand((sub) =>
17+
sub
18+
.setName('set')
19+
.setDescription('Set the honeypot channel and post the warning embed')
20+
.addChannelOption((opt) =>
21+
opt.setName('channel').setDescription('The channel to use as a honeypot').setRequired(true),
22+
),
23+
)
24+
25+
.addSubcommand((sub) =>
26+
sub
27+
.setName('method')
28+
.setDescription('Toggle between ban and kick for honeypot punishment')
29+
.addStringOption((opt) =>
30+
opt
31+
.setName('action')
32+
.setDescription('Punishment to apply when triggered')
33+
.setRequired(true)
34+
.addChoices(
35+
{ name: 'Ban (default)', value: 'ban' },
36+
{ name: 'Kick', value: 'kick' },
37+
),
38+
),
39+
)
40+
41+
.addSubcommand((sub) =>
42+
sub.setName('disable').setDescription('Disable the honeypot without deleting the channel'),
43+
)
44+
45+
.addSubcommand((sub) =>
46+
sub.setName('status').setDescription('Show the current honeypot configuration'),
47+
),
48+
49+
async execute(interaction, config, client) {
50+
try {
51+
await InteractionHelper.safeDefer(interaction, { flags: MessageFlags.Ephemeral });
52+
53+
const sub = interaction.options.getSubcommand();
54+
const guild = interaction.guild;
55+
const guildId = guild.id;
56+
57+
if (sub === 'set') {
58+
const channel = interaction.options.getChannel('channel');
59+
60+
if (!channel.isTextBased()) {
61+
throw new TitanBotError('Invalid channel type', ErrorTypes.VALIDATION, 'The honeypot channel must be a text channel.');
62+
}
63+
64+
const botMember = guild.members.me;
65+
const perms = channel.permissionsFor(botMember);
66+
if (!perms?.has('ViewChannel') || !perms?.has('ManageMessages')) {
67+
throw new TitanBotError('Missing permissions', ErrorTypes.PERMISSION, `I need **View Channel** and **Manage Messages** permissions in ${channel}.`);
68+
}
69+
70+
const existing = await getHoneypotConfig(guildId);
71+
const banCount = existing.banCount || 0;
72+
const method = existing.method || 'ban';
73+
74+
let messageId = null;
75+
try {
76+
const msg = await channel.send({
77+
embeds: [buildHoneypotWarningEmbed(method)],
78+
components: [buildHoneypotComponents(banCount, method)],
79+
});
80+
messageId = msg.id;
81+
} catch (err) {
82+
logger.warn(`Honeypot: could not post warning embed in ${channel.id}:`, err);
83+
}
84+
85+
await setHoneypotConfig(guildId, { channelId: channel.id, enabled: true, banCount, method, messageId });
86+
87+
await InteractionHelper.safeEditReply(interaction, {
88+
embeds: [
89+
createEmbed({
90+
title: 'Honeypot Set',
91+
description:
92+
`${channel} is now the honeypot channel.\n\n` +
93+
`Anyone who sends a message there will be **instantly ${method === 'kick' ? 'kicked' : 'banned'}**.\n\n` +
94+
`The warning embed has been posted in the channel.`,
95+
color: 'success',
96+
}),
97+
],
98+
});
99+
100+
} else if (sub === 'method') {
101+
const action = interaction.options.getString('action');
102+
const current = await getHoneypotConfig(guildId);
103+
104+
if (!current.channelId || !current.enabled) {
105+
throw new TitanBotError('Not configured', ErrorTypes.VALIDATION, 'Set up a honeypot channel first with `/honeypot set`.');
106+
}
107+
108+
await setHoneypotConfig(guildId, { ...current, method: action });
109+
await refreshHoneypotMessage(client, guildId);
110+
111+
await InteractionHelper.safeEditReply(interaction, {
112+
embeds: [
113+
createEmbed({
114+
title: 'Honeypot Method Updated',
115+
description: `The honeypot will now **${action}** users who send messages in the protected channel.\n\nThe warning embed has been updated.`,
116+
color: action === 'kick' ? 'warning' : 'error',
117+
}),
118+
],
119+
});
120+
121+
} else if (sub === 'disable') {
122+
const current = await getHoneypotConfig(guildId);
123+
if (!current.channelId || !current.enabled) {
124+
throw new TitanBotError('Not configured', ErrorTypes.VALIDATION, 'The honeypot is not currently enabled on this server.');
125+
}
126+
127+
await setHoneypotConfig(guildId, { ...current, enabled: false });
128+
129+
await InteractionHelper.safeEditReply(interaction, {
130+
embeds: [
131+
createEmbed({
132+
title: 'Honeypot Disabled',
133+
description: 'The honeypot has been disabled. The channel still exists but messages will no longer trigger any action.',
134+
color: 'warning',
135+
}),
136+
],
137+
});
138+
139+
} else if (sub === 'status') {
140+
const current = await getHoneypotConfig(guildId);
141+
const method = current.method || 'ban';
142+
143+
await InteractionHelper.safeEditReply(interaction, {
144+
embeds: [
145+
createEmbed({
146+
title: 'Honeypot Status',
147+
description: null,
148+
}).addFields(
149+
{ name: 'Enabled', value: current.enabled ? 'Yes' : 'No', inline: true },
150+
{ name: 'Channel', value: current.channelId ? `<#${current.channelId}>` : 'Not set', inline: true },
151+
{ name: 'Method', value: method.charAt(0).toUpperCase() + method.slice(1), inline: true },
152+
{ name: 'Total Actions', value: `${current.banCount || 0}`, inline: true },
153+
),
154+
],
155+
});
156+
}
157+
} catch (error) {
158+
await handleInteractionError(interaction, error, { command: 'honeypot' });
159+
}
160+
},
161+
};

0 commit comments

Comments
 (0)