Merge branch 'channels-refactor'

This commit is contained in:
Yuzu 2022-09-15 11:47:08 -05:00
commit c993699aab

View File

@ -75,7 +75,7 @@ export abstract class BaseChannel implements Model {
type: ChannelTypes; type: ChannelTypes;
/** If the channel is a TextChannel */ /** If the channel is a TextChannel */
isText(): this is TextChannel { isText(): this is TextChannel.T {
return textBasedChannels.includes(this.type); return textBasedChannels.includes(this.type);
} }
@ -216,82 +216,73 @@ export type TextBasedChannels =
/** /**
* Represents a text channel. * Represents a text channel.
* tbis trait can be implemented by using implements TextChannel.T
*/ */
export class TextChannel { export namespace TextChannel {
constructor(session: Session, data: DiscordChannel) { export interface T {
this.session = session; /** The session that instantiated the channel */
this.id = data.id; readonly session: Session;
this.name = data.name;
this.type = data.type as number;
this.rateLimitPerUser = data.rate_limit_per_user ?? 0;
this.nsfw = !!data.nsfw ?? false;
if (data.last_message_id) { /** id's refers to the identification of the channel */
this.lastMessageId = data.last_message_id; readonly id: Snowflake;
}
if (data.last_pin_timestamp) { /** Current channel name */
this.lastPinTimestamp = data.last_pin_timestamp; name?: string;
}
}
/** The session that instantiated the channel */ /** The type of the channel */
readonly session: Session; type: TextBasedChannels;
/** id's refers to the identification of the channel */ /** The id of the last message sent in this channel (or thread for GUILD_FORUM channels) (may not point to an existing or valid message or thread) */
readonly id: Snowflake; lastMessageId?: Snowflake;
/** Current channel name */ /** When the last pinned message was pinned. This may be undefined in events such as GUILD_CREATE when a message is not pinned. */
name?: string; lastPinTimestamp?: string;
/** The type of the channel */ /** Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected */
type: TextBasedChannels; // TODO: move this
// rateLimitPerUser: number;
/** The id of the last message sent in this channel (or thread for GUILD_FORUM channels) (may not point to an existing or valid message or thread) */ /** If the channel is NSFW (Not-Safe-For-Work content) */
lastMessageId?: Snowflake; // TODO: move this
// nsfw: boolean;
/** When the last pinned message was pinned. This may be undefined in events such as GUILD_CREATE when a message is not pinned. */ // methods to be implemented
lastPinTimestamp?: string;
/** Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected */ fetchPins(): Promise<Message[]>;
rateLimitPerUser: number; createInvite(options?: DiscordInviteOptions): Promise<Invite>;
fetchMessages(options?: GetMessagesOptions): Promise<Message[]>;
/** If the channel is NSFW (Not-Safe-For-Work content) */ sendTyping(): Promise<void>;
nsfw: boolean; pinMessage(messageId: Snowflake): Promise<void>;
unpinMessage(messageId: Snowflake): Promise<void>;
/** addReaction(
* Mixin messageId: Snowflake,
*/ reaction: EmojiResolvable,
// eslint-disable-next-line @typescript-eslint/ban-types ): Promise<void>;
static applyTo(klass: Function, ignore: (keyof TextChannel)[] = []): void { removeReaction(
const methods: (keyof TextChannel)[] = [ messageId: Snowflake,
'fetchPins', reaction: EmojiResolvable,
'createInvite', options: { userId: Snowflake },
'fetchMessages', ): Promise<void>;
'sendTyping', removeReactionEmoji(
'pinMessage', messageId: Snowflake,
'unpinMessage', reaction: EmojiResolvable,
'addReaction', ): Promise<void>;
'removeReaction', nukeReactions(messageId: Snowflake): Promise<void>;
'nukeReactions', fetchReactions(
'fetchPins', messageId: Snowflake,
'sendMessage', reaction: EmojiResolvable,
'editMessage', options: GetReactions,
'createWebhook', ): Promise<User[]>;
]; sendMessage(options: CreateMessage): Promise<Message>;
editMessage(messageId: Snowflake, options: EditMessage): Promise<Message>;
for (const method of methods) { createWebhook(options: CreateWebhook): Promise<Webhook>;
if (ignore.includes(method)) { continue; }
klass.prototype[method] = TextChannel.prototype[method];
}
} }
/** /**
* fetchPins makes an asynchronous request and gets the current channel pins. * fetchPins makes an asynchronous request and gets the current channel pins.
* @returns A promise that resolves with an array of Message objects. * @returns A promise that resolves with an array of Message objects.
*/ */
async fetchPins(): Promise<Message[] | []> { export async function fetchPins(this: T): Promise<Message[]> {
const messages = await this.session.rest.get<DiscordMessage[]>( const messages = await this.session.rest.get<DiscordMessage[]>(
CHANNEL_PINS(this.id), CHANNEL_PINS(this.id),
); );
@ -304,7 +295,7 @@ export class TextChannel {
* @param options - The options to create the invitation * @param options - The options to create the invitation
* @returns The created invite * @returns The created invite
*/ */
async createInvite(options?: DiscordInviteOptions): Promise<Invite> { export async function createInvite(this: T, options?: DiscordInviteOptions): Promise<Invite> {
const invite = await this.session.rest.post<DiscordInvite>( const invite = await this.session.rest.post<DiscordInvite>(
CHANNEL_INVITES(this.id), CHANNEL_INVITES(this.id),
options options
@ -328,7 +319,7 @@ export class TextChannel {
* @param options - The options to get the messages * @param options - The options to get the messages
* @returns The messages * @returns The messages
*/ */
async fetchMessages(options?: GetMessagesOptions): Promise<Message[] | []> { export async function fetchMessages(this: T, options?: GetMessagesOptions): Promise<Message[]> {
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
if (options?.limit! > 100) { throw Error('Values must be between 0-100'); } if (options?.limit! > 100) { throw Error('Values must be between 0-100'); }
const messages = await this.session.rest.get<DiscordMessage[]>( const messages = await this.session.rest.get<DiscordMessage[]>(
@ -339,7 +330,7 @@ export class TextChannel {
} }
/** sendTyping sends a typing POST request */ /** sendTyping sends a typing POST request */
async sendTyping(): Promise<void> { export async function sendTyping(this: T): Promise<void> {
await this.session.rest.post<undefined>(CHANNEL_TYPING(this.id), {}); await this.session.rest.post<undefined>(CHANNEL_TYPING(this.id), {});
} }
@ -349,7 +340,7 @@ export class TextChannel {
* @param messageId - The id of the message to pin * @param messageId - The id of the message to pin
* @returns The promise that resolves when the request is complete * @returns The promise that resolves when the request is complete
*/ */
async pinMessage(messageId: Snowflake): Promise<void> { export async function pinMessage(this: T, messageId: Snowflake): Promise<void> {
await Message.prototype.pin.call({ id: messageId, channelId: this.id, session: this.session }); await Message.prototype.pin.call({ id: messageId, channelId: this.id, session: this.session });
} }
@ -359,7 +350,7 @@ export class TextChannel {
* @param messageId - The id of the message to unpin * @param messageId - The id of the message to unpin
* @returns The promise of the request * @returns The promise of the request
*/ */
async unpinMessage(messageId: Snowflake): Promise<void> { export async function unpinMessage(this: T, messageId: Snowflake): Promise<void> {
await Message.prototype.unpin.call({ id: messageId, channelId: this.id, session: this.session }); await Message.prototype.unpin.call({ id: messageId, channelId: this.id, session: this.session });
} }
@ -370,7 +361,11 @@ export class TextChannel {
* @param reaction - The reaction to add * @param reaction - The reaction to add
* @returns The promise of the request * @returns The promise of the request
*/ */
async addReaction(messageId: Snowflake, reaction: EmojiResolvable): Promise<void> { export async function addReaction(
this: T,
messageId: Snowflake,
reaction: EmojiResolvable,
): Promise<void> {
await Message.prototype.addReaction.call( await Message.prototype.addReaction.call(
{ channelId: this.id, id: messageId, session: this.session }, { channelId: this.id, id: messageId, session: this.session },
reaction, reaction,
@ -384,7 +379,8 @@ export class TextChannel {
* @param reaction - The reaction to remove * @param reaction - The reaction to remove
* @param options - The user to remove the reaction from * @param options - The user to remove the reaction from
*/ */
async removeReaction( export async function removeReaction(
this: T,
messageId: Snowflake, messageId: Snowflake,
reaction: EmojiResolvable, reaction: EmojiResolvable,
options?: { userId: Snowflake }, options?: { userId: Snowflake },
@ -401,7 +397,11 @@ export class TextChannel {
* Same as Message.removeReactionEmoji(). * Same as Message.removeReactionEmoji().
* @param messageId - The message id to remove the reaction from. * @param messageId - The message id to remove the reaction from.
*/ */
async removeReactionEmoji(messageId: Snowflake, reaction: EmojiResolvable): Promise<void> { export async function removeReactionEmoji(
this: T,
messageId: Snowflake,
reaction: EmojiResolvable,
): Promise<void> {
await Message.prototype.removeReactionEmoji.call( await Message.prototype.removeReactionEmoji.call(
{ channelId: this.id, id: messageId, session: this.session }, { channelId: this.id, id: messageId, session: this.session },
reaction, reaction,
@ -413,7 +413,7 @@ export class TextChannel {
* @param messageId The message id to nuke reactions from. * @param messageId The message id to nuke reactions from.
* @returns A promise that resolves when the reactions are nuked. * @returns A promise that resolves when the reactions are nuked.
*/ */
async nukeReactions(messageId: Snowflake): Promise<void> { export async function nukeReactions(this: T, messageId: Snowflake): Promise<void> {
await Message.prototype.nukeReactions.call({ channelId: this.id, id: messageId }); await Message.prototype.nukeReactions.call({ channelId: this.id, id: messageId });
} }
@ -425,7 +425,8 @@ export class TextChannel {
* @param options - The options to get the reactions with. * @param options - The options to get the reactions with.
* @returns The users who reacted with this emoji on the message. * @returns The users who reacted with this emoji on the message.
*/ */
async fetchReactions( export async function fetchReactions(
this: T,
messageId: Snowflake, messageId: Snowflake,
reaction: EmojiResolvable, reaction: EmojiResolvable,
options?: GetReactions, options?: GetReactions,
@ -445,8 +446,13 @@ export class TextChannel {
* @param options - Options for a new message. * @param options - Options for a new message.
* @returns The sent message. * @returns The sent message.
*/ */
sendMessage(options: CreateMessage): Promise<Message> { export async function sendMessage(this: T, options: CreateMessage): Promise<Message> {
return Message.prototype.reply.call({ channelId: this.id, session: this.session }, options); const message = await Message.prototype.reply.call({
channelId: this.id,
session: this.session,
}, options);
return message;
} }
/** /**
@ -456,8 +462,18 @@ export class TextChannel {
* @param options - Options for edit a message. * @param options - Options for edit a message.
* @returns The edited message. * @returns The edited message.
*/ */
editMessage(messageId: Snowflake, options: EditMessage): Promise<Message> { export async function editMessage(
return Message.prototype.edit.call({ channelId: this.id, id: messageId, session: this.session }, options); this: T,
messageId: Snowflake,
options: EditMessage,
): Promise<Message> {
const message = await Message.prototype.edit.call({
channelId: this.id,
id: messageId,
session: this.session,
}, options);
return message;
} }
/** /**
@ -465,7 +481,7 @@ export class TextChannel {
* @param options - Options for a new webhook. * @param options - Options for a new webhook.
* @returns The created webhook. * @returns The created webhook.
*/ */
async createWebhook(options: CreateWebhook): Promise<Webhook> { export async function createWebhook(this: T, options: CreateWebhook): Promise<Webhook> {
const webhook = await this.session.rest.post<DiscordWebhook>( const webhook = await this.session.rest.post<DiscordWebhook>(
CHANNEL_WEBHOOKS(this.id), CHANNEL_WEBHOOKS(this.id),
{ {
@ -577,14 +593,19 @@ export class GuildChannel extends BaseChannel implements Model {
/** Channel type. */ /** Channel type. */
override type: Exclude<ChannelTypes, ChannelTypes.DM | ChannelTypes.GroupDm>; override type: Exclude<ChannelTypes, ChannelTypes.DM | ChannelTypes.GroupDm>;
/** Guild id. */ /** Guild id. */
guildId: Snowflake; guildId: Snowflake;
/** Channel topic */ /** Channel topic */
topic?: string; topic?: string;
/** Sorting position of the channel */ /** Sorting position of the channel */
position?: number; position?: number;
/** Id of the parent category for a channel (each parent category can contain up to 50 channels). */ /** Id of the parent category for a channel (each parent category can contain up to 50 channels). */
parentId?: Snowflake; parentId?: Snowflake;
/** Explicit permission overwrites for members and roles */ /** Explicit permission overwrites for members and roles */
permissionOverwrites: PermissionsOverwrites[]; permissionOverwrites: PermissionsOverwrites[];
@ -734,7 +755,7 @@ export interface UpdateVoiceState {
selfDeaf: boolean; selfDeaf: boolean;
} }
export abstract class BaseVoiceChannel extends GuildChannel { export abstract class BaseVoiceChannel extends GuildChannel implements Model {
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
super(session, data, guildId); super(session, data, guildId);
this.bitRate = data.bitrate; this.bitRate = data.bitrate;
@ -760,10 +781,21 @@ export abstract class BaseVoiceChannel extends GuildChannel {
} }
/** DMChannel */ /** DMChannel */
export class DMChannel extends BaseChannel implements Model { export class DMChannel extends BaseChannel implements Model, TextChannel.T {
constructor(session: Session, data: DiscordChannel) { constructor(session: Session, data: DiscordChannel) {
super(session, data); super(session, data);
this.name = data.name;
this.type = data.type as number;
if (data.last_message_id) {
this.lastMessageId = data.last_message_id;
}
if (data.last_pin_timestamp) {
this.lastPinTimestamp = data.last_pin_timestamp;
}
if (data.owner_id && data.recipients) { if (data.owner_id && data.recipients) {
this.user = new User(session, data.recipients.find(user => user.id === data.owner_id)!); this.user = new User(session, data.recipients.find(user => user.id === data.owner_id)!);
} else { } else {
@ -784,11 +816,16 @@ export class DMChannel extends BaseChannel implements Model {
override type: ChannelTypes.DM | ChannelTypes.GroupDm; override type: ChannelTypes.DM | ChannelTypes.GroupDm;
/** Onwer of the dm channel. */ /** Onwer of the dm channel. */
user: User; user: User;
/** If the channel is a DM Group it's has multiple users. */ /** If the channel is a DM Group it's has multiple users. */
group?: User[]; group?: User[];
/** Last message id. */ /** Last message id. */
lastMessageId?: Snowflake; lastMessageId?: Snowflake;
/** When the last pinned message was pinned. This may be undefined in events such as GUILD_CREATE when a message is not pinned. */
lastPinTimestamp?: string;
async close(): Promise<DMChannel> { async close(): Promise<DMChannel> {
const channel = await this.session.rest.delete<DiscordChannel>(CHANNEL(this.id), {}); const channel = await this.session.rest.delete<DiscordChannel>(CHANNEL(this.id), {});
@ -802,12 +839,12 @@ export class DMChannel extends BaseChannel implements Model {
} }
} }
export interface DMChannel extends Omit<TextChannel, 'type'>, Omit<BaseChannel, 'type'> { } Object.assign(DMChannel.prototype, TextChannel);
TextChannel.applyTo(DMChannel); export interface DMChannel extends BaseChannel, TextChannel.T {}
/** VoiceChannel */ /** VoiceChannel */
export class VoiceChannel extends BaseVoiceChannel { export class VoiceChannel extends BaseVoiceChannel implements Model, TextChannel.T {
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
super(session, data, guildId); super(session, data, guildId);
this.type = data.type as number; this.type = data.type as number;
@ -816,12 +853,12 @@ export class VoiceChannel extends BaseVoiceChannel {
override type: ChannelTypes.GuildVoice; override type: ChannelTypes.GuildVoice;
} }
export interface VoiceChannel extends TextChannel, BaseVoiceChannel { } Object.assign(VoiceChannel.prototype, TextChannel);
TextChannel.applyTo(VoiceChannel); export interface VoiceChannel extends BaseVoiceChannel, TextChannel.T { }
/** NewsChannel */ /** NewsChannel */
export class NewsChannel extends GuildChannel { export class NewsChannel extends GuildChannel implements Model, TextChannel.T {
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
super(session, data, guildId); super(session, data, guildId);
this.type = data.type as ChannelTypes.GuildNews; this.type = data.type as ChannelTypes.GuildNews;
@ -840,9 +877,9 @@ export class NewsChannel extends GuildChannel {
} }
} }
TextChannel.applyTo(NewsChannel); Object.assign(NewsChannel.prototype, TextChannel);
export interface NewsChannel extends TextChannel, GuildChannel { } export interface NewsChannel extends GuildChannel, TextChannel.T { }
/** StageChannel */ /** StageChannel */
export class StageChannel extends BaseVoiceChannel { export class StageChannel extends BaseVoiceChannel {
@ -857,7 +894,7 @@ export class StageChannel extends BaseVoiceChannel {
} }
/** ThreadChannel */ /** ThreadChannel */
export class ThreadChannel extends GuildChannel implements Model { export class ThreadChannel extends GuildChannel implements Model, TextChannel.T {
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
super(session, data, guildId); super(session, data, guildId);
this.type = data.type as number; this.type = data.type as number;
@ -924,9 +961,9 @@ export class ThreadChannel extends GuildChannel implements Model {
} }
} }
export interface ThreadChannel extends Omit<GuildChannel, 'type'>, Omit<TextChannel, 'type'> { } Object.assign(ThreadChannel.prototype, TextChannel);
TextChannel.applyTo(ThreadChannel); export interface ThreadChannel extends GuildChannel, TextChannel.T { }
/** ForumChannel */ /** ForumChannel */
export class ForumChannel extends GuildChannel { export class ForumChannel extends GuildChannel {
@ -996,14 +1033,14 @@ export class GuildTextChannel extends GuildChannel {
override type: ChannelTypes.GuildText; override type: ChannelTypes.GuildText;
} }
export interface GuildTextChannel extends GuildChannel, TextChannel { } Object.assign(GuildTextChannel.prototype, TextChannel);
TextChannel.applyTo(GuildTextChannel); export interface GuildTextChannel extends GuildChannel, TextChannel.T { }
/** ChannelFactory */ /** ChannelFactory */
export type Channel = export type Channel =
| GuildTextChannel | GuildTextChannel
| TextChannel | TextChannel.T
| VoiceChannel | VoiceChannel
| DMChannel | DMChannel
| NewsChannel | NewsChannel
@ -1077,7 +1114,8 @@ export class ChannelFactory {
return new CategoryChannel(session, channel); return new CategoryChannel(session, channel);
default: default:
if (textBasedChannels.includes(channel.type)) { if (textBasedChannels.includes(channel.type)) {
return new TextChannel(session, channel); // BLACK MAGIC DO NOT EDIT
return Object.assign(Object.create(TextChannel), { session, channel }) as TextChannel.T;
} }
return null as any; return null as any;
} }