@@ -2,6 +2,7 @@ import { ArgumentStream, Lexer, Parser, type IUnorderedStrategy } from '@sapphir
2
2
import { AliasPiece , type AliasPieceJSON } from '@sapphire/pieces' ;
3
3
import { isNullish , type Awaitable , type NonNullObject } from '@sapphire/utilities' ;
4
4
import {
5
+ ChannelType ,
5
6
ChatInputCommandInteraction ,
6
7
ContextMenuCommandInteraction ,
7
8
PermissionsBitField ,
@@ -20,6 +21,9 @@ import { PreconditionContainerArray, type PreconditionEntryResolvable } from '..
20
21
import { FlagUnorderedStrategy , type FlagStrategyOptions } from '../utils/strategies/FlagUnorderedStrategy' ;
21
22
import type { CommandStore } from './CommandStore' ;
22
23
24
+ const ChannelTypes = Object . values ( ChannelType ) . filter ( ( type ) => typeof type === 'number' ) as readonly ChannelType [ ] ;
25
+ const GuildChannelTypes = ChannelTypes . filter ( ( type ) => type !== ChannelType . DM && type !== ChannelType . GroupDM ) as readonly ChannelType [ ] ;
26
+
23
27
export class Command < PreParseReturn = Args , O extends Command . Options = Command . Options > extends AliasPiece < O > {
24
28
/**
25
29
* The {@link CommandStore} that contains this {@link Command}.
@@ -352,14 +356,15 @@ export class Command<PreParseReturn = Args, O extends Command.Options = Command.
352
356
}
353
357
354
358
/**
355
- * Appends the `DMOnly`, `GuildOnly`, `NewsOnly`, and `TextOnly` preconditions based on the values passed in
356
- * {@link Command.Options.runIn}, optimizing in specific cases (`NewsOnly` + `TextOnly` = `GuildOnly`; `DMOnly` +
357
- * `GuildOnly` = `null`), defaulting to `null`, which doesn't add a precondition.
359
+ * Appends the `RunIn` precondition based on the values passed, defaulting to `null`, which doesn't add a
360
+ * precondition.
358
361
* @param options The command options given from the constructor.
359
362
*/
360
363
protected parseConstructorPreConditionsRunIn ( options : Command . Options ) {
361
- const runIn = this . resolveConstructorPreConditionsRunType ( options . runIn ) ;
362
- if ( runIn !== null ) this . preconditions . append ( runIn as any ) ;
364
+ const types = this . resolveConstructorPreConditionsRunType ( options . runIn ) ;
365
+ if ( types !== null ) {
366
+ this . preconditions . append ( { name : CommandPreConditions . RunIn , context : { types } } ) ;
367
+ }
363
368
}
364
369
365
370
/**
@@ -411,87 +416,51 @@ export class Command<PreParseReturn = Args, O extends Command.Options = Command.
411
416
}
412
417
}
413
418
414
- private resolveConstructorPreConditionsRunType ( runIn : Command . Options [ 'runIn' ] ) : PreconditionContainerArray | CommandPreConditions | null {
415
- if ( isNullish ( runIn ) ) return null ;
416
- if ( typeof runIn === 'string' ) {
417
- switch ( runIn ) {
419
+ private resolveConstructorPreConditionsRunType ( types : Command . Options [ 'runIn' ] ) : readonly ChannelType [ ] | null {
420
+ if ( isNullish ( types ) ) return null ;
421
+ if ( typeof types === 'number' ) return [ types ] ;
422
+ if ( typeof types === 'string' ) {
423
+ switch ( types ) {
418
424
case 'DM' :
419
- return CommandPreConditions . DirectMessageOnly ;
425
+ return [ ChannelType . DM ] ;
420
426
case 'GUILD_TEXT' :
421
- return CommandPreConditions . GuildTextOnly ;
427
+ return [ ChannelType . GuildText ] ;
422
428
case 'GUILD_VOICE' :
423
- return CommandPreConditions . GuildVoiceOnly ;
429
+ return [ ChannelType . GuildVoice ] ;
424
430
case 'GUILD_NEWS' :
425
- return CommandPreConditions . GuildNewsOnly ;
431
+ return [ ChannelType . GuildAnnouncement ] ;
426
432
case 'GUILD_NEWS_THREAD' :
427
- return CommandPreConditions . GuildNewsThreadOnly ;
433
+ return [ ChannelType . AnnouncementThread ] ;
428
434
case 'GUILD_PUBLIC_THREAD' :
429
- return CommandPreConditions . GuildPublicThreadOnly ;
435
+ return [ ChannelType . PublicThread ] ;
430
436
case 'GUILD_PRIVATE_THREAD' :
431
- return CommandPreConditions . GuildPrivateThreadOnly ;
437
+ return [ ChannelType . PrivateThread ] ;
432
438
case 'GUILD_ANY' :
433
- return CommandPreConditions . GuildOnly ;
439
+ return GuildChannelTypes ;
434
440
default :
435
441
return null ;
436
442
}
437
443
}
438
444
439
445
// If there's no channel it can run on, throw an error:
440
- if ( runIn . length === 0 ) {
446
+ if ( types . length === 0 ) {
441
447
throw new Error ( `${ this . constructor . name } [${ this . name } ]: "runIn" was specified as an empty array.` ) ;
442
448
}
443
449
444
- if ( runIn . length === 1 ) {
445
- return this . resolveConstructorPreConditionsRunType ( runIn [ 0 ] ) ;
450
+ if ( types . length === 1 ) {
451
+ return this . resolveConstructorPreConditionsRunType ( types [ 0 ] ) ;
446
452
}
447
453
448
- const keys = new Set ( runIn ) ;
449
-
450
- const dm = keys . has ( 'DM' ) ;
451
- const guildText = keys . has ( 'GUILD_TEXT' ) ;
452
- const guildVoice = keys . has ( 'GUILD_VOICE' ) ;
453
- const guildNews = keys . has ( 'GUILD_NEWS' ) ;
454
- const guild = guildText && guildNews && guildVoice ;
455
-
456
- // If runs everywhere, optimise to null:
457
- if ( dm && guild ) return null ;
458
-
459
- const guildPublicThread = keys . has ( 'GUILD_PUBLIC_THREAD' ) ;
460
- const guildPrivateThread = keys . has ( 'GUILD_PRIVATE_THREAD' ) ;
461
- const guildNewsThread = keys . has ( 'GUILD_NEWS_THREAD' ) ;
462
- const guildThreads = guildPublicThread && guildPrivateThread && guildNewsThread ;
463
-
464
- // If runs in any thread, optimise to thread-only:
465
- if ( guildThreads && keys . size === 3 ) {
466
- return CommandPreConditions . GuildThreadOnly ;
454
+ const resolved = new Set < ChannelType > ( ) ;
455
+ for ( const typeResolvable of types ) {
456
+ for ( const type of this . resolveConstructorPreConditionsRunType ( typeResolvable ) ?? [ ] ) resolved . add ( type ) ;
467
457
}
468
458
469
- const preconditions = new PreconditionContainerArray ( ) ;
470
- if ( dm ) preconditions . append ( CommandPreConditions . DirectMessageOnly ) ;
471
- if ( guild ) {
472
- preconditions . append ( CommandPreConditions . GuildOnly ) ;
473
- } else {
474
- // GuildText includes PublicThread and PrivateThread
475
- if ( guildText ) {
476
- preconditions . append ( CommandPreConditions . GuildTextOnly ) ;
477
- } else {
478
- if ( guildPublicThread ) preconditions . append ( CommandPreConditions . GuildPublicThreadOnly ) ;
479
- if ( guildPrivateThread ) preconditions . append ( CommandPreConditions . GuildPrivateThreadOnly ) ;
480
- }
481
-
482
- // GuildNews includes NewsThread
483
- if ( guildNews ) {
484
- preconditions . append ( CommandPreConditions . GuildNewsOnly ) ;
485
- } else if ( guildNewsThread ) {
486
- preconditions . append ( CommandPreConditions . GuildNewsThreadOnly ) ;
487
- }
488
-
489
- if ( guildVoice ) {
490
- preconditions . append ( CommandPreConditions . GuildVoiceOnly ) ;
491
- }
492
- }
459
+ // If all types were resolved, optimize to null:
460
+ if ( resolved . size === ChannelTypes . length ) return null ;
493
461
494
- return preconditions ;
462
+ // Return the resolved types in ascending order:
463
+ return [ ...resolved ] . sort ( ( a , b ) => a - b ) ;
495
464
}
496
465
}
497
466
@@ -577,14 +546,24 @@ export enum CommandOptionsRunTypeEnum {
577
546
*/
578
547
export enum CommandPreConditions {
579
548
Cooldown = 'Cooldown' ,
549
+ /** @deprecated Use {@link RunIn} instead. */
580
550
DirectMessageOnly = 'DMOnly' ,
551
+ RunIn = 'RunIn' ,
552
+ /** @deprecated Use {@link RunIn} instead. */
581
553
GuildNewsOnly = 'GuildNewsOnly' ,
554
+ /** @deprecated Use {@link RunIn} instead. */
582
555
GuildNewsThreadOnly = 'GuildNewsThreadOnly' ,
556
+ /** @deprecated Use {@link RunIn} instead. */
583
557
GuildOnly = 'GuildOnly' ,
558
+ /** @deprecated Use {@link RunIn} instead. */
584
559
GuildPrivateThreadOnly = 'GuildPrivateThreadOnly' ,
560
+ /** @deprecated Use {@link RunIn} instead. */
585
561
GuildPublicThreadOnly = 'GuildPublicThreadOnly' ,
562
+ /** @deprecated Use {@link RunIn} instead. */
586
563
GuildTextOnly = 'GuildTextOnly' ,
564
+ /** @deprecated Use {@link RunIn} instead. */
587
565
GuildVoiceOnly = 'GuildVoiceOnly' ,
566
+ /** @deprecated Use {@link RunIn} instead. */
588
567
GuildThreadOnly = 'GuildThreadOnly' ,
589
568
NotSafeForWork = 'NSFW' ,
590
569
ClientPermissions = 'ClientPermissions' ,
@@ -715,7 +694,12 @@ export interface CommandOptions extends AliasPiece.Options, FlagStrategyOptions
715
694
* @since 2.0.0
716
695
* @default null
717
696
*/
718
- runIn ?: Command . RunInTypes | CommandOptionsRunTypeEnum | readonly ( Command . RunInTypes | CommandOptionsRunTypeEnum ) [ ] | null ;
697
+ runIn ?:
698
+ | ChannelType
699
+ | Command . RunInTypes
700
+ | CommandOptionsRunTypeEnum
701
+ | readonly ( ChannelType | Command . RunInTypes | CommandOptionsRunTypeEnum ) [ ]
702
+ | null ;
719
703
720
704
/**
721
705
* If {@link SapphireClient.typing} is true, this option will override it.
0 commit comments