Merge pull request #95 from DjDeveloperr/slash

Add SnowflakeBase, useful methods for permissions to GuildTextChannel, and jsdoc in a lot of places
This commit is contained in:
Helloyunho 2021-01-25 22:39:23 +09:00 committed by GitHub
commit 926d360d90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 346 additions and 103 deletions

View File

@ -44,16 +44,18 @@ export const guildEmojiUpdate: GatewayEventHandler = async (
} }
} }
gateway.client.emit('guildEmojisUpdate', guild)
for (const emoji of deleted) { for (const emoji of deleted) {
gateway.client.emit('guildEmojiDelete', guild, emoji) gateway.client.emit('guildEmojiDelete', emoji)
} }
for (const emoji of added) { for (const emoji of added) {
gateway.client.emit('guildEmojiAdd', guild, emoji) gateway.client.emit('guildEmojiAdd', emoji)
} }
for (const emoji of updated) { for (const emoji of updated) {
gateway.client.emit('guildEmojiUpdate', guild, emoji.before, emoji.after) gateway.client.emit('guildEmojiUpdate', emoji.before, emoji.after)
} }
} }
} }

View File

@ -113,11 +113,12 @@ export interface VoiceServerUpdateData {
guild: Guild guild: Guild
} }
/** All Client Events */
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type ClientEvents = { export type ClientEvents = {
/** When Client has successfully connected to Discord */ /** When Client has successfully connected to Discord */
ready: [shard: number] ready: [shard: number]
/** When a successful reconnect has been made */ /** When a reconnect was requested by Discord */
reconnect: [shard: number] reconnect: [shard: number]
/** When a successful session resume has been done */ /** When a successful session resume has been done */
resumed: [shard: number] resumed: [shard: number]
@ -178,25 +179,28 @@ export type ClientEvents = {
* @param guild Guild in which Emoji was added * @param guild Guild in which Emoji was added
* @param emoji The Emoji which was added * @param emoji The Emoji which was added
*/ */
guildEmojiAdd: [guild: Guild, emoji: Emoji] guildEmojiAdd: [emoji: Emoji]
/** /**
* An Emoji was deleted from Guild * An Emoji was deleted from Guild
* @param guild Guild from which Emoji was deleted
* @param emoji Emoji which was deleted * @param emoji Emoji which was deleted
*/ */
guildEmojiDelete: [Guild, Emoji] guildEmojiDelete: [emoji: Emoji]
/** /**
* An Emoji in a Guild was updated * An Emoji in a Guild was updated
* @param guild Guild in which Emoji was updated
* @param before Emoji object before update * @param before Emoji object before update
* @param after Emoji object after update * @param after Emoji object after update
*/ */
guildEmojiUpdate: [guild: Guild, before: Emoji, after: Emoji] guildEmojiUpdate: [before: Emoji, after: Emoji]
/** /**
* Guild's Integrations were updated * Guild's Integrations were updated
* @param guild The Guild object * @param guild The Guild object
*/ */
guildIntegrationsUpdate: [guild: Guild] guildIntegrationsUpdate: [guild: Guild]
/**
* Guild's Emojis were updated
* @param guild The Guild object
*/
guildEmojisUpdate: [guild: Guild]
/** /**
* A new Member has joined a Guild * A new Member has joined a Guild
* @param member The Member object * @param member The Member object

View File

@ -4,5 +4,6 @@ export const reconnect: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
d: any d: any
) => { ) => {
gateway.client.emit('reconnect', gateway.shards?.[0] ?? 0)
gateway.reconnect() gateway.reconnect()
} }

View File

@ -1,9 +1,9 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { ApplicationPayload } from '../types/application.ts' import { ApplicationPayload } from '../types/application.ts'
import { Base } from './base.ts' import { SnowflakeBase } from './base.ts'
import { User } from './user.ts' import { User } from './user.ts'
export class Application extends Base { export class Application extends SnowflakeBase {
id: string id: string
name: string name: string
icon: string icon: string

View File

@ -1,4 +1,5 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { Snowflake } from '../utils/snowflake.ts'
export class Base { export class Base {
client: Client client: Client
@ -7,3 +8,17 @@ export class Base {
this.client = client 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)
}
}

View File

@ -1,8 +1,8 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { ChannelPayload, ChannelTypes } from '../types/channel.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 type: ChannelTypes
id: string id: string
static cacheName = 'channel' static cacheName = 'channel'

View File

@ -9,7 +9,9 @@ import {
EmbedTypes, EmbedTypes,
EmbedVideo EmbedVideo
} from '../types/channel.ts' } from '../types/channel.ts'
import { Colors, ColorUtil } from '../utils/colorutil.ts'
/** Message Embed Object */
export class Embed { export class Embed {
title?: string title?: string
type?: EmbedTypes type?: EmbedTypes
@ -41,7 +43,7 @@ export class Embed {
this.fields = data?.fields this.fields = data?.fields
} }
// khk4912 /** Convert Embed Object to Embed Payload JSON */
toJSON(): EmbedPayload { toJSON(): EmbedPayload {
return { return {
title: this.title, title: this.title,
@ -60,63 +62,114 @@ export class Embed {
} }
} }
/** Set Title of the Embed */
setTitle(title: string): Embed { setTitle(title: string): Embed {
this.title = title this.title = title
return this return this
} }
/** Set Embed description */
setDescription(description: string): Embed { setDescription(description: string): Embed {
this.description = description this.description = description
return this return this
} }
/** Set Embed Type */
setType(type: EmbedTypes): Embed { setType(type: EmbedTypes): Embed {
this.type = type this.type = type
return this return this
} }
setURL(url: string): Embed { /** Set URL of the Embed */
this.url = url setURL(url: string | URL): Embed {
this.url = typeof url === 'object' ? url.toString() : url
return this return this
} }
setTimestamp(timestamp: string): Embed { /** Set Timestamp of the Embed */
this.timestamp = timestamp 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 return this
} }
setColor(hex: number): Embed { /** Set Color of the Embed */
this.color = hex 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 return this
} }
setFooter(footer: EmbedFooter): Embed { /** Set Footer of the Embed */
this.footer = footer 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 return this
} }
setImage(image: EmbedImage): Embed { /** Set Image of the Embed */
this.image = image setImage(image: EmbedImage | string): Embed {
this.image = typeof image === 'string' ? { url: image } : image
return this return this
} }
setThumbnail(thumbnail: EmbedThumbnail): Embed { /** Set Thumbnail Image of the Embed */
this.thumbnail = thumbnail setThumbnail(thumbnail: EmbedThumbnail | string): Embed {
this.thumbnail =
typeof thumbnail === 'string' ? { url: thumbnail } : thumbnail
return this return this
} }
setVideo(video: EmbedVideo): Embed { /** Set Embed Video */
this.video = video setVideo(video: EmbedVideo | string): Embed {
this.video = typeof video === 'string' ? { url: video } : video
return this return this
} }
setProvider(provider: EmbedProvider): Embed { /** Set Provider of the Embed */
this.provider = provider 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 return this
} }
setAuthor(author: EmbedAuthor): Embed { /** Set Author of the Embed */
this.author = author 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 return this
} }
@ -125,23 +178,29 @@ export class Embed {
return this 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) { if (this.fields === undefined) {
this.fields = [ this.fields = [field]
{
name: name,
value: value,
inline: inline
}
]
} else { } else {
this.fields.push({ this.fields.push(field)
name: name,
value: value,
inline: inline
})
} }
return this return this
} }
/** Adds multiple fields to the Embed */
addFields(...fields: EmbedField[]): Embed {
for (const field of fields) {
this.addField(field)
}
return this
}
} }

View File

@ -1,13 +1,24 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { EmojiPayload } from '../types/emoji.ts' import { EmojiPayload } from '../types/emoji.ts'
import { EMOJI } from '../types/endpoint.ts' import { EMOJI } from '../types/endpoint.ts'
import { Snowflake } from '../utils/snowflake.ts'
import { Base } from './base.ts' import { Base } from './base.ts'
import { Guild } from './guild.ts' import { Guild } from './guild.ts'
import { Role } from './role.ts' import { Role } from './role.ts'
import { User } from './user.ts' import { User } from './user.ts'
/** Guild Emoji Object */
export class Emoji extends Base { export class Emoji extends Base {
id: string | null 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 name: string | null
roles?: string[] roles?: string[]
user?: User 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.id === null) throw new Error('Emoji ID is not valid.')
if (this.guild === undefined) throw new Error('Guild is undefined') if (this.guild === undefined) throw new Error('Guild is undefined')
const roles = Array.isArray(data.roles) 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] : [data.roles instanceof Role ? data.roles.id : data.roles]
const res = await this.client.rest.patch(EMOJI(this.guild.id, this.id), { const res = await this.client.rest.patch(EMOJI(this.guild.id, this.id), {
...data, ...data,
@ -82,5 +93,5 @@ export interface ModifyGuildEmojiParams {
/** Name of the emoji */ /** Name of the emoji */
name?: string name?: string
/** Roles to which this emoji will be whitelisted */ /** Roles to which this emoji will be whitelisted */
roles?: string | Role | Array<string | Role>; roles?: string | Role | Array<string | Role>
} }

View File

@ -17,7 +17,7 @@ import {
GuildPruneCountPayload, GuildPruneCountPayload,
GuildBeginPrunePayload GuildBeginPrunePayload
} from '../types/guild.ts' } from '../types/guild.ts'
import { Base } from './base.ts' import { Base, SnowflakeBase } from './base.ts'
import { CreateGuildRoleOptions, RolesManager } from '../managers/roles.ts' import { CreateGuildRoleOptions, RolesManager } from '../managers/roles.ts'
import { InviteManager } from '../managers/invites.ts' import { InviteManager } from '../managers/invites.ts'
import { import {
@ -133,7 +133,7 @@ export class GuildBans {
} }
} }
export class Guild extends Base { export class Guild extends SnowflakeBase {
id: string id: string
name?: string name?: string
icon?: string icon?: string

View File

@ -3,7 +3,7 @@ import { Client } from '../models/client.ts'
import { GUILD_MEMBER } from '../types/endpoint.ts' import { GUILD_MEMBER } from '../types/endpoint.ts'
import { MemberPayload } from '../types/guild.ts' import { MemberPayload } from '../types/guild.ts'
import { Permissions } from '../utils/permissions.ts' import { Permissions } from '../utils/permissions.ts'
import { Base } from './base.ts' import { SnowflakeBase } from './base.ts'
import { Guild } from './guild.ts' import { Guild } from './guild.ts'
import { Role } from './role.ts' import { Role } from './role.ts'
import { User } from './user.ts' import { User } from './user.ts'
@ -15,7 +15,7 @@ export interface MemberData {
mute?: boolean mute?: boolean
} }
export class Member extends Base { export class Member extends SnowflakeBase {
id: string id: string
user: User user: User
nick?: string nick?: string

View File

@ -1,4 +1,4 @@
import { Base } from './base.ts' import { SnowflakeBase } from './base.ts'
import { import {
Attachment, Attachment,
MessageActivity, MessageActivity,
@ -21,7 +21,7 @@ import { Emoji } from './emoji.ts'
type AllMessageOptions = MessageOptions | Embed type AllMessageOptions = MessageOptions | Embed
export class Message extends Base { export class Message extends SnowflakeBase {
id: string id: string
channelID: string channelID: string
channel: TextChannel channel: TextChannel
@ -30,8 +30,7 @@ export class Message extends Base {
author: User author: User
member?: Member member?: Member
content: string content: string
timestamp: string editedTimestamp?: Date
editedTimestamp?: string
tts: boolean tts: boolean
mentions: MessageMentions mentions: MessageMentions
attachments: Attachment[] attachments: Attachment[]
@ -63,8 +62,10 @@ export class Message extends Base {
this.guildID = data.guild_id this.guildID = data.guild_id
this.author = author this.author = author
this.content = data.content this.content = data.content
this.timestamp = data.timestamp this.editedTimestamp =
this.editedTimestamp = data.edited_timestamp data.edited_timestamp === undefined
? undefined
: new Date(data.edited_timestamp)
this.tts = data.tts this.tts = data.tts
this.mentions = new MessageMentions(this.client, this) this.mentions = new MessageMentions(this.client, this)
this.attachments = data.attachments this.attachments = data.attachments
@ -91,8 +92,10 @@ export class Message extends Base {
this.channelID = data.channel_id ?? this.channelID this.channelID = data.channel_id ?? this.channelID
this.guildID = data.guild_id ?? this.guildID this.guildID = data.guild_id ?? this.guildID
this.content = data.content ?? this.content this.content = data.content ?? this.content
this.timestamp = data.timestamp ?? this.timestamp this.editedTimestamp =
this.editedTimestamp = data.edited_timestamp ?? this.editedTimestamp data.edited_timestamp === undefined
? this.editedTimestamp
: new Date(data.edited_timestamp)
this.tts = data.tts ?? this.tts this.tts = data.tts ?? this.tts
this.attachments = data.attachments ?? this.attachments this.attachments = data.attachments ?? this.attachments
this.embeds = data.embeds.map((v) => new Embed(v)) ?? this.embeds this.embeds = data.embeds.map((v) => new Embed(v)) ?? this.embeds

View File

@ -3,9 +3,9 @@ import {
MessageStickerFormatTypes, MessageStickerFormatTypes,
MessageStickerPayload MessageStickerPayload
} from '../types/channel.ts' } 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 id: string
packID: string packID: string
name: string name: string

View File

@ -77,6 +77,7 @@ export class ClientPresence {
} }
} }
/** Parses from Payload */
parse(payload: StatusPayload): ClientPresence { parse(payload: StatusPayload): ClientPresence {
this.afk = payload.afk this.afk = payload.afk
this.activity = payload.activities ?? undefined this.activity = payload.activities ?? undefined
@ -86,10 +87,12 @@ export class ClientPresence {
return this return this
} }
/** Parses from Payload and creates new ClientPresence */
static parse(payload: StatusUpdatePayload): ClientPresence { static parse(payload: StatusUpdatePayload): ClientPresence {
return new ClientPresence().parse(payload) return new ClientPresence().parse(payload)
} }
/** Creates Presence Payload */
create(): StatusPayload { create(): StatusPayload {
return { return {
afk: this.afk === undefined ? false : this.afk, afk: this.afk === undefined ? false : this.afk,
@ -100,6 +103,7 @@ export class ClientPresence {
} }
} }
/** Creates Activity Payload */
createActivity(): ActivityGame[] | null { createActivity(): ActivityGame[] | null {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const activity = const activity =
@ -118,47 +122,45 @@ export class ClientPresence {
} }
} }
/** Set Status of Presence */
setStatus(status: StatusType): ClientPresence { setStatus(status: StatusType): ClientPresence {
this.status = status this.status = status
return this return this
} }
/** Set Activity for Presence */
setActivity(activity: ActivityGame): ClientPresence { setActivity(activity: ActivityGame): ClientPresence {
this.activity = activity this.activity = activity
return this return this
} }
/** Set Activities for Presence */
setActivities(activities: ActivityGame[]): ClientPresence { setActivities(activities: ActivityGame[]): ClientPresence {
this.activity = activities this.activity = activities
return this return this
} }
/** Set AFK value */
setAFK(afk: boolean): ClientPresence { setAFK(afk: boolean): ClientPresence {
this.afk = afk this.afk = afk
return this return this
} }
/** Remove AFK (set false) */
removeAFK(): ClientPresence { removeAFK(): ClientPresence {
this.afk = false this.afk = false
return this return this
} }
/** Toggle AFK (boolean) value */
toggleAFK(): ClientPresence { toggleAFK(): ClientPresence {
this.afk = this.afk === undefined ? true : !this.afk this.afk = this.afk === undefined ? true : !this.afk
return this return this
} }
/** Set Since property of Activity */
setSince(since?: number): ClientPresence { setSince(since?: number): ClientPresence {
this.since = since this.since = since
return this return this
} }
// setClientStatus(
// client: 'desktop' | 'web' | 'mobile',
// status: StatusType
// ): ClientPresence {
// if (this.clientStatus === undefined) this.clientStatus = {}
// this.clientStatus[client] = status
// return this
// }
} }

View File

@ -1,12 +1,13 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { Base } from './base.ts' import { SnowflakeBase } from './base.ts'
import { RoleModifyPayload, RolePayload } from '../types/role.ts' import { RoleModifyPayload, RolePayload } from '../types/role.ts'
import { Permissions } from '../utils/permissions.ts' import { Permissions } from '../utils/permissions.ts'
import { Guild } from './guild.ts' import { Guild } from './guild.ts'
import { Member } from './member.ts' import { Member } from './member.ts'
import { User } from './user.ts' import { User } from './user.ts'
export class Role extends Base { /** Represents a Guild Role */
export class Role extends SnowflakeBase {
id: string id: string
guild: Guild guild: Guild
name: string name: string
@ -52,6 +53,17 @@ export class Role extends Base {
this.mentionable = data.mentionable ?? this.mentionable this.mentionable = data.mentionable ?? this.mentionable
} }
/** Delete the Role */
async delete(): Promise<Role | undefined> {
return this.guild.roles.delete(this)
}
/** Edit the Role */
async edit(options: RoleModifyPayload): Promise<Role> {
return this.guild.roles.edit(this, options)
}
/** Add the Role to a Member */
async addTo(member: Member | User | string): Promise<boolean> { async addTo(member: Member | User | string): Promise<boolean> {
if (member instanceof User) { if (member instanceof User) {
member = member.id member = member.id
@ -68,6 +80,7 @@ export class Role extends Base {
return member.roles.add(this.id) return member.roles.add(this.id)
} }
/** Remove the Role from a Member */
async removeFrom(member: Member | User | string): Promise<boolean> { async removeFrom(member: Member | User | string): Promise<boolean> {
if (member instanceof User) { if (member instanceof User) {
member = member.id member = member.id
@ -83,14 +96,6 @@ export class Role extends Base {
return member.roles.remove(this.id) return member.roles.remove(this.id)
} }
async delete(): Promise<Role | undefined> {
return this.guild.roles.delete(this)
}
async edit(options: RoleModifyPayload): Promise<Role> {
return this.guild.roles.edit(this, options)
}
} }
export interface RoleTags { export interface RoleTags {

View File

@ -8,6 +8,7 @@ import {
InteractionResponsePayload, InteractionResponsePayload,
InteractionResponseType InteractionResponseType
} from '../types/slash.ts' } from '../types/slash.ts'
import { SnowflakeBase } from './base.ts'
import { Embed } from './embed.ts' import { Embed } from './embed.ts'
import { Guild } from './guild.ts' import { Guild } from './guild.ts'
import { Member } from './member.ts' import { Member } from './member.ts'
@ -39,7 +40,7 @@ export interface InteractionResponse {
} }
} }
export class Interaction { export class Interaction extends SnowflakeBase {
client: Client client: Client
type: number type: number
token: string token: string
@ -59,6 +60,7 @@ export class Interaction {
member: Member member: Member
} }
) { ) {
super(client)
this.client = client this.client = client
this.type = data.type this.type = data.type
this.token = data.token this.token = data.token

View File

@ -19,10 +19,12 @@ import {
MESSAGE_REACTION_USER MESSAGE_REACTION_USER
} from '../types/endpoint.ts' } from '../types/endpoint.ts'
import { Collection } from '../utils/collection.ts' import { Collection } from '../utils/collection.ts'
import { Permissions } from '../utils/permissions.ts'
import { Channel } from './channel.ts' import { Channel } from './channel.ts'
import { Embed } from './embed.ts' import { Embed } from './embed.ts'
import { Emoji } from './emoji.ts' import { Emoji } from './emoji.ts'
import { Guild } from './guild.ts' import { Guild } from './guild.ts'
import { CategoryChannel } from './guildCategoryChannel.ts'
import { Invite } from './invite.ts' import { Invite } from './invite.ts'
import { Member } from './member.ts' import { Member } from './member.ts'
import { Message } from './message.ts' import { Message } from './message.ts'
@ -30,6 +32,7 @@ import { User } from './user.ts'
export type AllMessageOptions = MessageOptions | Embed export type AllMessageOptions = MessageOptions | Embed
/** Channel object for Text Channel type */
export class TextChannel extends Channel { export class TextChannel extends Channel {
lastMessageID?: string lastMessageID?: string
lastPinTimestamp?: string lastPinTimestamp?: string
@ -226,8 +229,15 @@ export class TextChannel extends Channel {
return res return res
} }
/** Trigger the typing indicator. NOT recommended to be used by bots unless you really want to. */
async triggerTyping(): Promise<TextChannel> {
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 { export class GuildTextChannel extends TextChannel {
guildID: string guildID: string
name: string name: string
@ -235,7 +245,7 @@ export class GuildTextChannel extends TextChannel {
permissionOverwrites: Overwrite[] permissionOverwrites: Overwrite[]
nsfw: boolean nsfw: boolean
parentID?: string parentID?: string
rateLimit: number slowmode: number
topic?: string topic?: string
guild: Guild guild: Guild
@ -257,7 +267,7 @@ export class GuildTextChannel extends TextChannel {
this.nsfw = data.nsfw this.nsfw = data.nsfw
this.parentID = data.parent_id this.parentID = data.parent_id
this.topic = data.topic this.topic = data.topic
this.rateLimit = data.rate_limit_per_user this.slowmode = data.rate_limit_per_user
} }
readFromData(data: GuildTextChannelPayload): void { readFromData(data: GuildTextChannelPayload): void {
@ -270,9 +280,10 @@ export class GuildTextChannel extends TextChannel {
this.nsfw = data.nsfw ?? this.nsfw this.nsfw = data.nsfw ?? this.nsfw
this.parentID = data.parent_id ?? this.parentID this.parentID = data.parent_id ?? this.parentID
this.topic = data.topic ?? this.topic 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( async edit(
options?: ModifyGuildTextChannelOption options?: ModifyGuildTextChannelOption
): Promise<GuildTextChannel> { ): Promise<GuildTextChannel> {
@ -280,7 +291,10 @@ export class GuildTextChannel extends TextChannel {
name: options?.name, name: options?.name,
position: options?.position, position: options?.position,
permission_overwrites: options?.permissionOverwrites, 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) 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<Invite> { async createInvite(options?: CreateInviteOptions): Promise<Invite> {
return this.guild.invites.create(this.id, options) return this.guild.invites.create(this.id, options)
} }
/** Get Permission Overties for a specific Member */
async overwritesFor(member: Member | string): Promise<Overwrite[]> {
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<Permissions> {
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<GuildTextChannel> {
return await this.edit({ name })
}
/** Edit topic of the channel */
async setTopic(topic: string): Promise<GuildTextChannel> {
return await this.edit({ topic })
}
/** Edit topic of the channel */
async setCategory(
category: CategoryChannel | string
): Promise<GuildTextChannel> {
return await this.edit({
parentID: typeof category === 'object' ? category.id : category
})
}
/** Edit position of the channel */
async setPosition(position: number): Promise<GuildTextChannel> {
return await this.edit({ position })
}
/** Edit Slowmode of the channel */
async setSlowmode(slowmode?: number | null): Promise<GuildTextChannel> {
return await this.edit({ slowmode: slowmode ?? null })
}
/** Edit NSFW property of the channel */
async setNSFW(nsfw: boolean): Promise<GuildTextChannel> {
return await this.edit({ nsfw })
}
/** Set Permission Overwrites of the Channel */
async setOverwrites(overwrites: Overwrite[]): Promise<GuildTextChannel> {
return await this.edit({ permissionOverwrites: overwrites })
}
/** Add a Permission Overwrite */
async addOverwrite(overwrite: Overwrite): Promise<GuildTextChannel> {
const overwrites = this.permissionOverwrites
overwrites.push(overwrite)
return await this.edit({ permissionOverwrites: overwrites })
}
/** Remove a Permission Overwrite */
async removeOverwrite(id: string): Promise<GuildTextChannel> {
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<GuildTextChannel> {
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 })
}
} }

View File

@ -1,13 +1,13 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { UserPayload } from '../types/user.ts' import { UserPayload } from '../types/user.ts'
import { UserFlagsManager } from '../utils/userFlags.ts' import { UserFlagsManager } from '../utils/userFlags.ts'
import { Base } from './base.ts' import { SnowflakeBase } from './base.ts'
import { ImageURL } from './cdn.ts' import { ImageURL } from './cdn.ts'
import { ImageSize, ImageFormats } from '../types/cdn.ts' import { ImageSize, ImageFormats } from '../types/cdn.ts'
import { DEFAULT_USER_AVATAR, USER_AVATAR } from '../types/endpoint.ts' import { DEFAULT_USER_AVATAR, USER_AVATAR } from '../types/endpoint.ts'
import { DMChannel } from './dmChannel.ts' import { DMChannel } from './dmChannel.ts'
export class User extends Base { export class User extends SnowflakeBase {
id: string id: string
username: string username: string
discriminator: string discriminator: string

View File

@ -97,7 +97,7 @@ export interface ModifyGuildTextChannelOption extends ModifyChannelOption {
type?: number type?: number
topic?: string | null topic?: string | null
nsfw?: boolean | null nsfw?: boolean | null
rateLimitPerUser?: number | null slowmode?: number | null
} }
export interface ModifyGuildNewsChannelOption extends ModifyChannelOption { export interface ModifyGuildNewsChannelOption extends ModifyChannelOption {

View File

@ -1,5 +1,11 @@
// Ported from https://github.com/discordjs/discord.js/blob/master/src/util/BitField.js // 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 */ /** Bit Field utility to work with Bits and Flags */
export class BitField { export class BitField {
@ -20,7 +26,7 @@ export class BitField {
} }
has(bit: BitFieldResolvable, ...args: any[]): boolean { 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 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 (typeof bit === 'number' && bit >= 0) return bit
if (bit instanceof BitField) return this.resolve(flags, bit.bitfield) if (bit instanceof BitField) return this.resolve(flags, bit.bitfield)
if (Array.isArray(bit)) if (Array.isArray(bit))
return bit return (bit.map as any)((p: any) => this.resolve(flags, p)).reduce(
.map((p) => this.resolve(flags, p)) (prev: any, p: any) => prev | p,
.reduce((prev, p) => prev | p, 0) 0
)
if (typeof bit === 'string' && typeof flags[bit] !== 'undefined') if (typeof bit === 'string' && typeof flags[bit] !== 'undefined')
return flags[bit] return flags[bit]
const error = new RangeError('BITFIELD_INVALID') const error = new RangeError('BITFIELD_INVALID')

View File

@ -1,10 +1,12 @@
// Ported from https://github.com/discordjs/discord.js/blob/master/src/util/Permissions.js // Ported from https://github.com/discordjs/discord.js/blob/master/src/util/Permissions.js
import { PermissionFlags } from '../types/permissionFlags.ts' import { PermissionFlags } from '../types/permissionFlags.ts'
import { BitField } from './bitfield.ts' import { BitField, BitFieldResolvable } from './bitfield.ts'
export type PermissionResolvable = export type PermissionResolvable =
| string | string
| string[]
| number | number
| number[]
| Permissions | Permissions
| PermissionResolvable[] | PermissionResolvable[]
@ -13,7 +15,7 @@ export class Permissions extends BitField {
static DEFAULT = 104324673 static DEFAULT = 104324673
static ALL = Object.values(PermissionFlags).reduce((all, p) => all | p, 0) static ALL = Object.values(PermissionFlags).reduce((all, p) => all | p, 0)
constructor(bits: any) { constructor(bits: BitFieldResolvable) {
super(PermissionFlags, bits) super(PermissionFlags, bits)
} }

View File

@ -1,3 +1,4 @@
/** Utility class to extract data from a Snowflake (Discord ID) */
export class Snowflake { export class Snowflake {
id: string id: string
@ -9,20 +10,20 @@ export class Snowflake {
return BigInt.asUintN(64, BigInt(this.id)) return BigInt.asUintN(64, BigInt(this.id))
} }
get timestamp(): string { get timestamp(): number {
return ((this.snowflake >> 22n) + 1420070400000n).toString() return Number((this.snowflake >> 22n) + 1420070400000n)
} }
get workerID(): string { get workerID(): number {
return ((this.snowflake & 0x3e0000n) >> 17n).toString() return Number((this.snowflake & 0x3e0000n) >> 17n)
} }
get processID(): string { get processID(): number {
return ((this.snowflake & 0x1f00n) >> 12n).toString() return Number((this.snowflake & 0x1f00n) >> 12n)
} }
get increment(): string { get increment(): number {
return (this.snowflake & 0xfffn).toString() return Number(this.snowflake & 0xfffn)
} }
get toString(): string { get toString(): string {