diff --git a/src/gateway/handlers/guildEmojiUpdate.ts b/src/gateway/handlers/guildEmojiUpdate.ts index fddb657..fbdb380 100644 --- a/src/gateway/handlers/guildEmojiUpdate.ts +++ b/src/gateway/handlers/guildEmojiUpdate.ts @@ -44,16 +44,18 @@ export const guildEmojiUpdate: GatewayEventHandler = async ( } } + gateway.client.emit('guildEmojisUpdate', guild) + for (const emoji of deleted) { - gateway.client.emit('guildEmojiDelete', guild, emoji) + gateway.client.emit('guildEmojiDelete', emoji) } for (const emoji of added) { - gateway.client.emit('guildEmojiAdd', guild, emoji) + gateway.client.emit('guildEmojiAdd', emoji) } for (const emoji of updated) { - gateway.client.emit('guildEmojiUpdate', guild, emoji.before, emoji.after) + gateway.client.emit('guildEmojiUpdate', emoji.before, emoji.after) } } } diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index 43499cd..1f4e326 100644 --- a/src/gateway/handlers/index.ts +++ b/src/gateway/handlers/index.ts @@ -113,11 +113,12 @@ export interface VoiceServerUpdateData { guild: Guild } +/** All Client Events */ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ClientEvents = { /** When Client has successfully connected to Discord */ ready: [shard: number] - /** When a successful reconnect has been made */ + /** When a reconnect was requested by Discord */ reconnect: [shard: number] /** When a successful session resume has been done */ resumed: [shard: number] @@ -178,25 +179,28 @@ export type ClientEvents = { * @param guild Guild in which Emoji was added * @param emoji The Emoji which was added */ - guildEmojiAdd: [guild: Guild, emoji: Emoji] + guildEmojiAdd: [emoji: Emoji] /** * An Emoji was deleted from Guild - * @param guild Guild from which Emoji was deleted * @param emoji Emoji which was deleted */ - guildEmojiDelete: [Guild, Emoji] + guildEmojiDelete: [emoji: Emoji] /** * An Emoji in a Guild was updated - * @param guild Guild in which Emoji was updated * @param before Emoji object before update * @param after Emoji object after update */ - guildEmojiUpdate: [guild: Guild, before: Emoji, after: Emoji] + guildEmojiUpdate: [before: Emoji, after: Emoji] /** * Guild's Integrations were updated * @param guild The Guild object */ guildIntegrationsUpdate: [guild: Guild] + /** + * Guild's Emojis were updated + * @param guild The Guild object + */ + guildEmojisUpdate: [guild: Guild] /** * A new Member has joined a Guild * @param member The Member object diff --git a/src/gateway/handlers/reconnect.ts b/src/gateway/handlers/reconnect.ts index 747273d..013c613 100644 --- a/src/gateway/handlers/reconnect.ts +++ b/src/gateway/handlers/reconnect.ts @@ -4,5 +4,6 @@ export const reconnect: GatewayEventHandler = async ( gateway: Gateway, d: any ) => { + gateway.client.emit('reconnect', gateway.shards?.[0] ?? 0) gateway.reconnect() } diff --git a/src/structures/application.ts b/src/structures/application.ts index fdab972..d302778 100644 --- a/src/structures/application.ts +++ b/src/structures/application.ts @@ -1,9 +1,9 @@ import { Client } from '../models/client.ts' import { ApplicationPayload } from '../types/application.ts' -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' import { User } from './user.ts' -export class Application extends Base { +export class Application extends SnowflakeBase { id: string name: string icon: string diff --git a/src/structures/base.ts b/src/structures/base.ts index c3f3e8d..e5806e5 100644 --- a/src/structures/base.ts +++ b/src/structures/base.ts @@ -1,4 +1,5 @@ import { Client } from '../models/client.ts' +import { Snowflake } from '../utils/snowflake.ts' export class Base { client: Client @@ -7,3 +8,17 @@ export class Base { this.client = client } } + +export class SnowflakeBase extends Base { + id!: string + + /** Get Snowflake Object */ + get snowflake(): Snowflake { + return new Snowflake(this.id) + } + + /** Timestamp of when resource was created */ + get timestamp(): Date { + return new Date(this.snowflake.timestamp) + } +} diff --git a/src/structures/channel.ts b/src/structures/channel.ts index 3097330..8e973b3 100644 --- a/src/structures/channel.ts +++ b/src/structures/channel.ts @@ -1,8 +1,8 @@ import { Client } from '../models/client.ts' import { ChannelPayload, ChannelTypes } from '../types/channel.ts' -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' -export class Channel extends Base { +export class Channel extends SnowflakeBase { type: ChannelTypes id: string static cacheName = 'channel' diff --git a/src/structures/embed.ts b/src/structures/embed.ts index 6abcedf..1936ddf 100644 --- a/src/structures/embed.ts +++ b/src/structures/embed.ts @@ -9,7 +9,9 @@ import { EmbedTypes, EmbedVideo } from '../types/channel.ts' +import { Colors, ColorUtil } from '../utils/colorutil.ts' +/** Message Embed Object */ export class Embed { title?: string type?: EmbedTypes @@ -41,7 +43,7 @@ export class Embed { this.fields = data?.fields } - // khk4912 + /** Convert Embed Object to Embed Payload JSON */ toJSON(): EmbedPayload { return { title: this.title, @@ -60,63 +62,114 @@ export class Embed { } } + /** Set Title of the Embed */ setTitle(title: string): Embed { this.title = title return this } + /** Set Embed description */ setDescription(description: string): Embed { this.description = description return this } + /** Set Embed Type */ setType(type: EmbedTypes): Embed { this.type = type return this } - setURL(url: string): Embed { - this.url = url + /** Set URL of the Embed */ + setURL(url: string | URL): Embed { + this.url = typeof url === 'object' ? url.toString() : url return this } - setTimestamp(timestamp: string): Embed { - this.timestamp = timestamp + /** Set Timestamp of the Embed */ + setTimestamp(timeString: string): Embed + setTimestamp(unixTimestamp: number): Embed + setTimestamp(dateObject: Date): Embed + setTimestamp(timestamp: string | Date | number): Embed { + this.timestamp = new Date(timestamp).toISOString() return this } - setColor(hex: number): Embed { - this.color = hex + /** Set Color of the Embed */ + setColor(hexInt: number): Embed + setColor(r: number, g: number, b: number): Embed + setColor(random: 'random'): Embed + setColor(hexStr: string): Embed + setColor(namedColor: keyof Colors): Embed + setColor( + color: number | 'random' | string | keyof Colors, + g?: number, + b?: number + ): Embed { + if (typeof color === 'number' && g === undefined && b === undefined) { + this.color = color + } else if (typeof color === 'string' && color.toLowerCase() === 'random') { + this.color = ColorUtil.resolveHex(ColorUtil.randomHex()) + } else if (typeof color === 'string' && color.startsWith('#')) { + this.color = ColorUtil.resolveHex(color) + } else if ( + typeof color === 'number' && + g !== undefined && + b !== undefined + ) { + this.color = ColorUtil.resolveRGB([color, g, b]) + } else if (typeof color === 'string') { + this.color = ColorUtil.resolveColor(color as keyof Colors) + } else + throw new Error( + 'Invalid Embed Color. Must be RGB, Hex (string or number), valid color name or a valid CSS color.' + ) return this } - setFooter(footer: EmbedFooter): Embed { - this.footer = footer + /** Set Footer of the Embed */ + setFooter(text: string, icon?: string): Embed + setFooter(footer: EmbedFooter): Embed + setFooter(footer: EmbedFooter | string, icon?: string): Embed { + this.footer = + typeof footer === 'string' ? { text: footer, icon_url: icon } : footer return this } - setImage(image: EmbedImage): Embed { - this.image = image + /** Set Image of the Embed */ + setImage(image: EmbedImage | string): Embed { + this.image = typeof image === 'string' ? { url: image } : image return this } - setThumbnail(thumbnail: EmbedThumbnail): Embed { - this.thumbnail = thumbnail + /** Set Thumbnail Image of the Embed */ + setThumbnail(thumbnail: EmbedThumbnail | string): Embed { + this.thumbnail = + typeof thumbnail === 'string' ? { url: thumbnail } : thumbnail return this } - setVideo(video: EmbedVideo): Embed { - this.video = video + /** Set Embed Video */ + setVideo(video: EmbedVideo | string): Embed { + this.video = typeof video === 'string' ? { url: video } : video return this } - setProvider(provider: EmbedProvider): Embed { - this.provider = provider + /** Set Provider of the Embed */ + setProvider(name: string, url?: string): Embed + setProvider(provider: EmbedProvider): Embed + setProvider(provider: EmbedProvider | string, url?: string): Embed { + this.provider = + typeof provider === 'string' ? { name: provider, url } : provider return this } - setAuthor(author: EmbedAuthor): Embed { - this.author = author + /** Set Author of the Embed */ + setAuthor(author: EmbedAuthor): Embed + setAuthor(name: string, image?: string): Embed + setAuthor(author: EmbedAuthor | string, image?: string): Embed { + this.author = + typeof author === 'string' ? { name: author, icon_url: image } : author return this } @@ -125,23 +178,29 @@ export class Embed { return this } - addField(name: string, value: string, inline?: boolean): Embed { + /** Adds a Field to the Embed */ + addField(field: EmbedField): Embed + addField(name: string, value: string, inline?: boolean): Embed + addField(name: string | EmbedField, value?: string, inline?: boolean): Embed { + if (typeof name !== 'object' && value === undefined) + throw new Error('field value is required') + const field: EmbedField = + typeof name === 'object' ? name : { name, value: value as string, inline } + if (this.fields === undefined) { - this.fields = [ - { - name: name, - value: value, - inline: inline - } - ] + this.fields = [field] } else { - this.fields.push({ - name: name, - value: value, - inline: inline - }) + this.fields.push(field) } return this } + + /** Adds multiple fields to the Embed */ + addFields(...fields: EmbedField[]): Embed { + for (const field of fields) { + this.addField(field) + } + return this + } } diff --git a/src/structures/emoji.ts b/src/structures/emoji.ts index 072a3e5..fe71889 100644 --- a/src/structures/emoji.ts +++ b/src/structures/emoji.ts @@ -1,13 +1,24 @@ import { Client } from '../models/client.ts' import { EmojiPayload } from '../types/emoji.ts' import { EMOJI } from '../types/endpoint.ts' +import { Snowflake } from '../utils/snowflake.ts' import { Base } from './base.ts' import { Guild } from './guild.ts' import { Role } from './role.ts' import { User } from './user.ts' +/** Guild Emoji Object */ export class Emoji extends Base { id: string | null + + get snowflake(): Snowflake | null { + return this.id === null ? null : new Snowflake(this.id) + } + + get timestamp(): Date | null { + return this.snowflake === null ? null : new Date(this.snowflake.timestamp) + } + name: string | null roles?: string[] user?: User @@ -48,7 +59,7 @@ export class Emoji extends Base { if (this.id === null) throw new Error('Emoji ID is not valid.') if (this.guild === undefined) throw new Error('Guild is undefined') const roles = Array.isArray(data.roles) - ? data.roles.map(role => (role instanceof Role ? role.id : role)) + ? data.roles.map((role) => (role instanceof Role ? role.id : role)) : [data.roles instanceof Role ? data.roles.id : data.roles] const res = await this.client.rest.patch(EMOJI(this.guild.id, this.id), { ...data, @@ -82,5 +93,5 @@ export interface ModifyGuildEmojiParams { /** Name of the emoji */ name?: string /** Roles to which this emoji will be whitelisted */ - roles?: string | Role | Array; + roles?: string | Role | Array } diff --git a/src/structures/guild.ts b/src/structures/guild.ts index ba79581..11a9d93 100644 --- a/src/structures/guild.ts +++ b/src/structures/guild.ts @@ -17,7 +17,7 @@ import { GuildPruneCountPayload, GuildBeginPrunePayload } from '../types/guild.ts' -import { Base } from './base.ts' +import { Base, SnowflakeBase } from './base.ts' import { CreateGuildRoleOptions, RolesManager } from '../managers/roles.ts' import { InviteManager } from '../managers/invites.ts' import { @@ -133,7 +133,7 @@ export class GuildBans { } } -export class Guild extends Base { +export class Guild extends SnowflakeBase { id: string name?: string icon?: string diff --git a/src/structures/member.ts b/src/structures/member.ts index 25e0b01..f035ddb 100644 --- a/src/structures/member.ts +++ b/src/structures/member.ts @@ -3,7 +3,7 @@ import { Client } from '../models/client.ts' import { GUILD_MEMBER } from '../types/endpoint.ts' import { MemberPayload } from '../types/guild.ts' import { Permissions } from '../utils/permissions.ts' -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' import { Guild } from './guild.ts' import { Role } from './role.ts' import { User } from './user.ts' @@ -15,7 +15,7 @@ export interface MemberData { mute?: boolean } -export class Member extends Base { +export class Member extends SnowflakeBase { id: string user: User nick?: string diff --git a/src/structures/message.ts b/src/structures/message.ts index 417f660..b697220 100644 --- a/src/structures/message.ts +++ b/src/structures/message.ts @@ -1,4 +1,4 @@ -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' import { Attachment, MessageActivity, @@ -21,7 +21,7 @@ import { Emoji } from './emoji.ts' type AllMessageOptions = MessageOptions | Embed -export class Message extends Base { +export class Message extends SnowflakeBase { id: string channelID: string channel: TextChannel @@ -30,8 +30,7 @@ export class Message extends Base { author: User member?: Member content: string - timestamp: string - editedTimestamp?: string + editedTimestamp?: Date tts: boolean mentions: MessageMentions attachments: Attachment[] @@ -63,8 +62,10 @@ export class Message extends Base { this.guildID = data.guild_id this.author = author this.content = data.content - this.timestamp = data.timestamp - this.editedTimestamp = data.edited_timestamp + this.editedTimestamp = + data.edited_timestamp === undefined + ? undefined + : new Date(data.edited_timestamp) this.tts = data.tts this.mentions = new MessageMentions(this.client, this) this.attachments = data.attachments @@ -91,8 +92,10 @@ export class Message extends Base { this.channelID = data.channel_id ?? this.channelID this.guildID = data.guild_id ?? this.guildID this.content = data.content ?? this.content - this.timestamp = data.timestamp ?? this.timestamp - this.editedTimestamp = data.edited_timestamp ?? this.editedTimestamp + this.editedTimestamp = + data.edited_timestamp === undefined + ? this.editedTimestamp + : new Date(data.edited_timestamp) this.tts = data.tts ?? this.tts this.attachments = data.attachments ?? this.attachments this.embeds = data.embeds.map((v) => new Embed(v)) ?? this.embeds diff --git a/src/structures/messageSticker.ts b/src/structures/messageSticker.ts index b14c6c1..d8cd475 100644 --- a/src/structures/messageSticker.ts +++ b/src/structures/messageSticker.ts @@ -3,9 +3,9 @@ import { MessageStickerFormatTypes, MessageStickerPayload } from '../types/channel.ts' -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' -export class MessageSticker extends Base { +export class MessageSticker extends SnowflakeBase { id: string packID: string name: string diff --git a/src/structures/presence.ts b/src/structures/presence.ts index a5c86af..bafff8e 100644 --- a/src/structures/presence.ts +++ b/src/structures/presence.ts @@ -77,6 +77,7 @@ export class ClientPresence { } } + /** Parses from Payload */ parse(payload: StatusPayload): ClientPresence { this.afk = payload.afk this.activity = payload.activities ?? undefined @@ -86,10 +87,12 @@ export class ClientPresence { return this } + /** Parses from Payload and creates new ClientPresence */ static parse(payload: StatusUpdatePayload): ClientPresence { return new ClientPresence().parse(payload) } + /** Creates Presence Payload */ create(): StatusPayload { return { afk: this.afk === undefined ? false : this.afk, @@ -100,6 +103,7 @@ export class ClientPresence { } } + /** Creates Activity Payload */ createActivity(): ActivityGame[] | null { // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions const activity = @@ -118,47 +122,45 @@ export class ClientPresence { } } + /** Set Status of Presence */ setStatus(status: StatusType): ClientPresence { this.status = status return this } + /** Set Activity for Presence */ setActivity(activity: ActivityGame): ClientPresence { this.activity = activity return this } + /** Set Activities for Presence */ setActivities(activities: ActivityGame[]): ClientPresence { this.activity = activities return this } + /** Set AFK value */ setAFK(afk: boolean): ClientPresence { this.afk = afk return this } + /** Remove AFK (set false) */ removeAFK(): ClientPresence { this.afk = false return this } + /** Toggle AFK (boolean) value */ toggleAFK(): ClientPresence { this.afk = this.afk === undefined ? true : !this.afk return this } + /** Set Since property of Activity */ setSince(since?: number): ClientPresence { this.since = since return this } - - // setClientStatus( - // client: 'desktop' | 'web' | 'mobile', - // status: StatusType - // ): ClientPresence { - // if (this.clientStatus === undefined) this.clientStatus = {} - // this.clientStatus[client] = status - // return this - // } } diff --git a/src/structures/role.ts b/src/structures/role.ts index 6f435c5..f3fea26 100644 --- a/src/structures/role.ts +++ b/src/structures/role.ts @@ -1,12 +1,13 @@ import { Client } from '../models/client.ts' -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' import { RoleModifyPayload, RolePayload } from '../types/role.ts' import { Permissions } from '../utils/permissions.ts' import { Guild } from './guild.ts' import { Member } from './member.ts' import { User } from './user.ts' -export class Role extends Base { +/** Represents a Guild Role */ +export class Role extends SnowflakeBase { id: string guild: Guild name: string @@ -52,6 +53,17 @@ export class Role extends Base { this.mentionable = data.mentionable ?? this.mentionable } + /** Delete the Role */ + async delete(): Promise { + return this.guild.roles.delete(this) + } + + /** Edit the Role */ + async edit(options: RoleModifyPayload): Promise { + return this.guild.roles.edit(this, options) + } + + /** Add the Role to a Member */ async addTo(member: Member | User | string): Promise { if (member instanceof User) { member = member.id @@ -68,6 +80,7 @@ export class Role extends Base { return member.roles.add(this.id) } + /** Remove the Role from a Member */ async removeFrom(member: Member | User | string): Promise { if (member instanceof User) { member = member.id @@ -83,14 +96,6 @@ export class Role extends Base { return member.roles.remove(this.id) } - - async delete(): Promise { - return this.guild.roles.delete(this) - } - - async edit(options: RoleModifyPayload): Promise { - return this.guild.roles.edit(this, options) - } } export interface RoleTags { diff --git a/src/structures/slash.ts b/src/structures/slash.ts index 7498cea..388aa56 100644 --- a/src/structures/slash.ts +++ b/src/structures/slash.ts @@ -8,6 +8,7 @@ import { InteractionResponsePayload, InteractionResponseType } from '../types/slash.ts' +import { SnowflakeBase } from './base.ts' import { Embed } from './embed.ts' import { Guild } from './guild.ts' import { Member } from './member.ts' @@ -39,7 +40,7 @@ export interface InteractionResponse { } } -export class Interaction { +export class Interaction extends SnowflakeBase { client: Client type: number token: string @@ -59,6 +60,7 @@ export class Interaction { member: Member } ) { + super(client) this.client = client this.type = data.type this.token = data.token diff --git a/src/structures/textChannel.ts b/src/structures/textChannel.ts index 552411d..5350479 100644 --- a/src/structures/textChannel.ts +++ b/src/structures/textChannel.ts @@ -19,10 +19,12 @@ import { MESSAGE_REACTION_USER } from '../types/endpoint.ts' import { Collection } from '../utils/collection.ts' +import { Permissions } from '../utils/permissions.ts' import { Channel } from './channel.ts' import { Embed } from './embed.ts' import { Emoji } from './emoji.ts' import { Guild } from './guild.ts' +import { CategoryChannel } from './guildCategoryChannel.ts' import { Invite } from './invite.ts' import { Member } from './member.ts' import { Message } from './message.ts' @@ -30,6 +32,7 @@ import { User } from './user.ts' export type AllMessageOptions = MessageOptions | Embed +/** Channel object for Text Channel type */ export class TextChannel extends Channel { lastMessageID?: string lastPinTimestamp?: string @@ -226,8 +229,15 @@ export class TextChannel extends Channel { return res } + + /** Trigger the typing indicator. NOT recommended to be used by bots unless you really want to. */ + async triggerTyping(): Promise { + await this.client.rest.api.channels[this.id].typing.post() + return this + } } +/** Represents a Text Channel but in a Guild */ export class GuildTextChannel extends TextChannel { guildID: string name: string @@ -235,7 +245,7 @@ export class GuildTextChannel extends TextChannel { permissionOverwrites: Overwrite[] nsfw: boolean parentID?: string - rateLimit: number + slowmode: number topic?: string guild: Guild @@ -257,7 +267,7 @@ export class GuildTextChannel extends TextChannel { this.nsfw = data.nsfw this.parentID = data.parent_id this.topic = data.topic - this.rateLimit = data.rate_limit_per_user + this.slowmode = data.rate_limit_per_user } readFromData(data: GuildTextChannelPayload): void { @@ -270,9 +280,10 @@ export class GuildTextChannel extends TextChannel { this.nsfw = data.nsfw ?? this.nsfw this.parentID = data.parent_id ?? this.parentID this.topic = data.topic ?? this.topic - this.rateLimit = data.rate_limit_per_user ?? this.rateLimit + this.slowmode = data.rate_limit_per_user ?? this.slowmode } + /** Edit the Guild Text Channel */ async edit( options?: ModifyGuildTextChannelOption ): Promise { @@ -280,7 +291,10 @@ export class GuildTextChannel extends TextChannel { name: options?.name, position: options?.position, permission_overwrites: options?.permissionOverwrites, - parent_id: options?.parentID + parent_id: options?.parentID, + nsfw: options?.nsfw, + topic: options?.topic, + rate_limit_per_user: options?.slowmode } const resp = await this.client.rest.patch(CHANNEL(this.id), body) @@ -328,4 +342,119 @@ export class GuildTextChannel extends TextChannel { async createInvite(options?: CreateInviteOptions): Promise { return this.guild.invites.create(this.id, options) } + + /** Get Permission Overties for a specific Member */ + async overwritesFor(member: Member | string): Promise { + member = (typeof member === 'string' + ? await this.guild.members.get(member) + : member) as Member + if (member === undefined) throw new Error('Member not found') + const roles = await member.roles.array() + + const overwrites: Overwrite[] = [] + + for (const overwrite of this.permissionOverwrites) { + if (overwrite.id === this.guild.id) { + overwrites.push(overwrite) + } else if (roles.some((e) => e.id === overwrite.id) === true) { + overwrites.push(overwrite) + } else if (overwrite.id === member.id) { + overwrites.push(overwrite) + } + } + + return overwrites + } + + /** Get Permissions for a Member in this Channel */ + async permissionsFor(member: Member | string): Promise { + const id = typeof member === 'string' ? member : member.id + if (id === this.guild.ownerID) return new Permissions(Permissions.ALL) + + member = (typeof member === 'string' + ? await this.guild.members.get(member) + : member) as Member + if (member === undefined) throw new Error('Member not found') + + if (member.permissions.has('ADMINISTRATOR') === true) + return new Permissions(Permissions.ALL) + + const overwrites = await this.overwritesFor(member) + const everyoneOW = overwrites.find((e) => e.id === this.guild.id) + const roleOWs = overwrites.filter((e) => e.type === 0) + const memberOWs = overwrites.filter((e) => e.type === 1) + + return member.permissions + .remove(everyoneOW !== undefined ? Number(everyoneOW.deny) : 0) + .add(everyoneOW !== undefined ? Number(everyoneOW.allow) : 0) + .remove(roleOWs.length === 0 ? 0 : roleOWs.map((e) => Number(e.deny))) + .add(roleOWs.length === 0 ? 0 : roleOWs.map((e) => Number(e.allow))) + .remove(memberOWs.length === 0 ? 0 : memberOWs.map((e) => Number(e.deny))) + .add(memberOWs.length === 0 ? 0 : memberOWs.map((e) => Number(e.allow))) + } + + /** Edit name of the channel */ + async setName(name: string): Promise { + return await this.edit({ name }) + } + + /** Edit topic of the channel */ + async setTopic(topic: string): Promise { + return await this.edit({ topic }) + } + + /** Edit topic of the channel */ + async setCategory( + category: CategoryChannel | string + ): Promise { + return await this.edit({ + parentID: typeof category === 'object' ? category.id : category + }) + } + + /** Edit position of the channel */ + async setPosition(position: number): Promise { + return await this.edit({ position }) + } + + /** Edit Slowmode of the channel */ + async setSlowmode(slowmode?: number | null): Promise { + return await this.edit({ slowmode: slowmode ?? null }) + } + + /** Edit NSFW property of the channel */ + async setNSFW(nsfw: boolean): Promise { + return await this.edit({ nsfw }) + } + + /** Set Permission Overwrites of the Channel */ + async setOverwrites(overwrites: Overwrite[]): Promise { + return await this.edit({ permissionOverwrites: overwrites }) + } + + /** Add a Permission Overwrite */ + async addOverwrite(overwrite: Overwrite): Promise { + const overwrites = this.permissionOverwrites + overwrites.push(overwrite) + return await this.edit({ permissionOverwrites: overwrites }) + } + + /** Remove a Permission Overwrite */ + async removeOverwrite(id: string): Promise { + if (this.permissionOverwrites.findIndex((e) => e.id === id) < 0) + throw new Error('Permission Overwrite not found') + const overwrites = this.permissionOverwrites.filter((e) => e.id !== id) + return await this.edit({ permissionOverwrites: overwrites }) + } + + /** Edit a Permission Overwrite */ + async editOverwrite(overwrite: Overwrite): Promise { + const index = this.permissionOverwrites.findIndex( + (e) => e.id === overwrite.id + ) + if (index < 0) throw new Error('Permission Overwrite not found') + const overwrites = this.permissionOverwrites + overwrites[index] = overwrite + return await this.edit({ permissionOverwrites: overwrites }) + } } diff --git a/src/structures/user.ts b/src/structures/user.ts index 8f2c99f..c0293ae 100644 --- a/src/structures/user.ts +++ b/src/structures/user.ts @@ -1,13 +1,13 @@ import { Client } from '../models/client.ts' import { UserPayload } from '../types/user.ts' import { UserFlagsManager } from '../utils/userFlags.ts' -import { Base } from './base.ts' +import { SnowflakeBase } from './base.ts' import { ImageURL } from './cdn.ts' import { ImageSize, ImageFormats } from '../types/cdn.ts' import { DEFAULT_USER_AVATAR, USER_AVATAR } from '../types/endpoint.ts' import { DMChannel } from './dmChannel.ts' -export class User extends Base { +export class User extends SnowflakeBase { id: string username: string discriminator: string diff --git a/src/types/channel.ts b/src/types/channel.ts index 77ee75d..25606a0 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -97,7 +97,7 @@ export interface ModifyGuildTextChannelOption extends ModifyChannelOption { type?: number topic?: string | null nsfw?: boolean | null - rateLimitPerUser?: number | null + slowmode?: number | null } export interface ModifyGuildNewsChannelOption extends ModifyChannelOption { diff --git a/src/utils/bitfield.ts b/src/utils/bitfield.ts index 54a595d..9ca4c0b 100644 --- a/src/utils/bitfield.ts +++ b/src/utils/bitfield.ts @@ -1,5 +1,11 @@ // Ported from https://github.com/discordjs/discord.js/blob/master/src/util/BitField.js -export type BitFieldResolvable = number | BitField | string | BitField[] +export type BitFieldResolvable = + | number + | number[] + | BitField + | string + | string[] + | BitField[] /** Bit Field utility to work with Bits and Flags */ export class BitField { @@ -20,7 +26,7 @@ export class BitField { } has(bit: BitFieldResolvable, ...args: any[]): boolean { - if (Array.isArray(bit)) return bit.every((p) => this.has(p)) + if (Array.isArray(bit)) return (bit.every as any)((p: any) => this.has(p)) return (this.bitfield & BitField.resolve(this.flags, bit)) === bit } @@ -89,9 +95,10 @@ export class BitField { if (typeof bit === 'number' && bit >= 0) return bit if (bit instanceof BitField) return this.resolve(flags, bit.bitfield) if (Array.isArray(bit)) - return bit - .map((p) => this.resolve(flags, p)) - .reduce((prev, p) => prev | p, 0) + return (bit.map as any)((p: any) => this.resolve(flags, p)).reduce( + (prev: any, p: any) => prev | p, + 0 + ) if (typeof bit === 'string' && typeof flags[bit] !== 'undefined') return flags[bit] const error = new RangeError('BITFIELD_INVALID') diff --git a/src/utils/permissions.ts b/src/utils/permissions.ts index e5d35ad..03b4b82 100644 --- a/src/utils/permissions.ts +++ b/src/utils/permissions.ts @@ -1,10 +1,12 @@ // Ported from https://github.com/discordjs/discord.js/blob/master/src/util/Permissions.js import { PermissionFlags } from '../types/permissionFlags.ts' -import { BitField } from './bitfield.ts' +import { BitField, BitFieldResolvable } from './bitfield.ts' export type PermissionResolvable = | string + | string[] | number + | number[] | Permissions | PermissionResolvable[] @@ -13,7 +15,7 @@ export class Permissions extends BitField { static DEFAULT = 104324673 static ALL = Object.values(PermissionFlags).reduce((all, p) => all | p, 0) - constructor(bits: any) { + constructor(bits: BitFieldResolvable) { super(PermissionFlags, bits) } diff --git a/src/utils/snowflake.ts b/src/utils/snowflake.ts index 2a0e040..9af3060 100644 --- a/src/utils/snowflake.ts +++ b/src/utils/snowflake.ts @@ -1,3 +1,4 @@ +/** Utility class to extract data from a Snowflake (Discord ID) */ export class Snowflake { id: string @@ -9,20 +10,20 @@ export class Snowflake { return BigInt.asUintN(64, BigInt(this.id)) } - get timestamp(): string { - return ((this.snowflake >> 22n) + 1420070400000n).toString() + get timestamp(): number { + return Number((this.snowflake >> 22n) + 1420070400000n) } - get workerID(): string { - return ((this.snowflake & 0x3e0000n) >> 17n).toString() + get workerID(): number { + return Number((this.snowflake & 0x3e0000n) >> 17n) } - get processID(): string { - return ((this.snowflake & 0x1f00n) >> 12n).toString() + get processID(): number { + return Number((this.snowflake & 0x1f00n) >> 12n) } - get increment(): string { - return (this.snowflake & 0xfffn).toString() + get increment(): number { + return Number(this.snowflake & 0xfffn) } get toString(): string {