├── sample.env ├── start.sh ├── .gitignore ├── start.bat ├── .gitattributes ├── .replit ├── package.json ├── sample-config.json ├── events ├── ready.js ├── guildMemberAdd.js └── interactionCreate.js ├── README.md └── index.js /sample.env: -------------------------------------------------------------------------------- 1 | TOKEN= -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | while true; do node index.js; done -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | config.json 3 | .env 4 | .DS_STORE -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | :runbot 4 | node index.js 5 | goto runbot -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | run="npm config set prefix=$(pwd)/node_modules/node && export PATH=$(pwd)/node_modules/node/bin:$PATH && clear && node index.js" 2 | language="nodejs" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "manualverification", 3 | "version": "2.0.3", 4 | "description": "A self-hosted manual Roblox verification bot for Discord.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/SoCuul/ManualVerification.git" 13 | }, 14 | "keywords": [ 15 | "roblox", 16 | "verification" 17 | ], 18 | "author": "SoCuul", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/SoCuul/ManualVerification/issues" 22 | }, 23 | "homepage": "https://github.com/SoCuul/ManualVerification#readme", 24 | "dependencies": { 25 | "discord.js": "^13.3.0", 26 | "dotenv": "^10.0.0", 27 | "rblxverify": "^1.1.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sample-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presence": { 3 | "activities": [ 4 | { 5 | "name": "with verification", 6 | "type": "PLAYING" 7 | } 8 | ], 9 | "switchActivityInterval": 30000, 10 | 11 | "status": "online" 12 | }, 13 | 14 | "info": { 15 | "guildID": "", 16 | "channelID": "", 17 | "includeBanButton": true/false 18 | }, 19 | 20 | "verified": { 21 | "notifyUser": true/false, 22 | "setNickname": true/false, 23 | "setRole": true/false, 24 | 25 | "roleID": "", 26 | "nicknamePrefix": "" 27 | }, 28 | 29 | "colours": { 30 | "verificationEmbed": "", 31 | "invalidVerificationEmbed": "", 32 | "acceptedEmbed": "", 33 | "declinedEmbed": "", 34 | "banEmbed": "" 35 | } 36 | } -------------------------------------------------------------------------------- /events/ready.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client) => { 2 | //Online message 3 | console.log(`${client.user.tag} is online.`) 4 | console.log(`${client.guilds.cache.size} servers`) 5 | console.log(`${client.guilds.cache.reduce((a, c) => a + c.memberCount, 0)} users`) 6 | 7 | //Set first status 8 | try { 9 | client.user.setPresence({ 10 | activities: [client.random(client.config.presence.activities)], 11 | status: client.config.presence.status 12 | }) 13 | } 14 | catch (error) { 15 | console.log('[Status Error] Could not set status') 16 | } 17 | 18 | //Set status each hour 19 | while (true) { 20 | await client.wait(client.config.presence.switchActivityInterval) 21 | 22 | //Set status 23 | try { 24 | client.user.setPresence({ 25 | activities: [client.random(client.config.presence.activities)], 26 | status: client.config.presence.status 27 | }) 28 | } 29 | catch (error) { 30 | console.log('[Status Error] Could not set status') 31 | } 32 | } 33 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecation Notice 2 | As of April 2023, this project has been discontinued. The underlying verification package has not been updated by me in a very long time and is non-functional in most cases. 3 | 4 | --- 5 | 6 | # ManualVerification 7 | 8 | 9 | ## About 10 | ManualVerification is a customizable self-hosted bot that provides a gateway for your Roblox based Discord server. 11 | You can view the user's linked account and other information and choose if you want to accept/decline the verification request. 12 | 13 | 14 | ## Features 15 | * Utilizes Discord interactions (no commands required) 16 | * Provides detailed user information (including account age) 17 | * Optional ban option 18 | * Completely customizable 19 | * Easy to setup 20 | * Configuration error-checking 21 | 22 | 23 | ## Download 24 | 25 | ### Stable 26 | Navigate to the [ManualVerification Releases](https://github.com/SoCuul/ManualVerification/releases/) and download the source code from the latest non pre-release version. 27 | 28 | ### Development 29 | If you would like to test out the newest (possibly not stable) version of the bot, you can download the development version [here](https://github.com/SoCuul/ManualVerification/archive/refs/heads/main.zip). 30 | 31 | 32 | ## Pre-Setup 33 | Make sure you have a bot application created on the [Discord Developer Portal](https://discord.com/developers/applications) and that the bot has the **Server Members Intent**. 34 | 35 | 36 | ## Main Setup 37 | 38 | ### Prerequisites 39 | You will need to have the latest LTS version of [node.js](https://nodejs.org/en/download/). After node.js is installed, run `npm install` in the project folder. 40 | 41 | ### Configuration 42 | **sample.env** - Rename file to `.env` and paste in a Discord bot token next to **TOKEN=**. 43 | 44 | **sample-config.json** - Rename file to `config.json` and customize the configuration settings to your liking. Note: A colour string must be a valid HEX/Discord.JS colour. 45 | 46 | ### Startup 47 | Run `node .` or one of the start scripts to run this bot. You can also use a process manager like [pm2](https://pm2.keymetrics.io/) to manage the bot. 48 | 49 | 50 | ## Replit Setup 51 | 52 | ### Info 53 | Hosting with replit is an alternative method to host ManualVerification. 54 | 55 | ### Step 1: Cloning the repo 56 | Click [this link](https://repl.it/github/socuul/manualverification) to create a new ManualVerification replit project. 57 | 58 | ### Step 2: Configuration 59 | **ENV** - Go to the sidebar and click **Secrets**. Create a new secret with a key of `TOKEN` and a value of your Discord bot token. 60 | 61 | **sample-config.json** - Rename file to `config.json` and customize the configuration settings to your liking. Note: A colour string must be a valid HEX/Discord.JS colour. 62 | 63 | ### Step 3: Webserver 64 | The webserver allows the bot to stay online 24/7. 65 | First, create a file called `modules.js`. In this file, put `module.exports = () => require('http').createServer((req, res) => res.end('ManualVerification Webserver')).listen(3000)`. 66 | 67 | ### Step 4: Pinging the webserver 68 | Follow this guide to learn how to ping your webserver to keep your bot online 24/7. 69 | Guide: https://replit.com/talk/learn/How-to-use-and-setup-UptimeRobot/9003 70 | 71 | ### Step 5: Installing dependencies 72 | Run the following command in the shell section of your terminal before starting your bot for the first time to install the required backend software: `npm i --save-dev node@16` 73 | 74 | ### Step 6: Startup 75 | You can now click the **Run** button to start up your bot. If the UptimeRobot pinging has been set-up successfully, your bot should be online 24/7. 76 | 77 | 78 | ## Contributing 79 | Contributions to this project are gladly appreciated. 80 | If you would like to add something to ManualVerification, create a pull request. 81 | 82 | 83 | ## Support 84 | If you need any help with this bot, you can join my [discord server](https://discord.com/invite/AY7WHt4Nrw). 85 | -------------------------------------------------------------------------------- /events/guildMemberAdd.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js') 2 | const rblxverify = require('rblxverify') 3 | 4 | module.exports = async (client, member) => { 5 | //Filter incorrect guilds 6 | if (member.guild.id !== client.config.info.guildID) return 7 | 8 | //Main variables 9 | let guild 10 | let channel 11 | 12 | //Get guild 13 | try { 14 | guild = await client.guilds.fetch(client.config.info.guildID) 15 | } 16 | catch (error) { 17 | return console.log('[Discord Error] Could not retrieve verification guild') 18 | } 19 | 20 | //Get channel 21 | try { 22 | if (guild.channels.cache.some(c => c.id === client.config.info.channelID)) { 23 | channel = await guild.channels.fetch(client.config.info.channelID) 24 | } 25 | else { 26 | return console.log('[Discord Error] Could not find verification channel') 27 | } 28 | } 29 | catch (error) { 30 | return console.log('[Discord Error] Could not retrieve verification channel') 31 | } 32 | 33 | //Get user verification 34 | let verifyInfo 35 | try { 36 | verifyInfo = await rblxverify.rover(member.id) 37 | } 38 | catch (error) { 39 | return console.log(`[Verify Error] There was an error verifying user: ${member.id}`) 40 | } 41 | 42 | //Check for valid verification 43 | if (verifyInfo.status !== 'success') { 44 | const embed = new Discord.MessageEmbed() 45 | .setColor('RED') 46 | .setTitle('Verification Error') 47 | .setDescription(`There was an error retrieving your verification information.\n\nPlease make sure you are verified at https://rover.link/login/\nIf you have already verified, try rejoining this discord server in a few minutes.`) 48 | .setFooter(member.guild.name, member.guild.iconURL({ dynamic: true })) 49 | .setTimestamp() 50 | 51 | //Notify user 52 | try { 53 | await member.send({ 54 | embeds: [ embed ] 55 | }) 56 | } 57 | catch (error) {} 58 | 59 | //Kick user 60 | try { 61 | await member.kick('User is not verified with RoVer') 62 | } 63 | catch (error) {} 64 | 65 | return 66 | } 67 | if (!verifyInfo.robloxID || !verifyInfo.robloxUsername) { 68 | const embed = new Discord.MessageEmbed() 69 | .setColor('RED') 70 | .setTitle('Verification Error') 71 | .setDescription(`There was information missing from the verification request.\n\nTry to re-verify at https://rover.link/login/\nOnce you have re-verified, try rejoining this discord server in a few minutes.`) 72 | .setFooter(member.guild.name, member.guild.iconURL({ dynamic: true })) 73 | .setTimestamp() 74 | 75 | //Notify user 76 | try { 77 | await member.send({ 78 | embeds: [ embed ] 79 | }) 80 | } 81 | catch (error) {} 82 | 83 | //Kick user 84 | try { 85 | await member.kick('User is not verified with RoVer') 86 | } 87 | catch (error) {} 88 | 89 | return 90 | } 91 | 92 | try { 93 | //Create verification embed 94 | const embed = new Discord.MessageEmbed() 95 | .setColor(client.config.colours.verificationEmbed) 96 | .setTitle('Verification Request') 97 | .setThumbnail(`https://www.roblox.com/Thumbs/Avatar.ashx?x=600&y=600&Format=Png&userid=${verifyInfo.robloxID}`) 98 | .addField('Discord Account', `**${member.user.tag}** (${member.id})`) 99 | .addField('Roblox Account', `[**${verifyInfo.robloxUsername}**](https://www.roblox.com/users/${verifyInfo.robloxID}/profile) (${verifyInfo.robloxID})`) 100 | .addField('Account Age', ``) 101 | .setFooter(member.guild.name, member.guild.iconURL({ dynamic: true })) 102 | .setTimestamp() 103 | 104 | //Create verification buttons 105 | const buttons = new Discord.MessageActionRow() 106 | .addComponents( 107 | new Discord.MessageButton() 108 | .setCustomId(`accept-${member.id}-${verifyInfo.robloxID}-${verifyInfo.robloxUsername}`) 109 | .setLabel('Accept') 110 | .setStyle('SUCCESS'), 111 | new Discord.MessageButton() 112 | .setCustomId(`decline-${member.id}-${verifyInfo.robloxID}-${verifyInfo.robloxUsername}`) 113 | .setLabel('Decline') 114 | .setStyle('PRIMARY') 115 | ) 116 | if (client.config.info.includeBanButton) buttons.addComponents( 117 | new Discord.MessageButton() 118 | .setCustomId(`ban-${member.id}-${verifyInfo.robloxID}-${verifyInfo.robloxUsername}`) 119 | .setLabel('Ban') 120 | .setStyle('DANGER') 121 | ) 122 | 123 | //Send verification message 124 | await channel.send({ 125 | embeds: [ embed ], 126 | components: [ buttons ] 127 | }) 128 | 129 | if (client.config.verified.notifyUser) { 130 | try { 131 | //Create notify embed 132 | const notifyEmbed = new Discord.MessageEmbed() 133 | .setColor(client.config.colours.verificationEmbed) 134 | .setTitle('Verification Pending') 135 | .setDescription('Please wait for a staff member to accept you into the server.') 136 | .setThumbnail(`https://www.roblox.com/Thumbs/Avatar.ashx?x=600&y=600&Format=Png&userid=${verifyInfo.robloxID}`) 137 | .addField('Roblox Account', `[**${verifyInfo.robloxUsername}**](https://www.roblox.com/users/${verifyInfo.robloxID}/profile)`) 138 | .setFooter(member.guild.name, member.guild.iconURL({ dynamic: true })) 139 | .setTimestamp() 140 | 141 | //Send notify message 142 | await member.send({ 143 | embeds: [ notifyEmbed ] 144 | }) 145 | } 146 | catch (error) { 147 | return 148 | } 149 | } 150 | } 151 | catch (error) { 152 | return console.log(`[Discord Error] Could not send verification message for user: ${member.id}`) 153 | } 154 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const Discord = require('discord.js'); 4 | const client = new Discord.Client({ 5 | intents: [ 6 | Discord.Intents.FLAGS.GUILDS, 7 | Discord.Intents.FLAGS.GUILD_MEMBERS 8 | ], 9 | allowedMentions: { repliedUser: false } 10 | }); 11 | 12 | require('dotenv').config(); 13 | 14 | //Attach to client 15 | client.config = require('./config.json'); 16 | client.wait = ms => new Promise(resolve => setTimeout(resolve, ms)); 17 | client.random = arr => arr[Math.floor(Math.random() * arr.length)] 18 | client.sendError = function (input) { 19 | if(!input) return 20 | 21 | return new Discord.MessageEmbed() 22 | .setColor('RED') 23 | .setTitle('Error') 24 | .setDescription(input) 25 | .setFooter(client.user.username, client.user.avatarURL({ dynamic: true })) 26 | } 27 | client.validColour = async (colour) => { 28 | try { 29 | await Discord.Util.resolveColor(colour) 30 | 31 | return true 32 | } 33 | catch (error) { 34 | return false 35 | } 36 | } 37 | 38 | //Load modules 39 | if(fs.existsSync('./modules.js')){ 40 | require('./modules.js')(client) 41 | } 42 | 43 | //Check config configuration 44 | (async () => { 45 | if(client.config){ 46 | //Presence 47 | if (typeof client.config.presence !== 'object' || Array.isArray(client.config.presence)) { 48 | console.log('[Config Error] "presence" must be an object') 49 | process.exit(0) 50 | } 51 | //Presence: Activities 52 | if (typeof client.config.presence.activities !== 'object' || !Array.isArray(client.config.presence.activities)) { 53 | console.log('[Config Error] "presence.activities" must be an array') 54 | process.exit(0) 55 | } 56 | //Presence: Switch Activity Interval 57 | if (typeof client.config.presence.switchActivityInterval !== 'number') { 58 | console.log('[Config Error] "presence.switchActivityInterval" must be an number') 59 | process.exit(0) 60 | } 61 | //Presence: Status 62 | if (typeof client.config.presence.status !== 'string') { 63 | console.log('[Config Error] "presence.status" must be an string') 64 | process.exit(0) 65 | } 66 | 67 | //Info 68 | if (typeof client.config.info !== 'object' || Array.isArray(client.config.info)) { 69 | console.log('[Config Error] "info" must be an object') 70 | process.exit(0) 71 | } 72 | //Info: Guild ID 73 | if (typeof client.config.info.guildID !== 'string') { 74 | console.log('[Config Error] "info.guildID" must be a string') 75 | process.exit(0) 76 | } 77 | //Info: Channel ID 78 | if (typeof client.config.info.channelID !== 'string') { 79 | console.log('[Config Error] "info.channelID" must be a string') 80 | process.exit(0) 81 | } 82 | //Info: Include Ban Button 83 | if (typeof client.config.info.includeBanButton !== 'boolean') { 84 | console.log('[Config Error] "info.includeBanButton" must be a boolean') 85 | process.exit(0) 86 | } 87 | 88 | //Verified 89 | if (typeof client.config.verified !== 'object' || Array.isArray(client.config.verified)) { 90 | console.log('[Config Error] "verified" must be an object') 91 | process.exit(0) 92 | } 93 | //Verified: Notify User 94 | if (typeof client.config.verified.notifyUser !== 'boolean') { 95 | console.log('[Config Error] "verified.notifyUser" must be a boolean') 96 | process.exit(0) 97 | } 98 | //Verified: Set Nickname Toggle 99 | if (typeof client.config.verified.setNickname !== 'boolean') { 100 | console.log('[Config Error] "verified.setNickname" must be a boolean') 101 | process.exit(0) 102 | } 103 | //Verified: Set Role Toggle 104 | if (typeof client.config.verified.setRole !== 'boolean') { 105 | console.log('[Config Error] "verified.setRole" must be a boolean') 106 | process.exit(0) 107 | } 108 | //Verified: Role ID 109 | if (typeof client.config.verified.setRole === true && typeof client.config.verified.roleID !== 'string') { 110 | console.log('[Config Error] "verified.roleID" must be a string') 111 | process.exit(0) 112 | } 113 | //Presence: Nickname prefix 114 | if (typeof client.config.verified.setNickname === true && client.config.verified.nicknamePrefix !== 'string') { 115 | console.log('[Config Error] "verified.nicknamePrefix" must be a string') 116 | process.exit(0) 117 | } 118 | 119 | //Colours 120 | if (typeof client.config.colours !== 'object' || Array.isArray(client.config.colours)) { 121 | console.log('[Config Error] "colours" must be an object') 122 | process.exit(0) 123 | } 124 | //Colours: Verification Embed 125 | if (!await client.validColour(client.config.colours.verificationEmbed)) { 126 | console.log('[Config Error] "colours.verificationEmbed" must be a valid colour') 127 | process.exit(0) 128 | } 129 | //Colours: Invalid Verification Embed 130 | if (!await client.validColour(client.config.colours.invalidVerificationEmbed)) { 131 | console.log('[Config Error] "colours.invalidVerificationEmbed" must be a valid colour') 132 | process.exit(0) 133 | } 134 | //Colours: Success Embed 135 | if (!await client.validColour(client.config.colours.successEmbed)) { 136 | console.log('[Config Error] "colours.successEmbed" must be a valid colour') 137 | process.exit(0) 138 | } 139 | //Colours: Failure Embed 140 | if (!await client.validColour(client.config.colours.failureEmbed)) { 141 | console.log('[Config Error] "colours.failureEmbed" must be a valid colour') 142 | process.exit(0) 143 | } 144 | //Colours: Ban Embed 145 | if (!await client.validColour(client.config.colours.banEmbed)) { 146 | console.log('[Config Error] "colours.banEmbed" must be a valid colour') 147 | process.exit(0) 148 | } 149 | } 150 | })() 151 | 152 | //Load events 153 | fs.readdir('./events/', (_err, files) => { 154 | files.forEach(file => { 155 | if (!file.endsWith('.js')) return; 156 | 157 | const event = require(`./events/${file}`); 158 | let eventName = file.split('.')[0]; 159 | 160 | client.on(eventName, event.bind(null, client)); 161 | console.log(`Event loaded: ${eventName}`); 162 | }); 163 | }); 164 | 165 | //Client Login 166 | try{ 167 | client.login(process.env.TOKEN) 168 | } 169 | catch(error){ 170 | console.log('[Error] Could not login. Please make sure the token is valid.') 171 | process.exit(1) 172 | } -------------------------------------------------------------------------------- /events/interactionCreate.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js') 2 | function truncateString(str, num) { 3 | if (str.length <= num) { 4 | return str 5 | } 6 | return str.slice(0, num) + '...' 7 | } 8 | 9 | module.exports = async (client, i) => { 10 | //Filter out non-commands 11 | if (!i.isButton()) return 12 | 13 | //Respond to non-guild commands 14 | if (!i.inGuild()) return await i.reply('You can only use commands in servers.') 15 | 16 | //Check for incorrect guild 17 | if (i.guild.id !== client.config.info.guildID) return 18 | 19 | //Parse button data 20 | const [ action, discordID, robloxID, robloxUsername ] = i.customId.split('-') 21 | 22 | //Check for valid button data 23 | if (!action || !discordID || !robloxID || !robloxUsername) return await i.update({ 24 | content: i.message.content || null 25 | }) 26 | 27 | //Deal with buttons 28 | switch (action) { 29 | case 'test': { 30 | //Defer response 31 | await i.deferReply({ 32 | ephemeral: true 33 | }) 34 | 35 | //Emit test event 36 | await client.emit('guildMemberAdd', i.member) 37 | 38 | //Send reply 39 | return await i.editReply({ 40 | content: '✅ Event emitted', 41 | ephemeral: true 42 | }) 43 | } 44 | case 'accept': { 45 | try { 46 | let errors = [] 47 | 48 | //Fetch member 49 | let member 50 | try { 51 | member = await i.guild.members.fetch(discordID) 52 | } 53 | catch (error) { 54 | const embed = new Discord.MessageEmbed() 55 | .setColor(client.config.colours.invalidVerificationEmbed) 56 | .setTitle('Invalid Verification') 57 | .setDescription('The user has probably left the server. Because of this, the verification could not be accepted.') 58 | .setTimestamp() 59 | return await i.update({ 60 | embeds: [ embed ] 61 | }) 62 | } 63 | 64 | //Set nickname 65 | if (client.config.verified.setNickname) { 66 | try { 67 | await member.setNickname( 68 | client.config.verified.nicknamePrefix.length ? `${client.config.verified.nicknamePrefix}${robloxUsername}` : robloxUsername.toString() 69 | , 'Manual Verification Accepted') 70 | } 71 | catch (error) { 72 | errors.push('Could not set nickname.') 73 | } 74 | } 75 | 76 | //Give role 77 | if (client.config.verified.setRole) { 78 | if (i.guild.roles.cache.some(r => r.id === client.config.verified.roleID)) { 79 | try { 80 | await member.roles.add(client.config.verified.roleID, 'Manual Verification Accepted') 81 | } 82 | catch (error) { 83 | errors.push('Could not give role.') 84 | } 85 | } 86 | else { 87 | errors.push('Could not find role in server.') 88 | } 89 | } 90 | 91 | //Notify user 92 | if (client.config.verified.notifyUser) { 93 | try { 94 | const notifyEmbed = new Discord.MessageEmbed() 95 | .setColor(client.config.colours.acceptedEmbed) 96 | .setTitle('Verification Accepted') 97 | .setDescription('Your verification request has been accepted.') 98 | .setThumbnail(`https://www.roblox.com/Thumbs/Avatar.ashx?x=600&y=600&Format=Png&userid=${robloxID}`) 99 | .addField('Roblox Account', `[**${robloxUsername}**](https://www.roblox.com/users/${robloxID}/profile)`) 100 | .setFooter(i.guild.name, i.guild.iconURL({ dynamic: true })) 101 | .setTimestamp() 102 | await member.send({ 103 | embeds: [ notifyEmbed ] 104 | }) 105 | } 106 | catch (error) { 107 | errors.push('Could not notify user.') 108 | } 109 | } 110 | 111 | //Modify Embed 112 | const embed = new Discord.MessageEmbed() 113 | .setColor(client.config.colours.acceptedEmbed) 114 | .setTitle('Verification Accepted') 115 | .setDescription(errors.length ? 'There were some problems verifying the member.' : 'The member has been verified.') 116 | .setThumbnail(`https://www.roblox.com/Thumbs/Avatar.ashx?x=600&y=600&Format=Png&userid=${robloxID}`) 117 | .addField('Discord Account', `**${member.user.tag}** (${member.id})`) 118 | .addField('Roblox Account', `[**${robloxUsername}**](https://www.roblox.com/users/${robloxID}/profile) (${robloxID})`) 119 | .addField('Account Age', ``) 120 | .setTimestamp() 121 | if (errors.length) embed.addField('Errors', errors.join('\n')) 122 | return await i.update({ 123 | embeds: [ embed ], 124 | components: [] 125 | }) 126 | } 127 | catch (error) { 128 | //Log error 129 | console.log(`[Accept Error] ${error}`) 130 | 131 | //Send response 132 | return await i.reply({ 133 | content: '❌ There was an error accepting the user.\nPlease try again later.' 134 | }) 135 | } 136 | } 137 | case 'decline': { 138 | try { 139 | let errors = [] 140 | 141 | //Fetch member 142 | let member 143 | try { 144 | member = await i.guild.members.fetch(discordID) 145 | } 146 | catch (error) { 147 | const embed = new Discord.MessageEmbed() 148 | .setColor(client.config.colours.invalidVerificationEmbed) 149 | .setTitle('Invalid Verification') 150 | .setDescription('The user has probably left the server. Because of this, the verification could not be declined.') 151 | .setTimestamp() 152 | return await i.update({ 153 | embeds: [ embed ] 154 | }) 155 | } 156 | 157 | //Notify user 158 | if (client.config.verified.notifyUser) { 159 | try { 160 | const notifyEmbed = new Discord.MessageEmbed() 161 | .setColor(client.config.colours.declinedEmbed) 162 | .setTitle('Verification Declined') 163 | .setDescription('Your verification request has been declined.\nYou have been removed from the server.') 164 | .setThumbnail(`https://www.roblox.com/Thumbs/Avatar.ashx?x=600&y=600&Format=Png&userid=${robloxID}`) 165 | .addField('Roblox Account', `[**${robloxUsername}**](https://www.roblox.com/users/${robloxID}/profile)`) 166 | .setFooter(i.guild.name, i.guild.iconURL({ dynamic: true })) 167 | .setTimestamp() 168 | await member.send({ 169 | embeds: [ notifyEmbed ] 170 | }) 171 | } 172 | catch (error) { 173 | errors.push('Could not notify user.') 174 | } 175 | } 176 | 177 | //Kick user 178 | try { 179 | await member.kick('Manual Verification Declined') 180 | } 181 | catch (error) { 182 | errors.push('Could not kick user.') 183 | } 184 | 185 | //Modify Embed 186 | const embed = new Discord.MessageEmbed() 187 | .setColor(client.config.colours.declinedEmbed) 188 | .setTitle('Verification Declined') 189 | .setDescription(errors.length ? 'There were some problems declining the member\'s verification.' : 'The member\'s verification has been declined.') 190 | .setThumbnail(`https://www.roblox.com/Thumbs/Avatar.ashx?x=600&y=600&Format=Png&userid=${robloxID}`) 191 | .addField('Discord Account', `**${member.user.tag}** (${member.id})`) 192 | .addField('Roblox Account', `[**${robloxUsername}**](https://www.roblox.com/users/${robloxID}/profile) (${robloxID})`) 193 | .addField('Account Age', ``) 194 | .setTimestamp() 195 | if (errors.length) embed.addField('Errors', errors.join('\n')) 196 | return await i.update({ 197 | embeds: [ embed ], 198 | components: [] 199 | }) 200 | } 201 | catch (error) { 202 | //Log error 203 | console.log(`[Decline Error] ${error}`) 204 | 205 | //Send response 206 | return await i.reply({ 207 | content: '❌ There was an error declining the user.\nPlease try again later.' 208 | }) 209 | } 210 | } 211 | case 'ban': { 212 | try { 213 | let errors = [] 214 | 215 | //Check for permission 216 | if (!i.member.permissions.has(Discord.Permissions.FLAGS.BAN_MEMBERS)) { 217 | const embed = new Discord.MessageEmbed() 218 | .setColor('RED') 219 | .setTitle('Missing Permissions') 220 | .setDescription('You have to have the **Ban Members** permission to do that!') 221 | return await i.reply({ 222 | embeds: [ embed ], 223 | ephemeral: true 224 | }) 225 | } 226 | 227 | //Fetch member 228 | let member 229 | try { 230 | member = await i.guild.members.fetch(discordID) 231 | } 232 | catch (error) { 233 | const embed = new Discord.MessageEmbed() 234 | .setColor('BLUE') 235 | .setTitle('Invalid Verification') 236 | .setDescription('The user has probably left the server. Because of this, the user could not be banned.') 237 | .addField('Discord Account', `**${member.user.tag}** (${member.id})`) 238 | .addField('Roblox Account', `[**${robloxUsername}**](https://www.roblox.com/users/${robloxID}/profile) (${robloxID})`) 239 | .addField('Account Age', ``) 240 | .setTimestamp() 241 | return await i.update({ 242 | embeds: [ embed ] 243 | }) 244 | } 245 | 246 | //Ban user 247 | if (client.config.verified.notifyUser) { 248 | try { 249 | await member.ban({ reason: 'Manual Verification Ban' }) 250 | } 251 | catch (error) { 252 | errors.push('Could not ban user.') 253 | } 254 | } 255 | 256 | //Modify Embed 257 | const embed = new Discord.MessageEmbed() 258 | .setColor(client.config.colours.banEmbed) 259 | .setTitle('Verification Ban') 260 | .setDescription(errors.length ? 'There were some problems banning the user.' : 'The user has been successfully banned.') 261 | .addField('Discord Account', `**${member.user.tag}** (${member.id})`) 262 | .addField('Roblox Account', `[**${robloxUsername}**](https://www.roblox.com/users/${robloxID}/profile) (${robloxID})`) 263 | .addField('Account Age', ``) 264 | .setTimestamp() 265 | if (errors.length ) embed.addField('Errors', errors.join('\n')) 266 | return await i.update({ 267 | embeds: [ embed ], 268 | components: [] 269 | }) 270 | } 271 | catch (error) { 272 | //Log error 273 | console.log(`[Ban Error] ${error}`) 274 | 275 | //Send response 276 | return await i.reply({ 277 | content: '❌ There was an error banning the user.\nPlease try again later.' 278 | }) 279 | } 280 | } 281 | default: { 282 | return await i.update({ 283 | content: i.message.content || null 284 | }) 285 | } 286 | } 287 | }; --------------------------------------------------------------------------------