mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2024-12-23 08:13:09 +02:00
Command handler stuff, presences, other stuff
This commit is contained in:
parent
7c0b1311dc
commit
bdcff221db
36
src/commands/new.js
Normal file
36
src/commands/new.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const {
|
||||||
|
Command,
|
||||||
|
OptionTypes
|
||||||
|
} = require('../modules/commands');
|
||||||
|
|
||||||
|
module.exports = class NewCommand extends Command {
|
||||||
|
constructor(client) {
|
||||||
|
super(client, {
|
||||||
|
internal: true,
|
||||||
|
name: 'new',
|
||||||
|
description: 'Create a new ticket',
|
||||||
|
options: [
|
||||||
|
// {
|
||||||
|
// name: 'category',
|
||||||
|
// type: OptionTypes.STRING,
|
||||||
|
// description: 'The category you would like to create a new ticket for',
|
||||||
|
// required: true,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'topic',
|
||||||
|
type: OptionTypes.STRING,
|
||||||
|
description: 'The topic of the ticket',
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(data) {
|
||||||
|
console.log(data.args);
|
||||||
|
console.log(data.channel.name);
|
||||||
|
console.log(data.member.user.tag);
|
||||||
|
console.log(data.guild.name);
|
||||||
|
console.log(data.token);
|
||||||
|
}
|
||||||
|
};
|
13
src/index.js
13
src/index.js
@ -69,10 +69,6 @@ const log = new Logger({
|
|||||||
logToFile: config.logs.enabled,
|
logToFile: config.logs.enabled,
|
||||||
keepFor: config.logs.keep_for,
|
keepFor: config.logs.keep_for,
|
||||||
custom: {
|
custom: {
|
||||||
listeners: {
|
|
||||||
title: 'info',
|
|
||||||
prefix: 'listeners'
|
|
||||||
},
|
|
||||||
commands: {
|
commands: {
|
||||||
title: 'info',
|
title: 'info',
|
||||||
prefix: 'commands'
|
prefix: 'commands'
|
||||||
@ -84,6 +80,7 @@ const log = new Logger({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { selectPresence } = require('./utils/discord');
|
||||||
const I18n = require('@eartharoid/i18n');
|
const I18n = require('@eartharoid/i18n');
|
||||||
const { CommandManager } = require('./modules/commands');
|
const { CommandManager } = require('./modules/commands');
|
||||||
const { PluginManager } = require('./modules/plugins');
|
const { PluginManager } = require('./modules/plugins');
|
||||||
@ -93,6 +90,10 @@ const {
|
|||||||
Intents
|
Intents
|
||||||
} = require('discord.js');
|
} = require('discord.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bot client
|
||||||
|
* @extends {Client}
|
||||||
|
*/
|
||||||
class Bot extends Client {
|
class Bot extends Client {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
@ -101,6 +102,7 @@ class Bot extends Client {
|
|||||||
'CHANNEL',
|
'CHANNEL',
|
||||||
'REACTION'
|
'REACTION'
|
||||||
],
|
],
|
||||||
|
presence: selectPresence(),
|
||||||
ws: {
|
ws: {
|
||||||
intents: Intents.NON_PRIVILEGED,
|
intents: Intents.NON_PRIVILEGED,
|
||||||
}
|
}
|
||||||
@ -126,7 +128,6 @@ class Bot extends Client {
|
|||||||
|
|
||||||
/** The command manager, used by internal and plugin commands */
|
/** The command manager, used by internal and plugin commands */
|
||||||
this.commands = new CommandManager(this);
|
this.commands = new CommandManager(this);
|
||||||
this.commands.load(); // load internal commands
|
|
||||||
|
|
||||||
/** The plugin manager */
|
/** The plugin manager */
|
||||||
this.plugins = new PluginManager(this);
|
this.plugins = new PluginManager(this);
|
||||||
@ -144,7 +145,7 @@ new Bot();
|
|||||||
|
|
||||||
const { version } = require('../package.json');
|
const { version } = require('../package.json');
|
||||||
process.on('unhandledRejection', error => {
|
process.on('unhandledRejection', error => {
|
||||||
log.notice('PLEASE INCLUDE THIS INFORMATION:');
|
log.notice('PLEASE INCLUDE THIS INFORMATION IF YOU ASK FOR HELP ABOUT THE FOLLOWING ERROR:');
|
||||||
log.warn(`Discord Tickets v${version}, Node v${process.versions.node} on ${process.platform}`);
|
log.warn(`Discord Tickets v${version}, Node v${process.versions.node} on ${process.platform}`);
|
||||||
log.warn('An error was not caught');
|
log.warn('An error was not caught');
|
||||||
if (error instanceof Error) log.warn(`Uncaught ${error.name}: ${error}`);
|
if (error instanceof Error) log.warn(`Uncaught ${error.name}: ${error}`);
|
||||||
|
31
src/listeners/interaction.js
Normal file
31
src/listeners/interaction.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
module.exports = {
|
||||||
|
event: 'INTERACTION_CREATE',
|
||||||
|
raw: true,
|
||||||
|
execute: async (client, interaction) => {
|
||||||
|
|
||||||
|
if (interaction.type !== 2) return;
|
||||||
|
|
||||||
|
const cmd = interaction.data.name;
|
||||||
|
|
||||||
|
if (!client.commands.commands.has(cmd))
|
||||||
|
return client.log.warn(`Received "${cmd}" command invocation, but the command manager does not have a "${cmd}" command`);
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
args: interaction.data.options,
|
||||||
|
channel: await client.channels.fetch(interaction.channel_id),
|
||||||
|
guild: await client.guilds.fetch(interaction.guild_id),
|
||||||
|
token: interaction.token
|
||||||
|
};
|
||||||
|
|
||||||
|
data.member = await data.guild.members.fetch(interaction.member.user.id);
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.log.commands(`Executing ${cmd} command (invoked by ${data.member.user.username.tag})`);
|
||||||
|
client.commands.commands.get(cmd).execute(data);
|
||||||
|
} catch (e) {
|
||||||
|
client.log.warn(`[COMMANDS] An error occurred whilst executed the ${cmd} command`);
|
||||||
|
client.log.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
5
src/listeners/message.js
Normal file
5
src/listeners/message.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
event: 'message',
|
||||||
|
execute: (client, message) => {
|
||||||
|
}
|
||||||
|
};
|
5
src/listeners/messageUpdate.js
Normal file
5
src/listeners/messageUpdate.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
event: 'messageUpdate',
|
||||||
|
execute: (client, m1, m2) => {
|
||||||
|
}
|
||||||
|
};
|
@ -16,9 +16,21 @@ module.exports = {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
// fail quietly, it doesn't really matter if it didn't work
|
// fail quietly, it doesn't really matter if it didn't work
|
||||||
log.debug('Failed to post to discordtickets-telemetry');
|
log.debug('Warning: failed to post to discordtickets-telemetry');
|
||||||
log.debug(e);
|
log.debug(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.commands.load(); // load internal commands
|
||||||
|
|
||||||
|
if (client.config.presence.presences.length > 1) {
|
||||||
|
const { selectPresence } = require('../utils/discord');
|
||||||
|
setInterval(() => {
|
||||||
|
let presence = selectPresence();
|
||||||
|
client.user.setPresence(presence);
|
||||||
|
client.log.debug(`Updated presence: ${presence.activity.type} ${presence.activity.name}`);
|
||||||
|
}, client.config.presence.duration * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -1,92 +1,106 @@
|
|||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
const { Client } = require('discord.js');
|
const { Client, GuildMember, Guild, Channel } = require('discord.js');
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { path } = require('../../utils/fs');
|
const { path } = require('../../utils/fs');
|
||||||
|
|
||||||
/** A plugin */
|
/**
|
||||||
module.exports = class Plugin {
|
* A command
|
||||||
|
*/
|
||||||
|
module.exports = class Command {
|
||||||
/**
|
/**
|
||||||
* Create a new Plugin
|
* A command option choice
|
||||||
* @param {Client} client The Discord Client
|
* @typedef CommandOptionChoice
|
||||||
* @param {String} id The plugin ID
|
* @property {string} name - Choice name (1-100)
|
||||||
* @param {Object} options Plugin options
|
* @property {(string|number)} value - choice value
|
||||||
* @param {String} options.name A human-friendly name (can be different to the name in package.json)
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command option
|
||||||
|
* @typedef CommandOption
|
||||||
|
* @property {number} type - [ApplicationCommandOptionType](https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptiontype)
|
||||||
|
* @property {string} name - Option name (1-32)
|
||||||
|
* @property {string} description - Option description (1-100)
|
||||||
|
* @property {boolean} [required] - Required?
|
||||||
|
* @property {CommandOptionChoice[]} [choices] - Array of choices
|
||||||
|
* @property {CommandOption[]} [options] - Array of options if this option is a subcommand/subcommand group
|
||||||
*/
|
*/
|
||||||
constructor(client, id, options = {}) {
|
|
||||||
/** The human-friendly name of the plugin */
|
/**
|
||||||
this.name = options.name || id;
|
* Create a new Command
|
||||||
|
* @param {Client} client - The Discord Client
|
||||||
|
* @param {Object} data - Command data
|
||||||
|
* @param {string} data.name - The name of the command (3-32)
|
||||||
|
* @param {string} data.description - The description of the command (1-100)
|
||||||
|
* @param {CommandOption[]} data.options - The command options, max of 10
|
||||||
|
*/
|
||||||
|
constructor(client, data) {
|
||||||
|
|
||||||
/** The Discord Client */
|
/** The Discord Client */
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
/** The PluginManager */
|
/** The CommandManager */
|
||||||
this.manager = this.client.plugins;
|
this.manager = this.client.commands;
|
||||||
|
|
||||||
// Object.assign(this, this.manager.plugins.get(id));
|
if (typeof data !== 'object') {
|
||||||
// make JSDoc happy
|
throw new TypeError(`Expected type of data to be an object, got ${typeof data}`);
|
||||||
|
|
||||||
let {
|
|
||||||
version,
|
|
||||||
author,
|
|
||||||
description
|
|
||||||
} = this.manager.plugins.get(id);
|
|
||||||
|
|
||||||
/** The unique ID of the plugin (NPM package name) */
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
/** The version of the plugin (NPM package version) */
|
|
||||||
this.version = version;
|
|
||||||
|
|
||||||
/** The plugin author's name (NPM package author) */
|
|
||||||
this.author = author;
|
|
||||||
|
|
||||||
/** The plugin description (NPM package description) */
|
|
||||||
this.description = description;
|
|
||||||
|
|
||||||
this.directory = {};
|
|
||||||
|
|
||||||
/** A cleaned version of the plugin's ID suitable for use in the directory name */
|
|
||||||
this.directory.name = this.id.replace(/@[-_a-zA-Z0-9]+\//, '');
|
|
||||||
|
|
||||||
/** The absolute path of the plugin directory */
|
|
||||||
this.directory.path = path(`./user/plugins/${this.directory.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the plugin directory if it doesn't already exist
|
|
||||||
* @returns {Boolean} True if created, false if it already existed
|
|
||||||
*/
|
|
||||||
createDirectory() {
|
|
||||||
if (!fs.existsSync(this.directory.path)) {
|
|
||||||
this.client.log.plugins(`Creating plugin directory for "${this.name}"`);
|
|
||||||
fs.mkdirSync(this.directory.path);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the plugin config file if it doesn't already exist
|
* The name of the command
|
||||||
* @param {Object} template The default config template
|
* @type {string}
|
||||||
* @returns {Boolean} True if created, false if it already existed
|
*/
|
||||||
*/
|
this.name = data.name;
|
||||||
createConfig(template) {
|
|
||||||
this.createDirectory();
|
/**
|
||||||
let file = join(this.directory.path, 'config.json');
|
* The command description
|
||||||
if (!fs.existsSync(file)) {
|
* @type {string}
|
||||||
this.client.log.plugins(`Creating plugin config file for "${this.name}"`);
|
*/
|
||||||
fs.writeFileSync(file, JSON.stringify(template, null, 2));
|
this.description = data.description;
|
||||||
return true;
|
|
||||||
} else {
|
/**
|
||||||
return false;
|
* The command options
|
||||||
|
* @type {CommandOption[]}
|
||||||
|
*/
|
||||||
|
this.options = data.options;
|
||||||
|
|
||||||
|
/** True if command is internal, false if it is from a plugin */
|
||||||
|
this.internal = data.internal;
|
||||||
|
|
||||||
|
|
||||||
|
this.manager.check(data); // validate
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.manager.register(this); // register the command
|
||||||
|
} catch (e) {
|
||||||
|
return this.client.log.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.client.api.applications(this.client.user.id).commands.post({ data }); // post command to Discord
|
||||||
|
|
||||||
|
let internal = this.internal ? 'internal ' : '';
|
||||||
|
this.client.log.commands(`Loaded ${internal}"${this.name}" command`);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main function where your code should go. Create functions and event listeners here
|
* [ApplicationCommandInteractionDataOption](https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondataoption)
|
||||||
|
* @typedef {Object} ApplicationCommandInteractionDataOption
|
||||||
|
* @property {string} name - Name of the parameter
|
||||||
|
* @property {*} value - The value
|
||||||
|
* @property {(undefined|ApplicationCommandInteractionDataOption[])} options - Present if the option is a subcommand/subcommand group
|
||||||
*/
|
*/
|
||||||
load() {}
|
|
||||||
|
/**
|
||||||
|
* The code to be executed when a command is invoked
|
||||||
|
* @abstract
|
||||||
|
* @param {Object} data - Object containing data about the command invocation
|
||||||
|
* @param {(undefined|ApplicationCommandInteractionDataOption[])} data.args - Command arguments
|
||||||
|
* @param {Channel} data.channel- The channel object
|
||||||
|
* @param {Guild} data.guild- The guild object
|
||||||
|
* @param {GuildMember} data.member - The member object
|
||||||
|
* @param {string} data.token - The token used to respond to the interaction
|
||||||
|
*/
|
||||||
|
async execute(data) {}
|
||||||
};
|
};
|
@ -1,4 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
CommandManager: require('./manager'),
|
CommandManager: require('./manager'),
|
||||||
Command: require('./command')
|
Command: require('./command'),
|
||||||
|
OptionTypes: {
|
||||||
|
SUB_COMMAND: 1,
|
||||||
|
SUB_COMMAND_GROUP: 2,
|
||||||
|
STRING: 3,
|
||||||
|
INTEGER: 4,
|
||||||
|
BOOLEAN: 5,
|
||||||
|
USER: 6,
|
||||||
|
CHANNEL: 7,
|
||||||
|
ROLE: 8,
|
||||||
|
},
|
||||||
};
|
};
|
@ -6,7 +6,9 @@ const Command = require('./command');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { path } = require('../../utils/fs');
|
const { path } = require('../../utils/fs');
|
||||||
|
|
||||||
/** Manages the loading of commands */
|
/**
|
||||||
|
* Manages the loading of commands
|
||||||
|
*/
|
||||||
module.exports = class CommandManager {
|
module.exports = class CommandManager {
|
||||||
/**
|
/**
|
||||||
* Create a CommandManager instance
|
* Create a CommandManager instance
|
||||||
@ -15,6 +17,7 @@ module.exports = class CommandManager {
|
|||||||
constructor(client) {
|
constructor(client) {
|
||||||
/** The Discord Client */
|
/** The Discord Client */
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
/** A discord.js Collection (Map) of loaded commands */
|
/** A discord.js Collection (Map) of loaded commands */
|
||||||
this.commands = new Collection();
|
this.commands = new Collection();
|
||||||
}
|
}
|
||||||
@ -24,10 +27,113 @@ module.exports = class CommandManager {
|
|||||||
const files = fs.readdirSync(path('./src/commands'))
|
const files = fs.readdirSync(path('./src/commands'))
|
||||||
.filter(file => file.endsWith('.js'));
|
.filter(file => file.endsWith('.js'));
|
||||||
|
|
||||||
for (const file of files) {
|
for (let file of files) {
|
||||||
const cmd = require(`../commands/${file}`);
|
try {
|
||||||
this.commands.set(cmd, new cmd(this.client));
|
file = require(`../../commands/${file}`);
|
||||||
|
new file(this.client);
|
||||||
|
} catch (e) {
|
||||||
|
this.client.log.warn('An error occurred whilst loading an internal command');
|
||||||
|
this.client.log.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Register a command */
|
||||||
|
register(cmd) {
|
||||||
|
const is_internal = (this.commands.has(cmd.name) && cmd.internal)
|
||||||
|
|| (this.commands.has(cmd.name) && this.commands.get(cmd.name).internal);
|
||||||
|
|
||||||
|
if (is_internal) {
|
||||||
|
let plugin = this.client.plugins.plugins.find(p => p.commands.includes(cmd.name));
|
||||||
|
if (plugin)
|
||||||
|
this.client.log.commands(`The "${plugin.name}" plugin has overridden the internal "${cmd.name}" command`);
|
||||||
|
else
|
||||||
|
this.client.log.commands(`An unknown plugin has overridden the internal "${cmd.name}" command`);
|
||||||
|
if(cmd.internal) return;
|
||||||
|
}
|
||||||
|
else if (this.commands.has(cmd.name))
|
||||||
|
throw new Error(`A non-internal command with the name "${cmd.name}" already exists`);
|
||||||
|
|
||||||
|
this.commands.set(cmd.name, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check the command data */
|
||||||
|
check(data) {
|
||||||
|
|
||||||
|
if (typeof data.name !== 'string')
|
||||||
|
throw new TypeError(`Expected type of command name to be a string, got ${typeof data.name}`);
|
||||||
|
|
||||||
|
if (data.name.length < 3 || data.name.length > 32)
|
||||||
|
throw new TypeError('Length of command name must be 3-32');
|
||||||
|
|
||||||
|
if (typeof data.description !== 'string')
|
||||||
|
throw new TypeError(`Expected type of command description to be a string, got ${typeof data.description}`);
|
||||||
|
|
||||||
|
if (data.name.length < 1 || data.name.length > 100)
|
||||||
|
throw new TypeError('Length of description must be 3-32');
|
||||||
|
|
||||||
|
if (typeof data.options !== 'undefined' && !(data.options instanceof Array))
|
||||||
|
throw new TypeError(`Expected type of command options to be undefined or an array, got ${typeof data.options}`);
|
||||||
|
|
||||||
|
if (data.options)
|
||||||
|
this.checkOptions(data.options);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check the command data's options */
|
||||||
|
checkOptions(options) {
|
||||||
|
let num = 0;
|
||||||
|
options.forEach(o => {
|
||||||
|
if (typeof o.type !== 'number')
|
||||||
|
throw new TypeError(`Expected type of option ${num} type to be a number, got ${typeof o.type}`);
|
||||||
|
|
||||||
|
if (typeof o.name !== 'string')
|
||||||
|
throw new TypeError(`Expected type of option ${num} name to be a string, got ${typeof o.name}`);
|
||||||
|
|
||||||
|
if (o.name.length < 3 || o.name.length > 32)
|
||||||
|
throw new TypeError(`Length of option ${num} name must be 3-32`);
|
||||||
|
|
||||||
|
if (typeof o.description !== 'string')
|
||||||
|
throw new TypeError(`Expected type of option ${num} description to be a string, got ${typeof o.description}`);
|
||||||
|
|
||||||
|
if (o.description.length < 1 || o.description.length > 100)
|
||||||
|
throw new TypeError(`Length of option ${num} description must be 1-100`);
|
||||||
|
|
||||||
|
if (typeof o.required !== 'undefined' && typeof o.required !== 'boolean')
|
||||||
|
throw new TypeError(`Expected type of option ${num} required to be undefined or a boolean, got ${typeof o.required}`);
|
||||||
|
|
||||||
|
if (typeof o.choices !== 'undefined' && !(o.choices instanceof Array))
|
||||||
|
throw new TypeError(`Expected type of option ${num} choices to be undefined or an array, got ${typeof o.choices}`);
|
||||||
|
|
||||||
|
if (o.choices)
|
||||||
|
this.checkOptionChoices(o.choices);
|
||||||
|
|
||||||
|
if (typeof o.options !== 'undefined' && !(o.options instanceof Array))
|
||||||
|
throw new TypeError(`Expected type of option ${num} options to be undefined or an array, got ${typeof o.options}`);
|
||||||
|
|
||||||
|
if (o.options)
|
||||||
|
this.checkOptions(o.options);
|
||||||
|
|
||||||
|
num++;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check command option choices */
|
||||||
|
checkOptionChoices(choices) {
|
||||||
|
let num = 0;
|
||||||
|
choices.forEach(c => {
|
||||||
|
if (typeof c.name !== 'string')
|
||||||
|
throw new TypeError(`Expected type of option choice ${num} name to be a string, got ${typeof c.name}`);
|
||||||
|
|
||||||
|
if (c.name.length < 1 || c.name.length > 100)
|
||||||
|
throw new TypeError(`Length of option choice ${num} name must be 1-100`);
|
||||||
|
|
||||||
|
if (typeof c.value !== 'string' && typeof c.value !== 'number')
|
||||||
|
throw new TypeError(`Expected type of option choice ${num} value to be a string or number, got ${typeof c.value}`);
|
||||||
|
|
||||||
|
num++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
@ -7,7 +7,11 @@ module.exports = client => {
|
|||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const listener = require(`../listeners/${file}`);
|
const listener = require(`../listeners/${file}`);
|
||||||
|
const exec = (...args) => listener.execute(client, ...args);
|
||||||
let on = listener.once ? 'once' : 'on';
|
let on = listener.once ? 'once' : 'on';
|
||||||
client[on](listener.event, (...args) => listener.execute(client, ...args));
|
if (listener.raw)
|
||||||
|
client.ws[on](listener.event, exec);
|
||||||
|
else
|
||||||
|
client[on](listener.event, exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -7,7 +7,9 @@ const fs = require('fs');
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { path } = require('../../utils/fs');
|
const { path } = require('../../utils/fs');
|
||||||
|
|
||||||
/** Manages the loading of plugins */
|
/**
|
||||||
|
* Manages the loading of plugins
|
||||||
|
*/
|
||||||
module.exports = class PluginManager {
|
module.exports = class PluginManager {
|
||||||
/**
|
/**
|
||||||
* Create a PluginManager instance
|
* Create a PluginManager instance
|
||||||
@ -16,6 +18,7 @@ module.exports = class PluginManager {
|
|||||||
constructor(client) {
|
constructor(client) {
|
||||||
/** The Discord Client */
|
/** The Discord Client */
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
/** A discord.js Collection (Map) of loaded plugins */
|
/** A discord.js Collection (Map) of loaded plugins */
|
||||||
this.plugins = new Collection();
|
this.plugins = new Collection();
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ const fs = require('fs');
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { path } = require('../../utils/fs');
|
const { path } = require('../../utils/fs');
|
||||||
|
|
||||||
/** A plugin */
|
/**
|
||||||
|
* A plugin
|
||||||
|
*/
|
||||||
module.exports = class Plugin {
|
module.exports = class Plugin {
|
||||||
/**
|
/**
|
||||||
* Create a new Plugin
|
* Create a new Plugin
|
||||||
@ -13,11 +15,9 @@ module.exports = class Plugin {
|
|||||||
* @param {String} id The plugin ID
|
* @param {String} id The plugin ID
|
||||||
* @param {Object} options Plugin options
|
* @param {Object} options Plugin options
|
||||||
* @param {String} options.name A human-friendly name (can be different to the name in package.json)
|
* @param {String} options.name A human-friendly name (can be different to the name in package.json)
|
||||||
|
* @param {String[]} options.commands An array of command names the plugin registers
|
||||||
*/
|
*/
|
||||||
constructor(client, id, options = {}) {
|
constructor(client, id, options = {}) {
|
||||||
/** The human-friendly name of the plugin */
|
|
||||||
this.name = options.name || id;
|
|
||||||
|
|
||||||
/** The Discord Client */
|
/** The Discord Client */
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
@ -33,25 +33,53 @@ module.exports = class Plugin {
|
|||||||
description
|
description
|
||||||
} = this.manager.plugins.get(id);
|
} = this.manager.plugins.get(id);
|
||||||
|
|
||||||
/** The unique ID of the plugin (NPM package name) */
|
/**
|
||||||
|
* The human-friendly name of the plugin
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.name = options.name || id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of commands from this plugin
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
this.commands = options.commands;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique ID of the plugin (NPM package name)
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
||||||
/** The version of the plugin (NPM package version) */
|
/**
|
||||||
|
* The version of the plugin (NPM package version)
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
||||||
/** The plugin author's name (NPM package author) */
|
/**
|
||||||
|
* The plugin author's name (NPM package author)
|
||||||
|
* @type {(undefined|string)}
|
||||||
|
*/
|
||||||
this.author = author;
|
this.author = author;
|
||||||
|
|
||||||
/** The plugin description (NPM package description) */
|
/**
|
||||||
|
* The plugin description (NPM package description)
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|
||||||
this.directory = {};
|
let clean = this.id.replace(/@[-_a-zA-Z0-9]+\//, '');
|
||||||
|
|
||||||
/** A cleaned version of the plugin's ID suitable for use in the directory name */
|
/**
|
||||||
this.directory.name = this.id.replace(/@[-_a-zA-Z0-9]+\//, '');
|
* Information about the plugin directory
|
||||||
|
* @property {string} name - A cleaned version of the plugin's ID suitable for use in the directory name
|
||||||
/** The absolute path of the plugin directory */
|
* @property {string} path - The absolute path of the plugin directory
|
||||||
this.directory.path = path(`./user/plugins/${this.directory.name}`);
|
*/
|
||||||
|
this.directory = {
|
||||||
|
name: clean,
|
||||||
|
path: path(`./user/plugins/${clean}`)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,8 +113,20 @@ module.exports = class Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the plugin config file to the defaults
|
||||||
|
* @param {Object} template The default config template
|
||||||
|
*/
|
||||||
|
resetConfig(template) {
|
||||||
|
this.createDirectory();
|
||||||
|
let file = join(this.directory.path, 'config.json');
|
||||||
|
this.client.log.plugins(`Resetting plugin config file for "${this.name}"`);
|
||||||
|
fs.writeFileSync(file, JSON.stringify(template, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main function where your code should go. Create functions and event listeners here
|
* The main function where your code should go. Create functions and event listeners here
|
||||||
|
* @abstract
|
||||||
*/
|
*/
|
||||||
load() {}
|
load() {}
|
||||||
};
|
};
|
45
src/utils/discord.js
Normal file
45
src/utils/discord.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const { PresenceData } = require('discord.js');
|
||||||
|
|
||||||
|
const config = require('../../user/config');
|
||||||
|
|
||||||
|
let current_presence = -1;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Select a presence from the config
|
||||||
|
* @returns {PresenceData}
|
||||||
|
*/
|
||||||
|
selectPresence() {
|
||||||
|
let length = config.presence.presences.length;
|
||||||
|
if (length === 0) return {};
|
||||||
|
|
||||||
|
let num;
|
||||||
|
if (length === 1)
|
||||||
|
num = 0;
|
||||||
|
else if (config.presence.randomise)
|
||||||
|
num = Math.floor(Math.random() * length);
|
||||||
|
else {
|
||||||
|
current_presence = current_presence + 1; // ++ doesn't work on negative numbers
|
||||||
|
if (current_presence === length)
|
||||||
|
current_presence = 0;
|
||||||
|
num = current_presence;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
activity: name,
|
||||||
|
status,
|
||||||
|
type,
|
||||||
|
url
|
||||||
|
} = config.presence.presences[num];
|
||||||
|
|
||||||
|
return {
|
||||||
|
activity: {
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
url
|
||||||
|
},
|
||||||
|
status
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,10 @@
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Make a relative path absolute
|
||||||
|
* @param {string} path - A path relative to the root of the project (like "./user/config.js")
|
||||||
|
* @returns {string} absolute path
|
||||||
|
*/
|
||||||
path: path => join(__dirname, '../../', path),
|
path: path => join(__dirname, '../../', path),
|
||||||
};
|
};
|
@ -34,23 +34,32 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
max_listeners: 10,
|
max_listeners: 10,
|
||||||
plugins: [
|
plugins: [
|
||||||
// 'dsctickets.plugin-name'
|
|
||||||
// 'discordtickets-portal'
|
|
||||||
],
|
|
||||||
presences: [
|
|
||||||
{
|
|
||||||
activity: '/new | /help',
|
|
||||||
type: 'PLAYING'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
activity: 'with tickets | /help',
|
|
||||||
type: 'PLAYING'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
activity: 'for new tickets | /help',
|
|
||||||
type: 'WATCHING'
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
|
presence: {
|
||||||
|
presences: [
|
||||||
|
{
|
||||||
|
activity: '/new | /help',
|
||||||
|
type: 'PLAYING',
|
||||||
|
status: 'online'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activity: 'with tickets | /help',
|
||||||
|
type: 'PLAYING'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activity: 'for new tickets | /help',
|
||||||
|
type: 'WATCHING'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activity: 'Minecraft',
|
||||||
|
type: 'STREAMING',
|
||||||
|
status: 'dnd',
|
||||||
|
url: 'https://www.twitch.tv/twitch'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
randomise: true,
|
||||||
|
duration: 60
|
||||||
|
},
|
||||||
super_secret_setting: true,
|
super_secret_setting: true,
|
||||||
update_notice: true,
|
update_notice: true,
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user