src/bot/config.js
'use babel';
'use strict';
/** Configuration for a bot */
export default class BotConfig {
/**
* @param {ConfigObject} [values] - The configuration to start with
* @param {ConfigObject} [defaults] - The defaults to start with
*/
constructor(values, defaults) {
this._values = {};
/** @type {ConfigObject} */
this.defaults = Object.assign({}, defaultDefaults, defaults);
this.loadDefaults();
this.values = values;
}
/**
* Adds to the current values, overwriting existing ones
* @param {ConfigObject} values - The values to add
*/
set values(values) {
Object.assign(this._values, values);
}
/** @type {ConfigObject} */
get values() {
return this._values;
}
/**
* Adds the defaults to the current values
* @param {boolean} [overwrite=false] - Whether or not the defaults should overwrite existing values
*/
loadDefaults(overwrite = false) {
if(overwrite) {
Object.assign(this._values, this.defaults);
} else {
for(const key of Object.keys(this.defaults)) {
if(!(key in this._values)) this._values[key] = this.defaults[key];
}
}
}
/**
* Loads configuration from yargs, adding a bunch of default bot options
* @param {Yargs} yargs - The yargs instance to use
* @param {boolean} [addOptions=true] - Whether or not to add options for GRAF's config
* @return {Yargs} The yargs instance
*/
yargs(yargs, addOptions = true) {
if(addOptions) {
yargs
// Authentication
.option('token', {
type: 'string',
alias: 't',
describe: 'API token for the bot account',
group: 'Authentication:'
})
// General
.option('owner', {
type: 'string',
alias: 'o',
describe: 'Discord user ID of the bot owner',
group: 'General:'
})
.option('invite', {
type: 'string',
alias: 'i',
describe: 'Discord instant invite to a server to contact the owner',
group: 'General:'
})
.option('playing-game', {
type: 'string',
default: this.defaults.playingGame,
alias: 'g',
describe: 'Text to show in the "Playing..." status',
group: 'General:'
})
.option('pagination-items', {
type: 'number',
default: this.defaults.paginationItems,
alias: 'I',
describe: 'Number of items per page in paginated commands',
group: 'General:'
})
.option('selfbot', {
type: 'boolean',
default: this.defaults.selfbot,
alias: 'B',
describe: 'Whether or not the bot should run as a selfbot',
group: 'General:'
})
.option('storage', {
type: 'string',
default: this.defaults.storage,
alias: 's',
describe: 'Path to storage directory',
group: 'General:',
normalize: true
})
.option('update-check', {
type: 'number',
default: this.defaults.updateCheck,
alias: 'U',
describe: 'How frequently to check for an update (in minutes, use 0 to disable)',
group: 'General:'
})
// Commands
.option('command-prefix', {
type: 'string',
default: this.defaults.commandPrefix,
alias: 'P',
describe: 'Default command prefix (blank to use only mentions)',
group: 'Commands:'
})
.option('command-editable', {
type: 'number',
default: this.defaults.commandEditable,
alias: 'E',
describe: 'How long a command message is editable (in seconds, use 0 to disable)',
group: 'Commands:'
})
.option('non-command-edit', {
type: 'boolean',
default: this.defaults.nonCommandEdit,
alias: 'N',
describe: 'Whether or not a non-command message can be edited into a command',
group: 'Commands:'
})
// Logging
.option('log', {
type: 'string',
default: this.defaults.log,
alias: 'l',
describe: 'Path to log file',
group: 'Logging:',
normalize: true
})
.option('log-max-size', {
type: 'number',
default: this.defaults.logMaxSize,
defaultDescription: '5MB',
alias: 'S',
describe: 'Maximum size of single log file (in bytes)',
group: 'Logging:'
})
.option('log-max-files', {
type: 'number',
default: this.defaults.logMaxFiles,
alias: 'F',
describe: 'Maximum amount of log files to keep',
group: 'Logging:'
})
.option('log-level', {
type: 'string',
default: this.defaults.logLevel,
alias: 'L',
describe: 'Log level to output to the log file (error, warn, info, verbose, message, debug)',
group: 'Logging:'
})
.option('console-level', {
type: 'string',
default: this.defaults.consoleLevel,
alias: 'C',
describe: 'Log level to output to the console (error, warn, info, verbose, message, debug)',
group: 'Logging:'
})
.option('log-messages', {
type: 'boolean',
default: false,
alias: 'M',
describe: 'Whether or not all chat messages should be logged',
group: 'Logging:'
})
// Stat sites
.option('carbon-url', {
type: 'string',
describe: 'The Carbon submission URL (for bot creators)',
group: 'Stats:'
})
.option('carbon-key', {
type: 'string',
describe: 'The Carbon key for the bot (for bot creators)',
group: 'Stats:'
})
.option('bdpw-url', {
type: 'string',
default: 'https://bots.discord.pw/api',
describe: 'The bots.discord.pw API URL (for bot creators)',
group: 'Stats:'
})
.option('bdpw-key', {
type: 'string',
describe: 'The bots.discord.pw key for the bot (for bot creators)',
group: 'Stats:'
})
.option('config', {
type: 'string',
alias: 'c',
describe: 'Path to JSON/YAML config file',
group: 'Special:',
normalize: true,
config: true,
configParser: configFile => {
const extension = require('path').extname(configFile).toLowerCase();
if(extension === '.json') {
return JSON.parse(require('fs').readFileSync(configFile));
} else if(extension === '.yml' || extension === '.yaml') {
return require('js-yaml').safeLoad(require('fs').readFileSync(configFile));
}
throw new Error('Unknown config file type.');
}
});
}
this.values = yargs.argv;
return yargs;
}
}
const defaultDefaults = {
playingGame: 'Message for help',
paginationItems: 10,
updateCheck: 60,
commandPrefix: '!',
commandEditable: 30,
nonCommandEdit: true,
selfbot: false,
storage: 'bot-storage',
log: 'bot.log',
logMaxSize: 5242880,
logMaxFiles: 5,
logLevel: 'info',
consoleLevel: 'info'
};
/**
* @typedef {Object} ConfigObject
* @property {string} [name] - Name of the bot (required to create a client)
* @property {string} [version] - Version of the bot (should follow semver, required to create a client)
* @property {string} [about] - Text information about the bot for the about command
* @property {string} [updateURL] - URL to a package.json file to check for updates with
* @property {string} [token] - The bot account API token to log in with
* @property {ClientOptions} [clientOptions] - The options to pass to the Client constructor
* @property {string} [owner] - The ID of the bot owner's Discord account
* @property {boolean} [selfbot] - Whether or not the bot should be running as a selfbot
* @property {boolean} [logMessages=true] - Whether or not all chat messages should be printed to the console
* @property {string} [storage=bot-storage] - Path to the local storage directory
* @property {string} [playingGame=Message-for-help] - Text to show the bot playing
* @property {number} [paginationItems=10] - Maximum number of items per page the default commands use when paginating
* @property {number} [updateCheck=60] - How frequently to check for updates (in minutes)
* @property {string} [commandPrefix=!] - The default command prefix (empty/null for mentions only)
* @property {number} [commandEditable=30] - How long commands are editable (in seconds)
* @property {boolean} [nonCommandEdit=true] - Whether or not commands will be run in messages that were edited that previously didn't have any commands
* @property {string} [log=bot.log] - Path to the log file
* @property {number} [logMaxSize=5242880] - Maximum size of the log file before splitting it (in bytes)
* @property {number} [logMaxFiles=5] - Maximum log files to keep
* @property {string} [logLevel=info] - The log level to output to the log file (error, warn, info, verbose, message, debug)
* @property {string} [consoleLevel=info] - The log level to output to the console (error, warn, info, verbose, message, debug)
* @property {string} [carbonUrl] - The Carbon submission URL (for bot creators)
* @property {string} [carbonKey] - The Carbon key for the bot (for bot creators)
* @property {string} [bdpwUrl=https://bots.discord.pw/api] - The bots.discord.pw API URL (for bot creators)
* @property {string} [bdpwKey] - The bots.discord.pw key for the bot (for bot creators)
*/
/** @external {ClientOptions} https://hydrabolt.github.io/discord.js/#!/docs/tag/master/typedef/ClientOptions */
/** @external {Yargs} http://yargs.js.org/docs/ */