Skip to content

Commit

Permalink
refactor(typeorm): simplify DB API (#2646)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranet committed Jul 23, 2024
1 parent 3df1081 commit 040e468
Show file tree
Hide file tree
Showing 145 changed files with 1,593 additions and 1,991 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"#lib/setup": "./dist/lib/setup/index.js",
"#lib/types": "./dist/lib/types/index.js",
"#lib/i18n/languageKeys": "./dist/lib/i18n/languageKeys/index.js",
"#lib/i18n": "./dist/lib/i18n/index.js",
"#lib/*": "./dist/lib/*.js",
"#languages": "./dist/languages/index.js",
"#root/*": "./dist/*.js"
Expand Down
80 changes: 38 additions & 42 deletions src/commands/Admin/conf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { configurableGroups, isSchemaGroup, isSchemaKey, readSettings, remove, reset, SchemaKey, set, writeSettings } from '#lib/database';
import { configurableGroups, isSchemaKey, readSettings, remove, reset, SchemaKey, set, writeSettingsTransaction } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { SettingsMenu, SkyraSubcommand } from '#lib/structures';
import type { GuildMessage } from '#lib/types';
Expand All @@ -7,10 +7,9 @@ import { isValidCustomEmoji, isValidSerializedTwemoji, isValidTwemoji } from '#l
import { inlineCode } from '@discordjs/builders';
import { ApplyOptions, RequiresClientPermissions } from '@sapphire/decorators';
import { CommandOptionsRunTypeEnum } from '@sapphire/framework';
import { filter } from '@sapphire/iterator-utilities/filter';
import { map } from '@sapphire/iterator-utilities/map';
import { filter, map, toArray } from '@sapphire/iterator-utilities';
import { send } from '@sapphire/plugin-editable-commands';
import { toTitleCase } from '@sapphire/utilities';
import { isNullish, toTitleCase } from '@sapphire/utilities';
import { PermissionFlagsBits } from 'discord.js';

@ApplyOptions<SkyraSubcommand.Options>({
Expand Down Expand Up @@ -40,15 +39,12 @@ export class UserCommand extends SkyraSubcommand {
const schemaValue = configurableGroups.getPathString(key.toLowerCase());
if (schemaValue === null) this.error(LanguageKeys.Commands.Admin.ConfGetNoExt, { key });

const output = await readSettings(message.guild, (settings) => {
return schemaValue.display(settings, args.t);
});
const settings = await readSettings(message.guild);
const output = schemaValue.display(settings, args.t);

if (isSchemaKey(schemaValue)) {
return send(message, {
content: args.t(LanguageKeys.Commands.Admin.ConfGet, { key: schemaValue.name, value: output }),
allowedMentions: { users: [], roles: [] }
});
const content = args.t(LanguageKeys.Commands.Admin.ConfGet, { key: schemaValue.name, value: output });
return send(message, { content, allowedMentions: { users: [], roles: [] } });
}

const title = key
Expand All @@ -64,64 +60,64 @@ export class UserCommand extends SkyraSubcommand {
}

public async set(message: GuildMessage, args: SkyraSubcommand.Args) {
const [key, schemaKey] = await this.fetchKey(args);
const response = await writeSettings(message.guild, async (settings) => {
await set(settings, schemaKey, args);
return schemaKey.display(settings, args.t);
});
const [key, schemaKey] = await this.#fetchKey(args);

await using trx = await writeSettingsTransaction(message.guild);
await trx.write(await set(trx.settings, schemaKey, args)).submit();

const response = schemaKey.display(trx.settings, args.t);
return send(message, {
content: args.t(LanguageKeys.Commands.Admin.ConfUpdated, { key, response: this.getTextResponse(response) }),
content: args.t(LanguageKeys.Commands.Admin.ConfUpdated, { key, response: this.#getTextResponse(response) }),
allowedMentions: { users: [], roles: [] }
});
}

public async remove(message: GuildMessage, args: SkyraSubcommand.Args) {
const [key, schemaKey] = await this.fetchKey(args);
const response = await writeSettings(message.guild, async (settings) => {
await remove(settings, schemaKey, args);
return schemaKey.display(settings, args.t);
});
const [key, schemaKey] = await this.#fetchKey(args);

await using trx = await writeSettingsTransaction(message.guild);
await trx.write(await remove(trx.settings, schemaKey, args)).submit();

const response = schemaKey.display(trx.settings, args.t);
return send(message, {
content: args.t(LanguageKeys.Commands.Admin.ConfUpdated, { key, response: this.getTextResponse(response) }),
content: args.t(LanguageKeys.Commands.Admin.ConfUpdated, { key, response: this.#getTextResponse(response) }),
allowedMentions: { users: [], roles: [] }
});
}

public async reset(message: GuildMessage, args: SkyraSubcommand.Args) {
const [key, schemaKey] = await this.fetchKey(args);
const response = await writeSettings(message.guild, async (settings) => {
reset(settings, schemaKey);
return schemaKey.display(settings, args.t);
});
const [key, schemaKey] = await this.#fetchKey(args);

await using trx = await writeSettingsTransaction(message.guild);
await trx.write(reset(schemaKey)).submit();

const response = schemaKey.display(trx.settings, args.t);
return send(message, {
content: args.t(LanguageKeys.Commands.Admin.ConfReset, { key, value: response }),
allowedMentions: { users: [], roles: [] }
});
}

private getTextResponse(response: string) {
#getTextResponse(response: string) {
return isValidCustomEmoji(response) || isValidSerializedTwemoji(response) || isValidTwemoji(response) ? response : inlineCode(response);
}

private async fetchKey(args: SkyraSubcommand.Args) {
async #fetchKey(args: SkyraSubcommand.Args) {
const key = await args.pick('string');

const value = configurableGroups.getPathString(key.toLowerCase());
if (value === null) this.error(LanguageKeys.Commands.Admin.ConfGetNoExt, { key });
if (value.dashboardOnly) this.error(LanguageKeys.Commands.Admin.ConfDashboardOnlyKey, { key });
if (isSchemaGroup(value)) {
this.error(LanguageKeys.Settings.Gateway.ChooseKey, {
keys: [
...map(
filter(value.childValues(), (value) => !value.dashboardOnly),
(value) => `\`${value.name}\``
)
]
});
if (isNullish(value) || value.dashboardOnly) {
this.error(LanguageKeys.Commands.Admin.ConfGetNoExt, { key });
}

if (isSchemaKey(value)) {
return [value.name, value as SchemaKey] as const;
}

return [value.name, value as SchemaKey] as const;
const keys = map(
filter(value.childValues(), (value) => !value.dashboardOnly),
(value) => inlineCode(value.name)
);
this.error(LanguageKeys.Settings.Gateway.ChooseKey, { keys: toArray(keys) });
}
}
103 changes: 51 additions & 52 deletions src/commands/Admin/roleset.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { GuildEntity, GuildSettings, readSettings, writeSettings, type UniqueRoleSet } from '#lib/database';
import { GuildEntity, readSettings, writeSettings, writeSettingsTransaction, type UniqueRoleSet } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { SkyraCommand, SkyraSubcommand } from '#lib/structures';
import { PermissionLevels, type GuildMessage } from '#lib/types';
import { ApplyOptions } from '@sapphire/decorators';
import { CommandOptionsRunTypeEnum } from '@sapphire/framework';
import { send } from '@sapphire/plugin-editable-commands';

const Root = LanguageKeys.Commands.Admin;

@ApplyOptions<SkyraSubcommand.Options>({
aliases: ['rs'],
description: LanguageKeys.Commands.Admin.RoleSetDescription,
detailedDescription: LanguageKeys.Commands.Admin.RoleSetExtended,
description: Root.RoleSetDescription,
detailedDescription: Root.RoleSetExtended,
permissionLevel: PermissionLevels.Administrator,
runIn: [CommandOptionsRunTypeEnum.GuildAny],
subcommands: [
Expand All @@ -32,46 +34,46 @@ export class UserCommand extends SkyraSubcommand {
const roles = await args.repeat('roleName');

// Get all rolesets from settings and check if there is an existing set with the name provided by the user
await writeSettings(message.guild, (settings) => {
await writeSettings(message.guild, (settings) => ({
// The set does exist so we want to only REMOVE provided roles from it
// Create a new array that we can use to overwrite the existing one in settings
settings[GuildSettings.Roles.UniqueRoleSets] = settings[GuildSettings.Roles.UniqueRoleSets].map((set) =>
rolesUniqueRoleSets: settings.rolesUniqueRoleSets.map((set) =>
set.name === name ? { name, roles: set.roles.filter((id: string) => !roles.find((role) => role.id === id)) } : set
);
});
)
}));

return send(message, args.t(LanguageKeys.Commands.Admin.RoleSetRemoved, { name, roles: roles.map((role) => role.name) }));
return send(message, args.t(Root.RoleSetRemoved, { name, roles: roles.map((role) => role.name) }));
}

public async reset(message: GuildMessage, args: SkyraSubcommand.Args) {
const [name, sets] = await Promise.all([
args.pick('string').catch(() => null),
// Get all rolesets from settings and check if there is an existing set with the name provided by the user
readSettings(message.guild, GuildSettings.Roles.UniqueRoleSets)
this.#getUniqueRoleSets(message)
]);
if (sets.length === 0) this.error(LanguageKeys.Commands.Admin.RoleSetResetEmpty);
if (sets.length === 0) this.error(Root.RoleSetResetEmpty);

if (!name) {
await writeSettings(message.guild, [[GuildSettings.Roles.UniqueRoleSets, []]]);
return send(message, args.t(LanguageKeys.Commands.Admin.RoleSetResetAll));
await writeSettings(message.guild, { rolesUniqueRoleSets: [] });
return send(message, args.t(Root.RoleSetResetAll));
}

const arrayIndex = sets.findIndex((set) => set.name === name);
if (arrayIndex === -1) this.error(LanguageKeys.Commands.Admin.RoleSetResetNotExists, { name });
if (arrayIndex === -1) this.error(Root.RoleSetResetNotExists, { name });

await writeSettings(message.guild, (settings) => {
settings[GuildSettings.Roles.UniqueRoleSets].splice(arrayIndex, 1);
await writeSettings(message.guild, {
rolesUniqueRoleSets: sets.toSpliced(arrayIndex, 1)
});

return send(message, args.t(LanguageKeys.Commands.Admin.RoleSetResetGroup, { name }));
return send(message, args.t(Root.RoleSetResetGroup, { name }));
}

// This subcommand will run if a user doesn't type add or remove. The bot will then add AND remove based on whether that role is in the set already.
public async auto(message: GuildMessage, args: SkyraSubcommand.Args) {
const name = await args.pick('string');

// Get all role sets from settings and check if there is an existing set with the name provided by the user
const sets = await readSettings(message.guild, GuildSettings.Roles.UniqueRoleSets);
const sets = await this.#getUniqueRoleSets(message);
const set = sets.find((set) => set.name === name);

// If this role set does not exist we have to create it
Expand All @@ -92,15 +94,15 @@ export class UserCommand extends SkyraSubcommand {
return { name, roles: newRoles };
});

await writeSettings(message.guild, [[GuildSettings.Roles.UniqueRoleSets, newSets]]);
return send(message, args.t(LanguageKeys.Commands.Admin.RoleSetUpdated, { name }));
await writeSettings(message.guild, { rolesUniqueRoleSets: newSets });
return send(message, args.t(Root.RoleSetUpdated, { name }));
}

// This subcommand will show the user a list of role sets and each role in that set.
public async list(message: GuildMessage, args: SkyraCommand.Args) {
// Get all rolesets from settings
const sets = await readSettings(message.guild, GuildSettings.Roles.UniqueRoleSets);
if (sets.length === 0) this.error(LanguageKeys.Commands.Admin.RoleSetNoRoleSets);
const sets = await this.#getUniqueRoleSets(message);
if (sets.length === 0) this.error(Root.RoleSetNoRoleSets);

const list = await this.handleList(message, args, sets);
return send(message, list.join('\n'));
Expand Down Expand Up @@ -135,21 +137,23 @@ export class UserCommand extends SkyraSubcommand {
if (changed) {
// If after cleaning up, all sets end up empty, reset and return error:
if (list.length === 0) {
await writeSettings(message.guild, [[GuildSettings.Roles.UniqueRoleSets, []]]);
this.error(LanguageKeys.Commands.Admin.RoleSetNoRoleSets);
await writeSettings(message.guild, { rolesUniqueRoleSets: [] });
this.error(Root.RoleSetNoRoleSets);
}

// Else, clean up:
await writeSettings(message.guild, (settings) => this.cleanRoleSets(message, settings));
await writeSettings(message.guild, (settings) => ({
rolesUniqueRoleSets: this.cleanRoleSets(message, settings)
}));
}

return list;
}

private cleanRoleSets(message: GuildMessage, settings: GuildEntity) {
private cleanRoleSets(message: GuildMessage, settings: Readonly<GuildEntity>) {
const guildRoles = message.guild.roles.cache;

settings[GuildSettings.Roles.UniqueRoleSets] = settings[GuildSettings.Roles.UniqueRoleSets]
return settings.rolesUniqueRoleSets
.map((set) => ({ name: set.name, roles: set.roles.filter((role) => guildRoles.has(role)) }))
.filter((set) => set.roles.length > 0);
}
Expand All @@ -158,36 +162,31 @@ export class UserCommand extends SkyraSubcommand {
const roles = await args.repeat('roleName');

// Get all rolesets from settings and check if there is an existing set with the name provided by the user
const created = await writeSettings(message.guild, (settings) => {
const allRoleSets = settings[GuildSettings.Roles.UniqueRoleSets];
const roleSet = allRoleSets.some((set) => set.name === name);

// If it does not exist we need to create a brand new set
if (!roleSet) {
allRoleSets.push({ name, roles: roles.map((role) => role.id) });
return true;
}
await using trx = await writeSettingsTransaction(message.guild);

const entries = trx.settings.rolesUniqueRoleSets;
const index = entries.findIndex((set) => set.name === name);

// If it does not exist we need to create a brand new set
if (index === -1) {
const rolesUniqueRoleSets = entries.concat({ name, roles: roles.map((role) => role.id) });
trx.write({ rolesUniqueRoleSets });
} else {
// The set does exist so we want to only ADD new roles in
// Create a new array that we can use to overwrite the existing one in settings
const sets = allRoleSets.map((set) => {
if (set.name !== name) return set;
const finalRoles = [...set.roles];
for (const role of roles) if (!finalRoles.includes(role.id)) finalRoles.push(role.id);

return { name, roles: finalRoles };
});
settings[GuildSettings.Roles.UniqueRoleSets] = sets;
const entry = entries[index];
const rolesUniqueRoleSets = entries.with(index, { name, roles: entry.roles.concat(roles.map((role) => role.id)) });
trx.write({ rolesUniqueRoleSets });
}
await trx.submit();

return false;
});
const created = index === -1;
const key = created ? Root.RoleSetCreated : Root.RoleSetAdded;
return send(message, args.t(key, { name, roles: roles.map((role) => role.name) }));
}

return send(
message,
args.t(created ? LanguageKeys.Commands.Admin.RoleSetCreated : LanguageKeys.Commands.Admin.RoleSetAdded, {
name,
roles: roles.map((role) => role.name)
})
);
async #getUniqueRoleSets(message: GuildMessage) {
const settings = await readSettings(message.guild);
return settings.rolesUniqueRoleSets;
}
}
14 changes: 6 additions & 8 deletions src/commands/Management/AutoModeration/automod-attachments.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Attachments;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['attachment-mode', 'attachments-mode', 'manage-attachment', 'manage-attachments'],
description: Root.AttachmentsDescription,
localizedNameKey: Root.AttachmentsName,
adderPropertyName: 'attachments',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
keyEnabled: 'selfmodAttachmentsEnabled',
keyOnInfraction: 'selfmodAttachmentsSoftAction',
keyPunishment: 'selfmodAttachmentsHardAction',
keyPunishmentDuration: 'selfmodAttachmentsHardActionDuration',
keyPunishmentThreshold: 'selfmodAttachmentsThresholdMaximum',
keyPunishmentThresholdPeriod: 'selfmodAttachmentsThresholdDuration'
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
14 changes: 6 additions & 8 deletions src/commands/Management/AutoModeration/automod-capitals.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Capitals;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['capitals-mode', 'manage-capitals'],
description: Root.CapitalsDescription,
localizedNameKey: Root.CapitalsName,
adderPropertyName: 'capitals',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
keyEnabled: 'selfmodCapitalsEnabled',
keyOnInfraction: 'selfmodCapitalsSoftAction',
keyPunishment: 'selfmodCapitalsHardAction',
keyPunishmentDuration: 'selfmodCapitalsHardActionDuration',
keyPunishmentThreshold: 'selfmodCapitalsThresholdMaximum',
keyPunishmentThresholdPeriod: 'selfmodCapitalsThresholdDuration'
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
Loading

0 comments on commit 040e468

Please sign in to comment.