Skip to content
This repository was archived by the owner on Aug 28, 2020. It is now read-only.

Commit

Permalink
refactor(SG): make the settings system lighter and easier (#1048)
Browse files Browse the repository at this point in the history
Co-authored-by: Vlad Frangu <[email protected]>
Co-authored-by: bdistin <[email protected]>
  • Loading branch information
3 people authored Jun 22, 2020
1 parent 4e496bd commit 7ab4136
Show file tree
Hide file tree
Showing 27 changed files with 1,812 additions and 2,834 deletions.
72 changes: 22 additions & 50 deletions src/commands/Admin/conf.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Argument, Command, CommandStore, GatewayStorage, Schema, SchemaEntry, SchemaFolder, SettingsFolder } from 'klasa';
import { toTitleCase, codeBlock } from '@klasa/utils';
import { Argument, Command, CommandStore, SchemaEntry, Gateway, Settings } from 'klasa';
import { toTitleCase } from '@klasa/utils';
import { ChannelType } from '@klasa/dapi-types';
import { codeblock } from 'discord-md-tags';

import type { Message, Guild } from '@klasa/core';

export default class extends Command {

private readonly configurableSchemaKeys = new Map<string, Schema | SchemaEntry>();

public constructor(store: CommandStore, directory: string, files: readonly string[]) {
super(store, directory, files, {
runIn: [ChannelType.GuildText],
Expand All @@ -31,26 +30,25 @@ export default class extends Command {
});
}

private get gateway(): Gateway {
return this.client.gateways.get('guilds') as Gateway;
}

public show(message: Message, [key]: [string]): Promise<Message[]> {
const schemaOrEntry = this.configurableSchemaKeys.get(key);
if (typeof schemaOrEntry === 'undefined') throw message.language.get('COMMAND_CONF_GET_NOEXT', key);
const guild = message.guild as Guild;
if (!key) return message.replyLocale('COMMAND_CONF_SERVER', [key, codeblock('asciidoc')`${this.displayFolder(guild.settings)}`]);

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const value = key ? message.guild!.settings.get(key) : message.guild!.settings;
if (SchemaEntry.is(schemaOrEntry)) {
return message.replyLocale('COMMAND_CONF_GET', [key, this.displayEntry(schemaOrEntry, value, message.guild as Guild)]);
}
const entry = this.gateway.schema.get(key);
if (!entry) throw message.language.get('COMMAND_CONF_GET_NOEXT', key);

return message.replyLocale('COMMAND_CONF_SERVER', [
key ? `: ${key.split('.').map(toTitleCase).join('/')}` : '',
codeBlock('asciidoc', this.displayFolder(value as SettingsFolder))
]);
const value = guild.settings.get(key);
return message.replyLocale('COMMAND_CONF_GET', [key, this.displayEntry(entry, value, guild)]);
}

public async set(message: Message, [key, valueToSet]: [string, string]): Promise<Message[]> {
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const [update] = await message.guild!.settings.update(key, valueToSet, { onlyConfigurable: true, arrayAction: 'add' });
const [update] = await message.guild!.settings.update(key, valueToSet, { arrayAction: 'add' });
return message.replyLocale('COMMAND_CONF_UPDATED', [key, this.displayEntry(update.entry, update.next, message.guild as Guild)]);
} catch (error) {
throw String(error);
Expand All @@ -60,7 +58,7 @@ export default class extends Command {
public async remove(message: Message, [key, valueToRemove]: [string, string]): Promise<Message[]> {
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const [update] = await message.guild!.settings.update(key, valueToRemove, { onlyConfigurable: true, arrayAction: 'remove' });
const [update] = await message.guild!.settings.update(key, valueToRemove, { arrayAction: 'remove' });
return message.replyLocale('COMMAND_CONF_UPDATED', [key, this.displayEntry(update.entry, update.next, message.guild as Guild)]);
} catch (error) {
throw String(error);
Expand All @@ -77,37 +75,24 @@ export default class extends Command {
}
}

public init(): void {
const { schema } = this.client.gateways.get('guilds') as GatewayStorage;
if (this.initFolderConfigurableRecursive(schema)) this.configurableSchemaKeys.set(schema.path, schema);
}

private displayFolder(settings: SettingsFolder): string {
private displayFolder(settings: Settings): string {
const array = [];
const folders = [];
const sections = new Map<string, string[]>();
let longest = 0;
for (const [key, value] of settings.schema.entries()) {
if (!this.configurableSchemaKeys.has(value.path)) continue;
for (const [key, value] of settings.gateway.schema.entries()) {
const values = sections.get(value.type) || [];
values.push(key);

if (value.type === 'Folder') {
folders.push(`// ${key}`);
} else {
const values = sections.get(value.type) || [];
values.push(key);

if (key.length > longest) longest = key.length;
if (values.length === 1) sections.set(value.type, values);
}
if (key.length > longest) longest = key.length;
if (values.length === 1) sections.set(value.type, values);
}
if (folders.length) array.push('= Folders =', ...folders.sort(), '');
if (sections.size) {
for (const keyType of [...sections.keys()].sort()) {
array.push(`= ${toTitleCase(keyType)}s =`,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...sections.get(keyType)!.sort().map(key =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
`${key.padEnd(longest)} :: ${this.displayEntry(settings.schema.get(key) as SchemaEntry, settings.get(key), settings.base!.target as Guild)}`),
`${key.padEnd(longest)} :: ${this.displayEntry(settings.gateway.schema.get(key) as SchemaEntry, settings.get(key), settings.target as Guild)}`),
'');
}
}
Expand All @@ -131,17 +116,4 @@ export default class extends Command {
`[ ${values.map(value => this.displayEntrySingle(entry, value, guild)).join(' | ')} ]`;
}

private initFolderConfigurableRecursive(folder: Schema): boolean {
const previousConfigurableCount = this.configurableSchemaKeys.size;
for (const value of folder.values()) {
if (SchemaFolder.is(value)) {
if (this.initFolderConfigurableRecursive(value)) this.configurableSchemaKeys.set(value.path, value);
} else if (value.configurable) {
this.configurableSchemaKeys.set(value.path, value);
}
}

return previousConfigurableCount !== this.configurableSchemaKeys.size;
}

}
74 changes: 23 additions & 51 deletions src/commands/General/User Settings/userconf.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Argument, Command, CommandStore, GatewayStorage, Schema, SchemaEntry, SchemaFolder, SettingsFolder } from 'klasa';
import { toTitleCase, codeBlock } from '@klasa/utils';
import { Argument, Command, CommandStore, Settings, SchemaEntry, Gateway } from 'klasa';
import { toTitleCase } from '@klasa/utils';
import { codeblock } from 'discord-md-tags';

import type { Message, Guild } from '@klasa/core';

export default class extends Command {

private readonly configurableSchemaKeys = new Map<string, Schema | SchemaEntry>();

public constructor(store: CommandStore, directory: string, files: readonly string[]) {
super(store, directory, files, {
guarded: true,
Expand All @@ -28,24 +27,23 @@ export default class extends Command {
});
}

private get gateway(): Gateway {
return this.client.gateways.get('users') as Gateway;
}

public show(message: Message, [key]: [string]): Promise<Message[]> {
const schemaOrEntry = this.configurableSchemaKeys.get(key);
if (typeof schemaOrEntry === 'undefined') throw message.language.get('COMMAND_CONF_GET_NOEXT', key);
if (!key) return message.replyLocale('COMMAND_CONF_SERVER', [key, codeblock('asciidoc')`${this.displayFolder(message.author.settings)}`]);

const value = key ? message.author.settings.get(key) : message.author.settings;
if (SchemaEntry.is(schemaOrEntry)) {
return message.replyLocale('COMMAND_CONF_GET', [key, this.displayEntry(schemaOrEntry, value, message.guild)]);
}
const entry = this.gateway.schema.get(key);
if (!entry) throw message.language.get('COMMAND_CONF_GET_NOEXT', key);

return message.replyLocale('COMMAND_CONF_SERVER', [
key ? `: ${key.split('.').map(toTitleCase).join('/')}` : '',
codeBlock('asciidoc', this.displayFolder(value as SettingsFolder))
]);
const value = message.author.settings.get(key);
return message.replyLocale('COMMAND_CONF_GET', [key, this.displayEntry(entry, value, message.guild)]);
}

public async set(message: Message, [key, valueToSet]: [string, string]): Promise<Message[]> {
try {
const [update] = await message.author.settings.update(key, valueToSet, { onlyConfigurable: true, arrayAction: 'add' });
const [update] = await message.author.settings.update(key, valueToSet, { arrayAction: 'add' });
return message.replyLocale('COMMAND_CONF_UPDATED', [key, this.displayEntry(update.entry, update.next, message.guild)]);
} catch (error) {
throw String(error);
Expand All @@ -54,8 +52,8 @@ export default class extends Command {

public async remove(message: Message, [key, valueToRemove]: [string, string]): Promise<Message[]> {
try {
const [update] = await message.author.settings.update(key, valueToRemove, { onlyConfigurable: true, arrayAction: 'remove' });
return message.replyLocale('COMMAND_CONF_UPDATED', [key, this.displayEntry(update.entry, update.next, message.guild as Guild)]);
const [update] = await message.author.settings.update(key, valueToRemove, { arrayAction: 'remove' });
return message.replyLocale('COMMAND_CONF_UPDATED', [key, this.displayEntry(update.entry, update.next, message.guild)]);
} catch (error) {
throw String(error);
}
Expand All @@ -64,43 +62,30 @@ export default class extends Command {
public async reset(message: Message, [key]: [string]): Promise<Message[]> {
try {
const [update] = await message.author.settings.reset(key);
return message.replyLocale('COMMAND_CONF_RESET', [key, this.displayEntry(update.entry, update.next, message.guild as Guild)]);
return message.replyLocale('COMMAND_CONF_RESET', [key, this.displayEntry(update.entry, update.next, message.guild)]);
} catch (error) {
throw String(error);
}
}

public init(): void {
const { schema } = this.client.gateways.get('users') as GatewayStorage;
if (this.initFolderConfigurableRecursive(schema)) this.configurableSchemaKeys.set(schema.path, schema);
}

private displayFolder(settings: SettingsFolder): string {
private displayFolder(settings: Settings): string {
const array = [];
const folders = [];
const sections = new Map<string, string[]>();
let longest = 0;
for (const [key, value] of settings.schema.entries()) {
if (!this.configurableSchemaKeys.has(value.path)) continue;
for (const [key, value] of settings.gateway.schema.entries()) {
const values = sections.get(value.type) || [];
values.push(key);

if (value.type === 'Folder') {
folders.push(`// ${key}`);
} else {
const values = sections.get(value.type) || [];
values.push(key);

if (key.length > longest) longest = key.length;
if (values.length === 1) sections.set(value.type, values);
}
if (key.length > longest) longest = key.length;
if (values.length === 1) sections.set(value.type, values);
}
if (folders.length) array.push('= Folders =', ...folders.sort(), '');
if (sections.size) {
for (const keyType of [...sections.keys()].sort()) {
array.push(`= ${toTitleCase(keyType)}s =`,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...sections.get(keyType)!.sort().map(key =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
`${key.padEnd(longest)} :: ${this.displayEntry(settings.schema.get(key) as SchemaEntry, settings.get(key), settings.base!.target as Guild)}`),
`${key.padEnd(longest)} :: ${this.displayEntry(settings.gateway.schema.get(key) as SchemaEntry, settings.get(key), null)}`),
'');
}
}
Expand All @@ -124,17 +109,4 @@ export default class extends Command {
`[ ${values.map(value => this.displayEntrySingle(entry, value, guild)).join(' | ')} ]`;
}

private initFolderConfigurableRecursive(folder: Schema): boolean {
const previousConfigurableCount = this.configurableSchemaKeys.size;
for (const value of folder.values()) {
if (SchemaFolder.is(value)) {
if (this.initFolderConfigurableRecursive(value)) this.configurableSchemaKeys.set(value.path, value);
} else if (value.configurable) {
this.configurableSchemaKeys.set(value.path, value);
}
}

return previousConfigurableCount !== this.configurableSchemaKeys.size;
}

}
7 changes: 5 additions & 2 deletions src/events/argumentError.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Event, Message } from '@klasa/core';
import { codeblock } from 'discord-md-tags';

import type { Argument } from 'klasa';

export default class extends Event {

public async run(message: Message, _argument: Argument, _params: readonly unknown[], error: string): Promise<void> {
await message.reply(mb => mb.setContent(error));
public async run(message: Message, argument: Argument, _params: readonly unknown[], error: Error | string): Promise<void> {
if (error instanceof Error) this.client.emit('wtf', `[ARGUMENT] ${argument.path}\n${error.stack || error}`);
if (typeof error === 'string') await message.reply(mb => mb.setContent(error));
else await message.reply(mb => mb.setContent(codeblock('JSON') `${error.message}`));
}

}
5 changes: 1 addition & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@ export * from './lib/schedule/ScheduledTask';

// lib/settings
export * from './lib/settings/gateway/Gateway';
export * from './lib/settings/gateway/GatewayDriver';
export * from './lib/settings/gateway/GatewayStorage';
export * from './lib/settings/gateway/GatewayStore';
export * from './lib/settings/schema/Schema';
export * from './lib/settings/schema/SchemaFolder';
export * from './lib/settings/schema/SchemaEntry';
export * from './lib/settings/Settings';
export * from './lib/settings/SettingsFolder';

// lib/structures
export * from './lib/structures/Argument';
Expand Down
19 changes: 5 additions & 14 deletions src/lib/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { SerializerStore } from './structures/SerializerStore';
import { TaskStore } from './structures/TaskStore';

// lib/settings
import { GatewayDriver } from './settings/gateway/GatewayDriver';
import { GatewayStore } from './settings/gateway/GatewayStore';
import { Gateway } from './settings/gateway/Gateway';

// lib/settings/schema
Expand Down Expand Up @@ -146,12 +146,6 @@ export interface CommandHandlingOptions {
*/
prefix?: string | string[] | null;

/**
* The regular expression prefix if one is provided
* @default null
*/
regexPrefix?: RegExp | null;

/**
* Amount of time in ms before the bot will respond to a users command since the last command that user has run
* @default 0
Expand Down Expand Up @@ -380,10 +374,10 @@ export class KlasaClient extends Client {
public permissionLevels: PermissionLevels;

/**
* The GatewayDriver instance where the gateways are stored
* The GatewayStore instance where the gateways are stored
* @since 0.5.0
*/
public gateways: GatewayDriver;
public gateways: GatewayStore;

/**
* The Schedule that runs the tasks
Expand Down Expand Up @@ -443,7 +437,7 @@ export class KlasaClient extends Client {
// eslint-disable-next-line
this.permissionLevels['validate']();

this.gateways = new GatewayDriver(this);
this.gateways = new GatewayStore(this);

const { guilds, users, clientStorage } = this.options.settings.gateways;
const guildSchema = guilds.schema(new Schema());
Expand All @@ -461,8 +455,6 @@ export class KlasaClient extends Client {
guildSchema.add('language', 'language', { default: this.options.language });
}

guildSchema.add('disableNaturalPrefix', 'boolean', { configurable: Boolean(this.options.commands.regexPrefix) });

// Register default gateways
this.gateways
.register(new Gateway(this, 'guilds', { ...guilds, schema: guildSchema }))
Expand Down Expand Up @@ -618,7 +610,6 @@ export class KlasaClient extends Client {
public static defaultGuildSchema = new Schema()
.add('prefix', 'string')
.add('language', 'language')
.add('disableNaturalPrefix', 'boolean')
.add('disabledCommands', 'command', {
array: true,
filter: (_client, command: Command, { language }) => {
Expand Down Expand Up @@ -656,7 +647,7 @@ declare module '@klasa/core/dist/src/lib/client/Client' {
tasks: TaskStore;
serializers: SerializerStore;
permissionLevels: PermissionLevels;
gateways: GatewayDriver;
gateways: GatewayStore;
schedule: Schedule;
ready: boolean;
mentionPrefix: RegExp | null;
Expand Down
12 changes: 1 addition & 11 deletions src/lib/extensions/KlasaMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export class KlasaMessage extends extender.get('Message') {
this.prompter = null;

try {
const prefix = this._mentionPrefix() || this._customPrefix() || this._naturalPrefix() || this._prefixLess();
const prefix = this._mentionPrefix() || this._customPrefix() || this._prefixLess();

if (!prefix) return;

Expand Down Expand Up @@ -277,16 +277,6 @@ export class KlasaMessage extends extender.get('Message') {
return mentionPrefix ? { length: mentionPrefix[0].length, regex: this.client.mentionPrefix } : null;
}

/**
* Checks if the natural prefix is used
* @since 0.5.0
*/
private _naturalPrefix(): CachedPrefix | null {
if (this.guildSettings.get('disableNaturalPrefix') || !this.client.options.commands.regexPrefix) return null;
const results = this.client.options.commands.regexPrefix.exec(this.content);
return results ? { length: results[0].length, regex: this.client.options.commands.regexPrefix } : null;
}

/**
* Checks if a prefixless scenario is possible
* @since 0.5.0
Expand Down
Loading

0 comments on commit 7ab4136

Please sign in to comment.