const { EmbedBuilder, ApplicationCommandType, AttachmentBuilder } = require('discord.js'); const { dangercordcheckVerification } = require('../../functions/dangerCordUtils.js'); const { createCanvas } = require('canvas'); const GuildSettings = require('../../database/models/GuildSettings'); const cfg = require('../../config/config.js'); const { moderationUtilities } = require('../../functions/utils.js'); module.exports = { name: 'verify', description: 'To verify yourself.', type: ApplicationCommandType.ChatInput, run: async (client, interaction) => { const guildSettings = await GuildSettings.findOne({ guildId: interaction.guild.id }); await interaction.deferReply({ ephemeral: true }); const guildId = interaction.guild.id; async function generateCaptcha(count) { // Setting up the variables for generation var upchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var lowchars = "abcdefghijklmnopqrstuvwxyz"; var nums = "0123456789"; var captcha = ""; for (var i = 0; i < count; i++) { var c = Math.floor(Math.random() * 3); if (c === 0) { captcha += upchars.charAt(Math.floor(Math.random() * upchars.length)); } else if (c === 1) { captcha += lowchars.charAt(Math.floor(Math.random() * lowchars.length)); } else { captcha += nums.charAt(Math.floor(Math.random() * nums.length)); } } return captcha; } async function createCaptcha(captcha) { const canvas = createCanvas(200, 75); const ctx = canvas.getContext('2d'); // Background color ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Text ctx.font = '30px Arial'; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; ctx.fillStyle = '#ffffff'; // Calculate spacing between characters to fit within canvas width const charWidth = 30; const totalWidth = captcha.length * charWidth; const startX = (canvas.width - totalWidth) / 2; // Draw each character with random y-offset for (let i = 0; i < captcha.length; i++) { const x = startX + i * charWidth + charWidth / 2; // Adjust x-position for each character const y = 37.5 + Math.random() * 30 - 15; // Random y-offset ctx.fillText(captcha[i], x, y); } // Adding noise for (let i = 0; i < 50; i++) { ctx.fillStyle = `rgba(255,255,255,${Math.random() * 0.9})`; ctx.fillRect(Math.random() * canvas.width, Math.random() * canvas.height, 1, 1); } // Adding white lines for (let i = 0; i < 30; i++) { ctx.strokeStyle = `rgba(255,255,255,${Math.random() * 1.8})`; ctx.beginPath(); ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.stroke(); } // Adding blue lines for (let i = 0; i < 30; i++) { ctx.strokeStyle = `rgba(0, 0, 255, ${Math.random() * 0.9})`; ctx.beginPath(); ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.stroke(); } // Rotation const rotation = Math.random() * 0.2 - 0.1; // Random rotation between -0.1 and 0.1 radians ctx.translate(canvas.width / 2, canvas.height / 2); ctx.rotate(rotation); ctx.translate(-canvas.width / 2, -canvas.height / 2); const buffer = canvas.toBuffer('image/png'); return buffer; } if (interaction.channel.id !== cfg.guilds[guildId].channelId) { return interaction.editReply({ content: cfg.messages.wrongChannel, ephemeral: true }) } if (guildSettings?.verifyEnabled === false || !guildSettings?.verifyEnabled) { return interaction.editReply({ content: cfg.messages.verifyDisabled, ephemeral: true }) } if (interaction.member.roles.cache.has(cfg.guilds[guildId].roleToAdd)) { return interaction.editReply({ content: cfg.messages.alreadyVerified, ephemeral: true }) } let attempts = 0; let blacklisted = false; const maxAttempts = cfg.maxAttempts; let validated = false; // Flag to track if user is already validated async function verifyUser(captcha) { attempts++; switch (attempts) { case attempts > maxAttempts: await interaction.user.send(cfg.messages.maxAttemptsExceeded); return; } } /* if (attempts > maxAttempts) { await interaction.user.send(cfg.messages.maxAttemptsExceeded); return; } */ const captcha = await generateCaptcha(cfg.captchaCount); switch (attempts) { case 1: await dangercordcheckVerification(interaction.user.id) .then(result => { console.log('Verification result:', result); switch (result) { case 1: { console.log(`${interaction.user.id} is not blacklisted at dangercord, lets continue with the verification process`) // Proceed with further actions break; } default: { console.log(`${interaction.user.id} is blacklisted at dangercord, we will not continue with the verification process`); blacklisted = true; // Set blacklisted to true // Handle the case when user is blacklisted interaction.editReply({ content: cfg.messages.cannotContinue, ephemeral: true }).then(() => { // Stop execution here return; }).catch(error => { console.error('Error:', error); }); break; } } }) .catch(error => { console.error('Error:', error); }); } /* if (attempts == 1) { // Check the Dangercord API using our custom library function await dangercordcheckVerification(interaction.user.id) .then(result => { console.log('Verification result:', result); if (result === 1) { console.log(`${interaction.user.id} is not blacklisted at dangercord, lets continue with the verification process`) // Proceed with further actions } else { console.log(`${interaction.user.id} is blacklisted at dangercord, we will not continue with the verification process`); blacklisted = true; // Set blacklisted to true // Handle the case when user is blacklisted interaction.editReply({ content: cfg.messages.cannotContinue, ephemeral: true }).then(() => { // Stop execution here return; }).catch(error => { console.error('Error:', error); }); } }) .catch(error => { console.error('Error:', error); }); } */ if (blacklisted == true) return const buff = await createCaptcha(captcha); const attachment = new AttachmentBuilder(buff, { name: 'captcha.png' }); const embed = new EmbedBuilder() .setTitle("Captcha Verification") .setDescription(`Please type the captcha below. You have ${cfg.captchaTimeout / 1000} seconds to respond. Attempt ${attempts}/${maxAttempts}`) .setColor('Green') .setImage('attachment://captcha.png') .setTimestamp(); await interaction.user.send({ embeds: [embed], files: [attachment] }).then(async (msg) => { await interaction.editReply({ content: cfg.messages.toVerify, ephemeral: true }); const filter = m => !m.author.bot && m.author.id === interaction.user.id && m.channel.id === msg.channel.id; const collector = msg.channel.createMessageCollector({ filter, time: cfg.captchaTimeout }); collector.on('collect', async (m) => { //if member left the server before the verification is done switch (!interaction.guild.members.cache.has(interaction.user.id)) { case true: { await interaction.user.send(cfg.messages.userLeft); collector.stop(); break; } default: { switch (m.content === captcha) { case true: { validated = true; await interaction.member.roles.add(cfg.guilds[guildId].roleToAdd); await interaction.member.roles.remove(cfg.guilds[guildId].firstRole); await interaction.user.send(cfg.messages.success); return; } default: { // If the content doesn't match captcha collector.stop(); break; } } } } }); /* if (!interaction.guild.members.cache.has(interaction.user.id)) { await interaction.user.send(cfg.messages.userLeft); collector.stop(); } else { if (m.content === captcha) { validated = true; await interaction.member.roles.add(cfg.guilds[guildId].roleToAdd); await interaction.member.roles.remove(cfg.guilds[guildId].firstRole); await interaction.user.send(cfg.messages.success); return } else { // If the content doesn't match captcha collector.stop(); } } */ collector.on('end', async (collected, reason) => { switch (reason) { case 'time': switch (validated) { case false: case null: await interaction.user.send(cfg.messages.tookTooLong); break; } break; case } /* if (reason === 'time' && !validated) { // Send timeout message only if not already validated await interaction.user.send(cfg.messages.tookTooLong); } else if (!validated) { // Only generate a new captcha and attempt verification if attempts < maxAttempts const newCaptcha = await generateCaptcha(cfg.captchaCount); await verifyUser(newCaptcha); } */ }); }).catch(async (err) => { await interaction.editReply({ content: cfg.messages.unableToDM, ephemeral: true }); }); verifyUser(captcha); } } /* Note from Naoki: This looks simple enough but damn. Canvas is driving me nuts due to how hard to work with it. Had to StackOverFlow-ed it to get it to work. I'm not sure if this is the best way to do it but it works. Big shoutout to StackOverFlow and the people I got the code from. */