diff --git a/.gitignore b/.gitignore index 86ddba1..405031f 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,4 @@ yarn.lock # PRIVACY XDDDD src/test/config.ts -.vscode - -src/test/ \ No newline at end of file +.vscode \ No newline at end of file diff --git a/src/gateway/handlers/channelCreate.ts b/src/gateway/handlers/channelCreate.ts new file mode 100644 index 0000000..4283362 --- /dev/null +++ b/src/gateway/handlers/channelCreate.ts @@ -0,0 +1,13 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import getChannelByType from '../../utils/getChannelByType.ts' + +export const channelCreate: GatewayEventHandler = ( + gateway: Gateway, + d: any +) => { + const channel = getChannelByType(gateway.client, d) + + if (channel !== undefined) { + gateway.client.emit('channelCreate', channel) + } +} diff --git a/src/gateway/handlers/channelDelete.ts b/src/gateway/handlers/channelDelete.ts new file mode 100644 index 0000000..1b06fbf --- /dev/null +++ b/src/gateway/handlers/channelDelete.ts @@ -0,0 +1,14 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import cache from '../../models/cache.ts' +import { Channel } from '../../structures/channel.ts' + +export const channelDelete: GatewayEventHandler = ( + gateway: Gateway, + d: any +) => { + const channel: Channel = cache.get('channel', d.id) + if (channel !== undefined) { + cache.del('channel', d.id) + gateway.client.emit('channelDelete', channel) + } +} diff --git a/src/gateway/handlers/channelPinsUpdate.ts b/src/gateway/handlers/channelPinsUpdate.ts new file mode 100644 index 0000000..40a0df3 --- /dev/null +++ b/src/gateway/handlers/channelPinsUpdate.ts @@ -0,0 +1,16 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import cache from '../../models/cache.ts' +import { TextChannel } from '../../structures/textChannel.ts' + +export const channelPinsUpdate: GatewayEventHandler = ( + gateway: Gateway, + d: any +) => { + const after: TextChannel = cache.get('textchannel', d.channel_id) + if (after !== undefined) { + const before = after.refreshFromData({ + last_pin_timestamp: d.last_pin_timestamp + }) + gateway.client.emit('channelPinsUpdate', before, after) + } +} diff --git a/src/gateway/handlers/channelUpdate.ts b/src/gateway/handlers/channelUpdate.ts new file mode 100644 index 0000000..8ce0d90 --- /dev/null +++ b/src/gateway/handlers/channelUpdate.ts @@ -0,0 +1,21 @@ +import cache from '../../models/cache.ts' +import { Channel } from '../../structures/channel.ts' +import getChannelByType from '../../utils/getChannelByType.ts' +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const channelUpdate: GatewayEventHandler = ( + gateway: Gateway, + d: any +) => { + const oldChannel: Channel = cache.get('channel', d.id) + + if (oldChannel !== undefined) { + if (oldChannel.type !== d.type) { + const channel: Channel = getChannelByType(gateway.client, d) ?? oldChannel + gateway.client.emit('channelUpdate', oldChannel, channel) + } else { + const before = oldChannel.refreshFromData(d) + gateway.client.emit('channelUpdate', before, oldChannel) + } + } +} diff --git a/src/gateway/handlers/guildBanAdd.ts b/src/gateway/handlers/guildBanAdd.ts new file mode 100644 index 0000000..6446050 --- /dev/null +++ b/src/gateway/handlers/guildBanAdd.ts @@ -0,0 +1,14 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import cache from '../../models/cache.ts' +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' + +export const guildBanAdd: GatewayEventHandler = (gateway: Gateway, d: any) => { + const guild: Guild = cache.get('guild', d.guild_id) + const user: User = + cache.get('user', d.user.id) ?? new User(gateway.client, d.user) + + if (guild !== undefined) { + gateway.client.emit('guildBanAdd', guild, user) + } +} diff --git a/src/gateway/handlers/guildBanRemove.ts b/src/gateway/handlers/guildBanRemove.ts new file mode 100644 index 0000000..904f383 --- /dev/null +++ b/src/gateway/handlers/guildBanRemove.ts @@ -0,0 +1,17 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import cache from '../../models/cache.ts' +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' + +export const guildBanRemove: GatewayEventHandler = ( + gateway: Gateway, + d: any +) => { + const guild: Guild = cache.get('guild', d.guild_id) + const user: User = + cache.get('user', d.user.id) ?? new User(gateway.client, d.user) + + if (guild !== undefined) { + gateway.client.emit('guildBanRemove', guild, user) + } +} diff --git a/src/gateway/handlers/guildCreate.ts b/src/gateway/handlers/guildCreate.ts new file mode 100644 index 0000000..e1bde3d --- /dev/null +++ b/src/gateway/handlers/guildCreate.ts @@ -0,0 +1,14 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import cache from '../../models/cache.ts' +import { Guild } from '../../structures/guild.ts' + +export const guildCreate: GatewayEventHandler = (gateway: Gateway, d: any) => { + let guild: Guild = cache.get('guild', d.id) + if (guild !== undefined) { + guild.refreshFromData(d) + } else { + guild = new Guild(gateway.client, d) + } + + gateway.client.emit('guildCreate', guild) +} diff --git a/src/gateway/handlers/guildDelete.ts b/src/gateway/handlers/guildDelete.ts new file mode 100644 index 0000000..abaa748 --- /dev/null +++ b/src/gateway/handlers/guildDelete.ts @@ -0,0 +1,13 @@ +import cache from '../../models/cache.ts' +import { Guild } from '../../structures/guild.ts' +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const guildDelte: GatewayEventHandler = (gateway: Gateway, d: any) => { + const guild: Guild = cache.get('guild', d.id) + + if (guild !== undefined) { + guild.refreshFromData(d) + cache.del('guild', d.id) + gateway.client.emit('guildDelete', guild) + } +} diff --git a/src/gateway/handlers/guildUpdate.ts b/src/gateway/handlers/guildUpdate.ts new file mode 100644 index 0000000..1a1847f --- /dev/null +++ b/src/gateway/handlers/guildUpdate.ts @@ -0,0 +1,11 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import cache from '../../models/cache.ts' +import { Guild } from '../../structures/guild.ts' + +export const guildUpdate: GatewayEventHandler = (gateway: Gateway, d: any) => { + const after: Guild = cache.get('guild', d.id) + if (after !== undefined) { + const before: Guild = after.refreshFromData(d) + gateway.client.emit('guildUpdate', before, after) + } +} diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts new file mode 100644 index 0000000..595ad28 --- /dev/null +++ b/src/gateway/handlers/index.ts @@ -0,0 +1,53 @@ +import { GatewayEventHandler } from '../index.ts' +import { GatewayEvents } from '../../types/gatewayTypes.ts' +import { channelCreate } from './channelCreate.ts' +import { channelDelete } from './channelDelete.ts' +import { channelUpdate } from './channelUpdate.ts' +import { channelPinsUpdate } from './channelPinsUpdate.ts' +import { guildCreate } from './guildCreate.ts' +import { guildDelte as guildDelete } from './guildDelete.ts' +import { guildUpdate } from './guildUpdate.ts' +import { guildBanAdd } from './guildBanAdd.ts' +import { ready } from './ready.ts' +import { guildBanRemove } from './guildBanRemove.ts' + +export const gatewayHandlers: { + [eventCode in GatewayEvents]: GatewayEventHandler | undefined +} = { + READY: ready, + RECONNECT: undefined, + RESUMED: undefined, + CHANNEL_CREATE: channelCreate, + CHANNEL_DELETE: channelDelete, + CHANNEL_UPDATE: channelUpdate, + CHANNEL_PINS_UPDATE: channelPinsUpdate, + GUILD_CREATE: guildCreate, + GUILD_DELETE: guildDelete, + GUILD_UPDATE: guildUpdate, + GUILD_BAN_ADD: guildBanAdd, + GUILD_BAN_REMOVE: guildBanRemove, + GUILD_EMOJIS_UPDATE: undefined, + GUILD_INTEGRATIONS_UPDATE: undefined, + GUILD_MEMBER_ADD: undefined, + GUILD_MEMBER_REMOVE: undefined, + GUILD_MEMBER_UPDATE: undefined, + GUILD_MEMBERS_CHUNK: undefined, + GUILD_ROLE_CREATE: undefined, + GUILD_ROLE_UPDATE: undefined, + GUILD_ROLE_DELETE: undefined, + INVITE_CREATE: undefined, + INVITE_DELETE: undefined, + MESSAGE_CREATE: undefined, + MESSAGE_UPDATE: undefined, + MESSAGE_DELETE: undefined, + MESSAGE_DELETE_BULK: undefined, + MESSAGE_REACTION_ADD: undefined, + MESSAGE_REACTION_REMOVE: undefined, + MESSAGE_REACTION_REMOVE_ALL: undefined, + MESSAGE_REACTION_REMOVE_EMOJI: undefined, + PRESENCE_UPDATE: undefined, + TYPING_START: undefined, + USER_UPDATE: undefined, + VOICE_SERVER_UPDATE: undefined, + WEBHOOKS_UPDATE: undefined +} diff --git a/src/gateway/handlers/ready.ts b/src/gateway/handlers/ready.ts new file mode 100644 index 0000000..ba44d58 --- /dev/null +++ b/src/gateway/handlers/ready.ts @@ -0,0 +1,11 @@ +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' +import { GuildPayload } from '../../types/guildTypes.ts' +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const ready: GatewayEventHandler = (gateway: Gateway, d: any) => { + gateway.client.user = new User(gateway.client, d.user) + gateway.sessionID = d.session_id + d.guilds.forEach((guild: GuildPayload) => new Guild(gateway.client, guild)) + gateway.client.emit('ready') +} diff --git a/src/gateway/index.ts b/src/gateway/index.ts new file mode 100644 index 0000000..809828a --- /dev/null +++ b/src/gateway/index.ts @@ -0,0 +1,199 @@ +import { unzlib } from 'https://deno.land/x/denoflate/mod.ts' +import { Client } from '../models/client.ts' +import { + DISCORD_GATEWAY_URL, + DISCORD_API_VERSION +} from '../consts/urlsAndVersions.ts' +import { GatewayResponse } from '../types/gatewayResponse.ts' +import { GatewayOpcodes, GatewayIntents } from '../types/gatewayTypes.ts' +import { gatewayHandlers } from './handlers/index.ts' + +/** + * Handles Discord gateway connection. + * You should not use this and rather use Client class. + * + * @beta + */ +class Gateway { + websocket: WebSocket + token: string + intents: GatewayIntents[] + connected = false + initialized = false + heartbeatInterval = 0 + heartbeatIntervalID?: number + sequenceID?: number + sessionID?: string + lastPingTimestemp = 0 + private heartbeatServerResponded = false + client: Client + + constructor (client: Client, token: string, intents: GatewayIntents[]) { + this.token = token + this.intents = intents + this.client = client + this.websocket = new WebSocket( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, + [] + ) + this.websocket.binaryType = 'arraybuffer' + this.websocket.onopen = this.onopen.bind(this) + this.websocket.onmessage = this.onmessage.bind(this) + this.websocket.onclose = this.onclose.bind(this) + this.websocket.onerror = this.onerror.bind(this) + } + + private onopen (): void { + this.connected = true + } + + private onmessage (event: MessageEvent): void { + let data = event.data + if (data instanceof ArrayBuffer) { + data = new Uint8Array(data) + } + if (data instanceof Uint8Array) { + data = unzlib(data) + data = new TextDecoder('utf-8').decode(data) + } + + const { op, d, s, t }: GatewayResponse = JSON.parse(data) + + switch (op) { + case GatewayOpcodes.HELLO: + this.heartbeatInterval = d.heartbeat_interval + this.heartbeatIntervalID = setInterval(() => { + if (this.heartbeatServerResponded) { + this.heartbeatServerResponded = false + } else { + clearInterval(this.heartbeatIntervalID) + this.websocket.close() + this.initWebsocket() + return + } + + this.websocket.send( + JSON.stringify({ + op: GatewayOpcodes.HEARTBEAT, + d: this.sequenceID ?? null + }) + ) + this.lastPingTimestemp = Date.now() + }, this.heartbeatInterval) + + if (!this.initialized) { + this.sendIdentify() + this.initialized = true + } else { + this.sendResume() + } + break + + case GatewayOpcodes.HEARTBEAT_ACK: + this.heartbeatServerResponded = true + this.client.ping = Date.now() - this.lastPingTimestemp + break + + case GatewayOpcodes.INVALID_SESSION: + // Because we know this gonna be bool + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (!d) { + setTimeout(this.sendResume, 3000) + } else { + setTimeout(this.sendIdentify, 3000) + } + break + + case GatewayOpcodes.DISPATCH: { + this.heartbeatServerResponded = true + if (s !== null) { + this.sequenceID = s + } + if (t !== null && t !== undefined) { + const handler = gatewayHandlers[t] + + if (handler !== undefined) { + handler(this, d) + } + } + break + } + default: + break + } + } + + private onclose (event: CloseEvent): void { + console.log(event.code) + // TODO: Handle close event codes. + } + + private onerror (event: Event | ErrorEvent): void { + const eventError = event as ErrorEvent + + console.log(eventError) + } + + private sendIdentify (): void { + this.websocket.send( + JSON.stringify({ + op: GatewayOpcodes.IDENTIFY, + d: { + token: this.token, + properties: { + $os: Deno.build.os, + $browser: 'discord.deno', + $device: 'discord.deno' + }, + compress: true, + shard: [0, 1], // TODO: Make sharding possible + intents: this.intents.reduce( + (previous, current) => previous | current, + 0 + ), + presence: { + // TODO: User should can customize this + status: 'online', + since: null, + afk: false + } + } + }) + ) + } + + private sendResume (): void { + this.websocket.send( + JSON.stringify({ + op: GatewayOpcodes.RESUME, + d: { + token: this.token, + session_id: this.sessionID, + seq: this.sequenceID + } + }) + ) + } + + initWebsocket (): void { + this.websocket = new WebSocket( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, + [] + ) + this.websocket.binaryType = 'arraybuffer' + this.websocket.onopen = this.onopen.bind(this) + this.websocket.onmessage = this.onmessage.bind(this) + this.websocket.onclose = this.onclose.bind(this) + this.websocket.onerror = this.onerror.bind(this) + } + + close (): void { + this.websocket.close(1000) + } +} + +export type GatewayEventHandler = (gateway: Gateway, d: any) => void + +export { Gateway } diff --git a/src/models/client.ts b/src/models/client.ts index 140cf89..5760c37 100644 --- a/src/models/client.ts +++ b/src/models/client.ts @@ -1,8 +1,9 @@ import { User } from '../structures/user.ts' import { GatewayIntents } from '../types/gatewayTypes.ts' -import { Gateway } from './gateway.ts' +import { Gateway } from '../gateway/index.ts' import { Rest } from './rest.ts' import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' + /** * Discord Client. */ diff --git a/src/models/gateway.ts b/src/models/gateway.ts deleted file mode 100644 index f696c25..0000000 --- a/src/models/gateway.ts +++ /dev/null @@ -1,309 +0,0 @@ -import { unzlib } from 'https://deno.land/x/denoflate/mod.ts' -import { Client } from './client.ts' -import { - DISCORD_GATEWAY_URL, - DISCORD_API_VERSION -} from '../consts/urlsAndVersions.ts' -import { GatewayResponse } from '../types/gatewayResponse.ts' -import { - GatewayOpcodes, - GatewayIntents, - GatewayEvents -} from '../types/gatewayTypes.ts' -import { GuildPayload } from '../types/guildTypes.ts' -import { User } from '../structures/user.ts' -import * as cache from './cache.ts' -import { Guild } from '../structures/guild.ts' -import { Channel } from '../structures/channel.ts' -import { ChannelTypes } from '../types/channelTypes.ts' -import { DMChannel } from '../structures/dmChannel.ts' -import { GroupDMChannel } from '../structures/groupChannel.ts' -import { GuildTextChannel } from '../structures/guildTextChannel.ts' -import { VoiceChannel } from '../structures/guildVoiceChannel.ts' -import { CategoryChannel } from '../structures/guildCategoryChannel.ts' -import { NewsChannel } from '../structures/guildNewsChannel.ts' - -/** - * Handles Discord gateway connection. - * You should not use this and rather use Client class. - * - * @beta - */ -class Gateway { - websocket: WebSocket - token: string - intents: GatewayIntents[] - connected = false - initialized = false - private heartbeatInterval = 0 - private heartbeatIntervalID?: number - private sequenceID?: number - private sessionID?: string - lastPingTimestemp = 0 - private heartbeatServerResponded = false - client: Client - - constructor (client: Client, token: string, intents: GatewayIntents[]) { - this.token = token - this.intents = intents - this.client = client - this.websocket = new WebSocket( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, - [] - ) - this.websocket.binaryType = 'arraybuffer' - this.websocket.onopen = this.onopen.bind(this) - this.websocket.onmessage = this.onmessage.bind(this) - this.websocket.onclose = this.onclose.bind(this) - this.websocket.onerror = this.onerror.bind(this) - } - - private onopen (): void { - this.connected = true - } - - private onmessage (event: MessageEvent): void { - let data = event.data - if (data instanceof ArrayBuffer) { - data = new Uint8Array(data) - } - if (data instanceof Uint8Array) { - data = unzlib(data) - data = new TextDecoder('utf-8').decode(data) - } - - const { op, d, s, t }: GatewayResponse = JSON.parse(data) - - switch (op) { - case GatewayOpcodes.HELLO: - this.heartbeatInterval = d.heartbeat_interval - this.heartbeatIntervalID = setInterval(() => { - if (this.heartbeatServerResponded) { - this.heartbeatServerResponded = false - } else { - clearInterval(this.heartbeatIntervalID) - this.websocket.close() - this.initWebsocket() - return - } - - this.websocket.send( - JSON.stringify({ - op: GatewayOpcodes.HEARTBEAT, - d: this.sequenceID ?? null - }) - ) - this.lastPingTimestemp = Date.now() - }, this.heartbeatInterval) - - if (!this.initialized) { - this.sendIdentify() - this.initialized = true - } else { - this.sendResume() - } - break - - case GatewayOpcodes.HEARTBEAT_ACK: - this.heartbeatServerResponded = true - this.client.ping = Date.now() - this.lastPingTimestemp - break - - case GatewayOpcodes.INVALID_SESSION: - // Because we know this gonna be bool - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!d) { - setTimeout(this.sendResume, 3000) - } else { - setTimeout(this.sendIdentify, 3000) - } - break - - case GatewayOpcodes.DISPATCH: - this.heartbeatServerResponded = true - if (s !== null) { - this.sequenceID = s - } - switch (t) { - case GatewayEvents.Ready: - this.client.user = new User(this.client, d.user) - this.sessionID = d.session_id - d.guilds.forEach((guild: GuildPayload) => { - cache.set('guild', guild.id, new Guild(this.client, guild)) - }) - this.client.emit('ready') - break - case GatewayEvents.Channel_Create: { - let channel: Channel | undefined - switch (d.type) { - case ChannelTypes.DM: - channel = new DMChannel(this.client, d) - break - case ChannelTypes.GROUP_DM: - channel = new GroupDMChannel(this.client, d) - break - case ChannelTypes.GUILD_TEXT: - channel = new GuildTextChannel(this.client, d) - break - case ChannelTypes.GUILD_VOICE: - channel = new VoiceChannel(this.client, d) - break - case ChannelTypes.GUILD_CATEGORY: - channel = new CategoryChannel(this.client, d) - break - case ChannelTypes.GUILD_NEWS: - channel = new NewsChannel(this.client, d) - break - default: - break - } - - if (channel !== undefined) { - cache.set('channel', channel.id, channel) - this.client.emit('channelCreate', channel) - } - break - } - case GatewayEvents.Channel_Update: { - const oldChannel: Channel = cache.get('channel', d.id) - - if (oldChannel !== undefined) { - if (oldChannel.type !== d.type) { - let channel: Channel = oldChannel - switch (d.type) { - case ChannelTypes.DM: - channel = new DMChannel(this.client, d) - break - case ChannelTypes.GROUP_DM: - channel = new GroupDMChannel(this.client, d) - break - case ChannelTypes.GUILD_TEXT: - channel = new GuildTextChannel(this.client, d) - break - case ChannelTypes.GUILD_VOICE: - channel = new VoiceChannel(this.client, d) - break - case ChannelTypes.GUILD_CATEGORY: - channel = new CategoryChannel(this.client, d) - break - case ChannelTypes.GUILD_NEWS: - channel = new NewsChannel(this.client, d) - break - default: - break - } - cache.set('channel', channel.id, channel) - this.client.emit('channelUpdate', oldChannel, channel) - } else { - const before = oldChannel.refreshFromData(d) - this.client.emit('channelUpdate', before, oldChannel) - } - } - break - } - case GatewayEvents.Channel_Delete: { - const channel: Channel = cache.get('channel', d.id) - if (channel !== undefined) { - cache.del('channel', d.id) - this.client.emit('channelDelete', channel) - } - break - } - case GatewayEvents.Channel_Pins_Update: { - const channel: Channel = cache.get('channel', d.channel_id) - if (channel !== undefined && d.last_pin_timestamp !== null) { - channel.refreshFromData({ - last_pin_timestamp: d.last_pin_timestamp - }) - this.client.emit('channelPinsUpdate', channel) - } - break - } - case GatewayEvents.Guild_Create: { - const guild: Guild = cache.get('guild', d.id) - if (guild !== undefined) { - guild.refreshFromData(guild) - } - break - } - default: - break - } - break - default: - break - } - } - - private onclose (event: CloseEvent): void { - console.log(event.code) - // TODO: Handle close event codes. - } - - private onerror (event: Event | ErrorEvent): void { - const eventError = event as ErrorEvent - - console.log(eventError) - } - - private sendIdentify (): void { - this.websocket.send( - JSON.stringify({ - op: GatewayOpcodes.IDENTIFY, - d: { - token: this.token, - properties: { - $os: Deno.build.os, - $browser: 'discord.deno', - $device: 'discord.deno' - }, - compress: true, - shard: [0, 1], // TODO: Make sharding possible - intents: this.intents.reduce( - (previous, current) => previous | current, - 0 - ), - presence: { - // TODO: User should can customize this - status: 'online', - since: null, - afk: false - } - } - }) - ) - } - - private sendResume (): void { - this.websocket.send( - JSON.stringify({ - op: GatewayOpcodes.RESUME, - d: { - token: this.token, - session_id: this.sessionID, - seq: this.sequenceID - } - }) - ) - } - - initWebsocket (): void { - this.websocket = new WebSocket( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, - [] - ) - this.websocket.binaryType = 'arraybuffer' - this.websocket.onopen = this.onopen.bind(this) - this.websocket.onmessage = this.onmessage.bind(this) - this.websocket.onclose = this.onclose.bind(this) - this.websocket.onerror = this.onerror.bind(this) - } - - close (): void { - this.websocket.close(1000) - } -} - -export { Gateway } diff --git a/src/structures/base.ts b/src/structures/base.ts index ecd9deb..c84124d 100644 --- a/src/structures/base.ts +++ b/src/structures/base.ts @@ -65,7 +65,7 @@ export class Base { return oldOne } - readFromData (data: { [k: string]: any }): void {} + protected readFromData (data: { [k: string]: any }): void {} // toJSON() {} } diff --git a/src/structures/channel.ts b/src/structures/channel.ts index ff83847..144b142 100644 --- a/src/structures/channel.ts +++ b/src/structures/channel.ts @@ -18,7 +18,7 @@ export class Channel extends Base { cache.set('channel', this.id, this) } - readFromData (data: ChannelPayload): void { + protected readFromData (data: ChannelPayload): void { super.readFromData(data) this.type = data.type ?? this.type this.id = data.id ?? this.id diff --git a/src/structures/dmChannel.ts b/src/structures/dmChannel.ts index 58244d7..104059f 100644 --- a/src/structures/dmChannel.ts +++ b/src/structures/dmChannel.ts @@ -13,7 +13,7 @@ export class DMChannel extends TextChannel { cache.set('dmchannel', this.id, this) } - readFromData (data: DMChannelPayload): void { + protected readFromData (data: DMChannelPayload): void { super.readFromData(data) this.recipients = data.recipients ?? this.recipients } diff --git a/src/structures/emoji.ts b/src/structures/emoji.ts index eb72c59..e7a19a3 100644 --- a/src/structures/emoji.ts +++ b/src/structures/emoji.ts @@ -37,7 +37,7 @@ export class Emoji extends Base { this.available = data.available } - readFromData (data: EmojiPayload): void { + protected readFromData (data: EmojiPayload): void { super.readFromData(data) this.id = data.id ?? this.id this.name = data.name ?? this.name diff --git a/src/structures/groupChannel.ts b/src/structures/groupChannel.ts index a419998..e6f8918 100644 --- a/src/structures/groupChannel.ts +++ b/src/structures/groupChannel.ts @@ -17,7 +17,7 @@ export class GroupDMChannel extends Channel { cache.set('groupchannel', this.id, this) } - readFromData (data: GroupDMChannelPayload): void { + protected readFromData (data: GroupDMChannelPayload): void { super.readFromData(data) this.name = data.name ?? this.name this.icon = data.icon ?? this.icon diff --git a/src/structures/guild.ts b/src/structures/guild.ts index f39ff6b..90234ac 100644 --- a/src/structures/guild.ts +++ b/src/structures/guild.ts @@ -8,6 +8,7 @@ import { Member } from './member.ts' import { Role } from './role.ts' import { VoiceState } from './voiceState.ts' import cache from '../models/cache.ts' +import getChannelByType from '../utils/getChannelByType.ts' export class Guild extends Base { id: string @@ -103,7 +104,7 @@ export class Guild extends Base { new Member(client, v) ) this.channels = data.channels?.map( - v => cache.get('channel', v.id) ?? new Channel(client, v) + v => cache.get('channel', v.id) ?? getChannelByType(this.client, v) ) this.presences = data.presences this.maxPresences = data.max_presences @@ -122,7 +123,7 @@ export class Guild extends Base { cache.set('guild', this.id, this) } - readFromData (data: GuildPayload): void { + protected readFromData (data: GuildPayload): void { super.readFromData(data) this.id = data.id ?? this.id this.unavailable = data.unavailable ?? this.unavailable @@ -177,7 +178,7 @@ export class Guild extends Base { ) ?? this.members this.channels = data.channels?.map( - v => cache.get('channel', v.id) ?? new Channel(this.client, v) + v => cache.get('channel', v.id) ?? getChannelByType(this.client, v) ) ?? this.members this.presences = data.presences ?? this.presences this.maxPresences = data.max_presences ?? this.maxPresences diff --git a/src/structures/guildCategoryChannel.ts b/src/structures/guildCategoryChannel.ts index c5869c5..bfb661d 100644 --- a/src/structures/guildCategoryChannel.ts +++ b/src/structures/guildCategoryChannel.ts @@ -25,7 +25,7 @@ export class CategoryChannel extends Channel { cache.set('guildcategorychannel', this.id, this) } - readFromData (data: GuildChannelCategoryPayload): void { + protected readFromData (data: GuildChannelCategoryPayload): void { super.readFromData(data) this.guildID = data.guild_id ?? this.guildID this.name = data.name ?? this.name diff --git a/src/structures/guildChannel.ts b/src/structures/guildChannel.ts deleted file mode 100644 index 6a45c7f..0000000 --- a/src/structures/guildChannel.ts +++ /dev/null @@ -1,35 +0,0 @@ -import cache from '../models/cache.ts' -import { Client } from '../models/client.ts' -import { GuildChannelPayload, Overwrite } from '../types/channelTypes.ts' -import { Channel } from './channel.ts' - -export class GuildChannel extends Channel { - guildID: string - name: string - position: number - permissionOverwrites: Overwrite[] - nsfw: boolean - parentID?: string - - constructor (client: Client, data: GuildChannelPayload) { - super(client, data) - this.guildID = data.guild_id - this.name = data.name - this.position = data.position - this.permissionOverwrites = data.permission_overwrites - this.nsfw = data.nsfw - this.parentID = data.parent_id - cache.set('guildchannel', this.id, this) - } - - readFromData (data: GuildChannelPayload): void { - super.readFromData(data) - this.guildID = data.guild_id ?? this.guildID - this.name = data.name ?? this.name - this.position = data.position ?? this.position - this.permissionOverwrites = - data.permission_overwrites ?? this.permissionOverwrites - this.nsfw = data.nsfw ?? this.nsfw - this.parentID = data.parent_id ?? this.parentID - } -} diff --git a/src/structures/guildTextChannel.ts b/src/structures/guildTextChannel.ts index 7f0b17e..1b8b4ea 100644 --- a/src/structures/guildTextChannel.ts +++ b/src/structures/guildTextChannel.ts @@ -1,9 +1,15 @@ import { Client } from '../models/client.ts' -import { GuildChannel } from './guildChannel.ts' -import { GuildTextChannelPayload } from '../types/channelTypes.ts' +import { GuildTextChannelPayload, Overwrite } from '../types/channelTypes.ts' import cache from '../models/cache.ts' +import { TextChannel } from './textChannel.ts' -export class GuildTextChannel extends GuildChannel { +export class GuildTextChannel extends TextChannel { + guildID: string + name: string + position: number + permissionOverwrites: Overwrite[] + nsfw: boolean + parentID?: string rateLimit: number topic?: string @@ -13,13 +19,26 @@ export class GuildTextChannel extends GuildChannel { constructor (client: Client, data: GuildTextChannelPayload) { super(client, data) + this.guildID = data.guild_id + this.name = data.name + this.position = data.position + this.permissionOverwrites = data.permission_overwrites + this.nsfw = data.nsfw + this.parentID = data.parent_id this.topic = data.topic this.rateLimit = data.rate_limit_per_user cache.set('guildtextchannel', this.id, this) } - readFromData (data: GuildTextChannelPayload): void { + protected readFromData (data: GuildTextChannelPayload): void { super.readFromData(data) + this.guildID = data.guild_id ?? this.guildID + this.name = data.name ?? this.name + this.position = data.position ?? this.position + this.permissionOverwrites = + data.permission_overwrites ?? this.permissionOverwrites + 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 } diff --git a/src/structures/guildVoiceChannel.ts b/src/structures/guildVoiceChannel.ts index a8d0fd5..5f2fc33 100644 --- a/src/structures/guildVoiceChannel.ts +++ b/src/structures/guildVoiceChannel.ts @@ -26,7 +26,7 @@ export class VoiceChannel extends Channel { cache.set('guildvoicechannel', this.id, this) } - readFromData (data: GuildVoiceChannelPayload): void { + protected readFromData (data: GuildVoiceChannelPayload): void { super.readFromData(data) this.bitrate = data.bitrate ?? this.bitrate this.userLimit = data.user_limit ?? this.userLimit diff --git a/src/structures/guildnewsChannel.ts b/src/structures/guildnewsChannel.ts index 51055ba..6d0d787 100644 --- a/src/structures/guildnewsChannel.ts +++ b/src/structures/guildnewsChannel.ts @@ -1,10 +1,36 @@ import { Client } from '../models/client.ts' -import { Channel } from './channel.ts' -import { GuildNewsChannelPayload } from '../types/channelTypes.ts' +import { GuildNewsChannelPayload, Overwrite } from '../types/channelTypes.ts' +import { TextChannel } from './textChannel.ts' + +export class NewsChannel extends TextChannel { + guildID: string + name: string + position: number + permissionOverwrites: Overwrite[] + nsfw: boolean + parentID?: string + topic?: string -export class NewsChannel extends Channel { - // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor (client: Client, data: GuildNewsChannelPayload) { super(client, data) + this.guildID = data.guild_id + this.name = data.name + this.position = data.position + this.permissionOverwrites = data.permission_overwrites + this.nsfw = data.nsfw + this.parentID = data.parent_id + this.topic = data.topic + } + + protected readFromData (data: GuildNewsChannelPayload): void { + super.readFromData(data) + this.guildID = data.guild_id ?? this.guildID + this.name = data.name ?? this.name + this.position = data.position ?? this.position + this.permissionOverwrites = + data.permission_overwrites ?? this.permissionOverwrites + this.nsfw = data.nsfw ?? this.nsfw + this.parentID = data.parent_id ?? this.parentID + this.topic = data.topic ?? this.topic } } diff --git a/src/structures/invite.ts b/src/structures/invite.ts index f37941a..83be8e4 100644 --- a/src/structures/invite.ts +++ b/src/structures/invite.ts @@ -31,7 +31,7 @@ export class Invite extends Base { this.approximatePresenceCount = data.approximate_presence_count } - readFromData (data: InvitePayload): void { + protected readFromData (data: InvitePayload): void { super.readFromData(data) this.code = data.code ?? this.code this.guild = data.guild ?? this.guild diff --git a/src/structures/member.ts b/src/structures/member.ts index 9a26e65..6086eb8 100644 --- a/src/structures/member.ts +++ b/src/structures/member.ts @@ -1,9 +1,12 @@ import cache from '../models/cache.ts' import { Client } from '../models/client.ts' import { MemberPayload } from '../types/guildTypes.ts' +import { Base } from './base.ts' import { User } from './user.ts' -export class Member extends User { +export class Member extends Base { + id: string + user: User nick?: string roles: string[] joinedAt: string @@ -12,7 +15,10 @@ export class Member extends User { mute: boolean constructor (client: Client, data: MemberPayload) { - super(client, data.user) + super(client) + this.id = data.user.id + this.user = + cache.get('user', data.user.id) ?? new User(this.client, data.user) this.nick = data.nick this.roles = data.roles this.joinedAt = data.joined_at @@ -22,8 +28,8 @@ export class Member extends User { cache.set('member', this.id, this) } - readFromData (data: MemberPayload): void { - super.readFromData(data) + protected readFromData (data: MemberPayload): void { + super.readFromData(data.user) this.nick = data.nick ?? this.nick this.roles = data.roles ?? this.roles this.joinedAt = data.joined_at ?? this.joinedAt diff --git a/src/structures/message.ts b/src/structures/message.ts index ded9ae8..8dca65d 100644 --- a/src/structures/message.ts +++ b/src/structures/message.ts @@ -76,14 +76,14 @@ export class Message extends Base { cache.set('message', this.id, this) } - readFromData (data: MessagePayload): void { + protected readFromData (data: MessagePayload): void { super.readFromData(data) this.channelID = data.channel_id ?? this.channelID this.guildID = data.guild_id ?? this.guildID this.author = cache.get('user', data.author.id) ?? - new User(this.client, data.author) ?? - this.author + this.author ?? + new User(this.client, data.author) this.content = data.content ?? this.content this.timestamp = data.timestamp ?? this.timestamp this.editedTimestamp = data.edited_timestamp ?? this.editedTimestamp diff --git a/src/structures/role.ts b/src/structures/role.ts index 2c990c5..442e24e 100644 --- a/src/structures/role.ts +++ b/src/structures/role.ts @@ -30,7 +30,7 @@ export class Role extends Base { cache.set('role', this.id, this) } - readFromData (data: RolePayload): void { + protected readFromData (data: RolePayload): void { super.readFromData(data) this.name = data.name ?? this.name this.color = data.color ?? this.color diff --git a/src/structures/textChannel.ts b/src/structures/textChannel.ts index f5a83b6..f328662 100644 --- a/src/structures/textChannel.ts +++ b/src/structures/textChannel.ts @@ -16,7 +16,7 @@ export class TextChannel extends Channel { cache.set('textchannel', this.id, this) } - readFromData (data: TextChannelPayload): void { + protected readFromData (data: TextChannelPayload): void { super.readFromData(data) this.lastMessageID = data.last_message_id ?? this.lastMessageID this.lastPinTimestamp = data.last_pin_timestamp ?? this.lastPinTimestamp diff --git a/src/structures/user.ts b/src/structures/user.ts index a1c5230..4d0ef1c 100644 --- a/src/structures/user.ts +++ b/src/structures/user.ts @@ -44,7 +44,7 @@ export class User extends Base { cache.set('user', this.id, this) } - readFromData (data: UserPayload): void { + protected readFromData (data: UserPayload): void { super.readFromData(data) this.username = data.username ?? this.username this.discriminator = data.discriminator ?? this.discriminator diff --git a/src/structures/voicestate.ts b/src/structures/voicestate.ts index 66c6f55..717880d 100644 --- a/src/structures/voicestate.ts +++ b/src/structures/voicestate.ts @@ -33,7 +33,7 @@ export class VoiceState extends Base { cache.set('voiceState', `${this.guildID}:${this.userID}`, this) } - readFromData (data: VoiceStatePayload): void { + protected readFromData (data: VoiceStatePayload): void { super.readFromData(data) this.channelID = data.channel_id ?? this.channelID this.sessionID = data.session_id ?? this.sessionID diff --git a/src/test/index.ts b/src/test/index.ts index 2b6115d..2763c68 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1,10 +1,11 @@ import { Client } from '../models/client.ts' import { GatewayIntents } from '../types/gatewayTypes.ts' import { TOKEN } from './config.ts' -import * as cache from '../models/cache.ts' -import { Member } from '../structures/member.ts' import { Channel } from '../structures/channel.ts' import { GuildTextChannel } from '../structures/guildTextChannel.ts' +import { TextChannel } from '../structures/textChannel.ts' +import { Guild } from '../structures/guild.ts' +import { User } from '../structures/user.ts' const bot = new Client() @@ -12,18 +13,66 @@ bot.on('ready', () => { console.log('READY!') }) +bot.on('channelDelete', (channel: Channel) => { + console.log('channelDelete', channel.id) +}) + bot.on('channelUpdate', (before: Channel, after: Channel) => { if (before instanceof GuildTextChannel && after instanceof GuildTextChannel) { - console.log(before.name) - console.log(after.name) + console.log('channelUpdate', before.name) + console.log('channelUpdate', after.name) } else { - console.log(before) - console.log(after) + console.log('channelUpdate', before.id) + console.log('channelUpdate', after.id) } }) +bot.on('channelCreate', (channel: Channel) => { + console.log('channelCreate', channel.id) +}) + +bot.on('channelPinsUpdate', (before: TextChannel, after: TextChannel) => { + console.log( + 'channelPinsUpdate', + before.lastPinTimestamp, + after.lastPinTimestamp + ) +}) + +bot.on('guildBanAdd', (guild: Guild, user: User) => { + console.log('guildBanAdd', guild.id, user.id) +}) + +bot.on('guildBanRemove', (guild: Guild, user: User) => { + console.log('guildBanRemove', guild.id, user.id) +}) + +bot.on('guildCreate', (guild: Guild) => { + console.log('guildCreate', guild.id) +}) + +bot.on('guildDelete', (guild: Guild) => { + console.log('guildDelete', guild.id) +}) + +bot.on('guildUpdate', (before: Guild, after: Guild) => { + console.log('guildUpdate', before.name, after.name) +}) + bot.connect(TOKEN, [ GatewayIntents.GUILD_MEMBERS, GatewayIntents.GUILD_PRESENCES, - GatewayIntents.GUILD_MESSAGES + GatewayIntents.GUILD_MESSAGES, + GatewayIntents.DIRECT_MESSAGES, + GatewayIntents.DIRECT_MESSAGE_REACTIONS, + GatewayIntents.DIRECT_MESSAGE_TYPING, + GatewayIntents.GUILDS, + GatewayIntents.GUILD_BANS, + GatewayIntents.GUILD_EMOJIS, + GatewayIntents.GUILD_INTEGRATIONS, + GatewayIntents.GUILD_INVITES, + GatewayIntents.GUILD_MESSAGE_REACTIONS, + GatewayIntents.GUILD_MESSAGE_TYPING, + GatewayIntents.GUILD_VOICE_STATES, + GatewayIntents.GUILD_WEBHOOKS ]) diff --git a/src/types/channelTypes.ts b/src/types/channelTypes.ts index c620d0c..0291065 100644 --- a/src/types/channelTypes.ts +++ b/src/types/channelTypes.ts @@ -3,17 +3,17 @@ import { EmojiPayload } from './emojiTypes.ts' import { MemberPayload } from './guildTypes.ts' import { UserPayload } from './userTypes.ts' -interface ChannelPayload { +export interface ChannelPayload { id: string type: ChannelTypes } -interface TextChannelPayload extends ChannelPayload { +export interface TextChannelPayload extends ChannelPayload { last_message_id?: string last_pin_timestamp?: string } -interface GuildChannelPayload extends ChannelPayload { +export interface GuildChannelPayload extends ChannelPayload { guild_id: string name: string position: number @@ -22,46 +22,46 @@ interface GuildChannelPayload extends ChannelPayload { parent_id?: string } -interface GuildTextChannelPayload +export interface GuildTextChannelPayload extends TextChannelPayload, GuildChannelPayload { rate_limit_per_user: number topic?: string } -interface GuildNewsChannelPayload +export interface GuildNewsChannelPayload extends TextChannelPayload, GuildChannelPayload { topic?: string } -interface GuildVoiceChannelPayload extends GuildChannelPayload { +export interface GuildVoiceChannelPayload extends GuildChannelPayload { bitrate: string user_limit: number } -interface DMChannelPayload extends TextChannelPayload { +export interface DMChannelPayload extends TextChannelPayload { recipients: UserPayload[] } -interface GroupDMChannelPayload extends DMChannelPayload { +export interface GroupDMChannelPayload extends DMChannelPayload { name: string icon?: string owner_id: string } -interface GuildChannelCategoryPayload +export interface GuildChannelCategoryPayload extends ChannelPayload, GuildChannelPayload {} -interface Overwrite { +export interface Overwrite { id: string type: number allow: string deny: string } -enum ChannelTypes { +export enum ChannelTypes { GUILD_TEXT = 0, DM = 1, GUILD_VOICE = 2, @@ -71,7 +71,7 @@ enum ChannelTypes { GUILD_STORE = 6 } -interface MessagePayload { +export interface MessagePayload { id: string channel_id: string guild_id?: string @@ -98,7 +98,7 @@ interface MessagePayload { flags?: number } -interface MessageOption { +export interface MessageOption { tts: boolean embed: Embed file: Attachment @@ -109,14 +109,14 @@ interface MessageOption { } } -interface ChannelMention { +export interface ChannelMention { id: string guild_id: string type: ChannelTypes name: string } -interface Attachment { +export interface Attachment { id: string filename: string size: number @@ -126,7 +126,7 @@ interface Attachment { width: number | undefined } -interface EmbedPayload { +export interface EmbedPayload { title?: string type?: EmbedTypes description?: string @@ -142,64 +142,70 @@ interface EmbedPayload { fields?: EmbedField[] } -type EmbedTypes = 'rich' | 'image' | 'video' | 'gifv' | 'article' | 'link' +export type EmbedTypes = + | 'rich' + | 'image' + | 'video' + | 'gifv' + | 'article' + | 'link' -interface EmbedField { +export interface EmbedField { name: string value: string inline?: boolean } -interface EmbedAuthor { +export interface EmbedAuthor { name?: string url?: string icon_url?: string proxy_icon_url?: string } -interface EmbedFooter { +export interface EmbedFooter { text: string icon_url?: string proxy_icon_url?: string } -interface EmbedImage { +export interface EmbedImage { url?: string proxy_url?: string height?: number width?: number } -interface EmbedProvider { +export interface EmbedProvider { name?: string url?: string } -interface EmbedVideo { +export interface EmbedVideo { url?: string height?: number width?: number } -interface EmbedThumbnail { +export interface EmbedThumbnail { url?: string proxy_url?: string height?: number width?: number } -interface Reaction { +export interface Reaction { count: number me: boolean emoji: EmojiPayload } -interface MessageActivity { +export interface MessageActivity { type: MessageTypes party_id?: string } -interface MessageApplication { +export interface MessageApplication { id: string cover_image?: string desription: string @@ -207,13 +213,13 @@ interface MessageApplication { name: string } -interface MessageReference { +export interface MessageReference { message_id?: string channel_id?: string guild_id?: string } -enum MessageTypes { +export enum MessageTypes { DEFAULT = 0, RECIPIENT_ADD = 1, RECIPIENT_REMOVE = 2, @@ -231,14 +237,14 @@ enum MessageTypes { GUILD_DISCOVERY_REQUALIFIED = 15 } -enum MessageActivityTypes { +export enum MessageActivityTypes { JOIN = 1, SPECTATE = 2, LISTEN = 3, JOIN_REQUEST = 4 } -enum MessageFlags { +export enum MessageFlags { CROSSPOSTED = 1 << 0, IS_CROSSPOST = 1 << 1, SUPPRESS_EMBEDS = 1 << 2, @@ -246,54 +252,7 @@ enum MessageFlags { URGENT = 1 << 4 } -interface FollowedChannel { +export interface FollowedChannel { channel_id: string webhook_id: string } - -interface Overwrite { - id: string - type: number - allow: string - deny: string -} -interface ChannelMention { - id: string - guild_id: string - type: ChannelTypes - name: string -} - -export { - ChannelPayload, - TextChannelPayload, - GuildChannelPayload, - GuildNewsChannelPayload, - GuildTextChannelPayload, - GuildVoiceChannelPayload, - GuildChannelCategoryPayload, - DMChannelPayload, - GroupDMChannelPayload, - Overwrite, - ChannelTypes, - ChannelMention, - Attachment, - Reaction, - MessageActivity, - MessageActivityTypes, - MessageFlags, - FollowedChannel, - MessageApplication, - MessageReference, - MessagePayload, - MessageOption, - EmbedPayload, - EmbedTypes, - EmbedFooter, - EmbedImage, - EmbedThumbnail, - EmbedVideo, - EmbedProvider, - EmbedAuthor, - EmbedField -} diff --git a/src/types/gatewayResponse.ts b/src/types/gatewayResponse.ts index a370ea7..982a19b 100644 --- a/src/types/gatewayResponse.ts +++ b/src/types/gatewayResponse.ts @@ -4,11 +4,9 @@ import { GatewayOpcodes, GatewayEvents } from '../types/gatewayTypes.ts' * Gateway response from Discord. * */ -interface GatewayResponse { +export interface GatewayResponse { op: GatewayOpcodes d: any s?: number t?: GatewayEvents } - -export { GatewayResponse } diff --git a/src/types/gatewayTypes.ts b/src/types/gatewayTypes.ts index 7285757..048a7fc 100644 --- a/src/types/gatewayTypes.ts +++ b/src/types/gatewayTypes.ts @@ -62,11 +62,9 @@ enum GatewayIntents { } enum GatewayEvents { - Hello = 'HELLO', Ready = 'READY', Resumed = 'RESUMED', Reconnect = 'RECONNECT', - Invalid_Session = 'INVALID_SESSION', Channel_Create = 'CHANNEL_CREATE', Channel_Update = 'CHANNEL_UPDATE', Channel_Delete = 'CHANNEL_DELETE', @@ -89,7 +87,7 @@ enum GatewayEvents { Invite_Delete = 'INVITE_DELETE', Message_Create = 'MESSAGE_CREATE', Message_Update = 'MESSAGE_UPDATE', - Message_Delete = 'MESSAG_DELETE', + Message_Delete = 'MESSAGE_DELETE', Message_Delete_Bulk = 'MESSAGE_DELETE_BULK', Message_Reaction_Add = 'MESSAGE_REACTION_ADD', Message_Reaction_Remove = 'MESSAGE_REACTION_REMOVE', @@ -98,7 +96,6 @@ enum GatewayEvents { Presence_Update = 'PRESENCE_UPDATE', Typing_Start = 'TYPING_START', User_Update = 'USER_UPDATE', - Voice_State_Update = 'VOICE_STATE_UPDATE', Voice_Server_Update = 'VOICE_SERVER_UPDATE', Webhooks_Update = 'WEBHOOKS_UPDATE' } @@ -395,5 +392,5 @@ interface WebhooksUpdate { channel_id: string } -//https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields +// https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields export { GatewayCloseCodes, GatewayOpcodes, GatewayIntents, GatewayEvents } diff --git a/src/types/guildTypes.ts b/src/types/guildTypes.ts index aba0f82..ac37d02 100644 --- a/src/types/guildTypes.ts +++ b/src/types/guildTypes.ts @@ -5,7 +5,7 @@ import { RolePayload } from './roleTypes.ts' import { UserPayload } from './userTypes.ts' import { VoiceStatePayload } from './voiceTypes.ts' -interface GuildPayload { +export interface GuildPayload { id: string name: string icon?: string @@ -53,7 +53,7 @@ interface GuildPayload { approximate_presence_count?: number } -interface MemberPayload { +export interface MemberPayload { user: UserPayload nick?: string roles: string[] @@ -99,7 +99,7 @@ enum SystemChannelFlags { SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1 } -type GuildFeatures = +export type GuildFeatures = | 'INVITE_SPLASH' | 'VIP_REGIONS' | 'VANITY_URL' @@ -112,5 +112,3 @@ type GuildFeatures = | 'FEATURABLE' | 'ANIMATED_ICON' | 'BANNER' - -export { MemberPayload, GuildPayload, GuildFeatures } diff --git a/src/types/presenceTypes.ts b/src/types/presenceTypes.ts index bb834d4..05209eb 100644 --- a/src/types/presenceTypes.ts +++ b/src/types/presenceTypes.ts @@ -1,6 +1,6 @@ import { UserPayload } from './userTypes.ts' -interface PresenceUpdatePayload { +export interface PresenceUpdatePayload { user: UserPayload guild_id: string status: string @@ -14,7 +14,7 @@ interface ClientStatus { web?: string } -interface ActivityPayload { +export interface ActivityPayload { name: string type: 0 | 1 | 2 | 3 | 4 | 5 url?: string | undefined @@ -68,5 +68,3 @@ enum ActivityFlags { SYNC = 1 << 4, PLAY = 1 << 5 } - -export { ActivityPayload, PresenceUpdatePayload } diff --git a/src/utils/getChannelByType.ts b/src/utils/getChannelByType.ts new file mode 100644 index 0000000..26675cd --- /dev/null +++ b/src/utils/getChannelByType.ts @@ -0,0 +1,53 @@ +import { Client } from '../models/client.ts' +import { + ChannelPayload, + ChannelTypes, + DMChannelPayload, + GroupDMChannelPayload, + GuildChannelCategoryPayload, + GuildNewsChannelPayload, + GuildTextChannelPayload, + GuildVoiceChannelPayload +} from '../types/channelTypes.ts' +import { DMChannel } from '../structures/dmChannel.ts' +import { GroupDMChannel } from '../structures/groupChannel.ts' +import { CategoryChannel } from '../structures/guildCategoryChannel.ts' +import { NewsChannel } from '../structures/guildNewsChannel.ts' +import { VoiceChannel } from '../structures/guildVoiceChannel.ts' +import { TextChannel } from '../structures/textChannel.ts' + +const getChannelByType = ( + client: Client, + data: + | GuildChannelCategoryPayload + | GuildNewsChannelPayload + | GuildTextChannelPayload + | GuildVoiceChannelPayload + | DMChannelPayload + | GroupDMChannelPayload + | ChannelPayload +): + | CategoryChannel + | NewsChannel + | TextChannel + | VoiceChannel + | DMChannel + | GroupDMChannel + | undefined => { + switch (data.type) { + case ChannelTypes.GUILD_CATEGORY: + return new CategoryChannel(client, data as GuildChannelCategoryPayload) + case ChannelTypes.GUILD_NEWS: + return new NewsChannel(client, data as GuildNewsChannelPayload) + case ChannelTypes.GUILD_TEXT: + return new TextChannel(client, data as GuildTextChannelPayload) + case ChannelTypes.GUILD_VOICE: + return new VoiceChannel(client, data as GuildVoiceChannelPayload) + case ChannelTypes.DM: + return new DMChannel(client, data as DMChannelPayload) + case ChannelTypes.GROUP_DM: + return new GroupDMChannel(client, data as GroupDMChannelPayload) + } +} + +export default getChannelByType diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..75766ce --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,3 @@ +import getChannelByType from './getChannelByType.ts' + +export default { getChannelByType } diff --git a/tsconfig.json b/tsconfig.json index a265a0a..5f09fcc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,7 @@ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ @@ -61,7 +61,7 @@ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "emitDecoratorMetadata": false /* Enables experimental support for emitting type metadata for decorators. */, /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */