├── .gitignore ├── LICENSE.txt ├── README.md ├── example ├── config.json └── images │ ├── emojis │ ├── crying.png │ ├── happy.png │ ├── poo.png │ └── sad.png │ └── icon.png ├── index.js ├── package.json ├── setup ├── channels.js ├── emojis.js ├── index.js ├── roles.js └── server.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | node_modules/ 3 | *.example.* -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Victor Truong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [create-discord-server](https://www.npmjs.com/package/create-discord-server) 2 | 3 | Set up an entire Discord server from a configuration file. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | # Use npm… 9 | $ npm i -g create-discord-server 10 | 11 | # …or Yarn 12 | $ yarn global add create-discord-server 13 | 14 | # …or npx (for one-off uses) 15 | $ npx create-discord-server [options] 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```bash 21 | Usage: create-discord-server [options] 22 | 23 | Set up an entire Discord server from a configuration file. 24 | 25 | Options: 26 | 27 | -V, --version output the version number 28 | -f, --file File containing server configuration 29 | -i, --server-id ID of the server to set up 30 | -t, --token Token to use when connecting to Discord API 31 | -h, --help output usage information 32 | ``` 33 | 34 | ## Setup 35 | 36 | ### Populating the configuration file 37 | 38 | Below is a detailed description of all the configurations that can be set. If a key isn't used, it can be omitted. 39 | 40 | #### `server` (`object`) 41 | 42 | - `afkTimeout` (`number`): Move user into AFK after x seconds (60, 300, 900, 1800, 3600) 43 | - `explicitContentFilter` (`number`): The level of the explicit content filter (0-2) 44 | - `icon` (`string`): Filepath of the server icon 45 | - `name` (`string`): Name of the server 46 | - `region` (`string`): Region of the server 47 | - `splash` (`string`): Filepath of the server splash 48 | - `verificationLevel` (`number`): The level of security in the server (0-4) 49 | 50 | #### `channels` (`array`) 51 | 52 | Each value needs to be a channel name (`string`) or an object that follows the below structure: 53 | 54 | - `name` (`string`): Name of the channel 55 | - `overwrites` (`array`): Permission overwrites 56 | - `reason` (`string`): Why this channel exists 57 | - `type` (`string`): Type of channel (category, text, voice) 58 | 59 | #### `emojis` (`array`) 60 | 61 | Each value needs to be an object following the below structure: 62 | 63 | - `image` (`string`): Filepath of the emoji 64 | - `name` (`string`): Name of the emoji 65 | - `reason` (`string`): Why this emoji exists 66 | - `roles` (`array`): Roles to limit the emoji to 67 | 68 | #### `roles` (`array`) 69 | 70 | Each value needs to be a role name (`string`) or an object that follows the below structure: 71 | 72 | - `color` (`string`): Hex color of the role 73 | - `hoist` (`boolean`): If this role is pinned in the user listing 74 | - `mentionable` (`boolean`): If the role can be mentioned 75 | - `name` (`string`): Name of the role 76 | - `permissions` (`array`): Permissions of the role 77 | - `position` (`number`): Position of this role 78 | 79 | You can also refer to the [example config](example/config.json) to get an idea of how to set up your config. 80 | 81 | ### Obtaining the server ID 82 | 83 | 84 | 85 | ### Obtaining the token 86 | 87 | 1. Go to and click on `Create an application`. 88 | 2. After the application is created, click on `Settings > Bot` and you will find the token under the Build-A-Bot section. This will be used for the `token` setting. 89 | 90 | ### Adding the bot to your server 91 | 92 | create-discord-server sets up your server through Discord's API, which is accessible through a Discord bot. The following guide will walk you through how to add it to your server: 93 | 94 | ### Granting the bot permission 95 | 96 | Initially, the bot will only have the permissions of a regular user. You will need to grant the bot additional permissions via a custom role. The Discord team has written instructions on how to do so here: . Give the bot permissions based on your use case (e.g. setting up channels = grant permission to manage channels). 97 | 98 | After all of the above has been completed, run `create-discord-server` with your token and server ID, and you're finished! 99 | 100 | ## License 101 | 102 | [MIT](LICENSE.txt) -------------------------------------------------------------------------------- /example/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "name": "My Server", 4 | "icon": "./images/icon.png" 5 | }, 6 | "channels": [ 7 | "hello", 8 | { 9 | "name": "random", 10 | "type": "text" 11 | }, 12 | { 13 | "name": "talk", 14 | "type": "voice" 15 | } 16 | ], 17 | "emojis": [ 18 | { 19 | "name": "crying", 20 | "image": "./images/emojis/crying.png" 21 | }, 22 | { 23 | "name": "happy", 24 | "image": "./images/emojis/happy.png" 25 | }, 26 | { 27 | "name": "poo", 28 | "image": "./images/emojis/poo.png" 29 | }, 30 | { 31 | "name": "sad", 32 | "image": "./images/emojis/sad.png" 33 | } 34 | ], 35 | "roles": [ 36 | { 37 | "name": "Admin", 38 | "color": "#ff0000" 39 | }, 40 | { 41 | "name": "Moderator", 42 | "color": "#00ff00" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /example/images/emojis/crying.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/create-discord-server/7e39e58d53714b7e25f19130f8cfa926309c63ac/example/images/emojis/crying.png -------------------------------------------------------------------------------- /example/images/emojis/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/create-discord-server/7e39e58d53714b7e25f19130f8cfa926309c63ac/example/images/emojis/happy.png -------------------------------------------------------------------------------- /example/images/emojis/poo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/create-discord-server/7e39e58d53714b7e25f19130f8cfa926309c63ac/example/images/emojis/poo.png -------------------------------------------------------------------------------- /example/images/emojis/sad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/create-discord-server/7e39e58d53714b7e25f19130f8cfa926309c63ac/example/images/emojis/sad.png -------------------------------------------------------------------------------- /example/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/create-discord-server/7e39e58d53714b7e25f19130f8cfa926309c63ac/example/images/icon.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const program = require('commander') 3 | const Discord = require('discord.js') 4 | const fs = require('fs') 5 | const ora = require('ora') 6 | const path = require('path') 7 | const setup = require('./setup') 8 | const package = require('./package') 9 | 10 | const findSetting = key => program[key] || config[key] // Search in config file if parameter isn't specified in command 11 | 12 | program 13 | .version(package.version) 14 | .description(package.description) 15 | .option('-f, --file ', 'File containing server configuration') 16 | .option('-i, --server-id ', 'ID of the server to set up') 17 | .option('-t, --token ', 'Token to use when connecting to Discord API') 18 | .parse(process.argv) 19 | 20 | const spinner = ora('Starting create-discord-server').start() 21 | 22 | if (!program.file) { 23 | spinner.fail('`file` parameter is missing') 24 | process.exit(1) 25 | } 26 | else if (!fs.existsSync(program.file)) { 27 | spinner.fail('Specified file doesn’t exist') 28 | process.exit(1) 29 | } 30 | const config = JSON.parse(fs.readFileSync(program.file)) 31 | const configDir = path.dirname(path.resolve(program.file)) 32 | process.chdir(configDir) // Work in the user's config directory 33 | 34 | const serverId = findSetting('serverId') 35 | if (!serverId) { 36 | spinner.fail('No server ID was found') 37 | process.exit(1) 38 | } 39 | const token = findSetting('token') 40 | if (!token) { 41 | spinner.fail('No token was found') 42 | process.exit(1) 43 | } 44 | 45 | const client = new Discord.Client() 46 | client.login(token) 47 | 48 | client.on('ready', async () => { 49 | spinner.info('Connected to Discord API') 50 | const workingGuild = client.guilds.get(serverId) 51 | const excludedKeys = ['serverId', 'token'] 52 | const settingsToProcess = Object.keys(config).filter(key => ( 53 | !excludedKeys.includes(key) && 54 | (typeof config[key] === 'object' && Object.keys(config[key]).length > 0) && 55 | typeof setup[key] === 'function' // Check if setting has a handler 56 | )) 57 | await Promise.all(settingsToProcess.map(key => { 58 | const setupFn = setup[key] 59 | const setting = config[key] 60 | return setupFn(setting, workingGuild, spinner) 61 | })) 62 | 63 | spinner.succeed('All done :)') 64 | client.destroy() 65 | }) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-discord-server", 3 | "version": "1.0.1", 4 | "description": "Set up an entire Discord server from a configuration file.", 5 | "main": "./index.js", 6 | "repository": "https://github.com/ifvictr/create-discord-server", 7 | "author": "Victor Truong ", 8 | "license": "MIT", 9 | "bin": { 10 | "create-discord-server": "./index.js" 11 | }, 12 | "dependencies": { 13 | "chalk": "^2.4.1", 14 | "commander": "^2.16.0", 15 | "discord.js": "^11.3.2", 16 | "ora": "^3.0.0" 17 | } 18 | } -------------------------------------------------------------------------------- /setup/channels.js: -------------------------------------------------------------------------------- 1 | module.exports = (channels, guild, spinner) => { 2 | return Promise.all(channels.map(async channel => { 3 | try { 4 | if (typeof channel === 'string') { 5 | channel = { name: channel } 6 | } 7 | const createdChannel = await guild.createChannel( 8 | channel.name, 9 | channel.type, 10 | channel.overwrites, 11 | channel.reason 12 | ) 13 | spinner.succeed(`Created ${createdChannel.type} channel #${createdChannel.name}`) 14 | } 15 | catch (e) { 16 | spinner.fail(`Failed to create #${channel.name}`) 17 | } 18 | })) 19 | } -------------------------------------------------------------------------------- /setup/emojis.js: -------------------------------------------------------------------------------- 1 | module.exports = (emojis, guild, spinner) => { 2 | return Promise.all(emojis.map(async emoji => { 3 | try { 4 | const createdEmoji = await guild.createEmoji( 5 | emoji.image, 6 | emoji.name, 7 | emoji.roles, 8 | emoji.reason 9 | ) 10 | spinner.succeed(`Created ${createdEmoji.name} emoji`) 11 | } 12 | catch (e) { 13 | spinner.fail(`Failed to create ${emoji.name} emoji`) 14 | } 15 | })) 16 | } -------------------------------------------------------------------------------- /setup/index.js: -------------------------------------------------------------------------------- 1 | exports.channels = require('./channels') 2 | exports.emojis = require('./emojis') 3 | exports.roles = require('./roles') 4 | exports.server = require('./server') -------------------------------------------------------------------------------- /setup/roles.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | 3 | module.exports = (roles, guild, spinner) => { 4 | return Promise.all(roles.map(async role => { 5 | try { 6 | if (typeof role === 'string') { 7 | role = { name: role } 8 | } 9 | const createdRole = await guild.createRole(role, role.reason) 10 | spinner.succeed(`Created ${chalk.bgHex(createdRole.hexColor)(createdRole.name)} role`) 11 | } 12 | catch (e) { 13 | spinner.fail(`Failed to create ${role.name} role`) 14 | } 15 | })) 16 | } -------------------------------------------------------------------------------- /setup/server.js: -------------------------------------------------------------------------------- 1 | const capitalize = str => str[0].toUpperCase() + str.substring(1) 2 | 3 | module.exports = async (server, guild, spinner) => { 4 | // Was throwing a `TypeError: Cannot read property 'edit' of undefined` 5 | // return Promise.all(Object.keys(server).map(key => { 6 | // const fn = guild[`set${capitalize(key)}`] // Dynamically find the correct method 7 | // if (typeof fn !== 'function') { 8 | // return 9 | // } 10 | // return fn(server[key]) 11 | // })) 12 | try { 13 | if (Number.isInteger(server.afkTimeout)) { 14 | await guild.setAFKTimeout(server.afkTimeout) 15 | spinner.succeed(`AFK timeout set to ${server.afkTimeout}`) 16 | } 17 | if (Number.isInteger(server.explicitContentFilter)) { 18 | await guild.setExplicitContentFilter(server.explicitContentFilter) 19 | spinner.succeed(`Explicit content filter set to ${server.explicitContentFilter}`) 20 | } 21 | if (server.icon) { 22 | await guild.setIcon(server.icon) 23 | spinner.succeed('Server icon set') 24 | } 25 | if (server.name) { 26 | await guild.setName(server.name) 27 | spinner.succeed(`Server name set to ${server.name}`) 28 | } 29 | if (server.region) { 30 | await guild.setRegion(server.region) 31 | spinner.succeed(`Server region set to ${server.region}`) 32 | } 33 | if (server.splash) { 34 | await guild.setSplash(server.splash) 35 | spinner.succeed('Server splash set') 36 | } 37 | if (Number.isInteger(server.verificationLevel)) { 38 | await guild.setVerificationLevel(server.verificationLevel) 39 | spinner.succeed(`Verification level set to ${server.verificationLevel}`) 40 | } 41 | } 42 | catch (e) { 43 | spinner.warn(`Caught an exception when configuring the server: ${e.message}`) 44 | } 45 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-regex@^3.0.0: 6 | version "3.0.0" 7 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 8 | 9 | ansi-styles@^3.2.1: 10 | version "3.2.1" 11 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 12 | dependencies: 13 | color-convert "^1.9.0" 14 | 15 | async-limiter@~1.0.0: 16 | version "1.0.0" 17 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" 18 | 19 | chalk@^2.0.1, chalk@^2.3.1, chalk@^2.4.1: 20 | version "2.4.1" 21 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 22 | dependencies: 23 | ansi-styles "^3.2.1" 24 | escape-string-regexp "^1.0.5" 25 | supports-color "^5.3.0" 26 | 27 | cli-cursor@^2.1.0: 28 | version "2.1.0" 29 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 30 | dependencies: 31 | restore-cursor "^2.0.0" 32 | 33 | cli-spinners@^1.1.0: 34 | version "1.3.1" 35 | resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" 36 | 37 | clone@^1.0.2: 38 | version "1.0.4" 39 | resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" 40 | 41 | color-convert@^1.9.0: 42 | version "1.9.2" 43 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" 44 | dependencies: 45 | color-name "1.1.1" 46 | 47 | color-name@1.1.1: 48 | version "1.1.1" 49 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" 50 | 51 | commander@^2.16.0: 52 | version "2.16.0" 53 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" 54 | 55 | defaults@^1.0.3: 56 | version "1.0.3" 57 | resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" 58 | dependencies: 59 | clone "^1.0.2" 60 | 61 | discord.js@^11.3.2: 62 | version "11.3.2" 63 | resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-11.3.2.tgz#3c4074c1cb1a4916582fb96ded5ffa1324411f95" 64 | dependencies: 65 | long "^4.0.0" 66 | prism-media "^0.0.2" 67 | snekfetch "^3.6.4" 68 | tweetnacl "^1.0.0" 69 | ws "^4.0.0" 70 | 71 | escape-string-regexp@^1.0.5: 72 | version "1.0.5" 73 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 74 | 75 | has-flag@^3.0.0: 76 | version "3.0.0" 77 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 78 | 79 | log-symbols@^2.2.0: 80 | version "2.2.0" 81 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" 82 | dependencies: 83 | chalk "^2.0.1" 84 | 85 | long@^4.0.0: 86 | version "4.0.0" 87 | resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" 88 | 89 | mimic-fn@^1.0.0: 90 | version "1.2.0" 91 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 92 | 93 | onetime@^2.0.0: 94 | version "2.0.1" 95 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 96 | dependencies: 97 | mimic-fn "^1.0.0" 98 | 99 | ora@^3.0.0: 100 | version "3.0.0" 101 | resolved "https://registry.yarnpkg.com/ora/-/ora-3.0.0.tgz#8179e3525b9aafd99242d63cc206fd64732741d0" 102 | dependencies: 103 | chalk "^2.3.1" 104 | cli-cursor "^2.1.0" 105 | cli-spinners "^1.1.0" 106 | log-symbols "^2.2.0" 107 | strip-ansi "^4.0.0" 108 | wcwidth "^1.0.1" 109 | 110 | prism-media@^0.0.2: 111 | version "0.0.2" 112 | resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-0.0.2.tgz#aa917b084576c4df6488e4ea8e7d6d44aed4b411" 113 | 114 | restore-cursor@^2.0.0: 115 | version "2.0.0" 116 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 117 | dependencies: 118 | onetime "^2.0.0" 119 | signal-exit "^3.0.2" 120 | 121 | safe-buffer@~5.1.0: 122 | version "5.1.2" 123 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 124 | 125 | signal-exit@^3.0.2: 126 | version "3.0.2" 127 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 128 | 129 | snekfetch@^3.6.4: 130 | version "3.6.4" 131 | resolved "https://registry.yarnpkg.com/snekfetch/-/snekfetch-3.6.4.tgz#d13e80a616d892f3d38daae4289f4d258a645120" 132 | 133 | strip-ansi@^4.0.0: 134 | version "4.0.0" 135 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 136 | dependencies: 137 | ansi-regex "^3.0.0" 138 | 139 | supports-color@^5.3.0: 140 | version "5.4.0" 141 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 142 | dependencies: 143 | has-flag "^3.0.0" 144 | 145 | tweetnacl@^1.0.0: 146 | version "1.0.0" 147 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.0.tgz#713d8b818da42068740bf68386d0479e66fc8a7b" 148 | 149 | wcwidth@^1.0.1: 150 | version "1.0.1" 151 | resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" 152 | dependencies: 153 | defaults "^1.0.3" 154 | 155 | ws@^4.0.0: 156 | version "4.1.0" 157 | resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" 158 | dependencies: 159 | async-limiter "~1.0.0" 160 | safe-buffer "~5.1.0" 161 | --------------------------------------------------------------------------------