├── LICENSE ├── README.md ├── Structures └── legendJsClient.js ├── commands ├── AntiAlts │ └── fetch-alts.js ├── info │ └── help.js └── verification │ ├── bypass.js │ ├── config.js │ └── verify.js ├── config.js ├── dashboard ├── assets │ ├── css │ │ ├── 404.css │ │ ├── Styles.css │ │ ├── dashboard.css │ │ ├── message.css │ │ ├── settings.css │ │ └── verify.css │ ├── images │ │ └── EABCE74A-CCDB-4F59-B80D-454EA66C1D49.png │ └── js │ │ └── script.js ├── index.js └── templates │ ├── 404.ejs │ ├── dashboard.ejs │ ├── index.ejs │ ├── message.ejs │ ├── partials │ ├── Navbar.ejs │ ├── general.ejs │ └── truncate.ejs │ ├── settings.ejs │ └── verify.ejs ├── index.js └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | NOTE 2 | 3 | Collaborator ( ZeroDiscord ) / alias. Zero [ Legal Associate - Contact ~ 0_0#6666 { Discord } ] alongwith the copyright holder have the discretion as to submit takedown requests upon any material that is possibly infringing 4 | the license holder's rights. This includes the following: 5 | - Commercial use 6 | - Modification & Distribution Under A Trademark Without Explicit Permit 7 | - Patent use 8 | - Warranty & Liability ( States The Code Owner Would Not Be Held Responsible In Cases of Unprecidented actions such as raids. ) 9 | All claims upon infringing material may route via DMCA Strikes 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Verification Bot 2 | an Advanced Verification Bot for Discord, has dashboard, online captcha etc. 3 | # Info & Credits 4 | - This bot was made by [legendjs#0001](https://github.com/legend-js-dev) 5 | - If you use this code and / or make a video on it without credits, your video/bot will be taken down 6 | - it took me 11 days to make this bot so please support me and consider joining my Discord Server 7 | - the bot might still have some bugs so if you find some please report them to me 8 | # How to run the bot 9 | - Go to `config.js` 10 | - add your stuff there 11 | - `npm init -y` 12 | - `npm install` 13 | - `node .` 14 | - Enjoy! 15 | # Services 16 | - if you want to hire me, you may dm me on Discord 17 | # Steps To Support me 18 | - Join my Discord Server ([ Developers Hub](https://discord.gg/gZnw8M3HCE)) 19 | - boost if you can, i am trying to reach 30 boosts 20 | - Star This repo 21 | - Fork This repo 22 | - if you're rich, you can send me a nitro in my dms 23 | - anything done to support me is appriciated 24 | -------------------------------------------------------------------------------- /Structures/legendJsClient.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'), 2 | { Client, Collection } = require('discord.js'), 3 | { readdirSync } = require('fs'), 4 | { prefix } = require('../config').bot, 5 | ms = require('ms'); 6 | 7 | module.exports = class legendJsClient extends Client { 8 | constructor(options) { 9 | super(options); 10 | this.commands = new Collection(); 11 | this.aliases = new Collection(); 12 | this.prefix = prefix; 13 | this.config = require('../config.js'); 14 | } 15 | 16 | loadCommands() { 17 | const client = this; 18 | readdirSync('./commands/').forEach(dir => { 19 | const commands = readdirSync(`./commands/${dir}/`).filter(file => 20 | file.endsWith('.js') 21 | ); 22 | for (let file of commands) { 23 | let pull = require(`../commands/${dir}/${file}`); 24 | 25 | if (pull.name) { 26 | client.commands.set(pull.name, pull); 27 | console.log(`[${pull.name.toUpperCase()}]: loaded!`); 28 | } else { 29 | console.log(`[${file.toUpperCase()}]: Error`); 30 | continue; 31 | } 32 | 33 | if (pull.aliases && Array.isArray(pull.aliases)) 34 | pull.aliases.forEach(alias => client.aliases.set(alias, pull.name)); 35 | } 36 | }); 37 | console.log('-------------------------------------'); 38 | console.log('[INFO]: Commands Loaded!'); 39 | } 40 | 41 | parseMs(str) { 42 | const parts = str.split(' '); 43 | const msParts = parts.map(part => ms(part)); 44 | if (msParts.includes(undefined)) return undefined; 45 | const res = msParts.reduce((a, b) => a + b); 46 | return res; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /commands/AntiAlts/fetch-alts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'fetch-alts', 3 | run: async (client, message, args) => { 4 | const Discord = require('discord.js'); 5 | const members = await message.guild.members.fetch(); 6 | const age = args.join(' '); 7 | if (!age) return message.channel.send(`:x: | **No age provided**`) 8 | const ms = client.parseMs(age); 9 | if (!ms) return message.channel.send(':x: | **Invalid age provided.**'); 10 | const alts = members 11 | .filter(member => Date.now() - member.user.createdTimestamp <= ms) 12 | .array(); 13 | if (!alts.length) return message.channel.send(":x: | **No alts found**"); 14 | const altsMap = alts.map((x, i) => { 15 | return `**${i + 1}. - ${x.id} - ${Math.round( 16 | (Date.now() - x.user.createdTimestamp) / client.parseMs('1d') 17 | )} days**\n${x.user.tag}`; 18 | }); 19 | let pages = []; 20 | for (var i = 0; i < altsMap.length; i += 10) { 21 | pages.push(altsMap.slice(i, i + 10)); 22 | } 23 | const symbols = ['➡️', '⏹', '⬅️']; 24 | let page = 0; 25 | let e = new Discord.MessageEmbed() 26 | .setDescription(pages[page].join('\n')) 27 | .setFooter(`Page ${page + 1} of ${pages.length} (${alts.length} entries)` + " | Made by ant#0768 & legendjs#0001") 28 | .setColor('RANDOM'); 29 | const msg = await message.channel.send({ embed: e }); 30 | symbols.forEach(symbol => msg.react(symbol)); 31 | let doing = true; 32 | while (doing) { 33 | let r; 34 | const filter = (r, u) => 35 | symbols.includes(r.emoji.name) && u.id == message.author.id; 36 | try { 37 | r = await msg.awaitReactions(filter, { 38 | max: 1, 39 | time: 20000, 40 | errors: ['time'] 41 | }); 42 | } catch { 43 | return message.channel.send(':x: | **Command timed out.**'); 44 | } 45 | const u = message.author; 46 | r = r.first(); 47 | if (r.emoji.name == symbols[0]) { 48 | if (!pages[page + 1]) 49 | msg.reactions 50 | .resolve(r.emoji.name) 51 | .users.remove(u.id) 52 | .catch(err => {}); 53 | else { 54 | page++; 55 | msg.reactions 56 | .resolve(r.emoji.name) 57 | .users.remove(u.id) 58 | .catch(err => {}); 59 | let newEmbed = new Discord.MessageEmbed() 60 | .setDescription(pages[page].join('\n')) 61 | .setFooter( 62 | `Page ${page + 1} of ${pages.length} (${alts.length} entries)` + " | Made by ant#0768 & LΣGΣПD#0001" 63 | ) 64 | .setColor('RANDOM'); 65 | msg.edit(newEmbed); 66 | } 67 | } else if (r.emoji.name == symbols[2]) { 68 | if (!pages[page - 1]) 69 | msg.reactions 70 | .resolve(r.emoji.name) 71 | .users.remove(u.id) 72 | .catch(err => {}); 73 | else { 74 | page--; 75 | msg.reactions 76 | .resolve(r.emoji.name) 77 | .users.remove(u.id) 78 | .catch(err => {}); 79 | let newEmbed = new Discord.MessageEmbed() 80 | .setDescription(pages[page].join('\n')) 81 | .setFooter( 82 | `Page ${page + 1} of ${pages.length} (${alts.length} entries)` + " | Made by ant#0768 & LΣGΣПD#0001" 83 | ) 84 | .setColor('RANDOM'); 85 | msg.edit(newEmbed); 86 | } 87 | } else if (r.emoji.name == symbols[1]) { 88 | msg.reactions.removeAll(); 89 | return; 90 | } 91 | } 92 | } 93 | }; 94 | -------------------------------------------------------------------------------- /commands/info/help.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | 3 | module.exports = { 4 | name: 'help', 5 | run: async (client, message, args) => { 6 | let embed = new Discord.MessageEmbed() 7 | .setTitle(`${client.user.username} | Help`) 8 | .setDescription(`The commands are listed below`) 9 | .addField(`Anti Alts`, '`fetch-alts`') 10 | .addField(`Verification`, '`bypass` | `config` | `verify`') 11 | .setThumbnail(client.user.displayAvatarURL()) 12 | .setColor('RANDOM') 13 | .setFooter(client.user.username + ' | made by legendjs#0001', client.user.displayAvatarURL()); 14 | return message.channel.send({ embed: embed }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /commands/verification/bypass.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'bypass', 3 | run: async (client, message, args, db) => { 4 | if (!message.channel.permissionsFor(message.author).has('MANAGE_GUILD')) 5 | return message.channel.send( 6 | ':x: | **You dont have permissions to use this Command!**' 7 | ); 8 | let options = ['add', 'remove']; 9 | function check(opt) { 10 | return options.some(x => x === opt); 11 | } 12 | async function fetchUser(ID) { 13 | let user = await client.users.fetch(ID); 14 | return user; 15 | } 16 | async function checkUser(ID) { 17 | let user = await fetchUser(ID); 18 | if (!user) return false; 19 | else return true; 20 | } 21 | let option = args[0]; 22 | let ID = 23 | args[1] || message.mentions.users.first() 24 | ? message.mentions.users.first().id 25 | : null; 26 | if (!option) 27 | return message.channel.send( 28 | `:x: | **The option must be one of ${options.join(', ')}**` 29 | ); 30 | if (!ID) 31 | return message.channel.send( 32 | `:x: | **The ID / mention is a required argument**` 33 | ); 34 | if (!check(option.toLowerCase())) 35 | return message.channel.send( 36 | `:x: | **The option arugument must be one of ${options.join(', ')}**` 37 | ); 38 | switch (option.toLowerCase()) { 39 | case 'add': 40 | if (!checkUser(ID)) 41 | return message.channel.send(`:x: | **The user doesnt exist**`); 42 | else { 43 | let role = message.guild.roles.cache.get( 44 | db.get(`role_${message.guild.id}`) 45 | ); 46 | if (role && message.guild.members.cache.get(ID)) { 47 | message.guild.members.cache 48 | .get(ID) 49 | .roles.add(role) 50 | .catch(err => {}); 51 | } 52 | let user = await fetchUser(ID); 53 | let pog = db.get(`bypass_${message.guild.id}`) || []; 54 | db.push(`bypass_${message.guild.id}`, { id: user.id }); 55 | let data = pog.find(x => x.id === ID); 56 | if (data) 57 | return message.channel.send( 58 | '**The user is already on the bypass list**' 59 | ); 60 | return message.channel.send( 61 | `${user.tag} has been added to the bypass list` 62 | ); 63 | } 64 | break; 65 | case 'remove': 66 | if (!checkUser(ID)) 67 | return message.channel.send(`:x: | **The user doesnt exist**`); 68 | else { 69 | let role = message.guild.roles.cache.get( 70 | db.get(`role_${message.guild.id}`) 71 | ); 72 | if (role && message.guild.members.cache.get(ID)) { 73 | message.guild.members.cache 74 | .get(ID) 75 | .roles.remove(role) 76 | .catch(err => {}); 77 | } 78 | let user = await fetchUser(ID); 79 | let pog = db.get(`bypass_${message.guild.id}`) || []; 80 | if (pog) { 81 | let data = pog.find(x => x.id === ID); 82 | if (!data) 83 | return message.channel.send( 84 | '**The user is not on the bypass list**' 85 | ); 86 | let index = pog.indexOf(data); 87 | delete pog[index]; 88 | var filter = pog.filter(x => { 89 | return x != null && x != ''; 90 | }); 91 | db.set(`bypass_${message.guild.id}`, filter); 92 | } 93 | return message.channel.send( 94 | `${user.tag} has been deleted from the bypass list` 95 | ); 96 | } 97 | break; 98 | } 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /commands/verification/config.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | 3 | module.exports = { 4 | name: 'config', 5 | run: async (client, message, args, db) => { 6 | if (!message.channel.permissionsFor(message.author).has('MANAGE_GUILD')) 7 | return message.channel.send( 8 | ':x: | **You dont have permissions to use this Command!**' 9 | ); 10 | let options = ['warningchannel', 'logs', 'punishment', 'role', 'show', 'toggle']; 11 | function check(opt, array) { 12 | return array.some(x => x.toLowerCase() === opt.toLowerCase()); 13 | } 14 | if (!args[0]) { 15 | return message.channel.send( 16 | `:x: | **Specify an option, The options are - ${options.join(', ')}**` 17 | ); 18 | } 19 | if (!check(args[0], options)) { 20 | return message.channel.send( 21 | `:x: | **The only options are ${options.join(', ')}**` 22 | ); 23 | } 24 | let channel = message.mentions.channels.first(); 25 | switch (args[0]) { 26 | case 'warningchannel': 27 | if (!channel) { 28 | return message.channel.send(':x: | **Specify the channel**'); 29 | } 30 | db.set(`warningchannel_${message.guild.id}`, channel.id); 31 | return message.channel.send('**The Warning Channel has been set**'); 32 | break; 33 | case 'logs': 34 | if (!channel) { 35 | return message.channel.send(':x: | **Specify the channel**'); 36 | } 37 | db.set(`logs_${message.guild.id}`, channel.id); 38 | return message.channel.send('**The logs channel has been set**'); 39 | break; 40 | case 'role': 41 | let role = 42 | message.mentions.roles.first() || 43 | message.guild.roles.cache.get(args[1]); 44 | if (!role) { 45 | return message.channel.send(':x: | **Specify the role**'); 46 | } 47 | db.set(`role_${message.guild.id}`, role.id); 48 | return message.channel.send('**The verification rooe has been set**'); 49 | break; 50 | case 'show': 51 | let warningChan = 52 | message.guild.channels.cache.get( 53 | db.get(`warningchannel_${message.guild.id}`) 54 | ) || 'None'; 55 | let logsChan = 56 | message.guild.channels.cache.get( 57 | db.get(`logs_${message.guild.id}`) 58 | ) || 'None'; 59 | let verificationRole = 60 | message.guild.roles.cache.get(db.get(`role_${message.guild.id}`)) || 61 | 'None'; 62 | let punish = db.get(`punishment_${message.guild.id}`) || 'None'; 63 | let embed = new Discord.MessageEmbed() 64 | .setTitle('Configuration') 65 | .setDescription( 66 | 'The configuration for this server is displayed below' 67 | ) 68 | .addField('Punishment', punish) 69 | .addField('Warning Channel', warningChan) 70 | .addField('logs Channel', logsChan) 71 | .addField('Verification Role', verificationRole) 72 | .setColor('RANDOM') 73 | .setFooter( 74 | message.guild.name + ' | made by legendjs#0001', 75 | message.guild.iconURL({ dynamic: true }) 76 | ); 77 | return message.channel.send({ embed: embed }); 78 | break; 79 | case 'punishment': 80 | const punishment = args[1].toLowerCase().trim(); 81 | const punishments = ['kick', 'ban']; 82 | if (!punishment) 83 | return message.channel.send('Please enter a punishment'); 84 | if (!punishments.includes(punishment)) 85 | return message.channel.send( 86 | `The **punishment** argument must be one of these:\n${punishments 87 | .map(x => `**${x}**`) 88 | .join(', ')}` 89 | ); 90 | db.set(`punishment_${message.guild.id}`, punishment); 91 | return message.channel.send( 92 | `The punishment for **${ 93 | message.guild.name 94 | }** has been set to: **${punishment}**` 95 | ); 96 | break; 97 | } 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /commands/verification/verify.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'verify', 3 | run: async (client, message, args, db) => { 4 | let verified = db.get(`verified_${message.guild.id}_${message.author.id}`); 5 | if (!verified) client.emit('guildMemberAdd', message.member); 6 | if (verified) message.channel.send(':x: | **You are already verified!**'); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bot: { 3 | token: 'B0T.T0K3N.H3R3', 4 | prefix: '!', 5 | clientSecret: 'CLIENT_SECRET', 6 | clientID: 'CLIENT_ID' 7 | }, 8 | website: { 9 | protocol: 'https://', 10 | domain: 'domain.name', 11 | port: 8080, 12 | captcha: { 13 | sitekey: 'SITE_KEY', 14 | secretkey: 'SECRET_KEY' 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /dashboard/assets/css/404.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #222222; 3 | } 4 | 5 | .mainbox { 6 | background-color: #222222; 7 | margin: auto; 8 | height: 600px; 9 | width: 600px; 10 | position: relative; 11 | } 12 | 13 | .err { 14 | color: #ffffff; 15 | font-weight: 100; 16 | text-shadow: #333333 1px 1px, #333333 2px 2px, #333333 3px 3px, #333333 4px 4px, #333333 5px 5px, #333333 6px 6px, #333333 7px 7px, #333333 8px 8px; 17 | font-family: 'Nunito Sans', sans-serif; 18 | font-size: 11rem; 19 | position:absolute; 20 | left: 20%; 21 | top: 8%; 22 | } 23 | 24 | .far { 25 | position: absolute; 26 | font-size: 8.5rem; 27 | left: 42%; 28 | top: 15%; 29 | color: #ffffff; 30 | font-weight: 100; 31 | text-shadow: #333333 1px 1px, #333333 2px 2px, #333333 3px 3px, #333333 4px 4px, #333333 5px 5px, #333333 6px 6px, #333333 7px 7px, #333333 8px 8px; 32 | } 33 | 34 | .err2 { 35 | color: #ffffff; 36 | font-family: 'Nunito Sans', sans-serif; 37 | font-size: 11rem; 38 | position:absolute; 39 | left: 68%; 40 | top: 8%; 41 | font-weight: 100; 42 | text-shadow: #333333 1px 1px, #333333 2px 2px, #333333 3px 3px, #333333 4px 4px, #333333 5px 5px, #333333 6px 6px, #333333 7px 7px, #333333 8px 8px; 43 | } 44 | 45 | .msg { 46 | text-align: center; 47 | font-family: 'Nunito Sans', sans-serif; 48 | font-size: 1.6rem; 49 | position:absolute; 50 | left: 16%; 51 | color: #fff; 52 | top: 45%; 53 | width: 75%; 54 | } 55 | 56 | .button { 57 | font-size: 20px; 58 | border-radius: 12px; 59 | margin: 12px; 60 | padding: 12px; 61 | color: #fff; 62 | background-color: #222222; 63 | border: 2px solid #fff; 64 | text-align: center; 65 | } 66 | 67 | .button:hover { 68 | border: 2px solid #222222; 69 | color: #222222; 70 | background-color: #fff; 71 | } 72 | 73 | h1 { 74 | text-align: center; 75 | font-size: 15em; 76 | font-weight: 100; 77 | text-shadow: #333333 1px 1px, #333333 2px 2px, #333333 3px 3px, #333333 4px 4px, #333333 5px 5px, #333333 6px 6px, #333333 7px 7px, #333333 8px 8px; 78 | } 79 | -------------------------------------------------------------------------------- /dashboard/assets/css/Styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap'); 2 | 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | background-color: #222222; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 10px; 16 | } 17 | ::-webkit-scrollbar-track { 18 | background: #f1f1f1; 19 | } 20 | ::-webkit-scrollbar-thumb { 21 | background: #888; 22 | } 23 | ::selection{ 24 | background: rgb(0,123,255,0.3); 25 | } 26 | .content{ 27 | max-width: 1250px; 28 | margin: auto; 29 | padding: 0 30px; 30 | } 31 | .navbar{ 32 | position: fixed; 33 | width: 100%; 34 | z-index: 2; 35 | padding: 25px 0; 36 | transition: all 0.3s ease; 37 | } 38 | .navbar.sticky{ 39 | background: #1b1b1b; 40 | padding: 10px 0; 41 | box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.1); 42 | } 43 | .navbar .content{ 44 | display: flex; 45 | align-items: center; 46 | justify-content: space-between; 47 | } 48 | .navbar .logo a{ 49 | color: #fff; 50 | font-size: 30px; 51 | font-weight: 600; 52 | text-decoration: none; 53 | } 54 | .navbar .menu-list{ 55 | display: inline-flex; 56 | } 57 | .menu-list li{ 58 | list-style: none; 59 | } 60 | .menu-list li a{ 61 | color: #fff; 62 | font-size: 18px; 63 | font-weight: 500; 64 | margin-left: 25px; 65 | text-decoration: none; 66 | transition: all 0.3s ease; 67 | } 68 | .menu-list li a:hover{ 69 | color: #007bff; 70 | } 71 | .icon{ 72 | color: #fff; 73 | font-size: 20px; 74 | cursor: pointer; 75 | display: none; 76 | } 77 | .menu-list .cancel-btn{ 78 | position: absolute; 79 | right: 30px; 80 | top: 20px; 81 | } 82 | @media (max-width: 1230px) { 83 | .content{ 84 | padding: 0 60px; 85 | } 86 | } 87 | @media (max-width: 1100px) { 88 | .content{ 89 | padding: 0 40px; 90 | } 91 | } 92 | @media (max-width: 900px) { 93 | .content{ 94 | padding: 0 30px; 95 | } 96 | } 97 | @media (max-width: 868px) { 98 | body.disabled{ 99 | overflow: hidden; 100 | } 101 | .icon{ 102 | display: block; 103 | } 104 | .icon.hide{ 105 | display: none; 106 | } 107 | .navbar .menu-list{ 108 | position: fixed; 109 | height: 100vh; 110 | width: 100%; 111 | max-width: 400px; 112 | left: -100%; 113 | top: 0px; 114 | display: block; 115 | padding: 40px 0; 116 | text-align: center; 117 | background: #222; 118 | transition: all 0.3s ease; 119 | } 120 | .navbar.show .menu-list{ 121 | left: 0%; 122 | } 123 | .navbar .menu-list li{ 124 | margin-top: 45px; 125 | } 126 | .navbar .menu-list li a{ 127 | font-size: 23px; 128 | margin-left: -100%; 129 | transition: 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); 130 | } 131 | .navbar.show .menu-list li a{ 132 | margin-left: 0px; 133 | } 134 | } 135 | @media (max-width: 380px) { 136 | .navbar .logo a{ 137 | font-size: 27px; 138 | } 139 | } 140 | 141 | .section-container section { 142 | height: 100vh; 143 | display: flex; 144 | flex-direction: column; 145 | align-items: center; 146 | justify-content: center; 147 | font-family: "Roboto", sans-serif; 148 | } 149 | 150 | .section-container section h3 { 151 | font-size: 40px; 152 | color: #fff; 153 | margin: 16px 0; 154 | } 155 | 156 | .section-container p { 157 | padding: 8px 0; 158 | color: #fff; 159 | text-align: center; 160 | margin: 10px; 161 | } 162 | 163 | .section-container .section-heading { 164 | width: 100%; 165 | text-align: center; 166 | border-top: 2px solid #53d7ee; 167 | border-bottom: 2px solid #53d7ee; 168 | margin-bottom: 10px; 169 | } 170 | 171 | .bot-icon { 172 | width: 200px; 173 | height: 200px; 174 | border-radius: 50%; 175 | } 176 | 177 | .btns { 178 | display: block; 179 | margin-left: auto; 180 | margin-right: auto; 181 | } 182 | 183 | .btns button { 184 | padding: 10px; 185 | margin: 10px; 186 | color: #7289DA; 187 | background-color: #222222; 188 | border: 2px solid #7289DA; 189 | border-radius: 7px; 190 | font-size: 1rem; 191 | width: 175px; 192 | height: 50px; 193 | } 194 | 195 | .btns button:hover { 196 | color: #fff; 197 | background-color: #7289DA; 198 | } 199 | 200 | footer { 201 | background: #111; 202 | background-color: #111; 203 | padding: 15px 23px; 204 | color: #fff; 205 | text-align: center; 206 | } 207 | footer span a { 208 | color: cyan; 209 | text-decoration: none; 210 | background: #111; 211 | } 212 | 213 | footer span { 214 | background: #111; 215 | } 216 | 217 | footer span a:hover{ 218 | text-decoration: underline; 219 | } 220 | -------------------------------------------------------------------------------- /dashboard/assets/css/dashboard.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Ubuntu:wght@400;500;700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | text-decoration: none; 8 | font-family: 'Poppins', sans-serif; 9 | } 10 | 11 | html { 12 | scroll-behavior: smooth; 13 | } 14 | 15 | body { 16 | background-color: #222222; 17 | } 18 | 19 | .navbar{ 20 | position: fixed; 21 | width: 100%; 22 | z-index: 2; 23 | padding: 25px 0; 24 | transition: all 0.3s ease; 25 | } 26 | .navbar.sticky{ 27 | background: #1b1b1b; 28 | padding: 10px 0; 29 | box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.1); 30 | } 31 | .navbar .content{ 32 | display: flex; 33 | align-items: center; 34 | justify-content: space-between; 35 | } 36 | .navbar .logo a{ 37 | color: #fff; 38 | font-size: 30px; 39 | font-weight: 600; 40 | text-decoration: none; 41 | } 42 | .navbar .menu-list{ 43 | display: inline-flex; 44 | } 45 | .menu-list li{ 46 | list-style: none; 47 | } 48 | .menu-list li a{ 49 | color: #fff; 50 | font-size: 18px; 51 | font-weight: 500; 52 | margin-left: 25px; 53 | text-decoration: none; 54 | transition: all 0.3s ease; 55 | } 56 | .menu-list li a:hover{ 57 | color: #007bff; 58 | } 59 | .icon{ 60 | color: #fff; 61 | font-size: 20px; 62 | cursor: pointer; 63 | display: none; 64 | } 65 | .menu-list .cancel-btn{ 66 | position: absolute; 67 | right: 30px; 68 | top: 20px; 69 | } 70 | @media (max-width: 1230px) { 71 | .content{ 72 | padding: 0 60px; 73 | } 74 | } 75 | @media (max-width: 1100px) { 76 | .content{ 77 | padding: 0 40px; 78 | } 79 | } 80 | @media (max-width: 900px) { 81 | .content{ 82 | padding: 0 30px; 83 | } 84 | } 85 | @media (max-width: 868px) { 86 | body.disabled{ 87 | overflow: hidden; 88 | } 89 | .icon{ 90 | display: block; 91 | } 92 | .icon.hide{ 93 | display: none; 94 | } 95 | .navbar .menu-list{ 96 | position: fixed; 97 | height: 100vh; 98 | width: 100%; 99 | max-width: 400px; 100 | left: -100%; 101 | top: 0px; 102 | display: block; 103 | padding: 40px 0; 104 | text-align: center; 105 | background: #222; 106 | transition: all 0.3s ease; 107 | } 108 | .navbar.show .menu-list{ 109 | left: 0%; 110 | } 111 | .navbar .menu-list li{ 112 | margin-top: 45px; 113 | } 114 | .navbar .menu-list li a{ 115 | font-size: 23px; 116 | margin-left: -100%; 117 | transition: 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); 118 | } 119 | .navbar.show .menu-list li a{ 120 | margin-left: 0px; 121 | } 122 | } 123 | @media (max-width: 380px) { 124 | .navbar .logo a{ 125 | font-size: 27px; 126 | } 127 | } 128 | 129 | .container { 130 | padding-top: 20px; 131 | display: flex; 132 | flex-direction: row; 133 | flex-wrap: wrap; 134 | justify-content: center; 135 | align-items: center; 136 | margin-bottom: 15px; 137 | } 138 | 139 | .stxt { 140 | padding-top: 100px; 141 | text-align: center; 142 | color: #fff; 143 | } 144 | 145 | .box { 146 | background-color: #333333; 147 | padding: 15px; 148 | border-radius: 12px; 149 | margin: 10px; 150 | } 151 | 152 | .box:hover { 153 | background-color: #444444; 154 | padding: 17px; 155 | } 156 | 157 | .box img { 158 | display: block; 159 | margin-left: auto; 160 | margin-right: auto; 161 | border-radius: 12px; 162 | width: 120px; 163 | height: 120px; 164 | } 165 | 166 | .box .guild-name { 167 | padding-top: 10px; 168 | text-align: center; 169 | color: #fff; 170 | } 171 | -------------------------------------------------------------------------------- /dashboard/assets/css/message.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Ubuntu&display=swap'); 2 | 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | background-color: #222222; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 10px; 16 | } 17 | ::-webkit-scrollbar-track { 18 | background: #f1f1f1; 19 | } 20 | ::-webkit-scrollbar-thumb { 21 | background: #888; 22 | } 23 | ::selection{ 24 | background: rgb(0,123,255,0.3); 25 | } 26 | .content{ 27 | max-width: 1250px; 28 | margin: auto; 29 | padding: 0 30px; 30 | } 31 | .navbar{ 32 | position: fixed; 33 | width: 100%; 34 | z-index: 2; 35 | padding: 25px 0; 36 | transition: all 0.3s ease; 37 | } 38 | .navbar.sticky{ 39 | background: #1b1b1b; 40 | padding: 10px 0; 41 | box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.1); 42 | } 43 | .navbar .content{ 44 | display: flex; 45 | align-items: center; 46 | justify-content: space-between; 47 | } 48 | .navbar .logo a{ 49 | color: #fff; 50 | font-size: 30px; 51 | font-weight: 600; 52 | text-decoration: none; 53 | } 54 | .navbar .menu-list{ 55 | display: inline-flex; 56 | } 57 | .menu-list li{ 58 | list-style: none; 59 | } 60 | .menu-list li a{ 61 | color: #fff; 62 | font-size: 18px; 63 | font-weight: 500; 64 | margin-left: 25px; 65 | text-decoration: none; 66 | transition: all 0.3s ease; 67 | } 68 | .menu-list li a:hover{ 69 | color: #007bff; 70 | } 71 | .icon{ 72 | color: #fff; 73 | font-size: 20px; 74 | cursor: pointer; 75 | display: none; 76 | } 77 | .menu-list .cancel-btn{ 78 | position: absolute; 79 | right: 30px; 80 | top: 20px; 81 | } 82 | @media (max-width: 1230px) { 83 | .content{ 84 | padding: 0 60px; 85 | } 86 | } 87 | @media (max-width: 1100px) { 88 | .content{ 89 | padding: 0 40px; 90 | } 91 | } 92 | @media (max-width: 900px) { 93 | .content{ 94 | padding: 0 30px; 95 | } 96 | } 97 | @media (max-width: 868px) { 98 | body.disabled{ 99 | overflow: hidden; 100 | } 101 | .icon{ 102 | display: block; 103 | } 104 | .icon.hide{ 105 | display: none; 106 | } 107 | .navbar .menu-list{ 108 | position: fixed; 109 | height: 100vh; 110 | width: 100%; 111 | max-width: 400px; 112 | left: -100%; 113 | top: 0px; 114 | display: block; 115 | padding: 40px 0; 116 | text-align: center; 117 | background: #222; 118 | transition: all 0.3s ease; 119 | } 120 | .navbar.show .menu-list{ 121 | left: 0%; 122 | } 123 | .navbar .menu-list li{ 124 | margin-top: 45px; 125 | } 126 | .navbar .menu-list li a{ 127 | font-size: 23px; 128 | margin-left: -100%; 129 | transition: 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); 130 | } 131 | .navbar.show .menu-list li a{ 132 | margin-left: 0px; 133 | } 134 | } 135 | @media (max-width: 380px) { 136 | .navbar .logo a{ 137 | font-size: 27px; 138 | } 139 | } 140 | 141 | .section-container section { 142 | height: 100vh; 143 | display: flex; 144 | flex-direction: column; 145 | align-items: center; 146 | justify-content: center; 147 | font-family: "Roboto", sans-serif; 148 | } 149 | 150 | .section-container section h3 { 151 | font-size: 40px; 152 | color: #fff; 153 | margin: 16px 0; 154 | } 155 | 156 | .section-container p { 157 | padding: 8px 0; 158 | color: #fff; 159 | text-align: center; 160 | margin: 10px; 161 | } 162 | 163 | .section-container .section-heading { 164 | width: 100%; 165 | text-align: center; 166 | border-top: 2px solid #53d7ee; 167 | border-bottom: 2px solid #53d7ee; 168 | margin-bottom: 10px; 169 | } 170 | 171 | footer { 172 | background: #111; 173 | background-color: #111; 174 | padding: 15px 23px; 175 | color: #fff; 176 | text-align: center; 177 | } 178 | footer span a { 179 | color: cyan; 180 | text-decoration: none; 181 | background: #111; 182 | } 183 | 184 | footer span { 185 | background: #111; 186 | } 187 | 188 | footer span a:hover{ 189 | text-decoration: underline; 190 | } 191 | 192 | .verify-i { 193 | font-size: 150px; 194 | border-radius: 50%; 195 | color: #fff; 196 | animation: animate 1.2s infinite; 197 | } 198 | 199 | @keyframes animate { 200 | 0% { 201 | transform: rotate(0deg) scale(0.8); 202 | } 203 | 5% { 204 | transform: rotate(0deg) scale(0.9); 205 | } 206 | 10% { 207 | transform: rotate(0deg) scale(0.8); 208 | } 209 | 15% { 210 | transform: rotate(0deg) scale(1); 211 | } 212 | 50% { 213 | transform: rotate(0deg) scale(0.8); 214 | } 215 | 100% { 216 | transform: rotate(0deg) scale(0.8); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /dashboard/assets/css/settings.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap'); 2 | 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | background-color: #222222; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 10px; 16 | } 17 | ::-webkit-scrollbar-track { 18 | background: #f1f1f1; 19 | } 20 | ::-webkit-scrollbar-thumb { 21 | background: #888; 22 | } 23 | ::selection{ 24 | background: rgb(0,123,255,0.3); 25 | } 26 | 27 | .navbar{ 28 | position: fixed; 29 | width: 100%; 30 | z-index: 2; 31 | padding: 25px 0; 32 | transition: all 0.3s ease; 33 | } 34 | .navbar.sticky{ 35 | background: #1b1b1b; 36 | padding: 10px 0; 37 | box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.1); 38 | } 39 | .navbar .content{ 40 | display: flex; 41 | align-items: center; 42 | justify-content: space-between; 43 | } 44 | .navbar .logo a{ 45 | color: #fff; 46 | font-size: 30px; 47 | font-weight: 600; 48 | text-decoration: none; 49 | } 50 | .navbar .menu-list{ 51 | display: inline-flex; 52 | } 53 | .menu-list li{ 54 | list-style: none; 55 | } 56 | .menu-list li a{ 57 | color: #fff; 58 | font-size: 18px; 59 | font-weight: 500; 60 | margin-left: 25px; 61 | text-decoration: none; 62 | transition: all 0.3s ease; 63 | } 64 | .menu-list li a:hover{ 65 | color: #007bff; 66 | } 67 | .icon{ 68 | color: #fff; 69 | font-size: 20px; 70 | cursor: pointer; 71 | display: none; 72 | } 73 | .menu-list .cancel-btn{ 74 | position: absolute; 75 | right: 30px; 76 | top: 20px; 77 | } 78 | @media (max-width: 1230px) { 79 | .content{ 80 | padding: 0 60px; 81 | } 82 | } 83 | @media (max-width: 1100px) { 84 | .content{ 85 | padding: 0 40px; 86 | } 87 | } 88 | @media (max-width: 900px) { 89 | .content{ 90 | padding: 0 30px; 91 | } 92 | } 93 | @media (max-width: 868px) { 94 | body.disabled{ 95 | overflow: hidden; 96 | } 97 | .icon{ 98 | display: block; 99 | } 100 | .icon.hide{ 101 | display: none; 102 | } 103 | .navbar .menu-list{ 104 | position: fixed; 105 | height: 100vh; 106 | width: 100%; 107 | max-width: 400px; 108 | left: -100%; 109 | top: 0px; 110 | display: block; 111 | padding: 40px 0; 112 | text-align: center; 113 | background: #222; 114 | transition: all 0.3s ease; 115 | } 116 | .navbar.show .menu-list{ 117 | left: 0%; 118 | } 119 | .navbar .menu-list li{ 120 | margin-top: 45px; 121 | } 122 | .navbar .menu-list li a{ 123 | font-size: 23px; 124 | margin-left: -100%; 125 | transition: 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); 126 | } 127 | .navbar.show .menu-list li a{ 128 | margin-left: 0px; 129 | } 130 | } 131 | @media (max-width: 380px) { 132 | .navbar .logo a{ 133 | font-size: 27px; 134 | } 135 | } 136 | 137 | .section-container section { 138 | height: 100vh; 139 | display: flex; 140 | flex-direction: column; 141 | align-items: center; 142 | justify-content: center; 143 | font-family: "Roboto", sans-serif; 144 | } 145 | 146 | .section-container section h3 { 147 | font-size: 40px; 148 | color: #fff; 149 | margin: 16px 0; 150 | } 151 | 152 | .section-container p { 153 | padding: 8px 0; 154 | color: #fff; 155 | text-align: center; 156 | margin: 10px; 157 | } 158 | 159 | .section-container .section-heading { 160 | width: 100%; 161 | text-align: center; 162 | border-top: 2px solid #53d7ee; 163 | border-bottom: 2px solid #53d7ee; 164 | margin-bottom: 10px; 165 | } 166 | 167 | .guildIcon { 168 | border-radius: 12px; 169 | } 170 | 171 | .guildName { 172 | color: #7289da; 173 | } 174 | 175 | .prefix { 176 | border-radius: 5px; 177 | color: #222222; 178 | background-color: #fff; 179 | } 180 | 181 | label { 182 | color: #fff; 183 | font-size: 1rem; 184 | } 185 | 186 | .btn { 187 | padding: 10px; 188 | margin: 10px; 189 | border: 2px solid #fff; 190 | color: #fff; 191 | background-color: #222222; 192 | border-radius: 5px; 193 | font-size: 1rem; 194 | } 195 | 196 | .btn:hover { 197 | color: #222222; 198 | background-color: #fff; 199 | border: 2px solid #222222; 200 | } 201 | 202 | h4 { 203 | margin: 10px; 204 | color: #fff; 205 | } 206 | 207 | select { 208 | width: 150px; 209 | height: 25px; 210 | border: 0 !important; 211 | text-align: center; 212 | box-shadow: none; 213 | background: #23272a; 214 | } 215 | 216 | .save-btn { 217 | color: #fff; 218 | background: #222222; 219 | padding: 10px; 220 | margin: 10px; 221 | border: 2px solid #fff; 222 | border-radius: 5px; 223 | font-size: 1rem; 224 | } 225 | -------------------------------------------------------------------------------- /dashboard/assets/css/verify.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap'); 2 | 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | background-color: #222222; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 10px; 16 | } 17 | ::-webkit-scrollbar-track { 18 | background: #f1f1f1; 19 | } 20 | ::-webkit-scrollbar-thumb { 21 | background: #888; 22 | } 23 | ::selection{ 24 | background: rgb(0,123,255,0.3); 25 | } 26 | .content{ 27 | max-width: 1250px; 28 | margin: auto; 29 | padding: 0 30px; 30 | } 31 | .navbar{ 32 | position: fixed; 33 | width: 100%; 34 | z-index: 2; 35 | padding: 25px 0; 36 | transition: all 0.3s ease; 37 | } 38 | .navbar.sticky{ 39 | background: #1b1b1b; 40 | padding: 10px 0; 41 | box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.1); 42 | } 43 | .navbar .content{ 44 | display: flex; 45 | align-items: center; 46 | justify-content: space-between; 47 | } 48 | .navbar .logo a{ 49 | color: #fff; 50 | font-size: 30px; 51 | font-weight: 600; 52 | text-decoration: none; 53 | } 54 | .navbar .menu-list{ 55 | display: inline-flex; 56 | } 57 | .menu-list li{ 58 | list-style: none; 59 | } 60 | .menu-list li a{ 61 | color: #fff; 62 | font-size: 18px; 63 | font-weight: 500; 64 | margin-left: 25px; 65 | text-decoration: none; 66 | transition: all 0.3s ease; 67 | } 68 | .menu-list li a:hover{ 69 | color: #007bff; 70 | } 71 | .icon{ 72 | color: #fff; 73 | font-size: 20px; 74 | cursor: pointer; 75 | display: none; 76 | } 77 | .menu-list .cancel-btn{ 78 | position: absolute; 79 | right: 30px; 80 | top: 20px; 81 | } 82 | @media (max-width: 1230px) { 83 | .content{ 84 | padding: 0 60px; 85 | } 86 | } 87 | @media (max-width: 1100px) { 88 | .content{ 89 | padding: 0 40px; 90 | } 91 | } 92 | @media (max-width: 900px) { 93 | .content{ 94 | padding: 0 30px; 95 | } 96 | } 97 | @media (max-width: 868px) { 98 | body.disabled{ 99 | overflow: hidden; 100 | } 101 | .icon{ 102 | display: block; 103 | } 104 | .icon.hide{ 105 | display: none; 106 | } 107 | .navbar .menu-list{ 108 | position: fixed; 109 | height: 100vh; 110 | width: 100%; 111 | max-width: 400px; 112 | left: -100%; 113 | top: 0px; 114 | display: block; 115 | padding: 40px 0; 116 | text-align: center; 117 | background: #222; 118 | transition: all 0.3s ease; 119 | } 120 | .navbar.show .menu-list{ 121 | left: 0%; 122 | } 123 | .navbar .menu-list li{ 124 | margin-top: 45px; 125 | } 126 | .navbar .menu-list li a{ 127 | font-size: 23px; 128 | margin-left: -100%; 129 | transition: 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); 130 | } 131 | .navbar.show .menu-list li a{ 132 | margin-left: 0px; 133 | } 134 | } 135 | @media (max-width: 380px) { 136 | .navbar .logo a{ 137 | font-size: 27px; 138 | } 139 | } 140 | 141 | .section-container section { 142 | height: 100vh; 143 | display: flex; 144 | flex-direction: column; 145 | align-items: center; 146 | justify-content: center; 147 | font-family: "Roboto", sans-serif; 148 | } 149 | 150 | .section-container section h3 { 151 | font-size: 40px; 152 | color: #fff; 153 | margin: 16px 0; 154 | } 155 | 156 | .section-container p { 157 | padding: 8px 0; 158 | color: #fff; 159 | text-align: center; 160 | margin: 10px; 161 | } 162 | 163 | .section-container .section-heading { 164 | width: 100%; 165 | text-align: center; 166 | border-top: 2px solid #53d7ee; 167 | border-bottom: 2px solid #53d7ee; 168 | margin-bottom: 10px; 169 | } 170 | 171 | footer { 172 | background: #111; 173 | background-color: #111; 174 | padding: 15px 23px; 175 | color: #fff; 176 | text-align: center; 177 | } 178 | footer span a { 179 | color: cyan; 180 | text-decoration: none; 181 | background: #111; 182 | } 183 | 184 | footer span { 185 | background: #111; 186 | } 187 | 188 | footer span a:hover{ 189 | text-decoration: underline; 190 | } 191 | 192 | .verify-i { 193 | font-size: 150px; 194 | border-radius: 50%; 195 | color: #fff; 196 | animation: animate 1.2s infinite; 197 | } 198 | 199 | @keyframes animate { 200 | 0% { 201 | transform: rotate(0deg) scale(0.8); 202 | } 203 | 5% { 204 | transform: rotate(0deg) scale(0.9); 205 | } 206 | 10% { 207 | transform: rotate(0deg) scale(0.8); 208 | } 209 | 15% { 210 | transform: rotate(0deg) scale(1); 211 | } 212 | 50% { 213 | transform: rotate(0deg) scale(0.8); 214 | } 215 | 100% { 216 | transform: rotate(0deg) scale(0.8); 217 | } 218 | } 219 | 220 | .h-captcha { 221 | display: block; 222 | margin-left: auto; 223 | margin-right: auto; 224 | } 225 | 226 | .submit { 227 | display: block; 228 | margin-left: auto; 229 | margin-right: auto; 230 | padding: 10px; 231 | border-radius: 12px; 232 | color: #111; 233 | font-size: 1rem; 234 | background-color: #fff; 235 | } 236 | -------------------------------------------------------------------------------- /dashboard/assets/images/EABCE74A-CCDB-4F59-B80D-454EA66C1D49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/legend-js-dev/verification-bot/d5f9a7a835ac1af37d87e04256833f4a4bdb063a/dashboard/assets/images/EABCE74A-CCDB-4F59-B80D-454EA66C1D49.png -------------------------------------------------------------------------------- /dashboard/assets/js/script.js: -------------------------------------------------------------------------------- 1 | const body = document.querySelector('body'), 2 | navbar = document.querySelector('.navbar'), 3 | menuBtn = document.querySelector('.menu-btn'), 4 | cancelBtn = document.querySelector('.cancel-btn'); 5 | 6 | menuBtn.onclick = () => { 7 | navbar.classList.add('show'); 8 | menuBtn.classList.add('hide'); 9 | body.classList.add('disabled'); 10 | }; 11 | 12 | cancelBtn.onclick = () => { 13 | body.classList.remove('disabled'); 14 | navbar.classList.remove('show'); 15 | menuBtn.classList.remove('hide'); 16 | }; 17 | 18 | window.onscroll = () => { 19 | this.scrollY > 20 20 | ? navbar.classList.add('sticky') 21 | : navbar.classList.remove('sticky'); 22 | }; 23 | 24 | function Submit(token) { 25 | document.getElementById('captcha').submit(); 26 | } 27 | -------------------------------------------------------------------------------- /dashboard/index.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'), 2 | url = require('url'), 3 | path = require('path'), 4 | express = require('express'), 5 | passport = require('passport'), 6 | Strategy = require('passport-discord').Strategy, 7 | session = require('express-session'), 8 | ejs = require('ejs'), 9 | fetch = require('node-fetch'), 10 | config = require('../config'), 11 | bodyParser = require('body-parser'), 12 | app = express(), 13 | db = require('quick.db'), 14 | MemoryStore = require('memorystore')(session), 15 | dataDir = path.resolve(`${process.cwd()}${path.sep}dashboard`), 16 | templateDir = path.resolve(`${dataDir}${path.sep}templates`), 17 | moment = require('moment'); 18 | let domain, callbackUrl; 19 | 20 | module.exports = function(client) { 21 | passport.serializeUser((user, done) => done(null, user)); 22 | passport.deserializeUser((obj, done) => done(null, obj)); 23 | 24 | try { 25 | const domainUrl = new URL(config.website.protocol + config.website.domain); 26 | domain = { 27 | host: domainUrl.hostname, 28 | protocol: domainUrl.protocol 29 | }; 30 | callbackUrl = `${domain.protocol}//${domain.host}/callback`; 31 | } catch (e) { 32 | console.log(e); 33 | throw new TypeError( 34 | '[ERROR]: Invalid domain Specified in the config file.' 35 | ); 36 | } 37 | 38 | console.log( 39 | `[WARNING]: add the following URL as a redirect URL in your bot's OAuth Section at the Discord Developer Portal\n${callbackUrl}` 40 | ); 41 | console.log('-------------------------------------'); 42 | 43 | passport.use( 44 | new Strategy( 45 | { 46 | clientID: config.bot.clientID, 47 | clientSecret: config.bot.clientSecret, 48 | callbackURL: callbackUrl, 49 | scope: ['identify', 'guilds', 'guilds.join'] 50 | }, 51 | (accessToken, refreshToken, profile, done) => { 52 | process.nextTick(() => done(null, profile)); 53 | } 54 | ) 55 | ); 56 | 57 | app.use( 58 | session({ 59 | store: new MemoryStore({ checkPeriod: 86400000 }), 60 | secret: 61 | 'J35U5.I3.B411IN.L0L.S0M3.R4ND0M.3TUFF.H3R3.XD.PUT.S0M3.M0R3.5TUFF.H3R3.PL5.L0L.XD.D0NT.W4ST3.Y0UR.T1M3.R34D1NG.TH15', 62 | resave: false, 63 | saveUninitialized: false 64 | }) 65 | ); 66 | 67 | app.use(passport.initialize()); 68 | app.use(passport.session()); 69 | app.use(bodyParser.json()); 70 | app.use( 71 | bodyParser.urlencoded({ 72 | extended: true 73 | }) 74 | ); 75 | 76 | app.locals.domain = config.website.domain; 77 | 78 | app.engine('html', ejs.renderFile); 79 | app.set('view engine', 'html'); 80 | 81 | app.use('/', express.static(path.resolve(`${dataDir}${path.sep}assets`))); 82 | app.use( 83 | '/dashboard', 84 | express.static(path.resolve(`${dataDir}${path.sep}assets`)) 85 | ); 86 | app.use( 87 | '/dashboard/:guildID', 88 | express.static(path.resolve(`${dataDir}${path.sep}assets`)) 89 | ); 90 | app.use( 91 | '/verify/:guildID/check', 92 | express.static(path.resolve(`${dataDir}${path.sep}assets`)) 93 | ); 94 | app.use( 95 | '/verify/:guildID', 96 | express.static(path.resolve(`${dataDir}${path.sep}assets`)) 97 | ); 98 | 99 | function render(res, req, template, data = {}) { 100 | const baseData = { 101 | bot: client, 102 | path: req.path, 103 | user: req.isAuthenticated() ? req.user : null 104 | }; 105 | res.render( 106 | path.resolve(`${templateDir}${path.sep}${template}`), 107 | Object.assign(baseData, data) 108 | ); 109 | } 110 | function checkAuth(req, res, next) { 111 | if (req.isAuthenticated()) return next(); 112 | req.session.backURL = req.url; 113 | res.redirect('/login'); 114 | } 115 | 116 | app.get( 117 | '/login', 118 | (req, res, next) => { 119 | if (req.session.backURL) { 120 | req.session.backURL = req.session.backURL; 121 | } else if (req.headers.referer) { 122 | const parsed = url.parse(req.headers.referer); 123 | if (parsed.hostname === app.locals.domain) { 124 | req.session.backURL = parsed.path; 125 | } 126 | } else { 127 | req.session.backURL = '/'; 128 | } 129 | next(); 130 | }, 131 | passport.authenticate('discord') 132 | ); 133 | 134 | app.get( 135 | '/callback', 136 | passport.authenticate('discord', { failureRedirect: '/' }), 137 | (req, res) => { 138 | if (req.session.backURL) { 139 | const url = req.session.backURL; 140 | req.session.backURL = null; 141 | res.redirect(url); 142 | } else { 143 | res.redirect('/'); 144 | } 145 | } 146 | ); 147 | 148 | app.get('/logout', function(req, res) { 149 | req.session.destroy(() => { 150 | req.logout(); 151 | res.redirect('/'); 152 | }); 153 | }); 154 | 155 | app.get('/', (req, res) => { 156 | render(res, req, 'index.ejs'); 157 | }); 158 | 159 | app.get('/404', (req, res) => { 160 | render(res, req, '404.ejs'); 161 | }); 162 | 163 | app.get('/dashboard', checkAuth, (req, res) => { 164 | render(res, req, 'dashboard.ejs', { perms: Discord.Permissions, config }); 165 | }); 166 | 167 | app.get('/dashboard/:guildID', checkAuth, async (req, res) => { 168 | const guild = client.guilds.cache.get(req.params.guildID); 169 | if (!guild) return res.redirect('/dashboard'); 170 | let member = guild.members.cache.get(req.user.id); 171 | if (!member) return res.redirect('/dashboard'); 172 | if (!member.permissions.has('MANAGE_GUILD')) 173 | return res.redirect('/dashboard'); 174 | 175 | render(res, req, 'settings.ejs', { guild, config, db }); 176 | }); 177 | 178 | app.get('/verify/:guildID', checkAuth, async (req, res) => { 179 | const guild = client.guilds.cache.get(req.params.guildID); 180 | if (!guild) return res.redirect('/404'); 181 | const member = guild.members.cache.get(req.user.id); 182 | if (!member) return res.redirect('/404'); 183 | render(res, req, 'verify.ejs', { config }); 184 | }); 185 | 186 | app.post('/verify/:guildID', checkAuth, async (req, res) => { 187 | const response = req.body['h-captcha-response'], 188 | { URLSearchParams } = require('url'), 189 | params = new URLSearchParams(), 190 | guild = client.guilds.cache.get(req.params.guildID), 191 | member = guild.members.cache.get(req.user.id), 192 | logs = guild.channels.cache.get(db.get(`logs_${guild.id}`)) || null; 193 | if (!member) return res.redirect('/404'); 194 | params.append('secret', config.website.captcha.secretkey); 195 | params.append('response', response); 196 | 197 | let resp = await fetch('https://hcaptcha.com/siteverify', { 198 | method: 'post', 199 | body: params 200 | }).then(res => res.json()); 201 | 202 | if (resp.success) { 203 | let ip = db.all().filter(x => x.data === req.headers['x-forwarded-for']); 204 | if (ip.length) { 205 | render(res, req, 'message.ejs', { 206 | message: `Verification failed, You have been ${ 207 | (db.get(`punishment_${req.params.guildID}`) || 'kick') === 'kick' 208 | ? 'kicked' 209 | : 'banned' 210 | } for verifying with a different Discord account!` 211 | }); 212 | (db.get(`punishment_${req.params.guildID}`) || 'kick') === 'kick' 213 | ? member.kick().catch(err => {}) 214 | : member.ban().catch(err => {}); 215 | let pog = 216 | (db.get(`punishment_${req.params.guildID}`) || 'kick') === 'kick' 217 | ? 'kicked' 218 | : 'banned'; 219 | let embed = new Discord.MesssageEmbed() 220 | .setTitle(`Verification Logs`) 221 | .setDescription(`Member ${pog}`) 222 | .setFooter(member.guild.name, member.guild.iconURL()) 223 | .addField(`Member`, `<@${member.user.id}> (${member.user.id})`) 224 | .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) 225 | .addField( 226 | `Account Age`, 227 | `Created ${moment(member.user.createdAt).fromNow()}` 228 | ) 229 | .addField('Reason', 'Verified with a different account') 230 | .setColor(`#00FF00`); 231 | if (logs) logs.send({ embed: embed }); 232 | } else { 233 | db.set( 234 | `ip_${req.params.guildID}_${req.user.id}`, 235 | req.headers['x-forwarded-for'] 236 | ); 237 | } 238 | render(res, req, 'message.ejs', { 239 | message: `You have successfully verified your account, We hope you have a pleasant stay in ${ 240 | guild.name 241 | }!` 242 | }); 243 | member.roles.add(db.get(`role_${guild.id}`)).catch(err => {}); 244 | db.set(`verified_${guild.id}_${member.user.id}`, true); 245 | } else { 246 | render(res, req, 'message.ejs', { 247 | message: 'Verificaton Failed, Join the Server and try again later' 248 | }); 249 | member.kick(); 250 | } 251 | }); 252 | 253 | app.post('/api/prefix', (req, res) => { 254 | let guildID = req.body.guildID; 255 | let prefix = req.body.prefix; 256 | if (!prefix) 257 | return res.json({ 258 | success: false, 259 | alert: { title: 'Oops!', message: 'No prefix provided', type: 'error' } 260 | }); 261 | if (prefix.length > 4) 262 | return res.json({ 263 | success: false, 264 | alert: { 265 | title: 'Oops!', 266 | message: 'The prefix can only have upto 4 letters', 267 | type: 'error' 268 | } 269 | }); 270 | let guild = client.guilds.cache.get(guildID); 271 | if (!guild) 272 | return res.json({ 273 | success: false, 274 | alert: { 275 | title: 'Oops!', 276 | message: 'Please refresh the page and try again', 277 | type: 'error' 278 | } 279 | }); 280 | db.set(`prefix_${guild.id}`, prefix); 281 | return res.json({ 282 | success: true, 283 | alert: { 284 | title: 'Success!', 285 | message: 'The prefix has been updated', 286 | type: 'success' 287 | } 288 | }); 289 | }); 290 | 291 | app.post('/api/save', async (req, res) => { 292 | const { guildID, LogsC, WarningC, VR, Punishment } = req.body; 293 | let guild = client.guilds.cache.get(guildID); 294 | if (!guild) 295 | return res.json({ 296 | success: false, 297 | alert: { 298 | title: 'Oops!', 299 | message: 'Please refresh the page and try again', 300 | type: 'error' 301 | } 302 | }); 303 | try { 304 | if (Number(LogsC)) { 305 | let channel = guild.channels.cache.get(LogsC); 306 | db.set(`logs_${guild.id}`, channel.id); 307 | } 308 | if (Number(WarningC)) { 309 | let channel = guild.channels.cache.get(WarningC); 310 | db.set(`warningchannel_${guild.id}`, channel.id); 311 | } 312 | if (Number(VR)) { 313 | let role = guild.roles.cache.get(VR); 314 | db.set(`role_${guild.id}`, role.id); 315 | } 316 | if (Punishment) { 317 | let cp = db.get(`punishment_${guild.id}`); 318 | if (Punishment !== cp) { 319 | db.set(`punishment_${guild.id}`, Punishment === 'k' ? 'kick' : 'ban'); 320 | } 321 | } 322 | } catch (err) { 323 | return res.json({ 324 | success: false, 325 | alert: { 326 | title: 'Oops!', 327 | message: 'An Unknown Error Occured, Try again later', 328 | type: 'error' 329 | } 330 | }); 331 | } 332 | return res.json({ 333 | success: true, 334 | alert: { 335 | title: 'Success!', 336 | message: 'The Changes have been saved', 337 | type: 'success' 338 | } 339 | }); 340 | }); 341 | 342 | app.listen(config.website.port, null, null, () => 343 | console.log(`[INFO]: The Dashboard is ready on port ${config.website.port}`) 344 | ); 345 | }; 346 | -------------------------------------------------------------------------------- /dashboard/templates/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 404 - Not Found 8 | 9 | 10 |
11 |
4
12 | 13 |
4
14 |
15 | Page Not Found
16 | 17 |
18 |
19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /dashboard/templates/dashboard.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dashboard 7 | 8 | 9 | 10 | 11 | 12 | <%- include('./partials/Navbar', { bot, user }); %> 13 | <%- include("partials/truncate") %> 14 |

Select a Server to configure

15 |
16 | <% user.guilds.forEach(guild => { 17 | const permsOnGuild = new perms(guild.permissions); 18 | if(!permsOnGuild.has("MANAGE_GUILD")) return; 19 | %> 20 | <% if (bot.guilds.cache.get(guild.id)) { %> 21 |
22 | 23 | <%- guild.icon ? `Guild Icon` : `Guild Icon` %> 24 |
<%- truncate(guild.name) %>
25 |
26 |
27 | <% } else { %> 28 |
29 | 30 | <%- guild.icon ? `Guild Icon` : `Guild Icon` %> 31 |
<%- truncate(guild.name) %>
32 |
33 |
34 | <% } %> 35 | <% 36 | }); 37 | %> 38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /dashboard/templates/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%- include('./partials/Navbar', { bot, user }); %> 11 |
12 |
13 | 14 |

Bot Name

15 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse dapibus, ex eget rhoncus imperdiet, erat eros aliquam sapien, eget tempus orci neque et velit. Ut orci dui, varius ac porta vel, vulputate luctus turpis.

16 |
17 | 18 |
19 |
20 |
21 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /dashboard/templates/message.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 29 |
30 |
31 | 32 |

Verification

33 |

<%= message %>

34 |
35 |
36 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /dashboard/templates/partials/Navbar.ejs: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /dashboard/templates/partials/general.ejs: -------------------------------------------------------------------------------- 1 |

General Configuration

2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /dashboard/templates/partials/truncate.ejs: -------------------------------------------------------------------------------- 1 | <% 2 | truncate = function(input) { 3 | return input.length > 20 ? `${input.slice(0, 20)}...` : input; 4 | } 5 | %> 6 | -------------------------------------------------------------------------------- /dashboard/templates/settings.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dashboard 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%- include('./partials/Navbar', { bot, user }); %> 13 |
14 |
15 | <%- guild.icon ? `Guild Icon` : `Guild Icon` %> 16 |

Configuration for <%= guild.name %>

17 |

Scroll down for the Configuration

18 |
19 |
20 | <%- include('./partials/general'); %> 21 |
22 |
23 |

Verification

24 |

Logs Channel

25 | 37 |

Warning Channel

38 | 50 |

Verification Role

51 | 63 |

Punishment

64 | 72 | 73 |
74 |
75 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /dashboard/templates/verify.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <%- include('./partials/Navbar', { bot, user }); %> 12 |
13 |
14 | 15 |

Verification

16 |

Please complete the captcha below to verify

17 |
18 |
data-callback="Submit">
19 |
20 |
21 |
22 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | //Verification bot by legendjs >:D 2 | const Client = require('./Structures/legendJsClient.js'), 3 | Discord = require('discord.js'), 4 | { prefix: defaultPrefix, token } = require('./config').bot, 5 | client = new Client({ disableMentions: 'everyone' }), 6 | db = require('quick.db'), 7 | dashboard = require('./dashboard/index'), 8 | moment = require('moment'), 9 | config = require('./config'); 10 | 11 | client.loadCommands(); 12 | 13 | console.log('-------------------------------------'); 14 | console.log(` 15 | ██╗ ███████╗ ██████╗ ███████╗███╗ ██╗██████╗ ██╗███████╗ 16 | ██║ ██╔════╝██╔════╝ ██╔════╝████╗ ██║██╔══██╗ ██║██╔════╝ 17 | ██║ █████╗ ██║ ███╗█████╗ ██╔██╗ ██║██║ ██║ ██║███████╗ 18 | ██║ ██╔══╝ ██║ ██║██╔══╝ ██║╚██╗██║██║ ██║ ██ ██║╚════██║ 19 | ███████╗███████╗╚██████╔╝███████╗██║ ╚████║██████╔╝██╗╚█████╔╝███████║ 20 | ╚══════╝╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚════╝ ╚══════╝ 21 | `); 22 | 23 | console.log('-------------------------------------'); 24 | console.log( 25 | '[CREDITS]: made by legend-js | https://github.com/legend-js-dev | legendjs#0001' 26 | ); 27 | console.log('[WARNING]: Do not remove the credits'); 28 | console.log('-------------------------------------'); 29 | //this took me some time so dont you dare remove credits, if u do remove credits then you will have copy right issues. 30 | client.on('ready', () => { 31 | console.log(`[INFO]: Ready on client (${client.user.tag})`); 32 | console.log( 33 | `[INFO]: watching ${client.guilds.cache.size} Servers, ${ 34 | client.channels.cache.size 35 | } channels & ${client.users.cache.size} users` 36 | ); 37 | console.log('-------------------------------------'); 38 | client.user.setActivity('Verification bot by legendjs :D', { 39 | type: 'WATCHING' 40 | }); 41 | }); 42 | 43 | client.on('message', async message => { 44 | if (message.author.bot) return; 45 | if (!message.guild) return; 46 | let prefix = db.get(`prefix_${message.guild.id}`) || defaultPrefix; 47 | if (!message.content.startsWith(prefix)) return; 48 | if (!message.member) 49 | message.member = await message.guild.members.fetch(message); 50 | 51 | const args = message.content 52 | .slice(prefix.length) 53 | .trim() 54 | .split(/ +/g); 55 | const cmd = args.shift().toLowerCase(); 56 | 57 | if (cmd.length === 0) return; 58 | 59 | let command = client.commands.get(cmd); 60 | if (!command) command = client.commands.get(client.aliases.get(cmd)); 61 | if (command) command.run(client, message, args, db); 62 | }); 63 | 64 | client.on('guildMemberAdd', async member => { 65 | let { guild, user } = member; 66 | let prefix = db.get(`prefix_${member.guild.id}`) || defaultPrefix; 67 | let bypassed = db.get(`bypass_${guild.id}`) || []; 68 | if (bypassed.includes(user.id)) return; 69 | let warningChan = member.guild.channels.cache.get( 70 | db.get(`warningchannel_${member.guild.id}`) 71 | ); 72 | let logsChan = member.guild.channels.cache.get( 73 | db.get(`logs_${member.guild.id}`) 74 | ); 75 | 76 | let embed = new Discord.MessageEmbed() 77 | .setTitle(`Verification Logs`) 78 | .setDescription(`Member Joined`) 79 | .setFooter(member.guild.name, member.guild.iconURL()) 80 | .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) 81 | .addField(`Member`, `<@${member.user.id}> (${member.user.id})`) 82 | .addField( 83 | `Account Age`, 84 | `Created ${moment(member.user.createdAt).fromNow()}` 85 | ) 86 | .setColor( 87 | `${ 88 | Date.now() - member.user.createdAt < 60000 * 60 * 24 * 7 89 | ? '#FF0000' 90 | : '#00FF00' 91 | }` 92 | ); //sets the color to red if the account age is less then a week else it sets it to green 93 | logsChan.send({ embed: embed }).catch(err => {}); 94 | member.user 95 | .send( 96 | `Hello ${member.user.username}, 97 | Welcome to ${member.guild.name}. This server is protected by ${ 98 | client.user.username 99 | }. To verify your account, please visit https://${ 100 | config.website.domain 101 | }/verify/${member.guild.id}\nYou have 15 minutes to complete verification! 102 | With kind regards, the ${member.guild.name} staff team.` 103 | ) 104 | .catch(err => { 105 | warningChan.send( 106 | `Hi <@${ 107 | member.user.id 108 | }>, it looks like your DM (s) are disabled, please enable them and use the \`${prefix}verify\` command` 109 | ); 110 | }); 111 | warningChan 112 | .send( 113 | `Hi <@${ 114 | member.user.id 115 | }>, to participate in this server, you must verify your account. Please read the DM I sent to you carefully. You have 15 minutes to complete verification!` 116 | ) 117 | .catch(err => {}); 118 | //totally didnt steal these messages from AltDentifier 119 | setTimeout(function() { 120 | if (!member) return; 121 | if (db.get(`verified_${guild.id}_${user.id}`) || false) { 122 | return; 123 | } else { 124 | let kicked = true; 125 | member.user 126 | .send('You have been kicked from the server for not responding!') 127 | .catch(err => {}); 128 | member.kick().catch(err => { 129 | kicked = false; 130 | }); 131 | let embed = new Discord.MessageEmbed() 132 | .setTitle(`Verification Logs`) 133 | .setDescription(`Member kicked`) 134 | .setFooter(member.guild.name, member.guild.iconURL()) 135 | .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) 136 | .addField(`Member`, `<@${member.user.id}> (${member.user.id})`) 137 | .addField('Reason', 'Member did not respond') 138 | .setColor('#00FF00'); 139 | 140 | let embed2 = new Discord.MessageEmbed() 141 | .setTitle(`Verification Logs`) 142 | .setDescription(`Failed to kick member`) 143 | .setFooter(member.guild.name, member.guild.iconURL()) 144 | .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) 145 | .addField(`Member`, `<@${member.user.id}> (${member.user.id})`) 146 | .addField('Reason', 'Member did not respond') 147 | .setColor('#FF0000'); 148 | if (kicked) return logsChan.send({ embed: embed }); 149 | else return logsChan.send({ embed: embed2 }); 150 | } 151 | }, 60000 * 15); 152 | }); 153 | 154 | client.on('guildMemberRemove', async member => { 155 | db.delete(`ip_${member.guild.id}_${member.user.id}`); 156 | db.delete(`verified_${member.guild.id}_${member.user.id}`); 157 | }); 158 | 159 | client.login(token).catch(err => { 160 | console.log('[ERROR]: Invalid Token Provided'); 161 | }); 162 | dashboard(client); 163 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verification-bot", 3 | "version": "1.0.0", 4 | "description": "an Advanced Verification Bot for Discord, has dashboard, online captcha etc.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "legendjs", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "deepmerge": "^4.2.2", 15 | "discord.js": "^12.5.3", 16 | "ejs": "^3.1.6", 17 | "express": "^4.17.1", 18 | "express-session": "^1.17.2", 19 | "is-mergeable-object": "^1.1.1", 20 | "memorystore": "^1.6.6", 21 | "moment": "^2.29.1", 22 | "ms": "^2.1.3", 23 | "node-fetch": "^2.6.1", 24 | "passport": "^0.4.1", 25 | "passport-discord": "^0.1.4", 26 | "quick.db": "^7.1.3" 27 | } 28 | } 29 | --------------------------------------------------------------------------------