-
-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move preconditions resolvers to dedicated files (#679)
- Loading branch information
Showing
22 changed files
with
515 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { PermissionsBitField, type PermissionResolvable } from 'discord.js'; | ||
import { CommandPreConditions } from '../types/Enums'; | ||
import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; | ||
|
||
/** | ||
* Appends the `ClientPermissions` precondition when {@link Command.Options.requiredClientPermissions} resolves to a | ||
* non-zero bitfield. | ||
* @param requiredClientPermissions The required client permissions. | ||
* @param preconditionContainerArray The precondition container array to append the precondition to. | ||
*/ | ||
export function parseConstructorPreConditionsRequiredClientPermissions( | ||
requiredClientPermissions: PermissionResolvable | undefined, | ||
preconditionContainerArray: PreconditionContainerArray | ||
) { | ||
const permissions = new PermissionsBitField(requiredClientPermissions); | ||
if (permissions.bitfield !== 0n) { | ||
preconditionContainerArray.append({ name: CommandPreConditions.ClientPermissions, context: { permissions } }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { container } from '@sapphire/pieces'; | ||
import type { Command } from '../structures/Command'; | ||
import { BucketScope, CommandPreConditions } from '../types/Enums'; | ||
import { type PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; | ||
|
||
/** | ||
* Appends the `Cooldown` precondition when {@link Command.Options.cooldownLimit} and | ||
* {@link Command.Options.cooldownDelay} are both non-zero. | ||
* | ||
* @param command The command to parse cooldowns for. | ||
* @param cooldownLimit The cooldown limit to use. | ||
* @param cooldownDelay The cooldown delay to use. | ||
* @param cooldownScope The cooldown scope to use. | ||
* @param cooldownFilteredUsers The cooldown filtered users to use. | ||
* @param preconditionContainerArray The precondition container array to append the precondition to. | ||
*/ | ||
export function parseConstructorPreConditionsCooldown<P, O extends Command.Options>( | ||
command: Command<P, O>, | ||
cooldownLimit: number | undefined, | ||
cooldownDelay: number | undefined, | ||
cooldownScope: BucketScope | undefined, | ||
cooldownFilteredUsers: string[] | undefined, | ||
preconditionContainerArray: PreconditionContainerArray | ||
) { | ||
const { defaultCooldown } = container.client.options; | ||
|
||
// We will check for whether the command is filtered from the defaults, but we will allow overridden values to | ||
// be set. If an overridden value is passed, it will have priority. Otherwise, it will default to 0 if filtered | ||
// (causing the precondition to not be registered) or the default value with a fallback to a single-use cooldown. | ||
const filtered = defaultCooldown?.filteredCommands?.includes(command.name) ?? false; | ||
const limit = cooldownLimit ?? (filtered ? 0 : defaultCooldown?.limit ?? 1); | ||
const delay = cooldownDelay ?? (filtered ? 0 : defaultCooldown?.delay ?? 0); | ||
|
||
if (limit && delay) { | ||
const scope = cooldownScope ?? defaultCooldown?.scope ?? BucketScope.User; | ||
const filteredUsers = cooldownFilteredUsers ?? defaultCooldown?.filteredUsers; | ||
preconditionContainerArray.append({ | ||
name: CommandPreConditions.Cooldown, | ||
context: { scope, limit, delay, filteredUsers } | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from './clientPermissions'; | ||
export * from './cooldown'; | ||
export * from './nsfw'; | ||
export * from './runIn'; | ||
export * from './userPermissions'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { CommandPreConditions } from '../types/Enums'; | ||
import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; | ||
|
||
/** | ||
* Appends the `NSFW` precondition if {@link SubcommandMappingMethod.nsfw} is set to true. | ||
* @param nsfw Whether this command is NSFW or not. | ||
* @param preconditionContainerArray The precondition container array to append the precondition to. | ||
*/ | ||
export function parseConstructorPreConditionsNsfw(nsfw: boolean | undefined, preconditionContainerArray: PreconditionContainerArray) { | ||
if (nsfw) preconditionContainerArray.append(CommandPreConditions.NotSafeForWork); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { isNullish } from '@sapphire/utilities'; | ||
import type { ChannelType } from 'discord.js'; | ||
import { Command } from '../structures/Command'; | ||
import type { CommandRunInUnion, CommandSpecificRunIn } from '../types/CommandTypes'; | ||
import { CommandPreConditions } from '../types/Enums'; | ||
import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; | ||
|
||
/** | ||
* Appends the `RunIn` precondition based on the values passed, defaulting to `null`, which doesn't add a | ||
* precondition. | ||
* @param runIn The command's `runIn` option field from the constructor. | ||
* @param resolveConstructorPreConditionsRunType The function to resolve the run type from the constructor. | ||
* @param preconditionContainerArray The precondition container array to append the precondition to. | ||
*/ | ||
export function parseConstructorPreConditionsRunIn( | ||
runIn: CommandRunInUnion | CommandSpecificRunIn, | ||
resolveConstructorPreConditionsRunType: (types: CommandRunInUnion) => readonly ChannelType[] | null, | ||
preconditionContainerArray: PreconditionContainerArray | ||
) { | ||
// Early return if there's no runIn option: | ||
if (isNullish(runIn)) return; | ||
|
||
if (Command.runInTypeIsSpecificsObject(runIn)) { | ||
const messageRunTypes = resolveConstructorPreConditionsRunType(runIn.messageRun); | ||
const chatInputRunTypes = resolveConstructorPreConditionsRunType(runIn.chatInputRun); | ||
const contextMenuRunTypes = resolveConstructorPreConditionsRunType(runIn.contextMenuRun); | ||
|
||
if (messageRunTypes !== null || chatInputRunTypes !== null || contextMenuRunTypes !== null) { | ||
preconditionContainerArray.append({ | ||
name: CommandPreConditions.RunIn, | ||
context: { | ||
types: { | ||
messageRun: messageRunTypes ?? [], | ||
chatInputRun: chatInputRunTypes ?? [], | ||
contextMenuRun: contextMenuRunTypes ?? [] | ||
} | ||
} | ||
}); | ||
} | ||
} else { | ||
const types = resolveConstructorPreConditionsRunType(runIn); | ||
if (types !== null) { | ||
preconditionContainerArray.append({ name: CommandPreConditions.RunIn, context: { types } }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { PermissionsBitField, type PermissionResolvable } from 'discord.js'; | ||
import { CommandPreConditions } from '../types/Enums'; | ||
import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; | ||
|
||
/** | ||
* Appends the `UserPermissions` precondition when {@link Command.Options.requiredUserPermissions} resolves to a | ||
* non-zero bitfield. | ||
* @param requiredUserPermissions The required user permissions. | ||
* @param preconditionContainerArray The precondition container array to append the precondition to. | ||
*/ | ||
export function parseConstructorPreConditionsRequiredUserPermissions( | ||
requiredUserPermissions: PermissionResolvable | undefined, | ||
preconditionContainerArray: PreconditionContainerArray | ||
) { | ||
const permissions = new PermissionsBitField(requiredUserPermissions); | ||
if (permissions.bitfield !== 0n) { | ||
preconditionContainerArray.append({ name: CommandPreConditions.UserPermissions, context: { permissions } }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { PermissionFlagsBits } from 'discord.js'; | ||
import { parseConstructorPreConditionsRequiredClientPermissions } from '../../src/lib/precondition-resolvers/clientPermissions'; | ||
import { CommandPreConditions } from '../../src/lib/types/Enums'; | ||
import { PreconditionContainerArray } from '../../src/lib/utils/preconditions/PreconditionContainerArray'; | ||
import type { PreconditionContainerSingle } from '../../src/lib/utils/preconditions/PreconditionContainerSingle'; | ||
import type { PermissionPreconditionContext } from '../../src/preconditions/ClientPermissions'; | ||
|
||
describe('parseConstructorPreConditionsRequiredClientPermissions', () => { | ||
test('GIVEN valid permissions THEN appends to preconditionContainerArray', () => { | ||
const preconditionContainerArray = new PreconditionContainerArray(); | ||
parseConstructorPreConditionsRequiredClientPermissions(PermissionFlagsBits.Administrator, preconditionContainerArray); | ||
expect(preconditionContainerArray.entries.length).toBe(1); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe(CommandPreConditions.ClientPermissions); | ||
expect( | ||
((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context as PermissionPreconditionContext).permissions?.has( | ||
PermissionFlagsBits.Administrator | ||
) | ||
).toBe(true); | ||
}); | ||
|
||
test('GIVEN no permissions THEN does not append to preconditionContainerArray', () => { | ||
const preconditionContainerArray = new PreconditionContainerArray(); | ||
parseConstructorPreConditionsRequiredClientPermissions(undefined, preconditionContainerArray); | ||
expect(preconditionContainerArray.entries.length).toBe(0); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import type { CooldownOptions } from '../../src/lib/SapphireClient'; | ||
import { parseConstructorPreConditionsCooldown } from '../../src/lib/precondition-resolvers/cooldown'; | ||
import { BucketScope } from '../../src/lib/types/Enums'; | ||
import { PreconditionContainerArray } from '../../src/lib/utils/preconditions/PreconditionContainerArray'; | ||
import type { PreconditionContainerSingle } from '../../src/lib/utils/preconditions/PreconditionContainerSingle'; | ||
|
||
describe('parseConstructorPreConditionsCooldown', () => { | ||
vi.mock('@sapphire/pieces', async () => { | ||
const mod = await vi.importActual<typeof import('@sapphire/pieces')>('@sapphire/pieces'); | ||
const { BucketScope } = await import('../../src/lib/types/Enums'); | ||
|
||
return { | ||
...mod, | ||
container: { | ||
client: { | ||
options: { | ||
defaultCooldown: { | ||
limit: 1, | ||
delay: 2, | ||
scope: BucketScope.User, | ||
filteredCommands: undefined, | ||
filteredUsers: undefined | ||
} as CooldownOptions | ||
} | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
afterAll(() => { | ||
vi.restoreAllMocks(); | ||
}); | ||
|
||
test('when limit and delay are undefined, sets limit to default limit and delay to default delay', () => { | ||
const preconditionContainerArray = new PreconditionContainerArray(); | ||
|
||
parseConstructorPreConditionsCooldown({ name: 'test' } as any, undefined, undefined, undefined, undefined, preconditionContainerArray); | ||
|
||
expect(preconditionContainerArray.entries.length).toBe(1); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe('Cooldown'); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toMatchObject({ | ||
scope: BucketScope.User, | ||
limit: 1, | ||
delay: 2, | ||
filteredUsers: undefined | ||
}); | ||
}); | ||
|
||
test('when limit and delay are defined, sets limit to passed limit and delay to passed delay', () => { | ||
const preconditionContainerArray = new PreconditionContainerArray(); | ||
parseConstructorPreConditionsCooldown({ name: 'test' } as any, 5, 10, undefined, undefined, preconditionContainerArray); | ||
|
||
expect(preconditionContainerArray.entries.length).toBe(1); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe('Cooldown'); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toMatchObject({ | ||
scope: BucketScope.User, | ||
limit: 5, | ||
delay: 10, | ||
filteredUsers: undefined | ||
}); | ||
}); | ||
|
||
test('when scope, filteredUsers, limit, and delay are defined, sets all values to passed values', () => { | ||
const preconditionContainerArray = new PreconditionContainerArray(); | ||
parseConstructorPreConditionsCooldown({ name: 'test' } as any, 5, 10, BucketScope.Guild, ['user1', 'user2'], preconditionContainerArray); | ||
|
||
expect(preconditionContainerArray.entries.length).toBe(1); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe('Cooldown'); | ||
expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toMatchObject({ | ||
scope: BucketScope.Guild, | ||
limit: 5, | ||
delay: 10, | ||
filteredUsers: ['user1', 'user2'] | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.