mirror of
https://github.com/keanuplayz/TravBot-v3.git
synced 2024-08-15 02:33:12 +00:00
Split command resolution part of help command
This commit is contained in:
parent
20fb2135c7
commit
72ff144cc0
12 changed files with 146 additions and 86 deletions
|
@ -1,61 +1,28 @@
|
|||
import {Command, NamedCommand, loadableCommands, categories, getPermissionName, CHANNEL_TYPE} from "../../core";
|
||||
import {toTitleCase, requireAllCasesHandledFor} from "../../lib";
|
||||
import {Command, NamedCommand, CHANNEL_TYPE, getPermissionName, getCommandList, getCommandInfo} from "../../core";
|
||||
import {requireAllCasesHandledFor} from "../../lib";
|
||||
|
||||
export default new NamedCommand({
|
||||
description: "Lists all commands. If a command is specified, their arguments are listed as well.",
|
||||
usage: "([command, [subcommand/type], ...])",
|
||||
aliases: ["h"],
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const commands = await loadableCommands;
|
||||
const commands = await getCommandList();
|
||||
let output = `Legend: \`<type>\`, \`[list/of/stuff]\`, \`(optional)\`, \`(<optional type>)\`, \`([optional/list/...])\``;
|
||||
|
||||
for (const [category, headers] of categories) {
|
||||
let tmp = `\n\n===[ ${toTitleCase(category)} ]===`;
|
||||
// Ignore empty categories, including ["test"].
|
||||
let hasActualCommands = false;
|
||||
|
||||
for (const header of headers) {
|
||||
if (header !== "test") {
|
||||
const command = commands.get(header)!;
|
||||
tmp += `\n- \`${header}\`: ${command.description}`;
|
||||
hasActualCommands = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasActualCommands) output += tmp;
|
||||
for (const [category, commandList] of commands) {
|
||||
output += `\n\n===[ ${category} ]===`;
|
||||
for (const command of commandList) output += `\n- \`${command.name}\`: ${command.description}`;
|
||||
}
|
||||
|
||||
channel.send(output, {split: true});
|
||||
},
|
||||
any: new Command({
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// Setup the root command
|
||||
const commands = await loadableCommands;
|
||||
let header = args.shift() as string;
|
||||
let command = commands.get(header);
|
||||
if (!command || header === "test") return channel.send(`No command found by the name \`${header}\`.`);
|
||||
if (!(command instanceof NamedCommand))
|
||||
return channel.send(`Command is not a proper instance of NamedCommand.`);
|
||||
if (command.name) header = command.name;
|
||||
|
||||
// Search categories
|
||||
let category = "Unknown";
|
||||
for (const [referenceCategory, headers] of categories) {
|
||||
if (headers.includes(header)) {
|
||||
category = toTitleCase(referenceCategory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Gather info
|
||||
const result = await command.resolveInfo(args);
|
||||
|
||||
if (result.type === "error") return channel.send(result.message);
|
||||
|
||||
const [result, category] = await getCommandInfo(args);
|
||||
if (typeof result === "string") return channel.send(result);
|
||||
let append = "";
|
||||
command = result.command;
|
||||
|
||||
if (result.args.length > 0) header += " " + result.args.join(" ");
|
||||
const command = result.command;
|
||||
const header = result.args.length > 0 ? `${result.header} ${result.args.join(" ")}` : result.header;
|
||||
|
||||
if (command.usage === "") {
|
||||
const list: string[] = [];
|
||||
|
|
|
@ -102,7 +102,7 @@ export default new NamedCommand({
|
|||
description: "Display info about a guild by finding its name or ID.",
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// If a guild ID is provided (avoid the "number" subcommand because of inaccuracies), search for that guild
|
||||
if (args.length === 1 && /^\d{17,19}$/.test(args[0])) {
|
||||
if (args.length === 1 && /^\d{17,}$/.test(args[0])) {
|
||||
const id = args[0];
|
||||
const targetGuild = client.guilds.cache.get(id);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ export default new NamedCommand({
|
|||
"Filters emotes by via a regular expression. Flags can be added by adding a dash at the end. For example, to do a case-insensitive search, do %prefix%lsemotes somepattern -i",
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// If a guild ID is provided, filter all emotes by that guild (but only if there aren't any arguments afterward)
|
||||
if (args.length === 1 && /^\d{17,19}$/.test(args[0])) {
|
||||
if (args.length === 1 && /^\d{17,}$/.test(args[0])) {
|
||||
const guildID: string = args[0];
|
||||
|
||||
displayEmoteList(
|
||||
|
|
|
@ -17,8 +17,8 @@ export default new NamedCommand({
|
|||
// handles reacts by message id/distance
|
||||
else if (args.length >= 2) {
|
||||
const last = args[args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator.
|
||||
const URLPattern = /^(?:https:\/\/discord.com\/channels\/(\d{17,19})\/(\d{17,19})\/(\d{17,19}))$/;
|
||||
const copyIDPattern = /^(?:(\d{17,19})-(\d{17,19}))$/;
|
||||
const URLPattern = /^(?:https:\/\/discord.com\/channels\/(\d{17,})\/(\d{17,})\/(\d{17,}))$/;
|
||||
const copyIDPattern = /^(?:(\d{17,})-(\d{17,}))$/;
|
||||
|
||||
// https://discord.com/channels/<Guild ID>/<Channel ID>/<Message ID> ("Copy Message Link" Button)
|
||||
if (URLPattern.test(last)) {
|
||||
|
@ -70,7 +70,7 @@ export default new NamedCommand({
|
|||
args.pop();
|
||||
}
|
||||
// <Message ID>
|
||||
else if (/^\d{17,19}$/.test(last)) {
|
||||
else if (/^\d{17,}$/.test(last)) {
|
||||
try {
|
||||
target = await channel.messages.fetch(last);
|
||||
} catch {
|
||||
|
|
|
@ -30,14 +30,16 @@ import {parseVars, requireAllCasesHandledFor} from "../lib";
|
|||
*/
|
||||
|
||||
// RegEx patterns used for identifying/extracting each type from a string argument.
|
||||
// The reason why \d{17,} is used is because the max safe number for JS numbers is 16 characters when stringified (decimal). Beyond that are IDs.
|
||||
const patterns = {
|
||||
channel: /^<#(\d{17,19})>$/,
|
||||
role: /^<@&(\d{17,19})>$/,
|
||||
emote: /^<a?:.*?:(\d{17,19})>$/,
|
||||
messageLink: /^https?:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(?:\d{17,19}|@me)\/(\d{17,19})\/(\d{17,19})$/,
|
||||
messagePair: /^(\d{17,19})-(\d{17,19})$/,
|
||||
user: /^<@!?(\d{17,19})>$/,
|
||||
id: /^(\d{17,19})$/
|
||||
channel: /^<#(\d{17,})>$/,
|
||||
role: /^<@&(\d{17,})>$/,
|
||||
emote: /^<a?:.*?:(\d{17,})>$/,
|
||||
// The message type won't include <username>#<tag>. At that point, you may as well just use a search usernames function. Even then, tags would only be taken into account to differentiate different users with identical usernames.
|
||||
messageLink: /^https?:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(?:\d{17,}|@me)\/(\d{17,})\/(\d{17,})$/,
|
||||
messagePair: /^(\d{17,})-(\d{17,})$/,
|
||||
user: /^<@!?(\d{17,})>$/,
|
||||
id: /^(\d{17,})$/
|
||||
};
|
||||
|
||||
// Maybe add a guild redirect... somehow?
|
||||
|
@ -106,9 +108,10 @@ interface ExecuteCommandMetadata {
|
|||
permission: number;
|
||||
nsfw: boolean;
|
||||
channelType: CHANNEL_TYPE;
|
||||
symbolicArgs: string[]; // i.e. <channel> instead of <#...>
|
||||
}
|
||||
|
||||
interface CommandInfo {
|
||||
export interface CommandInfo {
|
||||
readonly type: "info";
|
||||
readonly command: Command;
|
||||
readonly subcommandInfo: Collection<string, Command>;
|
||||
|
@ -117,6 +120,7 @@ interface CommandInfo {
|
|||
readonly nsfw: boolean;
|
||||
readonly channelType: CHANNEL_TYPE;
|
||||
readonly args: string[];
|
||||
readonly header: string;
|
||||
}
|
||||
|
||||
interface CommandInfoError {
|
||||
|
@ -131,14 +135,9 @@ interface CommandInfoMetadata {
|
|||
args: string[];
|
||||
usage: string;
|
||||
readonly originalArgs: string[];
|
||||
readonly header: string;
|
||||
}
|
||||
|
||||
export const defaultMetadata = {
|
||||
permission: 0,
|
||||
nsfw: false,
|
||||
channelType: CHANNEL_TYPE.ANY
|
||||
};
|
||||
|
||||
// Each Command instance represents a block that links other Command instances under it.
|
||||
export class Command {
|
||||
public readonly description: string;
|
||||
|
@ -298,12 +297,15 @@ export class Command {
|
|||
// Then capture any potential errors.
|
||||
try {
|
||||
if (typeof this.run === "string") {
|
||||
// Although I *could* add an option in the launcher to attach arbitrary variables to this var string...
|
||||
// I'll just leave it like this, because instead of using var strings for user stuff, you could just make "run" a template string.
|
||||
await menu.channel.send(
|
||||
parseVars(
|
||||
this.run,
|
||||
{
|
||||
author: menu.author.toString(),
|
||||
prefix: getPrefix(menu.guild)
|
||||
prefix: getPrefix(menu.guild),
|
||||
command: `${metadata.header} ${metadata.symbolicArgs.join(", ")}`
|
||||
},
|
||||
"???"
|
||||
)
|
||||
|
@ -332,6 +334,7 @@ export class Command {
|
|||
const isMessagePair = patterns.messagePair.test(param);
|
||||
|
||||
if (this.subcommands.has(param)) {
|
||||
metadata.symbolicArgs.push(param);
|
||||
return this.subcommands.get(param)!.execute(args, menu, metadata);
|
||||
} else if (this.channel && patterns.channel.test(param)) {
|
||||
const id = patterns.channel.exec(param)![1];
|
||||
|
@ -339,6 +342,7 @@ export class Command {
|
|||
|
||||
// Users can only enter in this format for text channels, so this restricts it to that.
|
||||
if (channel instanceof TextChannel) {
|
||||
metadata.symbolicArgs.push("<channel>");
|
||||
menu.args.push(channel);
|
||||
return this.channel.execute(args, menu, metadata);
|
||||
} else {
|
||||
|
@ -358,6 +362,7 @@ export class Command {
|
|||
const role = menu.guild.roles.cache.get(id);
|
||||
|
||||
if (role) {
|
||||
metadata.symbolicArgs.push("<role>");
|
||||
menu.args.push(role);
|
||||
return this.role.execute(args, menu, metadata);
|
||||
} else {
|
||||
|
@ -370,6 +375,7 @@ export class Command {
|
|||
const emote = menu.client.emojis.cache.get(id);
|
||||
|
||||
if (emote) {
|
||||
metadata.symbolicArgs.push("<emote>");
|
||||
menu.args.push(emote);
|
||||
return this.emote.execute(args, menu, metadata);
|
||||
} else {
|
||||
|
@ -395,6 +401,7 @@ export class Command {
|
|||
|
||||
if (channel instanceof TextChannel || channel instanceof DMChannel) {
|
||||
try {
|
||||
metadata.symbolicArgs.push("<message>");
|
||||
menu.args.push(await channel.messages.fetch(messageID));
|
||||
return this.message.execute(args, menu, metadata);
|
||||
} catch {
|
||||
|
@ -411,6 +418,7 @@ export class Command {
|
|||
const id = patterns.user.exec(param)![1];
|
||||
|
||||
try {
|
||||
metadata.symbolicArgs.push("<user>");
|
||||
menu.args.push(await menu.client.users.fetch(id));
|
||||
return this.user.execute(args, menu, metadata);
|
||||
} catch {
|
||||
|
@ -419,6 +427,7 @@ export class Command {
|
|||
};
|
||||
}
|
||||
} else if (this.id && this.idType && patterns.id.test(param)) {
|
||||
metadata.symbolicArgs.push("<id>");
|
||||
const id = patterns.id.exec(param)![1];
|
||||
|
||||
// Probably modularize the findXByY code in general in libd.
|
||||
|
@ -486,9 +495,11 @@ export class Command {
|
|||
requireAllCasesHandledFor(this.idType);
|
||||
}
|
||||
} else if (this.number && !Number.isNaN(Number(param)) && param !== "Infinity" && param !== "-Infinity") {
|
||||
metadata.symbolicArgs.push("<number>");
|
||||
menu.args.push(Number(param));
|
||||
return this.number.execute(args, menu, metadata);
|
||||
} else if (this.any) {
|
||||
metadata.symbolicArgs.push("<any>");
|
||||
menu.args.push(param);
|
||||
return this.any.execute(args, menu, metadata);
|
||||
} else {
|
||||
|
@ -502,8 +513,16 @@ export class Command {
|
|||
}
|
||||
|
||||
// What this does is resolve the resulting subcommand as well as the inherited properties and the available subcommands.
|
||||
public async resolveInfo(args: string[]): Promise<CommandInfo | CommandInfoError> {
|
||||
return this.resolveInfoInternal(args, {...defaultMetadata, args: [], usage: "", originalArgs: [...args]});
|
||||
public async resolveInfo(args: string[], header: string): Promise<CommandInfo | CommandInfoError> {
|
||||
return this.resolveInfoInternal(args, {
|
||||
permission: 0,
|
||||
nsfw: false,
|
||||
channelType: CHANNEL_TYPE.ANY,
|
||||
header,
|
||||
args: [],
|
||||
usage: "",
|
||||
originalArgs: [...args]
|
||||
});
|
||||
}
|
||||
|
||||
private async resolveInfoInternal(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {Client, Permissions, Message, TextChannel, DMChannel, NewsChannel} from "discord.js";
|
||||
import {loadableCommands} from "./loader";
|
||||
import {defaultMetadata} from "./command";
|
||||
import {getPrefix} from "./interface";
|
||||
|
||||
// For custom message events that want to cancel the command handler on certain conditions.
|
||||
|
@ -20,6 +19,13 @@ const lastCommandInfo: {
|
|||
channel: null
|
||||
};
|
||||
|
||||
const defaultMetadata = {
|
||||
permission: 0,
|
||||
nsfw: false,
|
||||
channelType: 0, // CHANNEL_TYPE.ANY, apparently isn't initialized at this point yet
|
||||
symbolicArgs: []
|
||||
};
|
||||
|
||||
// Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined.
|
||||
// Note: guild.available will never need to be checked because the message starts in either a DM channel or an already-available guild.
|
||||
export function attachMessageHandlerToClient(client: Client) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Onion Lasers Command Handler //
|
||||
export {Command, NamedCommand, CHANNEL_TYPE} from "./command";
|
||||
export {addInterceptRule} from "./handler";
|
||||
export {launch} from "./interface";
|
||||
|
@ -12,5 +13,5 @@ export {
|
|||
getMemberByUsername,
|
||||
callMemberByUsername
|
||||
} from "./libd";
|
||||
export {loadableCommands, categories} from "./loader";
|
||||
export {getCommandList, getCommandInfo} from "./loader";
|
||||
export {hasPermission, getPermissionLevel, getPermissionName} from "./permissions";
|
||||
|
|
|
@ -25,6 +25,11 @@ export function botHasPermission(guild: Guild | null, permission: number): boole
|
|||
// Pagination function that allows for customization via a callback.
|
||||
// Define your own pages outside the function because this only manages the actual turning of pages.
|
||||
|
||||
const FIVE_BACKWARDS_EMOJI = "⏪";
|
||||
const BACKWARDS_EMOJI = "⬅️";
|
||||
const FORWARDS_EMOJI = "➡️";
|
||||
const FIVE_FORWARDS_EMOJI = "⏩";
|
||||
|
||||
/**
|
||||
* Takes a message and some additional parameters and makes a reaction page with it. All the pagination logic is taken care of but nothing more, the page index is returned and you have to send a callback to do something with it.
|
||||
*/
|
||||
|
@ -43,30 +48,42 @@ export async function paginate(
|
|||
const turn = (amount: number) => {
|
||||
page += amount;
|
||||
|
||||
if (page < 0) page += total;
|
||||
else if (page >= total) page -= total;
|
||||
if (page >= total) {
|
||||
page %= total;
|
||||
} else if (page < 0) {
|
||||
// Assuming 3 total pages, it's a bit tricker, but if we just take the modulo of the absolute value (|page| % total), we get (1 2 0 ...), and we just need the pattern (2 1 0 ...). It needs to reverse order except for when it's 0. I want to find a better solution, but for the time being... total - (|page| % total) unless (|page| % total) = 0, then return 0.
|
||||
const flattened = Math.abs(page) % total;
|
||||
if (flattened !== 0) page = total - flattened;
|
||||
}
|
||||
|
||||
message.edit(callback(page, true));
|
||||
};
|
||||
const BACKWARDS_EMOJI = "⬅️";
|
||||
const FORWARDS_EMOJI = "➡️";
|
||||
const handle = (emote: string, reacterID: string) => {
|
||||
if (senderID === reacterID) {
|
||||
switch (emote) {
|
||||
case FIVE_BACKWARDS_EMOJI:
|
||||
if (total > 5) turn(-5);
|
||||
break;
|
||||
case BACKWARDS_EMOJI:
|
||||
turn(-1);
|
||||
break;
|
||||
case FORWARDS_EMOJI:
|
||||
turn(1);
|
||||
break;
|
||||
case FIVE_FORWARDS_EMOJI:
|
||||
if (total > 5) turn(5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for reactions and call the handler.
|
||||
let backwardsReactionFive = total > 5 ? await message.react(FIVE_BACKWARDS_EMOJI) : null;
|
||||
let backwardsReaction = await message.react(BACKWARDS_EMOJI);
|
||||
let forwardsReaction = await message.react(FORWARDS_EMOJI);
|
||||
let forwardsReactionFive = total > 5 ? await message.react(FIVE_FORWARDS_EMOJI) : null;
|
||||
unreactEventListeners.set(message.id, handle);
|
||||
|
||||
const collector = message.createReactionCollector(
|
||||
(reaction, user) => {
|
||||
if (user.id === senderID) {
|
||||
|
@ -88,8 +105,10 @@ export async function paginate(
|
|||
// When time's up, remove the bot's own reactions.
|
||||
collector.on("end", () => {
|
||||
unreactEventListeners.delete(message.id);
|
||||
backwardsReactionFive?.users.remove(message.author);
|
||||
backwardsReaction.users.remove(message.author);
|
||||
forwardsReaction.users.remove(message.author);
|
||||
forwardsReactionFive?.users.remove(message.author);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import {Collection} from "discord.js";
|
||||
import glob from "glob";
|
||||
import {Command, NamedCommand} from "./command";
|
||||
import {NamedCommand, CommandInfo} from "./command";
|
||||
import {toTitleCase} from "../lib";
|
||||
|
||||
// Internally, it'll keep its original capitalization. It's up to you to convert it to title case when you make a help command.
|
||||
export const categories = new Collection<string, string[]>();
|
||||
const categories = new Collection<string, string[]>();
|
||||
|
||||
/** Returns the cache of the commands if it exists and searches the directory if not. */
|
||||
export const loadableCommands = (async () => {
|
||||
const commands = new Collection<string, Command>();
|
||||
const commands = new Collection<string, NamedCommand>();
|
||||
// Include all .ts files recursively in "src/commands/".
|
||||
const files = await globP("src/commands/**/*.ts");
|
||||
// Extract the usable parts from "src/commands/" if:
|
||||
|
@ -79,3 +80,53 @@ function globP(path: string) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of categories and their associated commands.
|
||||
*/
|
||||
export async function getCommandList(): Promise<Collection<string, NamedCommand[]>> {
|
||||
const list = new Collection<string, NamedCommand[]>();
|
||||
const commands = await loadableCommands;
|
||||
|
||||
for (const [category, headers] of categories) {
|
||||
const commandList: NamedCommand[] = [];
|
||||
for (const header of headers.filter((header) => header !== "test")) commandList.push(commands.get(header)!);
|
||||
// Ignore empty categories like "miscellaneous" (if it's empty).
|
||||
if (commandList.length > 0) list.set(toTitleCase(category), commandList);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a command based on the arguments given.
|
||||
* - Returns a string if there was an error.
|
||||
* - Returns a CommandInfo/category tuple if it was a success.
|
||||
*/
|
||||
export async function getCommandInfo(args: string[]): Promise<[CommandInfo, string] | string> {
|
||||
// Use getCommandList() instead if you're just getting the list of all commands.
|
||||
if (args.length === 0) return "No arguments were provided!";
|
||||
|
||||
// Setup the root command
|
||||
const commands = await loadableCommands;
|
||||
let header = args.shift()!;
|
||||
const command = commands.get(header);
|
||||
if (!command || header === "test") return `No command found by the name \`${header}\`.`;
|
||||
if (!(command instanceof NamedCommand)) return "Command is not a proper instance of NamedCommand.";
|
||||
// If it's an alias, set the header to the original command name.
|
||||
if (command.name) header = command.name;
|
||||
|
||||
// Search categories
|
||||
let category = "Unknown";
|
||||
for (const [referenceCategory, headers] of categories) {
|
||||
if (headers.includes(header)) {
|
||||
category = toTitleCase(referenceCategory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Gather info
|
||||
const result = await command.resolveInfo(args, header);
|
||||
if (result.type === "error") return result.message;
|
||||
else return [result, category];
|
||||
}
|
||||
|
|
|
@ -144,14 +144,13 @@ export abstract class GenericStructure {
|
|||
|
||||
constructor(tag?: string) {
|
||||
this.__meta__ = tag || this.__meta__;
|
||||
Object.defineProperty(this, "__meta__", {
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
public save(asynchronous = true) {
|
||||
const tag = this.__meta__;
|
||||
/// @ts-ignore
|
||||
delete this.__meta__;
|
||||
FileManager.write(tag, this, asynchronous);
|
||||
this.__meta__ = tag;
|
||||
FileManager.write(this.__meta__, this, asynchronous);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ client.on("message", async (message) => {
|
|||
|
||||
export function extractFirstMessageLink(message: string): [string, string, string] | null {
|
||||
const messageLinkMatch = message.match(
|
||||
/([!<])?https?:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(\d{17,19})\/(\d{17,19})\/(\d{17,19})(>)?/
|
||||
/([!<])?https?:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(\d{17,})\/(\d{17,})\/(\d{17,})(>)?/
|
||||
);
|
||||
if (messageLinkMatch === null) return null;
|
||||
const [, leftToken, guildID, channelID, messageID, rightToken] = messageLinkMatch;
|
||||
|
|
|
@ -91,15 +91,13 @@ class StorageStructure extends GenericStructure {
|
|||
super("storage");
|
||||
this.users = {};
|
||||
this.guilds = {};
|
||||
|
||||
for (let id in data.users) if (/\d{17,19}/g.test(id)) this.users[id] = new User(data.users[id]);
|
||||
|
||||
for (let id in data.guilds) if (/\d{17,19}/g.test(id)) this.guilds[id] = new Guild(data.guilds[id]);
|
||||
for (let id in data.users) if (/\d{17,}/g.test(id)) this.users[id] = new User(data.users[id]);
|
||||
for (let id in data.guilds) if (/\d{17,}/g.test(id)) this.guilds[id] = new Guild(data.guilds[id]);
|
||||
}
|
||||
|
||||
/** Gets a user's profile if they exist and generate one if not. */
|
||||
public getUser(id: string): User {
|
||||
if (!/\d{17,19}/g.test(id))
|
||||
if (!/\d{17,}/g.test(id))
|
||||
console.warn(`"${id}" is not a valid user ID! It will be erased when the data loads again.`);
|
||||
|
||||
if (id in this.users) return this.users[id];
|
||||
|
@ -112,7 +110,7 @@ class StorageStructure extends GenericStructure {
|
|||
|
||||
/** Gets a guild's settings if they exist and generate one if not. */
|
||||
public getGuild(id: string): Guild {
|
||||
if (!/\d{17,19}/g.test(id))
|
||||
if (!/\d{17,}/g.test(id))
|
||||
console.warn(`"${id}" is not a valid guild ID! It will be erased when the data loads again.`);
|
||||
|
||||
if (id in this.guilds) return this.guilds[id];
|
||||
|
|
Loading…
Reference in a new issue