diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..46d619e --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +TOKEN="discord bot token here" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de56e8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules/ +docker-compose.yml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2d5d7f6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM node:12-alpine + +COPY package*.json ./ +RUN npm install +COPY . ./ + +CMD npm start \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1480649 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Simple Discord Verification Bot + +Originally created for the [Domaincord Community](https://discord.gg/R6wTYQ9). + +## Usage +I'm assuming you already know how to get a bot token and add the bot to your server. + +1. Rename the `.env.example` file to `.env` and fill in the appropriate values. +2. Edit the files `intro-message.md`, `community-guidelines.md`, and `verification-message.md` to add your own server rules and custom verification message. Do not change the filenames. +3. Open `index.js` and edit the values of the variables on Lines 2-5. Don't touch anything else unless you know what you are doing. +4. Run `npm install` +5. Run `npm start` or use a process manager like [pm2](https://pm2.keymetrics.io/) to keep the bot running across server restarts and automatically restart the bot if it crashes. +6. As the guild owner, run the command `!svm` in the channel you want designated as the verification channel. **Make sure the bot has access to send messages, delete messages, and react to messages in this channel!** + +## Command Output +![screenshot of command output](https://i.imgur.com/WtcdYsM.png) diff --git a/cloudbuild.yml b/cloudbuild.yml new file mode 100644 index 0000000..2a36827 --- /dev/null +++ b/cloudbuild.yml @@ -0,0 +1,24 @@ +steps: + # Build the container image + - name: "gcr.io/cloud-builders/docker" + args: ["build", "-t", "gcr.io/$PROJECT_ID/${_REPO}", "."] + # Push the container image to Container Registry + - name: "gcr.io/cloud-builders/docker" + args: ["push", "gcr.io/$PROJECT_ID/${_REPO}"] + # Update existing compute engine vm instance with new Docker image + - name: "gcr.io/cloud-builders/gcloud" + args: + [ + "compute", + "instances", + "update-container", + "${_REPO}", + "--zone", + "${_ZONE}", + "--container-image=gcr.io/$PROJECT_ID/${_REPO}", + ] +substitutions: + _REPO: "verification-bot" + _ZONE: "us-east1-b" +timeout: "1600s" +images: ["gcr.io/$PROJECT_ID/${_REPO}"] diff --git a/community-guidelines.md b/community-guidelines.md new file mode 100644 index 0000000..5c6ecd6 --- /dev/null +++ b/community-guidelines.md @@ -0,0 +1,9 @@ +**TL;DR** Use Common sense. 🧠 If you think you'll get a warning for it, don't do it! + +🌸 **Be kind** and friendly to all members. + +💬 **Actively contribute** to topical discussions, but **avoid discussing things that are generally considered illegal, offensive, insulting, or disturbing**. + +🛑 Try to **avoid conflict** as much as possible in your public messages. If you happen to offend someone, politely talking to them via DM is a good way to work out any issues. + +📢 We love to help you grow your business and gain loyal new customers, but **please refrain from directly advertising your products or services without first asking permission** from an admin. diff --git a/index.js b/index.js new file mode 100644 index 0000000..9b32282 --- /dev/null +++ b/index.js @@ -0,0 +1,121 @@ +// Some hardcoded variables that you can change +const prefix = "!" +const guild_id = "488540747361026058" +const verified_role_id = "652669689683509249" +const welcome_emoji_id = "637434582500900864" +const reaction_emoji_id = "720420182538977281" + +// Don't touch the code beflow this line! +if (process.env.NODE_ENV === 'development') { + require('dotenv').config() +} +const fs = require('fs') +const Discord = require('discord.js') +const client = new Discord.Client() +let roleName = "" + +client.on('ready', async () => { + console.log(`Logged in as ${client.user.tag}!`) + await client.user.setActivity('Verify in #verification'); + roleName = client.guilds.get(guild_id).roles.get(verified_role_id).name +}) + +client.on('message', msg => { + + const args = msg.content + .slice(prefix.length) + .trim() + .split(/ +/g) + + const command = args + .shift() + .toLowerCase() + .replace('/', '') + + // svm = set verification message + if ( + !msg.author.bot + && msg.content.indexOf(prefix) === 0 + && command === 'svm' + ) { + + // If sender of message is not the guild owner, cancel action + if (msg.member.guild.owner.id !== msg.member.id) return + + const introMessageContent = fs.readFileSync('intro-message.md', {encoding:'utf8', flag:'r'}) + const communityGuidelinesContent = fs.readFileSync('community-guidelines.md', {encoding:'utf8', flag:'r'}) + const verificationMessageContent = fs.readFileSync('verification-message.md', {encoding:'utf8', flag:'r'}) + + const embed = new Discord.RichEmbed() + + const welcomeEmoji = `<:${msg.guild.emojis.get(welcome_emoji_id).identifier}>` + const welcomeTitle = `${welcomeEmoji} Welcome to ${msg.guild.name}!` + + embed.addField(welcomeTitle, introMessageContent) + embed.addField('🎗 Community Guidelines', communityGuidelinesContent) + embed.addField('🔐 Getting Verified', verificationMessageContent) + + msg.channel.send({embed}).then(theVerificationMessage => theVerificationMessage.react(reaction_emoji_id)) + msg.delete() + } + + return +}) + +client.on('messageReactionAdd', ( { message: { channel } }, user ) => { + if (/verification/.test(channel.name)) { + channel.guild.fetchMember(user).then(member => { + return member.addRole(verified_role_id) + }).then(() => { + console.log(`The ${roleName} role has been added to member ${user.tag} successfully!`) + }).catch(error => { + console.error(error) + }) + } +}) + +client.on('messageReactionRemove', ( { message: { channel } }, user ) => { + if (/verification/.test(channel.name)) { + channel.guild.fetchMember(user).then(member => { + return member.removeRole(verified_role_id) + }).then(() => { + console.log(`The ${roleName} has been removed from member ${user.tag} successfully!`) + }).catch(error => { + console.error(error) + }) + } +}) + +client.on('raw', ({ d: data, t: event }) => { + if (['MESSAGE_REACTION_ADD', 'MESSAGE_REACTION_REMOVE'].includes(event)) { + const { channel_id, user_id, message_id, emoji } = data + + const channel = client.channels.get(channel_id) + + if (!channel.messages.has(message_id)) channel.fetchMessage( + message_id + ).then( message => { + const reaction = message.reactions.get( + emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name + ) + + const user = client.users.get(user_id) + + if (reaction) reaction.users.set(user_id, user) + + return client.emit( + event === 'MESSAGE_REACTION_ADD' + ? 'messageReactionAdd' + : 'messageReactionRemove', + reaction, + user + ) + }) + } +}) + +if (process.env.TOKEN !== null) { + client.login(process.env.TOKEN) +} else { + console.error('Bot token is empty!') +} \ No newline at end of file diff --git a/intro-message.md b/intro-message.md new file mode 100644 index 0000000..0965a4d --- /dev/null +++ b/intro-message.md @@ -0,0 +1,3 @@ +_Find Your Brand._ + +We are a dedicated community that brings together domain name enthusiasts and entrepreneurs of all experience levels. Get help with setting up confusing DNS records, ask questions about web hosting, and connect with like-minded individuals to discuss things going on around the web. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..29cb058 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,57 @@ +{ + "name": "domaincord-verification-bot", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "discord.js": { + "version": "11.6.4", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.6.4.tgz", + "integrity": "sha512-cK6rH1PuGjSjpmEQbnpuTxq1Yv8B89SotyKUFcr4RhnsiZnfBfDOev7DD7v5vhtEyyj51NuMWFoRJzgy/m08Uw==", + "requires": { + "long": "^4.0.0", + "prism-media": "^0.0.4", + "snekfetch": "^3.6.4", + "tweetnacl": "^1.0.0", + "ws": "^6.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "prism-media": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.4.tgz", + "integrity": "sha512-dG2w7WtovUa4SiYTdWn9H8Bd4JNdei2djtkP/Bk9fXq81j5Q15ZPHYSwhUVvBRbp5zMkGtu0Yk62HuMcly0pRw==" + }, + "snekfetch": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz", + "integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw==" + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c729d14 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "domaincord-verification-bot", + "private": false, + "dependencies": { + "discord.js": "^11.5.1", + "dotenv": "^8.2.0" + }, + "scripts": { + "start": "node index.js", + "dev": "NODE_ENV=development nodemon index.js" + } +} diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml new file mode 100644 index 0000000..98a5470 --- /dev/null +++ b/shrinkwrap.yaml @@ -0,0 +1,64 @@ +dependencies: + discord.js: 11.6.4 + dotenv: 8.2.0 +packages: + /async-limiter/1.0.1: + dev: false + resolution: + integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + /discord.js/11.6.4: + dependencies: + long: 4.0.0 + prism-media: 0.0.4 + snekfetch: 3.6.4 + tweetnacl: 1.0.3 + ws: 6.2.1 + dev: false + engines: + node: '>=6.0.0' + peerDependencies: + '@discordjs/opus': ^0.1.0 + '@discordjs/uws': ^10.149.0 + bufferutil: ^4.0.0 + erlpack: discordapp/erlpack + libsodium-wrappers: ^0.7.3 + node-opus: ^0.2.7 + opusscript: ^0.0.6 + sodium: ^2.0.3 + resolution: + integrity: sha512-cK6rH1PuGjSjpmEQbnpuTxq1Yv8B89SotyKUFcr4RhnsiZnfBfDOev7DD7v5vhtEyyj51NuMWFoRJzgy/m08Uw== + /dotenv/8.2.0: + dev: false + engines: + node: '>=8' + resolution: + integrity: sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + /long/4.0.0: + dev: false + resolution: + integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + /prism-media/0.0.4: + dev: false + resolution: + integrity: sha512-dG2w7WtovUa4SiYTdWn9H8Bd4JNdei2djtkP/Bk9fXq81j5Q15ZPHYSwhUVvBRbp5zMkGtu0Yk62HuMcly0pRw== + /snekfetch/3.6.4: + deprecated: use node-fetch instead + dev: false + resolution: + integrity: sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw== + /tweetnacl/1.0.3: + dev: false + resolution: + integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + /ws/6.2.1: + dependencies: + async-limiter: 1.0.1 + dev: false + resolution: + integrity: sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== +registry: 'https://registry.npmjs.org/' +shrinkwrapMinorVersion: 9 +shrinkwrapVersion: 3 +specifiers: + discord.js: ^11.5.1 + dotenv: ^8.2.0 diff --git a/verification-message.md b/verification-message.md new file mode 100644 index 0000000..5f79ac9 --- /dev/null +++ b/verification-message.md @@ -0,0 +1,3 @@ +**To agree to the rules and receive permission to view all of our channels and send messages, click on the reaction below.** You are responsible for reading the contents of this channel before agreeing. + +We (Domaincord or Domaincord Staff) can not and will not be held liable for any damages or losses that result from your use of this server, the provided channels, direct messages, or any other form of communication or transaction that arise from using the aforementioned for any purpose. You agree to this by using this server.