Home Reference Source Repository

src/index.js

'use babel';
'use strict';

import Discord from 'discord.js';
import { LocalStorage } from 'node-localstorage';
import winston from 'winston';
import * as config from './config';
import version from './version';
import * as registry from './commands/registry';
import * as dispatcher from './commands/dispatcher';
import Setting from './database/setting';
import ModRole from './database/mod-role';
import UsableChannel from './database/usable-channel';
import * as permissions from './permissions';
import FriendlyError from './errors/friendly';
import CommandFormatError from './errors/command-format';
import Util from './util';

import HelpCommand from './commands/general/help';
import AboutCommand from './commands/general/about';
import PrefixCommand from './commands/general/prefix';
import EvalCommand from './commands/general/eval';
import ListRolesCommand from './commands/general/list-roles';
import ListModRolesCommand from './commands/roles/list';
import AddModRoleCommand from './commands/roles/add';
import DeleteModRoleCommand from './commands/roles/delete';
import ClearModRolesCommand from './commands/roles/clear';
import ListAllowedChannelsCommand from './commands/channels/list';
import AllowChannelCommand from './commands/channels/allow';
import DisallowChannelCommand from './commands/channels/disallow';
import ClearAllowedChannelsCommand from './commands/channels/clear';

export const serverCommandPatterns = {};
export const unprefixedCommandPattern = /^([^\s]+)/i;

export const graf = {
	client: null,
	version: version,
	registry: registry,
	dispatcher: dispatcher,
	permissions: permissions,
	util: Util,
	errors: {
		FriendlyError: FriendlyError,
		CommandFormatError: CommandFormatError
	},

	Setting: Setting,
	ModRole: ModRole,
	UsableChannel: UsableChannel,

	createClient(configObj) {
		config.setValues(configObj);

		// Output safe config
		const debugConfig = Object.assign({}, config.values);
		if(debugConfig.email) debugConfig.email = '--snip--';
		if(debugConfig.password) debugConfig.password = '--snip--';
		if(debugConfig.token) debugConfig.token = '--snip--';
		for(const key of Object.keys(debugConfig)) if(key.length === 1 || key.includes('-')) delete debugConfig[key];
		this.logger.debug('Configuration:', debugConfig);

		// Verify some stuff
		if(!config.values.token && (!config.values.email || !config.values.password)) throw new Error('Invalid credentials; either "token" or both "email" and "password" must be specified on the config.');
		if(!config.values.botName) throw new Error('"botName" must be specified on the config.');
		if(!config.values.botVersion) throw new Error('"botVersion" must be specified on the config.');

		// Create client
		const clientOptions = { autoReconnect: config.values.autoReconnect, forceFetchUsers: true, disableEveryone: config.values.disableEveryone };
		const client = new Discord.Client(clientOptions);
		this.logger.info('Client created.', clientOptions);
		client.on('error', err => { this.logger.error(err); });
		client.on('warn', err => { this.logger.warn(err); });
		client.on('debug', err => { this.logger.debug(err); });
		client.on('disconnected', () => { this.logger.error('Disconnected.'); });
		client.on('ready', () => {
			this.logger.info(`Bot is ready; logged in as ${client.user.username}#${client.user.discriminator} (ID: ${client.user.id})`);
			if(config.values.playingGame) client.setPlayingGame(config.values.playingGame);
		});

		// Set up command handling
		client.on('message', message => {
			if(message.author.equals(client.user)) return;
			dispatcher.handleMessage(message).catch(err => { this.logger.error(err); });
		});
		client.on('messageUpdated', (oldMessage, newMessage) => {
			if(newMessage.author.equals(client.user)) return;
			dispatcher.handleMessage(newMessage, oldMessage).catch(err => { this.logger.error(err); });
		});

		// Log in
		const loginCallback = err => { if(err) this.logger.error('Failed to login.', err); };
		if(config.values.token) {
			this.logger.info('Logging in with token...');
			client.loginWithToken(config.values.token, config.values.email, config.values.password, loginCallback);
		} else {
			this.logger.info('Logging in with email and password...');
			client.login(config.values.email, config.values.password, loginCallback);
		}

		// Check for updates now and at an interval
		if(config.values.updatePackageURL) {
			Util._checkForUpdate();
			if(config.values.updateCheck > 0) setInterval(Util._checkForUpdate, config.values.updateCheck * 60 * 1000);
		}

		this.client = client;
		return client;
	},

	registerCommands(commands) {
		for(const command of commands) this.registry.register(command);
	},

	nameGroups(groups) {
		for(const group of groups) this.registry.nameGroup(...group);
	},

	registerDefaultCommands() {
		this.registerCommands([
			HelpCommand,
			AboutCommand,
			PrefixCommand,
			EvalCommand,
			ListRolesCommand,
			ListModRolesCommand,
			AddModRoleCommand,
			DeleteModRoleCommand,
			ClearModRolesCommand,
			ListAllowedChannelsCommand,
			AllowChannelCommand,
			DisallowChannelCommand,
			ClearAllowedChannelsCommand
		]);
		this.nameGroups([
			['general', 'General'],
			['roles', 'Roles'],
			['channels', 'Channels']
		]);
	},

	get config() {
		if(!this._config) {
			config.setDefaults();
			this._config = config;
		}
		return this._config;
	},

	get logger() {
		if(!this._logger) {
			this._logger = new winston.Logger({
				transports: [
					new winston.transports.Console({
						level: config.values.consoleLevel,
						colorize: true,
						timestamp: true,
						handleExceptions: true,
						humanReadableUnhandledException: true
					})
				]
			});
			if(config.values.log) {
				this._logger.add(winston.transports.File, {
					level: config.values.logLevel,
					filename: config.values.log,
					maxsize: config.values.logMaxSize,
					maxFiles: config.values.logMaxFiles,
					tailable: true,
					json: false,
					handleExceptions: true,
					humanReadableUnhandledException: true
				});
			}
		}

		return this._logger;
	},

	get storage() {
		if(!this._storage) this._storage = new LocalStorage(config.values.storage);
		return this._storage;
	}
};
export default graf;