feat: add externallyUsed.ts and support BothCommands again

This commit is contained in:
Jacob Nguyen
2022-05-21 23:54:27 -05:00
parent 7ae5ecf1a6
commit fc81bfc6d7
3 changed files with 60 additions and 51 deletions

View File

@@ -1,6 +1,5 @@
import type { APIGuildMember } from 'discord-api-types/v9';
import type {
Awaitable,
ChatInputCommandInteraction,
Guild,
GuildMember,
@@ -13,6 +12,8 @@ import type {
} from 'discord.js';
import { None, Option, Some } from 'ts-results';
import type { Nullish } from '../../types/handler';
import type { Client } from 'discord.js';
import { ExternallyUsed } from '../utilities/externallyUsed';
function firstSome<T>(...args: Option<T>[]): Nullish<T> {
for (const op of args) {
@@ -20,10 +21,12 @@ function firstSome<T>(...args: Option<T>[]): Nullish<T> {
}
return null;
}
//
//Will need refactoring after applying context in practice
//
//Could I refactor with Either monad?
/**
* The Context class will provide values that are shared between
* Message and ChatInputCommandInteraction
*
*/
export default class Context {
private constructor(
private oMsg: Option<Message> = None,
@@ -32,50 +35,50 @@ export default class Context {
this.oMsg = oMsg;
this.oInterac = oInterac;
}
@ExternallyUsed
public get message() {
return this.oMsg.unwrap();
}
@ExternallyUsed
public get interaction() {
return this.oInterac.unwrap();
}
@ExternallyUsed
public get id(): Snowflake {
return firstSome(
this.oInterac.map(i => i.id),
this.oMsg.map(m => m.id),
)!;
}
@ExternallyUsed
public get channel(): Nullish<TextBasedChannel> {
return firstSome(
this.oMsg.map(m => m.channel),
this.oInterac.map(i => i.channel),
);
}
@ExternallyUsed
public get user(): User {
return firstSome(
this.oMsg.map(m => m.author),
this.oInterac.map(i => i.user),
)!;
}
@ExternallyUsed
public get createdTimestamp(): number {
return firstSome(
this.oMsg.map(m => m.createdTimestamp),
this.oInterac.map(i => i.createdTimestamp),
)!;
}
@ExternallyUsed
public get guild(): Guild {
return firstSome(
this.oMsg.map(m => m.guild),
this.oInterac.map(i => i.guild),
)!;
}
@ExternallyUsed
public get guildId(): Snowflake {
return firstSome(
this.oMsg.map(m => m.guildId),
@@ -86,6 +89,7 @@ export default class Context {
/*
* interactions can return APIGuildMember if the guild it is emitted from is not cached
*/
@ExternallyUsed
public get member(): Nullish<GuildMember | APIGuildMember> {
return firstSome(
this.oMsg.map(m => m.member),
@@ -99,50 +103,35 @@ export default class Context {
}
return new Context(Some(wrappable), None);
}
@ExternallyUsed
public isEmpty() {
return this.oMsg.none && this.oInterac.none;
}
/*
* Returns the underlying Context but allows for doing other operations
*/
public onInteraction(onInteraction: (interaction: ChatInputCommandInteraction) => Awaitable<void>): Context {
if (this.oInterac.some) {
onInteraction(this.oInterac.val);
return Context.wrap(this.oInterac.val);
}
return this;
}
public onMessage(onMessage: (message: Message) => Awaitable<void>): Context {
if (this.oMsg.some) {
onMessage(this.oMsg.val);
return Context.wrap(this.oMsg.val);
}
return this;
}
public takeInteractionValue<T>(extract: (interaction: ChatInputCommandInteraction) => T): Nullish<T> {
if (this.oInterac.none) return null;
return extract(this.oInterac.val);
}
public takeMessageValue<T>(extract: (message: Message) => T): Nullish<T> {
if (this.oMsg.none) return null;
return extract(this.oMsg.val);
}
public reply(content: Omit<InteractionReplyOptions, 'fetchReply'> | ReplyMessageOptions): Promise<Context> {
//TODO: make this queueable
@ExternallyUsed
public reply(content: Omit<InteractionReplyOptions, 'fetchReply'> | ReplyMessageOptions) {
return firstSome(
this.oInterac.map(async i => {
await i.reply(content as InteractionReplyOptions);
return new Context(Some((await i.fetchReply()) as Message), Some(i));
this.oInterac.map(i => {
return i.reply(content as InteractionReplyOptions).then(() => i.fetchReply());
}),
this.oMsg.map(async m => {
const reply = await m.reply(content as ReplyMessageOptions);
return new Context(Some(reply), this.oInterac);
this.oMsg.map(m => {
return m.reply(content as ReplyMessageOptions);
}),
)!;
}
@ExternallyUsed
public get client(): Client {
return firstSome(
this.oMsg.map(m => m.client),
this.oInterac.map(i => i.client),
)!;
}
@ExternallyUsed
public get inGuild(): boolean {
return firstSome(
this.oMsg.map(m => m.inGuild()),
this.oInterac.map(i => i.inGuild()),
)!;
}
}

View File

@@ -0,0 +1,18 @@
/**
* This function denotes usage of decorated method is external
* Also, makes method appear 'used' in IDEs
* @param _target
* @param _propertyKey
* @param _descriptor
* @constructor
*/
export function ExternallyUsed(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_target: unknown,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_propertyKey: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_descriptor: PropertyDescriptor,
) {
return void 0;
}

View File

@@ -15,7 +15,9 @@ export function correctModuleType<T extends keyof ModuleDefs>(
plug: Module | undefined,
type: T,
): plug is ModuleDefs[T] {
return plug !== undefined && plug.type === type;
// Another way to check if type is equivalent,
// It will check based on flag system instead
return plug !== undefined && (plug.type & type) !== 0;
}
export function isChatInputCommand(i: CommandInteraction): i is ChatInputCommandInteraction {