discord-leveling-super
Version:
Easy and customizable leveling framework for your Discord bot.
794 lines (761 loc) • 38.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const util_1 = require("util");
const fs_1 = require("fs");
const Emitter_1 = __importDefault(require("./classes/Emitter"));
const LevelingError_1 = __importDefault(require("./classes/LevelingError"));
const Errors_1 = __importDefault(require("./structures/Errors"));
const DefaultObject_1 = __importDefault(require("./structures/DefaultObject"));
const UtilsManager_1 = __importDefault(require("./managers/UtilsManager"));
const DatabaseManager_1 = __importDefault(require("./managers/DatabaseManager"));
const FetchManager_1 = __importDefault(require("./managers/FetchManager"));
const RanksManager_1 = __importDefault(require("./managers/RanksManager"));
const XPManager_1 = __importDefault(require("./managers/XPManager"));
const LevelManager_1 = __importDefault(require("./managers/LevelManager"));
const TotalXPManager_1 = __importDefault(require("./managers/TotalXPManager"));
const SettingsManager_1 = __importDefault(require("./managers/SettingsManager"));
const package_json_1 = __importDefault(require("../package.json"));
const colors = {
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
reset: '\x1b[0m'
};
/**
* The Leveling class.
* @extends {Emitter}
*/
class Leveling extends Emitter_1.default {
/**
* Leveling options object.
* @type {LevelingOptions}
*/
options;
/**
* Discord Bot Client
* @type {Client}
*/
client;
/**
* Module ready status.
* @type {Boolean}
*/
ready;
/**
* Module errored status.
* @type {Boolean}
*/
errored;
/**
* Database checking interval.
* @type {NodeJS.Timeout}
*/
interval;
/**
* Leveling error class.
* @type {LevelingError}
*/
LevelingError;
/**
* Utils manager methods object.
* @type {UtilsManager}
*/
utils;
/**
* Module version.
* @type {String}
*/
version;
/**
* Link to the module's documentation website.
* @type {String}
*/
docs;
/**
* Database manager methods object.
* @type {DatabaseManager}
*/
database;
/**
* XP manager methods object.
* @type {FetchManager}
*/
fetcher;
/**
* Settings manager methods class.
* @type {SettingsManager}
*/
settings;
/**
* XP manager methods object.
* @type {XPManager}
*/
xp;
/**
* Level manager methods object.
* @type {LevelManager}
*/
levels;
/**
* Total XP manager methods object.
* @type {LevelManager}
*/
totalXP;
/**
* Ranks manager methods object.
* @type {RanksManager}
*/
ranks;
/**
* The Leveling class.
* @param {Client} client Discord Bot Client.
* @param {LevelingOptions} options Leveling options object.
*/
constructor(client, options = {}) {
super();
this.LevelingError = LevelingError_1.default;
this.utils = new UtilsManager_1.default(options, client);
this.options = this.utils.checkOptions(options.optionsChecker, options || {});
this.client = client;
this.ready = false;
this.errored = false;
this.interval = null;
this.version = package_json_1.default.version;
this.docs = 'https://dls-docs.js.org';
this.database = null;
this.fetcher = null;
this.settings = null;
this.xp = null;
this.levels = null;
this.totalXP = null;
this.ranks = null;
this.init();
}
/**
* Kills the Leveling instance.
* @fires Leveling#destroy
* @returns {Leveling | boolean} Leveling instance.
*/
kill() {
if (!this.ready)
return false;
clearInterval(this.interval);
this.ready = false;
this.LevelingError = null;
this.interval = null;
this.utils = null;
this.database = null;
this.fetcher = null;
this.settings = null;
this.xp = null;
this.levels = null;
this.totalXP = null;
this.ranks = null;
this.emit('destroy');
return this;
}
/**
* Starts the module.
* @fires Leveling#ready
* @returns {Promise<Boolean>} If started successfully: true; else: Error instance.
*/
init() {
let attempt = 0;
let attempts = this.options?.errorHandler?.attempts == 0 ? Infinity : this.options?.errorHandler?.attempts;
const time = this.options?.errorHandler?.time;
const retryingTime = (time / 1000).toFixed(1);
const sleep = (0, util_1.promisify)(setTimeout);
const check = () => new Promise(resolve => {
this._init().then(x => {
if (x) {
this.errored = false;
this.ready = true;
return console.log(`${colors.green}Started successfully! :)`);
}
resolve(x);
}).catch(err => resolve(err));
});
return this.options?.errorHandler?.handleErrors ? this._init().catch(async (err) => {
if (!(err instanceof LevelingError_1.default))
this.errored = true;
console.log(`${colors.red}Failed to start the module:${colors.cyan}`);
console.log(err);
if (err.message.includes('This module is supporting only Node.js v14 or newer.'))
process.exit(1);
else
console.log(`${colors.magenta}Retrying in ${retryingTime} seconds...${colors.reset}`);
while (attempt < attempts && attempt !== -1) {
await sleep(time);
if (attempt < attempts)
check().then(async (res) => {
if (res.message) {
attempt++;
console.log(`${colors.red}Failed to start the module:${colors.cyan}`);
console.log(err);
console.log(`\x1b[34mAttempt ${attempt}${attempts == Infinity ? '.' : `/${attempts}`}`);
if (attempt == attempts) {
console.log(`${colors.green}Failed to start the module within ${attempts} attempts...${colors.reset}`);
process.exit(1);
}
console.log(`${colors.magenta}Retrying in ${retryingTime} seconds...`);
await sleep(time);
}
else
attempt = -1;
});
}
}) : this._init();
}
/**
* Initializates the module.
* @returns {Promise<boolean>} If started successfully: true; else: Error instance.
* @private
*/
_init() {
const storagePath = this.options.storagePath || './leveling.json';
const updateCountdown = this.options.updateCountdown;
const isFileExist = (0, fs_1.existsSync)(storagePath);
return new Promise(async (resolve, reject) => {
try {
if (!this.client)
return reject(new LevelingError_1.default(Errors_1.default.noClient));
if (this.errored)
return;
if (this.ready)
return;
if (this.options?.checkStorage) {
if (!isFileExist)
(0, fs_1.writeFileSync)(storagePath, '{}');
try {
if (storagePath.endsWith('package.json'))
return reject(new LevelingError_1.default(Errors_1.default.reservedName('package.json')));
if (storagePath.endsWith('package-lock.json'))
return reject(new LevelingError_1.default(Errors_1.default.reservedName('package-lock.json')));
const data = (0, fs_1.readFileSync)(storagePath);
JSON.parse(data.toString());
}
catch (err) {
if (err.message.includes('Unexpected') && err.message.includes('JSON'))
return reject(new LevelingError_1.default(Errors_1.default.wrongStorageData));
else
reject(err);
}
}
if (this.options.updater?.checkUpdates) {
const version = await this.utils.checkUpdates();
if (!version.updated) {
console.log('\n\n');
console.log(colors.green + '╔═══════════════════════════════════════════════════════════════╗');
console.log(colors.green + '║ @ discord-leveling-super - [] X ║');
console.log(colors.green + '║═══════════════════════════════════════════════════════════════║');
console.log(colors.yellow + `║ The module is ${colors.red}out of date!${colors.yellow} ║`);
console.log(colors.magenta + '║ New version is available! ║');
console.log(colors.blue + `║ ${version.installedVersion} --> ${version.packageVersion} ║`);
console.log(colors.cyan + '║ Run "npm i discord-leveling-super@latest" ║');
console.log(colors.cyan + '║ to update! ║');
console.log(colors.white + '║ View the full changelog here: ║');
console.log(colors.red + '║ https://dls-docs.js.org/#/docs/main/stable/general/changelog ║');
console.log(colors.green + '╚═══════════════════════════════════════════════════════════════╝\x1b[37m');
console.log('\n\n');
}
else {
if (this.options?.updater?.upToDateMessage) {
console.log('\n\n');
console.log(colors.green + '╔═══════════════════════════════════════════════════════════════╗');
console.log(colors.green + '║ @ discord-leveling-super - [] X ║');
console.log(colors.green + '║═══════════════════════════════════════════════════════════════║');
console.log(colors.yellow + `║ The module is ${colors.cyan}up of date!${colors.yellow} ║`);
console.log(colors.magenta + '║ No updates are available. ║');
console.log(colors.blue + `║ Current version is ${version.packageVersion}. ║`);
console.log(colors.cyan + '║ Enjoy! ║');
console.log(colors.white + '║ View the full changelog here: ║');
console.log(colors.red + '║ https://dls-docs.js.org/#/docs/main/stable/general/changelog ║');
console.log(colors.green + '╚═══════════════════════════════════════════════════════════════╝\x1b[37m');
console.log('\n\n');
}
}
}
// if (this.options?.checkStorage == undefined ? true : this.options?.checkStorage) {
// const storageExists = (0, fs_1.existsSync)(storagePath);
// const interval = setInterval(() => {
// if (!storageExists) {
// try {
// if (storagePath?.endsWith('package.json'))
// throw new LevelingError_1.default(Errors_1.default.reservedName('package.json'));
// if (storagePath?.endsWith('package-lock.json'))
// throw new LevelingError_1.default(Errors_1.default.reservedName('package-lock.json'));
// (0, fs_1.writeFileSync)(storagePath, '{}', 'utf-8');
// }
// catch (err) {
// throw new LevelingError_1.default(Errors_1.default.notReady);
// }
// console.log(`${colors.red}failed to find a database file; created another one...${colors.reset}`);
// }
// try {
// if (!storageExists)
// (0, fs_1.writeFileSync)(storagePath, '{}', 'utf-8');
// const data = (0, fs_1.readFileSync)(storagePath);
// JSON.parse(data.toString());
// }
// catch (err) {
// if (err.message.includes('Unexpected token') ||
// err.message.includes('Unexpected end'))
// reject(new LevelingError_1.default(Errors_1.default.wrongStorageData));
// else {
// reject(err);
// throw err;
// }
// }
// }, updateCountdown);
// this.interval = interval;
// }
this.start();
this.ready = true;
this.emit('ready');
return resolve(true);
}
catch (err) {
this.errored = true;
reject(err);
}
});
}
/**
* Starts all the managers.
* @returns {Boolean} If successfully started: true.
* @private
*/
start() {
this.utils = new UtilsManager_1.default(this.options, this.client);
this.database = new DatabaseManager_1.default(this.options);
this.fetcher = new FetchManager_1.default(this.options);
this.settings = new SettingsManager_1.default(this.options, this.client);
this.xp = new XPManager_1.default(this.options);
this.levels = new LevelManager_1.default(this.options);
this.totalXP = new TotalXPManager_1.default(this.options);
this.ranks = new RanksManager_1.default(this.options, this.client);
if (!this.client?.on) {
console.log(new LevelingError_1.default(Errors_1.default.invalidClient));
process.exit(1);
}
this.client.on('messageCreate', async (message) => {
if (this.ready) {
const data = this.fetcher.fetchAll();
const guildID = message.guild.id;
const memberID = message.author.id;
const settings = {
xp: this.settings.get('xp', guildID),
maxXP: this.settings.get('maxXP', guildID),
multiplier: this.settings.get('multiplier', guildID),
status: this.settings.get('status', guildID),
ignoreBots: this.settings.get('ignoreBots', guildID),
lockedChannels: this.settings.get('lockedChannels', guildID),
ignoredUsers: this.settings.get('ignoredUsers', guildID),
filter: this.settings.get('filter', guildID)
};
const filterFunction = (settings.filter || this.options.filter).toString();
const filter = filterFunction.includes('{') ?
filterFunction.split('{').slice(1).join('').slice(0, -1) :
'return ' + filterFunction.split('=>').slice(1).join('');
const options = {
xp: settings.xp || this.options.xp,
maxXP: settings.maxXP || this.options.maxXP,
multiplier: settings.multiplier || this.options.multiplier,
status: settings.status == null ? this.options.status : settings.status,
ignoreBots: settings.ignoreBots == null ? this.options.ignoreBots : settings.ignoreBots,
lockedChannels: settings.lockedChannels || this.options.lockedChannels,
ignoredUsers: settings.ignoredUsers || this.options.ignoredUsers,
filter: new Function('msg', filter)
};
const lockedChannelsArray = [];
const ignoredUsersArray = [];
const ignoredGuildsArray = [];
const lockedChannels = options.lockedChannels;
const ignoredUsers = options.ignoredUsers;
const ignoredGuilds = this.options.ignoredGuilds;
for (let i of lockedChannels) {
const type = this.utils.typeOf(i);
switch (type) {
case 'String':
lockedChannelsArray.push(i);
break;
default:
lockedChannelsArray.push(i);
}
}
const invalidChannelTypes = lockedChannelsArray.filter(x => typeof x !== 'string');
if (invalidChannelTypes.length) {
throw new LevelingError_1.default(Errors_1.default.lockedChannels.invalidTypes
+ '\n[\n'
+ lockedChannels
.map((x) => {
const type = this.utils.typeOf(x);
const isOk = type == 'String' ? '(ok)' : '';
return ` ${x} - ${type} ${isOk}`;
})
.join('\n')
+ '\n]');
}
const invalidChannels = lockedChannelsArray.filter(x => x.length !== 18 && x.length !== 19);
if (invalidChannels.length)
return console.log(new LevelingError_1.default(Errors_1.default.lockedChannels.invalidChannels(invalidChannels)));
for (let i of ignoredUsers) {
const type = this.utils.typeOf(i);
switch (type) {
case 'String':
ignoredUsersArray.push(i);
break;
default:
ignoredUsersArray.push(i);
}
}
const invalidUserTypes = ignoredUsersArray.filter(x => typeof x !== 'string');
if (invalidUserTypes.length) {
throw new LevelingError_1.default(Errors_1.default.ignoredUsers.invalidTypes
+ '\n[\n'
+ ignoredUsers
.map((x) => {
const type = this.utils.typeOf(x);
const isOk = type == 'String' ? '(ok)' : '';
return ` ${x} - ${type} ${isOk}`;
})
.join('\n')
+ '\n]');
}
const invalidUsers = ignoredUsersArray.filter(x => x.length !== 18 && x.length !== 19);
if (invalidUsers.length && ignoredUsers.length)
return console.log(new LevelingError_1.default(Errors_1.default.ignoredUsers.invalidUsers(ignoredUsers)));
for (let i of ignoredGuilds) {
const type = this.utils.typeOf(i);
switch (type) {
case 'String':
ignoredGuildsArray.push(i);
break;
default:
ignoredGuildsArray.push(i);
}
}
const invalidGuildTypes = ignoredGuildsArray.filter(x => typeof x !== 'string');
if (invalidGuildTypes.length) {
throw new LevelingError_1.default(Errors_1.default.ignoredGuilds.invalidTypes
+ '\n[\n'
+ ignoredGuilds
.map((x) => {
const type = this.utils.typeOf(x);
const isOk = type == 'String' ? '(ok)' : '';
return ` ${x} - ${type} ${isOk}`;
})
.join('\n')
+ '\n]');
}
const invalidGuilds = ignoredGuildsArray.filter(x => x.length !== 18 && x.length !== 19);
if (invalidGuilds.length && ignoredGuilds.length)
throw new LevelingError_1.default(Errors_1.default.ignoredGuilds.invalidGuilds(invalidGuilds));
const levelingStatus = options.status;
const isFiltered = options.filter(message);
const isUserIgnored = ignoredUsersArray.includes(message.author.id);
const isLockedChannel = lockedChannelsArray.includes(message.channel.id);
const isGuildIgnored = ignoredGuildsArray.includes(message.guild.id);
const isBot = options.ignoreBots && message.author.bot;
const guildData = data[guildID];
let memberData = guildData?.[memberID];
if (levelingStatus && isFiltered &&
!isLockedChannel && !isUserIgnored &&
!isBot && !isGuildIgnored) {
if (!memberData) {
this.utils.reset(memberID, guildID);
memberData = DefaultObject_1.default;
return;
}
const level = this.database.fetch(`${guildID}.${memberID}.level`);
const memberMultiplier = this.database.fetch(`${guildID}.${memberID}.multiplier`);
const userXP = this.database.fetch(`${guildID}.${memberID}.xp`);
const userMaxXP = this.database.fetch(`${guildID}.${memberID}.maxXP`);
const settingsEXP = this.settings.get('xp', guildID);
const settingsXP = Array.isArray(settingsEXP) ? Math.floor(Math.random() * (settingsEXP[1] - settingsEXP[0] + 1)) + settingsEXP[0] : settingsEXP;
const xp = Array.isArray(options.xp) ? Math.floor(Math.random() * (options.xp[1] - options.xp[0] + 1)) + options.xp[0] : options.xp;
const multiplier = memberMultiplier == 1 ? options.multiplier : memberMultiplier;
const memberXP = xp * multiplier;
const newLevel = level + 1;
this.totalXP.add(memberXP, memberID, guildID, true);
if ((memberData.xp + xp) >= memberData.maxXP || (memberXP + xp) >= memberData.maxXP) {
const newMaxXP = options.maxXP * newLevel;
this.xp.set(0, memberID, guildID, true);
this.levels.add(1, memberID, guildID, true);
this.database.set(`${guildID}.${memberID}.maxXP`, newMaxXP);
this.database.set(`${guildID}.${memberID}.difference`, newMaxXP);
this.emit('levelUp', {
guildID,
user: message.author,
level: newLevel,
maxXP: newMaxXP,
multiplier,
sendMessage: (msg, channel) => {
const type = this.utils.typeOf(msg);
let messageOptions;
let textChannel;
switch (type) {
case 'String':
messageOptions = {
content: msg
};
break;
case 'Object':
messageOptions = msg;
break;
case 'EmbedBuilder':
messageOptions = {
embeds: [
msg
]
};
break;
case 'AttachmentBuilder':
messageOptions = {
files: [
messageOptions
]
};
break;
default:
throw new LevelingError_1.default(Errors_1.default.sendMessage.invalidTypes.msg + type);
}
if (channel) {
const channelType = this.utils.typeOf(channel);
switch (channelType) {
case 'String':
textChannel = this.client.channels.cache.get(channel);
break;
case 'Channel':
textChannel = channel;
break;
case 'TextChannel':
textChannel = channel;
break;
default:
throw new LevelingError_1.default(Errors_1.default.sendMessage.invalidTypes.channel + channelType);
}
if (!textChannel)
throw new LevelingError_1.default(Errors_1.default.sendMessage.channelNotFound);
return textChannel.send(messageOptions);
}
return message.channel.send(messageOptions);
}
});
return
}
this.xp.add(memberXP, memberID, guildID, true);
this.database.set(`${guildID}.${memberID}.difference`, (userMaxXP - userXP) - settingsXP);
}
}
});
return true;
}
}
module.exports = Leveling;
// ---------------------------------------
// Typedefs area starts here...
// ---------------------------------------
/**
* @typedef {Object} VersionData
* @property {Boolean} updated Is the module updated.
* @property {installedVersion} installedVersion Version of module that you have installed.
* @property {packageVersion} packageVersion Latest version of the module.
*/
/**
* @typedef {Object} RankData
* @property {UserData} userData User's data object.
* @property {Number} level User's level
* @property {Number} xp User's amount of XP.
* @property {Number} totalXP User's total amount of XP.
* @property {Number} maxXP How much XP in total the user need to reach the next level.
* @property {Number} difference The difference between max XP and current amount of XP. It shows how much XP he need to reach the next level.
* @property {Number} multiplier XP Multiplier.
*/
/**
* @typedef {Object} LeaderboardData Leaderboard data object.
* @property {String} userID User's ID.
* @property {Number} level User's level.
* @property {Number} xp User's amount of XP.
* @property {Number} totalXP User's amount of total XP.
* @property {Number} maxXP User's data object.
* @property {User} user User's data object.
* @property {Number} difference User's amount of total XP.
* @property {Number} multiplier XP Multiplier.
*/
/**
* @typedef {Object} LevelingOptions Default Leveling options.
* @property {String} [storagePath='./leveling.json'] Full path to a JSON file. Default: './leveling.json'.
* @property {Boolean} [checkStorage=true] Checks the if database file exists and if it has errors. Default: true
* @property {Boolean} [ignoreBots=true] If true, every message from bots won't give them XP. Default: true.
* @property {Number} [xp=5] Amount of XP that user will receive after sending a message. Default: 5.
* @property {Boolean} [status=true] You can enable or disable the leveling system using this option. Default: true.
* @property {Number} [maxXP=300] Amount of XP that user will totally need to reach the next level. This value will double for each level. Default: 300.
* @property {String[]} [lockedChannels=[]] Array of channel IDs that won't give XP to users. Default: [].
* @property {String[]} [ignoredGuilds=[]] Array of guilds on which none of the members will be given XP. Default: [].
* @property {Boolean} [multiplier=1] XP multiplier. Default: 1.
* @property {FilterFunction} [filter=() => true] Callback function that must return a boolean value, it will add XP only to authors of filtered messages. Default: null.
* @property {Number} [updateCountdown=1000] Checks for if storage file exists in specified time (in ms). Default: 1000.
* @property {UpdaterOptions} [updater] Update Checker options object.
* @property {ErrorHandlerOptions} [errorHandler] Error Handler options object.
*/
/**
* @typedef {Object} UpdaterOptions Updatee options object.
* @property {Boolean} [checkUpdates=true] Sends the update state message in console on start. Default: true.
* @property {Boolean} [upToDateMessage=true] Sends the message in console on start if module is up to date. Default: true.
*/
/**
* @typedef {Object} ErrorHandlerOptions
* @property {Boolean} [handleErrors=true] Handles all errors on startup. Default: true.
* @property {Number} [attempts=5] Amount of attempts to load the module. Use 0 for infinity attempts. Default: 5.
* @property {Number} [time=3000] Time between every attempt to start the module (in ms). Default: 3000.
*/
/**
* @typedef {Object} CheckerOptions Options object for an 'Leveling.utils.checkOptions' method.
* @property {Boolean} [ignoreInvalidTypes=false] Allows the method to ignore the options with invalid types. Default: false.
* @property {Boolean} [ignoreUnspecifiedOptions=false] Allows the method to ignore the unspecified Default: false.
* @property {Boolean} [ignoreInvalidOptions=false] Allows the method to ignore the unexisting Default: false.
* @property {Boolean} [showProblems=false] Allows the method to show all the problems in the console. Default: false.
* @property {Boolean} [sendLog=false] Allows the method to send the result in the console. Default: false.
* @property {Boolean} [sendSuccessLog=false] Allows the method to send the result if no problems were found. Default: false.
*/
/**
* @typedef {Object} UserData User data object.
* @property {String} id User's ID.
* @property {String} username User's username.
* @property {String} tag User's tag.
* @property {String} discriminator User's discriminator.
*/
/**
* @typedef {Object} LevelData
* @property {Number} xp User's amount of XP.
* @property {Number} totalXP User's total amount of XP.
* @property {Number} level User's level.
* @property {Number} maxXP How much XP in total the user need to reach the next level.
* @property {Number} difference The difference between max XP and current amount of XP. It shows how much XP he need to reach the next level.
* @property {Number} multiplier User's XP multiplier.
* @property {Boolean} onMessage The value will be true if the event was called on 'messageCreate' bot event.
*/
/**
* @typedef {Object} XPData
* @property {String} guildID Guild ID.
* @property {String} userID User ID.
* @property {Number} xp User's amount of XP.
* @property {Number} totalXP User's total amount of XP.
* @property {Number} level User's level.
* @property {Number} maxXP How much XP in total the user need to reach the next level.
* @property {Number} difference The difference between max XP and current amount of XP. It shows how much XP he need to reach the next level.
* @property {Number} multiplier User's XP multiplier.
* @property {Boolean} onMessage The value will be true if the event was called on 'messageCreate' bot event.
*/
/**
* @typedef {Object} LevelUpData
* @property {String} guildID Guild ID.
* @property {User} user The user that reached a new level.
* @property {Number} level New level.
* @property {Number} maxXP How much XP in total the user need to reach the next level.
* @property {Number} difference The difference between max XP and current amount of XP. It shows how much XP he need to reach the next level.
* @property {Number} multiplier User's XP multiplier.
* @property {Boolean} onMessage The value will be true if the event was called on 'messageCreate' bot event.
*/
/**
* @typedef {Object} SettingsTypes
* @property {Number} xp Amount of XP that user will receive after sending a message.
* @property {Number} maxXP Amount of XP that user will totally need to reach the next level. This value will double for each level.
* @property {Number} multiplier XP multiplier.
* @property {Boolean} status You can enable or disable the leveling system using this option.
* @property {String[]} ignoredUsers Array of user IDs that won't give XP.
* @property {String[]} lockedChannels Array of channel IDs that won't give XP to users.
* @property {Boolean} ignoreBots If true, every message from bots won't give them XP.
* @property {String | FilterFunction} filter Callback function that must return a boolean value, it will add XP only to authors of filtered messages.
*/
/**
* @typedef {Object} SettingsArrays
* @property {String[]} ignoredUsers Array of user IDs that won't give XP.
* @property {String[]} lockedChannels Array of channel IDs that won't give XP to users.
*/
/**
* A function that will send a specified message to a specified channel.
* @callback SendMessage
* @param {String | MessageEmbed | MessageAttachment | MessageOptions} msg Message string, embed, attachment or message options.
* @param {String | Channel} channel Channel or it's ID.
* @returns {Promise<Message>}
*/
/**
* Filter function that accepts a message;
* it must return a boolean value and it will add XP
* only to authors of filtered messages.;
* Use 'null' to disable the filter. Default: '() => true'.
* @callback FilterFunction
* @param {Message} msg
* @returns {Boolean} Boolean value.
*/
// ---------------------------------------
// Events area starts here...
// ---------------------------------------
/**
* Emits when the module is ready.
* @event Leveling#ready
* @param {void} data Void event.
*/
/**
* Emits when the module is destroyed.
* @event Leveling#destroy
* @param {void} data Void event.
*/
/**
* Emits when someone's got the next level.
* @event Leveling#levelUp
* @param {LevelUpData} data Level up data object.
*/
/**
* Emits when someone's set the level.
* @event Leveling#setLevel
* @param {LevelData} data Level data object.
*/
/**
* Emits when someone's added the levels.
* @event Leveling#addLevel
* @param {XPData} data Level data object.
*/
/**
* Emits when someone's subtracted the levels.
* @event Leveling#subtractLevel
* @param {LevelData} data Level data object.
*/
/**
* Emits when someone's set the XP.
* @event Leveling#setXP
* @param {LevelData} data Level data object.
*/
/**
* Emits when someone's add the XP.
* @event Leveling#addXP
* @param {XPData} data Level data object.
*/
/**
* Emits when someone's subtracted the XP.
* @event Leveling#subtractXP
* @param {LevelData} data Level data object.
*/
/**
* Emits when someone's set the total XP.
* @event Leveling#setTotalXP
* @param {LevelData} data Level data object.
*/
/**
* Emits when someone's added the total XP.
* @event Leveling#addTotalXP
* @param {XPData} data Level data object.
*/
/**
* Emits when someone's subtracted the total XP.
* @event Leveling#subtractTotalXP
* @param {LevelData} data Level data object.
*/