diff --git a/.gitignore b/.gitignore index 371b02f..bca4b1a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -# ignore config -config.js - # ignore bot auth link link.txt diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..37d49c8 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +service: node main.js +web: echo "Web Dyno Unnecessary" diff --git a/config.js b/config.js new file mode 100644 index 0000000..d8359a8 --- /dev/null +++ b/config.js @@ -0,0 +1,53 @@ +const config = { + // Bot Owner, level 10 by default. A User ID. Should never be anything else than the bot owner's ID. + //"ownerID": "NNNNNNNNNNNNNNNNNN", + + // Bot Token + //"token": "YOUR TOKEN HERE", + // Prefix for bot commands + "prefix": "&", + + // Application Channel Settings + //"listener": "NNNNNNNNNNNNNNNNNN", + //"webhook_id": "NNNNNNNNNNNNNNNNNN", + + // Google Form Settings + "discord_tag": "Discord Username", + "character_server_name": "Character & Realm Name", + "battle_tag": "Battle Tag", + + // Internal Category/Channel Settings + "internal" : { + //"category": "NNNNNNNNNNNNNNNNNN", + "channel_prefix": "Internal-", + // base ranks to give internal persmissions to + "ranks": ["Discord Admin", "Officer", "Gilthridge"], + // gives bots higher permissions than the above + "bots": ["BOTS"] + }, + + // Open/Applicant Category/Channel Settings + "open" : { + "channel": "TRUE", + //"category": "NNNNNNNNNNNNNNNNNN", + "channel_prefix": "Open-", + // base ranks to give internal persmissions to + "ranks": ["Discord Admin", "Officer", "Gilthridge"], + // gives bots higher permissions than the above + "bots": ["BOTS"] + }, + + "auto_role" : { + "enabled": "TRUE", + "role": "Applicant" + }, + + "language": { + "create_channel": { + "reason": "New Application", + "topic": "New Application by %user%" + } + } + }; + + module.exports = config; diff --git a/config.js.example b/config.js.example deleted file mode 100644 index 61f0102..0000000 --- a/config.js.example +++ /dev/null @@ -1,52 +0,0 @@ -// This is an example of the configuration file you need to setup and use. Please note you need to rename this file to -// config.js after making the appropriate edits to the below. Many fields have been left as defaults, the rest should -// all have explanations of what is needed in the readme file. - -const config = { - // Bot Owner, level 10 by default. A User ID. Should never be anything else than the bot owner's ID. - "ownerID": "NNNNNNNNNNNNNNNNNN", - - // Bot Token - "token": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - // Prefix for bot commands - "prefix": "&", - - // Application Channel Settings - "listener": "NNNNNNNNNNNNNNNNNN", - "webhook_id": "NNNNNNNNNNNNNNNNNN", - - // Google Form Settings - "discord_tag": "Discord Tag", - "character_server_name": "Character & Realm", - "battle_tag": "BattleTag", - - // Internal Category/Channel Settings - "internal" : { - "category": "NNNNNNNNNNNNNNNNNN", - "channel_prefix": "", - // base ranks to give internal persmissions to - "ranks": ["Officer","Elite","Veteran","Senior","Raider"], - // gives bots higher permissions than the above - "bots": ["Bots"] - }, - - // Open/Applicant Category/Channel Settings - "open" : { - "channel": "TRUE", - "category": "NNNNNNNNNNNNNNNNNN", - "channel_prefix": "", - // base ranks to give internal persmissions to - "ranks": ["Officer"], - // gives bots higher permissions than the above - "bots": ["Bots"] - }, - - "language": { - "create_channel": { - "reason": "New Application", - "topic": "New Application by %user%" - } - } - }; - - module.exports = config; \ No newline at end of file diff --git a/events/process-app.js b/events/process-app.js index 390926b..29b8df9 100644 --- a/events/process-app.js +++ b/events/process-app.js @@ -2,95 +2,106 @@ const Discord = require('discord.js') const fs = require('fs') const config = require('@root/config.js') -async function run (client, message) { +async function run (client, message) { const { author, guild } = message - - // EXTRACT DATA - - // Loops through embed fields to find specified character/realm and discord labels from config file. + + // EXTRACT DATA + + // Loops through embed fields to find specified character/realm and discord labels from config file. var fieldsLength = message.embeds[0].fields.length - for (var i = 0; i < fieldsLength; i++) { - if (message.embeds[0].fields[i].name == config.character_server_name) { + for (var i = 0; i < fieldsLength; i++) { + if (message.embeds[0].fields[i].name == config.character_server_name) { // Gets the character and realm from the embed message the bot sends characterServerName = message.embeds[0].fields[i].value } - if (message.embeds[0].fields[i].name == config.discord_tag) { + if (message.embeds[0].fields[i].name == config.discord_tag) { // Gets the discord tag of the applicant and finds the user ID userTag = message.embeds[0].fields[i].value } - if (message.embeds[0].fields[i].name == config.battle_tag) { + if (message.embeds[0].fields[i].name == config.battle_tag) { // Applicants battle tag battleTag = message.embeds[0].fields[i].value - } - } - + } + } + // Takes userTag from application and gets the user object + await guild.members.fetch() let userObj = client.users.cache.find(user => user.tag === userTag) + console.log(userObj) + console.log(client.users.cache) + // Sets user.id. If they gave wrong discord tag or it cannot be found, this will send the error message to the log file - if(userObj) { - userObj = userObj.id - } else { + if(userObj) { + userObj = userObj.id + } else { // Gets current date and time - var date = Date() - fs.appendFileSync('./logs/log.txt', 'Invalid Discord user tag, ' + userTag + ', for applicant ' + characterServerName + ' at ' + date + '\n') + var date = Date() + fs.appendFileSync('./logs/log.txt', 'Invalid Discord user tag, ' + userTag + ', for applicant ' + characterServerName + ' at ' + date + '\n') } + // AUTO ROLE + if(config.auto_role.enabled == "TRUE") { + if(userObj) { + var role = guild.roles.cache.find(role => role.name === config.auto_role.role); + guild.member(userObj).roles.add(role); + } + } // CHANNEL WORK // Checks to make sure internal category is set properly - let internal_category = guild.channels.cache.get(config.internal.category) + let internal_category = guild.channels.cache.get(process.env.INTERNAL_CATEGORY_ID || config.internal.category) if(!internal_category || !internal_category.type === 'category') { fs.appendFileSync('./logs/errorlog.txt', 'Internal category is not a valid category.') console.error('Internal category is not a valid category.') return - } + } // Checks to make sure open category is set properly - const open_category = guild.channels.cache.get(config.open.category) + const open_category = guild.channels.cache.get(process.env.OPEN_CATEGORY_ID || config.open.category) /* if(!open_category || !open_category.type === 'category') { fs.appendFileSync('./errorlog.txt', 'Open category is not a valid category.'); console.error('Open category is not a valid category.'); return; - } + } */ // Sets the parent category if there is one set in config if(!open_category || !open_category.type === 'category') { fs.appendFileSync('./logs/errorlog.txt', 'Open category is invalid or not set in config. Placing Open applications in base discord channel.' + '\n') - console.error('Open category is invalid or not set in config. Placing Open applications in base discord channel.') + console.error('Open category is invalid or not set in config. Placing Open applications in base discord channel.') } - + // PERMISSIONS - + // Sets roles from config for rank setup // Internal ranks var iranks = [] - for (var i = 0; i < config.internal.ranks.length; i++) { + for (var i = 0; i < config.internal.ranks.length; i++) { iranks.push(message.guild.roles.cache.find(role => role.name === config.internal.ranks[i])) } - + // Internal bot ranks var iranksbots = [] - for (var i = 0; i < config.internal.bots.length; i++) { + for (var i = 0; i < config.internal.bots.length; i++) { iranksbots.push(message.guild.roles.cache.find(role => role.name === config.internal.bots[i])) } // Open ranks var oranks = [] - for (var i = 0; i < config.open.ranks.length; i++) { + for (var i = 0; i < config.open.ranks.length; i++) { oranks.push(message.guild.roles.cache.find(role => role.name === config.open.ranks[i])) } // Open bot ranks var oranksbots = [] - for (var i = 0; i < config.open.bots.length; i++) { + for (var i = 0; i < config.open.bots.length; i++) { oranksbots.push(message.guild.roles.cache.find(role => role.name === config.open.bots[i])) - } - + } + // Creates overwrites array for open channel creation const open_overwrites = [{ // everyone @@ -115,8 +126,8 @@ async function run (client, message) { if (config.open.channel == "TRUE") { // Conditonal based on if the user inputted a proper discord ID if(userObj) { - open_overwrites.push({ - id: userObj, + open_overwrites.push({ + id: userObj, allow: ['SEND_MESSAGES','VIEW_CHANNEL'] }) } @@ -135,32 +146,32 @@ async function run (client, message) { id : oranksbots[i], allow: ['SEND_MESSAGES','VIEW_CHANNEL','MANAGE_MESSAGES'] }) - } + } // Creates the open channel const openAppChannel = await guild.channels.create(config.open.channel_prefix + characterServerName, { - type: 'text', + type: 'text', permissionOverwrites: open_overwrites, parent: open_category, topic: config.language.create_channel.topic.replace('%user%', characterServerName) }) - // Copies the embed and sends to open app channel - let openEmbed = await openAppChannel.send(new Discord.MessageEmbed(message.embeds[0])) - + // Copies the embed and sends to open app channel + let openEmbed = await openAppChannel.send(new Discord.MessageEmbed(message.embeds[0])) + // Gets current date and time var date = Date() - fs.appendFileSync('./logs/log.txt', 'Applcation submitted for: ' + userTag + ' / ' + characterServerName + ' at ' + date + '\n', function (err) { + fs.appendFileSync('./logs/log.txt', 'Applcation submitted for: ' + userTag + ' / ' + characterServerName + ' at ' + date + '\n', function (err) { if (err) { fs.appendFileSync('./logs/errorlog.txt', err + ' at ' + date + '\n') return console.log(err) } - }) - + }) + // Prints error to their specific channel if they used incorrect discord tag - if(!userObj) { - // :x: + if(!userObj) { + // :x: openEmbed.react('❌') openAppChannel.send('Invalid Discord user tag, ' + userTag + ', for applicant ' + characterServerName + '. This could be due to the wrong discord tag or incorrect format of discord tag. It also may be due to that user not being cached yet. In which case you need to manually add the user to the channel permissions.') } else { @@ -209,9 +220,9 @@ async function run (client, message) { // INTERNAL CHANNEL - // Creates the internal channel + // Creates the internal channel const internalAppChannel = await guild.channels.create(config.internal.channel_prefix + characterServerName, { - type: 'text', + type: 'text', permissionOverwrites: internal_overwrites, parent: internal_category, topic: config.language.create_channel.topic.replace('%user%', characterServerName) @@ -219,9 +230,9 @@ async function run (client, message) { // MESSAGE SEND AND CLEAN UP - // Copies embed and sends to internal app channel - internalAppChannel.send(new Discord.MessageEmbed(message.embeds[0])) - + // Copies embed and sends to internal app channel + internalAppChannel.send(new Discord.MessageEmbed(message.embeds[0])) + // Deletes message from original applications category // 1000 = 1 sec message.delete({ timeout: 1000}) diff --git a/features/features/app-base.js b/features/features/app-base.js index 1d602e8..5697610 100644 --- a/features/features/app-base.js +++ b/features/features/app-base.js @@ -1,16 +1,16 @@ const config = require('@root/config.js') const processApp = require('@events/process-app.js') -const listenerChannel = config.listener -const webhookId = config.webhook_id +const listenerChannel = process.env.TRIGGER_CHANNEL_ID || config.listener +const webhookId = process.env.WEBHOOK_ID || config.webhook_id const webhookTestId = config.test_webhook_id module.exports = (client) => { client.on('message', (message) => { - const { channel } = message - + const { channel } = message + // if not one of our application webhooks, ignore it and exit out - if ((message.author.id !== webhookId) && (message.author.id !== webhookTestId)) return + if ((message.author.id !== webhookId) && (message.author.id !== webhookTestId)) return // checks if webhook is not setup in config if (!webhookId) { @@ -25,6 +25,6 @@ module.exports = (client) => { } // at this point we know its our app webhooks so call processApp - processApp(client, message) + processApp(client, message) }) -} \ No newline at end of file +} diff --git a/main.js b/main.js index 6db8399..eee0dd8 100644 --- a/main.js +++ b/main.js @@ -3,29 +3,39 @@ require('module-alias/register') const path = require('path') const Commando = require('discord.js-commando') +const fs = require('fs-extra') + const config = require('@root/config.js') const loadFeatures = require('@root/features/load-features') +// Set up logging files +var log = './logs/log.txt'; +var errorlog = './logs/errorlog.txt' + +fs.ensureFileSync(log) +fs.ensureFileSync(errorlog) + +//Create Discord Client const client = new Commando.CommandoClient({ - owner: config.ownerID, + owner: process.env.OWNER_ID || config.ownerID, commandPrefix: config.prefix }) -client.on('ready', async () => { +client.on('ready', async () => { client.registry - .registerGroups([ - ['moderation', 'Moderation commands'], + .registerGroups([ + ['moderation', 'Moderation commands'], ['applications', 'Application Commands'], ]) .registerDefaults() .registerCommandsIn(path.join(__dirname, 'cmds')) - + loadFeatures(client) - console.log('The client is ready!') + console.log('The client is ready!') }) module.exports.client = client -client.login(config.token) \ No newline at end of file +client.login(process.env.TOKEN || config.token) diff --git a/package-lock.json b/package-lock.json index 531b391..5eec503 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "Tutorial", - "version": "1.0.0", + "name": "Application-Bot", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -56,6 +56,11 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "axios": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", @@ -269,13 +274,20 @@ } }, "discord.js-commando": { - "version": "git+https://github.com/discordjs/commando.git#198d7604e3725ee88dceab9b5f296edb1b7580a5", + "version": "git+https://github.com/discordjs/commando.git#9c2e5405fdb9a1006e6eb7554e18d0861a4c857f", "from": "git+https://github.com/discordjs/commando.git", "requires": { "common-tags": "^1.8.0", + "emoji-regex": "^9.2.0", + "is-promise": "^4.0.0", "require-all": "^3.0.0" } }, + "emoji-regex": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", + "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==" + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -286,6 +298,17 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs-minipass": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", @@ -327,6 +350,11 @@ "path-is-absolute": "^1.0.0" } }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -375,11 +403,25 @@ "number-is-nan": "^1.0.0" } }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "kareem": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", @@ -868,6 +910,11 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index ae96003..29f6eea 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "commando-provider-mongo": "^1.1.1", "discord.js": "^12.3.1", "discord.js-commando": "git+https://github.com/discordjs/commando.git", + "fs-extra": "^9.1.0", "module-alias": "^2.2.2", "mongoose": "^5.10.11" }, @@ -24,8 +25,8 @@ "@schemas": "./schemas", "@util": "./util", "@images": "./images", - "@features": "./features/features", - "@cmds": "./cmds", + "@features": "./features/features", + "@cmds": "./cmds", "@events": "./events", "@rules_regs": "./rules regs" }