|
| 1 | +import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; |
| 2 | +import { getFromDb, setInDb } from '../utils/database/wrapper.js'; |
| 3 | +import { ModerationService } from './moderationService.js'; |
| 4 | +import { logger } from '../utils/logger.js'; |
| 5 | + |
| 6 | +const getHoneypotKey = (guildId) => `guild:${guildId}:honeypot`; |
| 7 | +const HONEYPOT_IMAGE = 'https://honeypot.riskymh.dev/honeypot.png'; |
| 8 | + |
| 9 | +export async function getHoneypotConfig(guildId) { |
| 10 | + try { |
| 11 | + const data = await getFromDb(getHoneypotKey(guildId), null); |
| 12 | + return data || { channelId: null, enabled: false, banCount: 0 }; |
| 13 | + } catch (err) { |
| 14 | + logger.error(`Failed to get honeypot config for ${guildId}:`, err); |
| 15 | + return { channelId: null, enabled: false, banCount: 0 }; |
| 16 | + } |
| 17 | +} |
| 18 | + |
| 19 | +export async function setHoneypotConfig(guildId, config) { |
| 20 | + try { |
| 21 | + await setInDb(getHoneypotKey(guildId), config); |
| 22 | + return true; |
| 23 | + } catch (err) { |
| 24 | + logger.error(`Failed to set honeypot config for ${guildId}:`, err); |
| 25 | + return false; |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +export function buildHoneypotWarningEmbed(banCount = 0) { |
| 30 | + return new EmbedBuilder() |
| 31 | + .setColor(0xFF0000) |
| 32 | + .setTitle('DO NOT SEND MESSAGES IN THIS CHANNEL') |
| 33 | + .setDescription( |
| 34 | + 'This channel is used to catch spam bots and scammers.\n' + |
| 35 | + 'Any messages sent here will result in a **ban**.', |
| 36 | + ) |
| 37 | + .setThumbnail(HONEYPOT_IMAGE); |
| 38 | +} |
| 39 | + |
| 40 | +export function buildHoneypotComponents(banCount = 0) { |
| 41 | + return new ActionRowBuilder().addComponents( |
| 42 | + new ButtonBuilder() |
| 43 | + .setCustomId('honeypot_ban_count') |
| 44 | + .setLabel(`🍯 Kicks: ${banCount}`) |
| 45 | + .setStyle(ButtonStyle.Danger) |
| 46 | + .setDisabled(true), |
| 47 | + ); |
| 48 | +} |
| 49 | + |
| 50 | +export async function refreshHoneypotMessage(client, guildId) { |
| 51 | + try { |
| 52 | + const config = await getHoneypotConfig(guildId); |
| 53 | + if (!config.channelId || !config.messageId) return; |
| 54 | + |
| 55 | + const channel = client.channels.cache.get(config.channelId); |
| 56 | + if (!channel) return; |
| 57 | + |
| 58 | + const msg = await channel.messages.fetch(config.messageId).catch(() => null); |
| 59 | + if (!msg) return; |
| 60 | + |
| 61 | + await msg.edit({ |
| 62 | + embeds: [buildHoneypotWarningEmbed(config.banCount)], |
| 63 | + components: [buildHoneypotComponents(config.banCount)], |
| 64 | + }); |
| 65 | + } catch (err) { |
| 66 | + logger.error(`Honeypot: failed to refresh warning message in guild ${guildId}:`, err); |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | + |
| 71 | +export async function handleHoneypotMessage(message, client) { |
| 72 | + const { guild, author } = message; |
| 73 | + await message.delete().catch(() => null); |
| 74 | + const botMember = guild.members.me; |
| 75 | + |
| 76 | + try { |
| 77 | + await ModerationService.banUser({ |
| 78 | + guild, |
| 79 | + user: author, |
| 80 | + moderator: botMember, |
| 81 | + reason: 'Honeypot triggered — sent a message in a protected channel.', |
| 82 | + deleteDays: 1, |
| 83 | + }); |
| 84 | + |
| 85 | + logger.info(`Honeypot: banned ${author.tag} (${author.id}) in guild ${guild.id}`); |
| 86 | + const config = await getHoneypotConfig(guild.id); |
| 87 | + const newCount = (config.banCount || 0) + 1; |
| 88 | + await setHoneypotConfig(guild.id, { ...config, banCount: newCount }); |
| 89 | + await refreshHoneypotMessage(client, guild.id); |
| 90 | + } catch (err) { |
| 91 | + logger.error(`Honeypot: failed to ban ${author.tag} in guild ${guild.id}:`, err); |
| 92 | + } |
| 93 | +} |
0 commit comments