diff --git a/.gitignore b/.gitignore index ddf707b..6f6a9de 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,6 @@ dist # Used ESLint for linting so block package-lock package-lock.json yarn.lock + +# PRIVACY XDDDD +src/test/config.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 0dd8765..7848ab6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,8 @@ "deno.enable": true, "deno.lint": false, "deno.unstable": false, - "deepscan.enable": true + "deepscan.enable": true, + "deno.import_intellisense_origins": { + "https://deno.land": true + } } \ No newline at end of file diff --git a/src/consts/urlsAndVersions.ts b/src/consts/urlsAndVersions.ts index 7e7ef2f..128dc52 100644 --- a/src/consts/urlsAndVersions.ts +++ b/src/consts/urlsAndVersions.ts @@ -1,6 +1,6 @@ -export const DISCORD_API_URL = 'https://discord.com/api' //여기 v8 들어가야하는거 아닌가... <- gateway.ts 참조바람 왜 거기 endpoint에서 import에서 에러나지 왜?? +export const DISCORD_API_URL = 'https://discord.com/api' -export const DISCORD_GATEWAY_URL = 'wss://gateway.discord.com' +export const DISCORD_GATEWAY_URL = 'wss://gateway.discord.gg' export const DISCORD_CDN_URL = 'https://cdn.discordapp.com' diff --git a/src/models/client.ts b/src/models/client.ts new file mode 100644 index 0000000..c77a9f4 --- /dev/null +++ b/src/models/client.ts @@ -0,0 +1,25 @@ +import { User } from '../structures/user.ts' +import { GatewayIntents } from '../types/gatewayTypes.ts' +import { Gateway } from './gateway.ts' +import { Rest } from "./rest.ts" + +/** + * Discord Client. + */ +export class Client { + gateway?: Gateway + rest?: Rest + user?: User + ping = 0 + + constructor () {} + + /** + * This function is used for connect to discord. + * @param token Your token. This is required. + * @param intents Gateway intents in array. This is required. + */ + connect (token: string, intents: GatewayIntents[]) { + this.gateway = new Gateway(this, token, intents) + } +} diff --git a/src/models/gateway.ts b/src/models/gateway.ts index d68bf3b..09fbffe 100644 --- a/src/models/gateway.ts +++ b/src/models/gateway.ts @@ -1,10 +1,16 @@ -import { inflate } from 'https://deno.land/x/denoflate/mod.ts' +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 } from '../types/gatewayTypes.ts' +import { + GatewayOpcodes, + GatewayIntents, + GatewayEvents +} from '../types/gatewayTypes.ts' +import { User } from '../structures/user.ts' /** * Handles Discord gateway connection. @@ -15,20 +21,31 @@ import { GatewayOpcodes, GatewayIntents } from '../types/gatewayTypes.ts' class Gateway { websocket: WebSocket token: string - intents: [GatewayIntents] + intents: GatewayIntents[] connected = false initialized = false heartbeatInterval = 0 heartbeatIntervalID?: number + heartbeatCheckerIntervalID?: number sequenceID?: number + sessionID?: string + lastPingTimestemp = 0 heartbeatServerResponded = false + client: Client - constructor (token: string, intents: [GatewayIntents]) { - this.websocket = new WebSocket( - `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json` - ) + constructor (client: Client, token: string, intents: GatewayIntents[]) { this.token = token this.intents = intents + this.client = client + this.websocket = new WebSocket( + `${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) } onopen () { @@ -41,16 +58,8 @@ class Gateway { data = new Uint8Array(data) } if (data instanceof Uint8Array) { - const dataSuffix = data.slice(-4) - - if ( - dataSuffix[0] === 0 && - dataSuffix[1] === 0 && - dataSuffix[2] === 0xff && - dataSuffix[3] === 0xff - ) { - data = inflate(data) - } + data = unzlib(data) + data = new TextDecoder('utf-8').decode(data) } const { op, d, s, t }: GatewayResponse = JSON.parse(data) @@ -59,56 +68,122 @@ class Gateway { case GatewayOpcodes.HELLO: this.heartbeatInterval = d.heartbeat_interval this.heartbeatIntervalID = setInterval(() => { + if (this.heartbeatServerResponded) { + this.heartbeatServerResponded = false + } else { + clearInterval(this.heartbeatIntervalID) + clearInterval(this.heartbeatCheckerIntervalID) + this.websocket.close() + this.initWebsocket() + return + } + this.websocket.send( JSON.stringify({ op: GatewayOpcodes.HEARTBEAT, d: this.sequenceID ?? null }) ) - - if (this.heartbeatServerResponded) { - this.heartbeatServerResponded = false - } else { - // TODO: Add heartbeat failed error - } + this.lastPingTimestemp = Date.now() }, this.heartbeatInterval) - this.websocket.send( - JSON.stringify({ - 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 - } - }) - ) + if (!this.initialized) { + this.sendIdentify() + this.initialized = true + } else { + this.websocket.send( + JSON.stringify({ + op: GatewayOpcodes.RESUME, + d: { + token: this.token, + session_id: this.sessionID, + seq: this.sequenceID + } + }) + ) + } break case GatewayOpcodes.HEARTBEAT_ACK: this.heartbeatServerResponded = true + this.client.ping = Date.now() - this.lastPingTimestemp + break + + case GatewayOpcodes.INVALID_SESSION: + setTimeout(this.sendIdentify, 3000) break case GatewayOpcodes.DISPATCH: - switch (t) { + 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 + break + default: + break + } + break default: - return + break } } + + onclose (event: CloseEvent) { + // TODO: Handle close event codes. + } + + onerror (event: Event | ErrorEvent) { + const eventError = event as ErrorEvent + + console.log(eventError) + } + + sendIdentify () { + 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 + } + } + }) + ) + } + + initWebsocket () { + this.websocket = new WebSocket( + `${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 () { + this.websocket.close(1000) + } } export { Gateway } diff --git a/src/models/rest.ts b/src/models/rest.ts new file mode 100644 index 0000000..5226e90 --- /dev/null +++ b/src/models/rest.ts @@ -0,0 +1,12 @@ +import { Client } from "./client.ts"; + +class Rest { + client: Client + constructor(client: Client) { + this.client = client + } + + //TODO: make endpoints function +} + +export { Rest } \ No newline at end of file diff --git a/src/structures/base.ts b/src/structures/base.ts index a2ccbc5..6f99de4 100644 --- a/src/structures/base.ts +++ b/src/structures/base.ts @@ -1,10 +1,10 @@ -// 일단 대충 여러 봇 라이브러리에서 본 구조 가져오는 중.. +import { Client } from '../models/client.ts' -class Base { - id?: string - constructor (id: string | undefined) { - if (id) { - this.id = id - } +export class Base { + // property 읍 + client: Client + constructor (client: Client) { + this.client = client } } +// discord.js 보는중 diff --git a/src/structures/cdn.ts b/src/structures/cdn.ts new file mode 100644 index 0000000..a56520c --- /dev/null +++ b/src/structures/cdn.ts @@ -0,0 +1,11 @@ +import { ImageFormats, ImageSize } from "../types/cdnTypes.ts"; + +export const ImageURL = ( + url: string, + format: ImageFormats, + size?: ImageSize | 128 +) => { + if (url.includes('a_')) { + return url + '.gif' + '?size=' + size + } else return url + '.' + format + '?size=' + size +} \ No newline at end of file diff --git a/src/structures/channel.ts b/src/structures/channel.ts index e69de29..50e5ae7 100644 --- a/src/structures/channel.ts +++ b/src/structures/channel.ts @@ -0,0 +1,29 @@ +import { Client } from '../models/client.ts' +import { ChannelPayload, ChannelTypes } from '../types/channelTypes.ts' +import { Base } from './base.ts' +import { PrivateChannel } from './dm.ts' +import { TextChannel } from './textChannel.ts' + +export class Channel extends Base { + type: ChannelTypes + id: string + + constructor (client: Client, data: ChannelPayload) { + super(client) + this.type = data.type + this.id = data.id + } + + get mention () { + return `<#${this.id}>` + } + + static from (data: ChannelPayload, client: Client) { + switch (data.type) { + case ChannelTypes.GUILD_TEXT: + return new TextChannel(client, data) + case ChannelTypes.DM: + return new PrivateChannel(client, data) + } + } +} diff --git a/src/structures/dm.ts b/src/structures/dm.ts new file mode 100644 index 0000000..3540419 --- /dev/null +++ b/src/structures/dm.ts @@ -0,0 +1,9 @@ +import { Client } from '../models/client.ts' +import { ChannelPayload } from '../types/channelTypes.ts' +import { Channel } from './channel.ts' + +export class PrivateChannel extends Channel implements ChannelPayload { + constructor (client: Client, data: ChannelPayload) { + super(client, data) + } +} diff --git a/src/structures/embed.ts b/src/structures/embed.ts index e69de29..13331ce 100644 --- a/src/structures/embed.ts +++ b/src/structures/embed.ts @@ -0,0 +1,32 @@ +import { Client } from '../models/client.ts' +import { + EmbedAuthor, + EmbedField, + EmbedFooter, + EmbedImage, + EmbedPayload, + EmbedProvider, + EmbedThumbnail, + EmbedTypes, + EmbedVideo +} from '../types/channelTypes.ts' +import { Base } from './base.ts' + +export class Embed extends Base implements EmbedPayload { + title?: string + type?: EmbedTypes + description?: string + url?: string + timestamp?: string + color?: number + footer?: EmbedFooter + image?: EmbedImage + thumbnail?: EmbedThumbnail + video?: EmbedVideo + provider?: EmbedProvider + author?: EmbedAuthor + fields?: EmbedField[] + constructor (client: Client, data: EmbedPayload) { + super(client) + } +} diff --git a/src/structures/emoji.ts b/src/structures/emoji.ts new file mode 100644 index 0000000..a419515 --- /dev/null +++ b/src/structures/emoji.ts @@ -0,0 +1,33 @@ +import { Client } from '../models/client.ts' +import { EmojiPayload } from '../types/emojiTypes.ts' +import { Base } from './base.ts' +import { User } from './user.ts' + +export class Emoji extends Base implements EmojiPayload { + id: string + name: string + roles?: [] + user?: User + require_colons?: boolean + managed?: boolean + animated?: boolean + available?: boolean + + get CustomEmoji () { + if (this.animated === false) { + return `<:${this.name}:${this.id}>` + } else return `` + } + + constructor (client: Client, data: EmojiPayload) { + super(client) + this.id = data.id + this.name = data.name + this.roles = data.roles + this.user = data.user + this.require_colons = data.require_colons + this.managed = data.managed + this.animated = data.animated + this.available = data.available + } +} diff --git a/src/structures/guild.ts b/src/structures/guild.ts index e69de29..bdac283 100644 --- a/src/structures/guild.ts +++ b/src/structures/guild.ts @@ -0,0 +1,106 @@ +import { Client } from '../models/client.ts' +import { EmojiPayload } from '../types/emojiTypes.ts' +import { GuildFeatures, GuildPayload } from '../types/guildTypes.ts' +import { PresenceUpdatePayload } from '../types/presenceTypes.ts' +import { VoiceStatePayload } from '../types/voiceTypes.ts' +import { Base } from './base.ts' +import { Channel } from './channel.ts' +import { Emoji } from './emoji.ts' +import { Member } from './member.ts' +import { Role } from './role.ts' + +export class Guild extends Base implements GuildPayload { + id: string + name: string + icon: string | undefined + icon_hash?: string | undefined + splash: string | undefined + discovery_splash: string | undefined + owner?: boolean | undefined + owner_id: string + permissions?: string | undefined + region: string + afk_channel_id: string | undefined + afk_timeout: number + widget_enabled?: boolean | undefined + widge_channel_id?: string | undefined + verification_level: string + default_message_notifications: string + explicit_content_filter: string + roles: Role[] + emojis: Emoji[] + features: GuildFeatures[] + mfa_level: string + application_id: string | undefined + system_channel_id: string | undefined + system_channel_flags: string + rules_channel_id: string | undefined + joined_at?: string | undefined + large?: boolean | undefined + unavailable: boolean + member_count?: number | undefined + voice_states?: VoiceStatePayload[] | undefined + members?: Member[] | undefined + channels?: Channel[] | undefined + presences?: PresenceUpdatePayload[] | undefined + max_presences?: number | undefined + max_members?: number | undefined + vanity_url_code: string | undefined + description: string | undefined + banner: string | undefined + premium_tier: number + premium_subscription_count?: number | undefined + preferred_locale: string + public_updates_channel_id: string | undefined + max_video_channel_users?: number | undefined + approximate_number_count?: number | undefined + approximate_presence_count?: number | undefined + + constructor (client: Client, data: GuildPayload) { + super(client) + this.id = data.id + this.name = data.name + this.icon = data.icon + this.icon_hash = data.icon_hash + this.splash = data.splash + this.discovery_splash = data.discovery_splash + this.owner = data.owner + this.owner_id = data.owner_id + this.permissions = data.permissions + this.region = data.region + this.afk_timeout = data.afk_timeout + this.afk_channel_id = data.afk_channel_id + this.widget_enabled = data.widget_enabled + this.widge_channel_id = data.widge_channel_id + this.verification_level = data.verification_level + this.default_message_notifications = data.default_message_notifications + this.explicit_content_filter = data.explicit_content_filter + this.roles = data.roles + this.emojis = data.emojis + this.features = data.features + this.mfa_level = data.mfa_level + this.system_channel_id = data.system_channel_id + this.system_channel_flags = data.system_channel_flags + this.rules_channel_id = data.rules_channel_id + this.joined_at = data.joined_at + this.large = data.large + this.unavailable = data.unavailable + this.member_count = data.member_count + this.voice_states = data.voice_states + this.members = data.members + this.channels = data.channels + this.presences = data.presences + this.max_presences = data.max_presences + this.max_members = data.max_members + this.vanity_url_code = data.vanity_url_code + this.description = data.description + this.banner = data.banner + this.premium_tier = data.premium_tier + this.premium_subscription_count = data.premium_subscription_count + this.preferred_locale = data.preferred_locale + this.public_updates_channel_id = data.public_updates_channel_id + this.max_video_channel_users = data.max_video_channel_users + this.approximate_number_count = data.approximate_number_count + this.approximate_presence_count = data.approximate_presence_count + } +} diff --git a/src/structures/guildChannel.ts b/src/structures/guildChannel.ts new file mode 100644 index 0000000..576dfb7 --- /dev/null +++ b/src/structures/guildChannel.ts @@ -0,0 +1,9 @@ +import { Client } from '../models/client.ts' +import { ChannelPayload } from '../types/channelTypes.ts' +import { Channel } from './channel.ts' + +export class GuildChannel extends Channel { + constructor (client: Client, data: ChannelPayload) { + super(client, data) + } +} diff --git a/src/structures/invite.ts b/src/structures/invite.ts new file mode 100644 index 0000000..723c2f5 --- /dev/null +++ b/src/structures/invite.ts @@ -0,0 +1,34 @@ +import { Client } from '../models/client.ts' +import { Channel } from '../structures/channel.ts' +import { Guild } from '../structures/guild.ts' +import { User } from '../structures/user.ts' +import { InvitePayload } from '../types/inviteTypes.ts' +import { Base } from './base.ts' + +export class Invite extends Base implements InvitePayload { + code: string + guild?: Guild + channel: Channel + inviter?: User + target_user?: User + target_user_type?: number + approximate_presence_count?: number + approximate_member_count?: number + + + get link () { + return `discord.gg/${this.code}` + } + + constructor (client: Client, data: InvitePayload) { + super(client) + this.code = data.code + this.guild = data.guild + this.channel = data.channel + this.inviter = data.inviter + this.target_user = data.target_user + this.target_user_type = data.target_user_type + this.approximate_member_count = data.approximate_member_count + this.approximate_presence_count = data.approximate_presence_count + } +} diff --git a/src/structures/member.ts b/src/structures/member.ts new file mode 100644 index 0000000..7208ea9 --- /dev/null +++ b/src/structures/member.ts @@ -0,0 +1,28 @@ +import { Client } from '../models/client.ts' +import { MemberPayload } from '../types/guildTypes.ts' +import { UserPayload } from '../types/userTypes.ts' +import { Base } from './base.ts' +import { Guild } from './guild.ts' +import { Role } from './role.ts' +import { User } from './user.ts' + +export class Member extends Base implements MemberPayload { + user: User + nick: string | undefined + roles: Role[] + joined_at: string + premium_since?: string | undefined + deaf: boolean + mute: boolean + + constructor (client: Client, data: MemberPayload) { + super(client) + this.user = data.user + this.nick = data.nick + this.roles = data.roles + this.joined_at = data.joined_at + this.premium_since = data.premium_since + this.deaf = data.deaf + this.mute = data.mute + } +} diff --git a/src/structures/message.ts b/src/structures/message.ts index 3c63375..a72acee 100644 --- a/src/structures/message.ts +++ b/src/structures/message.ts @@ -1 +1,69 @@ -// 여긴가 +import { Base } from './base.ts' +import { + Attachment, + ChannelMention, + EmbedPayload, + MessageActivity, + MessageApplication, + MessagePayload, + MessageReference, + Reaction +} from '../types/channelTypes.ts' +import { Client } from '../models/client.ts' +import { User } from './user.ts' +import { Role } from './role.ts' +import { Embed } from './embed.ts' + +class Message extends Base implements MessagePayload { + id: string + channel_id: string + guild_id?: string | undefined + author: User + member?: any + content: string + timestamp: string + edited_timestamp: string | undefined + tts: boolean + mention_everyone: boolean + mentions: User[] + mention_roles: Role[] + mention_channels?: ChannelMention[] | undefined + attachments: Attachment[] + embeds: EmbedPayload[] + reactions?: Reaction[] | undefined + nonce?: string | number | undefined + pinned: boolean + webhook_id?: string | undefined + type: number + activity?: MessageActivity + application?: MessageApplication + message_reference?: MessageReference + flags?: number | undefined + + constructor (client: Client, data: MessagePayload) { + super(client) + this.id = data.id + this.channel_id = data.channel_id + this.guild_id = data.guild_id + this.author = data.author + this.member = data.member + this.content = data.content + this.timestamp = data.timestamp + this.edited_timestamp = data.edited_timestamp + this.tts = data.tts + this.mention_everyone = data.mention_everyone + this.mentions = data.mentions + this.mention_roles = data.mention_roles + this.attachments = data.attachments + this.embeds = data.embeds + this.reactions = data.reactions + this.nonce = data.nonce + this.pinned = data.pinned + this.webhook_id = data.webhook_id + this.type = data.type + this.activity = data.activity + this.application = data.application + this.message_reference = data.message_reference + this.flags = data.flags + } +} diff --git a/src/structures/role.ts b/src/structures/role.ts new file mode 100644 index 0000000..a286f3d --- /dev/null +++ b/src/structures/role.ts @@ -0,0 +1,30 @@ +import { Client } from '../models/client.ts' +import { Base } from './base.ts' +import { RolePayload } from '../types/roleTypes.ts' + +export class Role extends Base implements RolePayload { + id: string + name: string + color: number + hoist: boolean + position: number + permissions: string + managed: boolean + mentionable: boolean + + get mention () { + return `<@&${this.id}>` + } + + constructor (client: Client, data: RolePayload) { + super(client) + this.id = data.id + this.name = data.name + this.color = data.color + this.hoist = data.hoist + this.position = data.position + this.permissions = data.permissions + this.managed = data.managed + this.mentionable = data.mentionable + } +} diff --git a/src/structures/textChannel.ts b/src/structures/textChannel.ts index e69de29..ce8af41 100644 --- a/src/structures/textChannel.ts +++ b/src/structures/textChannel.ts @@ -0,0 +1,36 @@ +import { Client } from '../models/client.ts' +import { GuildChannel } from './guildChannel.ts' +import { ChannelPayload } from '../types/channelTypes.ts' +import { User } from './user.ts' + +export class TextChannel extends GuildChannel implements ChannelPayload { + id: string + type: number + guild_id?: string | undefined + position?: number | undefined + approximate_member_count?: any + name?: string | undefined + topic?: string | undefined + nsfw?: boolean | undefined + last_message_id?: string | undefined + bitrate?: number | undefined + user_limit?: number | undefined + rate_limit_per_user?: number | undefined + recipients?: User + icon?: string | undefined + owner_id?: string | undefined + application_id?: string | undefined + parent_id?: string | undefined + last_pin_timestamp?: string | undefined + + + get mention () { + return `<#${this.id}>` + } + + constructor (client: Client, data: ChannelPayload) { + super(client, data) + this.id = data.id + this.type = data.type + } +} diff --git a/src/structures/user.ts b/src/structures/user.ts new file mode 100644 index 0000000..afb64ff --- /dev/null +++ b/src/structures/user.ts @@ -0,0 +1,44 @@ +import { Client } from '../models/client.ts' +import { UserPayload } from '../types/userTypes.ts' +import { Base } from './base.ts' + +export class User extends Base implements UserPayload { + id: string + username: string + discriminator: string + avatar?: string + bot?: boolean + system?: boolean + mfa_enabled?: boolean + locale?: string + verified?: boolean + email?: string + flags?: number + premium_type?: 0 | 1 | 2 + public_flags?: number + + get nickMention () { + return `<@!${this.id}>` + } + + get mention () { + return `<@${this.id}>` + } + + constructor (client: Client, data: UserPayload) { + super(client) + this.id = data.id + this.username = data.username + this.discriminator = data.discriminator + this.avatar = data.avatar + this.bot = data.bot + this.system = data.system + this.mfa_enabled = data.mfa_enabled + this.locale = data.locale + this.verified = data.verified + this.email = data.email + this.flags = data.flags + this.premium_type = data.premium_type + this.public_flags = data.public_flags + } +} diff --git a/src/structures/voicestate.ts b/src/structures/voicestate.ts new file mode 100644 index 0000000..369e438 --- /dev/null +++ b/src/structures/voicestate.ts @@ -0,0 +1,33 @@ +import { Client } from "../models/client.ts" +import { VoiceStatePayload } from "../types/voiceTypes.ts" +import { Base } from "./base.ts" +import { Member } from "./member.ts" + +export class VoiceState extends Base implements VoiceStatePayload { + guild_id?: string + channel_id: string | undefined + user_id: string + member?: Member + session_id: string + deaf: boolean + mute: boolean + self_deaf: boolean + self_mute: boolean + self_stream?: boolean + self_video: boolean + suppress: boolean + + constructor (client: Client, data: VoiceStatePayload) { + super(client) + this.channel_id = data.channel_id + this.session_id = data.session_id + this.user_id = data.user_id + this.deaf = data.deaf + this.mute = data.mute + this.self_deaf = data.self_deaf + this.self_mute = data.self_mute + this.self_stream = data.self_stream + this.self_video = data.self_video + this.suppress = data.suppress + } +} \ No newline at end of file diff --git a/src/structures/webhook.ts b/src/structures/webhook.ts new file mode 100644 index 0000000..aa9ab2c --- /dev/null +++ b/src/structures/webhook.ts @@ -0,0 +1,23 @@ +import { Client } from "../models/client.ts" +import { WebhookPayload } from "../types/webhookTypes.ts" +import { Base } from "./base.ts" +import { User } from "./user.ts" + +export class VoiceState extends Base implements WebhookPayload { + id: string + type: 1 | 2 + guild_id?: string + channel_id: string + user?: User + name: string | undefined + avatar: string | undefined + token?: string + application_id: string | undefined + + constructor (client: Client, data: WebhookPayload) { + super(client) + this.id = data.id + this.type = data.type + this.channel_id = data.channel_id + } +} \ No newline at end of file diff --git a/src/test/config.ts.sample b/src/test/config.ts.sample new file mode 100644 index 0000000..44a8029 --- /dev/null +++ b/src/test/config.ts.sample @@ -0,0 +1,2 @@ +const TOKEN = '' +export { TOKEN } diff --git a/src/test/index.ts b/src/test/index.ts new file mode 100644 index 0000000..a320599 --- /dev/null +++ b/src/test/index.ts @@ -0,0 +1,7 @@ +import { Client } from '../models/client.ts' +import { GatewayIntents } from '../types/gatewayTypes.ts' +import { TOKEN } from './config.ts' + +const bot = new Client() + +bot.connect(TOKEN, [GatewayIntents.GUILD_MESSAGES]) diff --git a/src/types/cdnTypes.ts b/src/types/cdnTypes.ts new file mode 100644 index 0000000..56e2be2 --- /dev/null +++ b/src/types/cdnTypes.ts @@ -0,0 +1,2 @@ +export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 +export type ImageFormats = "jpg" | "jpeg" | "png" | "webp" | "gif" \ No newline at end of file diff --git a/src/types/channelTypes.ts b/src/types/channelTypes.ts new file mode 100644 index 0000000..5b55656 --- /dev/null +++ b/src/types/channelTypes.ts @@ -0,0 +1,251 @@ +import { Member } from '../structures/member.ts' +import { Role } from '../structures/role.ts' +import { User } from '../structures/user.ts' +import { EmojiPayload } from './emojiTypes.ts' + +interface ChannelPayload { + id: string + type: ChannelTypes + guild_id?: string + position?: number + approximate_member_count?: Overwrite + name?: string + topic?: string + nsfw?: boolean + last_message_id?: string + bitrate?: number + user_limit?: number + rate_limit_per_user?: number + recipients?: User + icon?: string + owner_id?: string + application_id?: string + parent_id?: string + last_pin_timestamp?: string +} + +interface Overwrite { + id: string + type: number + allow: string + deny: string +} + +enum ChannelTypes { + GUILD_TEXT = 0, + DM = 1, + GUILD_VOICE = 2, + GROUP_DM = 3, + GUILD_CATEGORY = 4, + GUILD_NEWS = 5, + GUILD_STORE = 6 +} + +interface MessagePayload { + id: string + channel_id: string + guild_id?: string + author: User + member?: Member + content: string + timestamp: string + edited_timestamp: string | undefined + tts: boolean + mention_everyone: boolean + mentions: User[] + mention_roles: Role[] + mention_channels?: ChannelMention[] + attachments: Attachment[] + embeds: EmbedPayload[] + reactions?: Reaction[] + nonce?: number | string + pinned: boolean + webhook_id?: string + type: number + activity?: MessageActivity + application?: MessageApplication + message_reference?: MessageReference + flags?: number +} + +interface ChannelMention { + id: string + guild_id: string + type: ChannelTypes + name: string +} + +interface Attachment { + id: string + filename: string + size: number + url: string + proxy_url: string + height: number | undefined + width: number | undefined +} + +interface EmbedPayload { + title?: string + type?: EmbedTypes + description?: string + url?: string + timestamp?: string + color?: number + footer?: EmbedFooter + image?: EmbedImage + thumbnail?: EmbedThumbnail + video?: EmbedVideo + provider?: EmbedProvider + author?: EmbedAuthor + fields?: EmbedField[] +} + +type EmbedTypes = 'rich' | 'image' | 'video' | 'gifv' | 'article' | 'link' + +interface EmbedField { + name: string + value: string + inline?: boolean +} + +interface EmbedAuthor { + name?: string + url?: string + icon_url?: string + proxy_icon_url?: string +} + +interface EmbedFooter { + text: string + icon_url?: string + proxy_icon_url?: string +} + +interface EmbedImage { + url?: string + proxy_url?: string + height?: number + width?: number +} + +interface EmbedProvider { + name?: string + url?: string +} + +interface EmbedVideo { + url?: string + height?: number + width?: number +} + +interface EmbedThumbnail { + url?: string + proxy_url?: string + height?: number + width?: number +} + +interface Reaction { + count: number + me: boolean + emoji: EmojiPayload +} + +interface MessageActivity { + type: MessageTypes + party_id?: string +} + +interface MessageApplication { + id: string + cover_image?: string + desription: string + icon: string | undefined + name: string +} + +interface MessageReference { + message_id?: string + channel_id?: string + guild_id?: string +} + +enum MessageTypes { + DEFAULT = 0, + RECIPIENT_ADD = 1, + RECIPIENT_REMOVE = 2, + CALL = 3, + CHANNEL_NAME_CHANGE = 4, + CHANNEL_ICON_CHANGE = 5, + CHANNEL_PINNED_MESSAGE = 6, + GUILD_MEMBER_JOIN = 7, + USER_PREMIUM_GUILD_SUBSCRIPTION = 8, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, + CHANNEL_FOLLOW_ADD = 12, + GUILD_DISCOVERY_DISQUALIFIED = 14, + GUILD_DISCOVERY_REQUALIFIED = 15 +} + +enum MessageActivityTypes { + JOIN = 1, + SPECTATE = 2, + LISTEN = 3, + JOIN_REQUEST = 4 +} + +enum MessageFlags { + CROSSPOSTED = 1 << 0, + IS_CROSSPOST = 1 << 1, + SUPPRESS_EMBEDS = 1 << 2, + SOURCE_MESSAGE_DELETED = 1 << 3, + URGENT = 1 << 4 +} + +interface FollowedChannel { + channel_id: string + webhook_id: string +} + +interface Reaction { + count: number + me: boolean + emoji: EmojiPayload +} + +interface Overwrite { + id: string + type: number + allow: string + deny: string +} +interface ChannelMention { + id: string + guild_id: string + type: ChannelTypes + name: string +} + +export { + ChannelPayload, + ChannelTypes, + ChannelMention, + Attachment, + Reaction, + MessageActivity, + MessageApplication, + MessageReference, + MessagePayload, + EmbedPayload, + EmbedTypes, + EmbedFooter, + EmbedImage, + EmbedThumbnail, + EmbedVideo, + EmbedProvider, + EmbedAuthor, + EmbedField +} diff --git a/src/types/emojiTypes.ts b/src/types/emojiTypes.ts new file mode 100644 index 0000000..15a5128 --- /dev/null +++ b/src/types/emojiTypes.ts @@ -0,0 +1,12 @@ +import { User } from '../structures/user.ts' + +export interface EmojiPayload { + id: string + name: string + roles?: [] + user?: User + require_colons?: boolean + managed?: boolean + animated?: boolean + available?: boolean +} diff --git a/src/types/endpoint.ts b/src/types/endpoint.ts index d2d4ad2..c7c68b6 100644 --- a/src/types/endpoint.ts +++ b/src/types/endpoint.ts @@ -4,7 +4,7 @@ import { DISCORD_API_URL, DISCORD_API_VERSION, DISCORD_CDN_URL -} from '../consts/urlsAndVersions' +} from '../consts/urlsAndVersions.ts' //Guild Endpoints const GUILDS = `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds` @@ -16,10 +16,6 @@ const GUILD_WIDGET = (guildID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/widget` const GUILD_EMOJI = (guildID: string, emoji_id: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/emojis/${emoji_id}` -const GUILD_EMOJIS = (guildID: string) => - `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/emojis` -const GUILD_REGIONS = (guildID: string) => - `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/regions` const GUILD_ROLE = (guildID: string, roleID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/roles/${roleID}` const GUILD_ROLES = (guildID: string) => @@ -40,17 +36,24 @@ const GUILD_CHANNELS = (guildID: string, channelID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/channels` const GUILD_MEMBER = (guildID: string, memberID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members/${memberID}` +const GUILD_MEMBERS = (guildID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members` const GUILD_MEMBER_ROLE = (guildID: string, memberID: string, roleID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members/${memberID}/roles/${roleID}` -const GUILD_INVITES = (guildID: string) => +const GUILD_INVITES = (guildID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/invites` const GUILD_LEAVE = (guildID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/@me/guilds/${guildID}` const GUILD_PRUNE = (guildID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/prune` -const VANITY_URL = (guildID: string) => +const GUILD_VANITY_URL = (guildID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/vanity-url` - +const GUILD_NICK = (guildID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members/@me/nick` +const GUILD_WIDGET_IMAGE = (guildID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/widget.png` +const GUILD_PREVIEW = (guildID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/preview` //Channel Endpoints const CHANNEL = (channelID: string) => @@ -65,11 +68,24 @@ const CHANNEL_CROSSPOST = (channelID: string, messageID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/messages/${messageID}/crosspost` const MESSAGE_REACTIONS = (channelID: string, messageID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/messages/${messageID}/reactions` -const MESSAGE_REACTION = (channelID: string, messageID: string, emoji: string) => +const MESSAGE_REACTION = ( + channelID: string, + messageID: string, + emoji: string +) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/messages/${messageID}/reactions/${emoji}` -const MESSAGE_REACTION_ME = (channelID: string, messageID: string, emojiID: string) => +const MESSAGE_REACTION_ME = ( + channelID: string, + messageID: string, + emojiID: string +) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/messages/${messageID}/reactions/${emojiID}/@me` -const MESSAGE_REACTION_USER = (channelID: string, messageID: string, emojiID: string, userID: string) => +const MESSAGE_REACTION_USER = ( + channelID: string, + messageID: string, + emojiID: string, + userID: string +) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/messages/${messageID}/reactions/${emojiID}/${userID}` const CHANNEL_BULK_DELETE = (channelID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/messages/bulk-delete` @@ -77,8 +93,16 @@ const CHANNEL_FOLLOW = (channelID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/followers` const CHANNEL_INVITES = (channelID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/invites` -const CHANNEL_PINS = (channelID: string) => +const CHANNEL_PIN = (channelID: string, messageID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/pins/${messageID}` +const CHANNEL_PINS = (channelID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/pins` +const CHANNEL_PERMISSION = (channelID: string, overrideID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/permissions/${overrideID}` +const CHANNEL_TYPING = (channelID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/typing` +const GROUP_RECIPIENT = (channelID: string, userID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/recipient/${userID}` //User Endpoints const CURRENT_USER = `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/@me` @@ -90,8 +114,6 @@ const LEAVE_GUILD = (guildID: string) => const USER = (userID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/${userID}` - - //Webhook Endpoints const CHANNEL_WEBHOOKS = (channelID: string) => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}/webhooks` @@ -111,23 +133,48 @@ const GATEWAY = `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/gateway` const GATEWAY_BOT = `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/gateway/bot` //CDN Endpoints -const CUSTOM_EMOJI = (emojiID: string) => - `${DISCORD_CDN_URL}/emojis/${emojiID}.png` +const CUSTOM_EMOJI = (emojiID: string) => + `${DISCORD_CDN_URL}/emojis/${emojiID}` const GUILD_ICON = (guildID: string, iconID: number) => - `${DISCORD_CDN_URL}/icons/${guildID}/${iconID}.png` + `${DISCORD_CDN_URL}/icons/${guildID}/${iconID}` const GUILD_SPLASH = (guildID: string, guildSPLASH: string) => - `${DISCORD_CDN_URL}/splashes/${guildID}/${guildSPLASH}.png` -const GUILD_DISCOVERY_SPLASH = (guildID: string, guildDiscoverySplash: string) => - `${DISCORD_CDN_URL}/discovery-splashes/${guildID}/${guildDiscoverySplash}.png ` + `${DISCORD_CDN_URL}/splashes/${guildID}/${guildSPLASH}` +const GUILD_DISCOVERY_SPLASH = ( + guildID: string, + guildDiscoverySplash: string +) => + `${DISCORD_CDN_URL}/discovery-splashes/${guildID}/${guildDiscoverySplash}` const GUILD_BANNER = (guildID: string, guildBANNER: string) => - `${DISCORD_CDN_URL}/banners/${guildID}/${guildBANNER}.png` + `${DISCORD_CDN_URL}/banners/${guildID}/${guildBANNER}` const DEFAULT_USER_AVATAR = (iconID: string) => - `${DISCORD_CDN_URL}/embed/avatars/${iconID}.png` + `${DISCORD_CDN_URL}/embed/avatars/${iconID}` const USER_AVATAR = (userID: string, iconID: string) => - `${DISCORD_CDN_URL}/avatars/${userID}/${iconID}.png` + `${DISCORD_CDN_URL}/avatars/${userID}/${iconID}` const APPLICATION_ASSET = (applicationID: string, assetID: number) => - `${DISCORD_CDN_URL}/app-icons/${applicationID}/${assetID}.png` -const ACHIEVEMENT_ICON = (applicationID: string, achievementID: string, iconHASH: string) => - `${DISCORD_CDN_URL}/app-assets/${applicationID}/achievements/${achievementID}/icons/${iconHASH}.png` + `${DISCORD_CDN_URL}/app-icons/${applicationID}/${assetID}` +const ACHIEVEMENT_ICON = ( + applicationID: string, + achievementID: string, + iconHASH: string +) => + `${DISCORD_CDN_URL}/app-assets/${applicationID}/achievements/${achievementID}/icons/${iconHASH}` const TEAM_ICON = (teamID: string, iconID: string) => - `${DISCORD_CDN_URL}/team-icons/${teamID}/${iconID}.png` \ No newline at end of file + `${DISCORD_CDN_URL}/team-icons/${teamID}/${iconID}` + +//Emoji Endpoints +const EMOJI = (guildID: string, emojiID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/emojis/${emojiID}` +const EMOJIS = (guildID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/emojis` + +//Template Endpoint +const TEMPLATE = (templateCODE: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/templates/${templateCODE}` + +//Invite Endpoint +const INVITE = (inviteCODE: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/invites/${inviteCODE}` + +//Voice Endpoint +const VOICE_REGIONS = (guildID: string) => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/regions` diff --git a/src/types/gatewayResponse.ts b/src/types/gatewayResponse.ts index 2399f5f..a370ea7 100644 --- a/src/types/gatewayResponse.ts +++ b/src/types/gatewayResponse.ts @@ -1,4 +1,4 @@ -import { GatewayOpcodes } from '../types/gatewayTypes.ts' +import { GatewayOpcodes, GatewayEvents } from '../types/gatewayTypes.ts' /** * Gateway response from Discord. @@ -8,7 +8,7 @@ interface GatewayResponse { op: GatewayOpcodes d: any s?: number - t?: string + t?: GatewayEvents } export { GatewayResponse } diff --git a/src/types/gatewayTypes.ts b/src/types/gatewayTypes.ts index 3875d89..59782d5 100644 --- a/src/types/gatewayTypes.ts +++ b/src/types/gatewayTypes.ts @@ -1,4 +1,11 @@ // https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway +// https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events +import { Emoji } from '../structures/emoji.ts' +import { Member } from '../structures/member.ts' +import { Role } from '../structures/role.ts' +import { User } from '../structures/user.ts' +import { MemberPayload } from './guildTypes.ts' +import { ActivityPayload, PresenceUpdatePayload } from './presenceTypes.ts' /** * Gateway OPcodes from Discord docs. @@ -55,4 +62,339 @@ enum GatewayIntents { DIRECT_MESSAGE_TYPING = 1 << 13 } -export { GatewayCloseCodes, GatewayOpcodes, 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', + Channel_Pins_Update = 'CHANNEL_PIN_UPDATE', + Guild_Create = 'GUILD_CREATE', + Guild_Update = 'GUILD_UPDATE', + Guild_Delete = 'GUILD_DELETE', + Guild_Ban_Add = 'GUILD_BAN_ADD', + Guild_Ban_Remove = 'GUILD_BAN_REMOVE', + Guild_Emojis_Update = 'GUILD_EMOJIS_UPDATE', + Guild_Integrations_Update = 'GUILD_INTEGRATIONS_UPDATE', + Guild_Member_Add = 'GUILD_MEMBER_ADD', + Guild_Member_Remove = 'GUILD_MEMBER_REMOVE', + Guild_Member_Update = 'GUILD_MEMBER_UPDATE', + Guild_Members_Chunk = 'GUILD_MEMBERS_CHUNK', + Guild_Role_Create = 'GUILD_ROLE_CREATE', + Guild_Role_Update = 'GUILD_ROLE_UPDATE', + Guild_Role_Delete = 'GUILD_ROLE_DELETE', + Invite_Create = 'INVITE_CREATE', + Invite_Delete = 'INVITE_DELETE', + Message_Create = 'MESSAGE_CREATE', + Message_Update = 'MESSAGE_UPDATE', + Message_Delete = 'MESSAG_DELETE', + Message_Delete_Bulk = 'MESSAGE_DELETE_BULK', + Message_Reaction_Add = 'MESSAGE_REACTION_ADD', + Message_Reaction_Remove = 'MESSAGE_REACTION_REMOVE', + Message_Reaction_Remove_All = 'MESSAGE_REACTION_REMOVE_ALL', + Message_Reaction_Remove_Emoji = 'MESSAGE_REACTION_REMOVE_EMOJI', + 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' +} + +interface IdentityPayload { + token: string + properties: IdentityConnection + compress?: boolean + large_threshold?: number + shard?: number[] + presence?: UpdateStatus + guildSubscriptions?: boolean + intents: number +} + +enum UpdateStatus { + online = 'online', + dnd = 'dnd', + afk = 'idle', + invisible = 'invisible', + offline = 'offline' +} + +interface IdentityConnection { + $os: 'linux' + $browser: 'discord.deno' + $device: 'discord.deno' +} + +interface Resume { + token: string + session_id: string + seq: number +} + +interface GuildRequestMembers { + guild_id: string | string[] + query?: string + limit: number + presences?: boolean //do you have any problems? tell me! i am so handsome + user_ids?: string | string[] + nonce?: string +} + +interface GatewayVoiceStateUpdate { + guild_id: string + channel_id: string + self_mute: boolean + self_deaf: boolean +} + +interface GatewayStatusUpdate { + since: number | undefined + activities: ActivityPayload[] + status: string + afk: boolean +} + +interface Hello { + heartbeat_interval: number +} + +interface ReadyEvent { + v: number + user: User + privateChannels: [] + guilds: [] + session_id: string + shard?: number[] +} + +interface ChannelPinsUpdate { + guild_id?: string + channel_id: string + last_pin_timestamp?: string +} + +interface GuildBanAdd { + guild_id: string + user: User +} + +interface GuildBanRemove { + guild_id: string + user: User +} + +interface GuildEmojiUpdate { + guild_id: string + emojis: [] +} + +interface GuildIntegrationsUpdate { + guild_id: string +} + +interface GuildMemberAddExtra { + guild_id: string +} + +interface GuildMemberRemove { + guild_id: string + user: User +} +interface GuildMemberUpdate { + guild_id: string + roles: string[] + user: User + nick?: string | undefined + joined_at: string + premium_since?: string | undefined +} + +interface GuildMemberChunk { + guild_id: string + members: MemberPayload[] + chunk_index: number + chunk_count: number + not_found?: [] + presences?: PresenceUpdatePayload[] + nonce?: string +} + +interface GuildRoleCreate { + guild_id: string + role: Role +} + +interface GuildRoleUpdate { + guild_id: string + role: Role +} + +interface GuildRoleDelete { + guild_id: string + role_id: string +} + +interface InviteCreate { + channel_id: string + code: string + created_at: string + guild_id?: string + inviter?: User + max_age: number + max_uses: number + target_user?: User + target_user_type?: number + temporary: boolean + uses: number +} + +interface InviteDelete { + channel_id: string + guild_id?: string + code: string +} + +interface MessageDelete { + id: string + channel_id: string + guild_id?: string +} + +interface MessageDeleteBulk { + ids: string[] + channel_id: string + guild_id: string +} + +interface MessageReactionAdd { + user_id: string + channel_id: string + message_id: string + guild_id?: string + emoji: Emoji +} + +interface MessageReactionRemove { + user_id: string + channel_id: string + message_id: string + guild_id?: string + emoji: Emoji +} + +interface MessageReactionRemoveAll { + channel_id: string + guild_id?: string + message_id: string + emoji: Emoji +} + +interface MessageReactionRemove { + channel_id: string + guild_id?: string + message_id: string + emoji: Emoji +} + +interface PresenceUpdate { + user: User + guild_id: string + status: string + activities: ActivityPayload[] + client_status: UpdateStatus[] +} + +interface CilentStatus { + desktop?: string + moblie?: string + web?: string +} + +interface Activity { + name: string + type: number + url?: string | undefined + created_at: number + timestamps?: string + application_id: string + details?: string | undefined + state?: string | undefined + emoji?: Emoji | undefined + party?: ActivityParty + assets?: ActivityAssets + secrets?: ActivitySecrets + instance?: boolean + flags?: number +} + +enum ActivityTypes { + GAME = 0, + STREAMING = 1, + LISTENING = 2, + CUSTOM = 4, + COMPETING = 5 +} + +interface ActivityTimestamps { + start?: number + end?: number +} + +interface ActivityEmoji { + name: string + id?: string + animated?: boolean +} + +interface ActivityParty { + id?: string + size?: number[] +} + +interface ActivityAssets { + large_image?: string + large_text?: string + small_image?: string + small_text?: string +} + +interface ActivitySecrets { + join?: string + spectate?: string + match?: string +} + +enum ActivityFlags { + INSTANCE = 1 << 0, + JOIN = 1 << 1, + SPECTATE = 1 << 2, + JOIN_REQUEST = 1 << 3, + SYNC = 1 << 4, + PLAY = 1 << 5 +} + +interface TypeStart { + channel_id: string + guild_id?: string + user_id: string + timestamp: number + member?: Member +} + +interface VoiceServerUpdate { + token: string + guild_id: string + endpoint: string +} + +interface WebhooksUpdate { + guild_id: string + channel_id: string +} + +//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 new file mode 100644 index 0000000..31e01f6 --- /dev/null +++ b/src/types/guildTypes.ts @@ -0,0 +1,117 @@ +import { Channel } from '../structures/channel.ts' +import { Emoji } from '../structures/emoji.ts' +import { Member } from '../structures/member.ts' +import { Role } from '../structures/role.ts' +import { User } from '../structures/user.ts' +import { PresenceUpdatePayload } from './presenceTypes.ts' +import { VoiceStatePayload } from './voiceTypes.ts' + +interface GuildPayload { + id: string + name: string + icon: string | undefined + icon_hash?: string | undefined + splash: string | undefined + discovery_splash: string | undefined + owner?: boolean + owner_id: string + permissions?: string + region: string + afk_channel_id: string | undefined + afk_timeout: number + widget_enabled?: boolean + widge_channel_id?: string | undefined + verification_level: string + default_message_notifications: string + explicit_content_filter: string + roles: Role[] + emojis: Emoji[] + features: GuildFeatures[] + mfa_level: string + application_id: string | undefined + system_channel_id: string | undefined + system_channel_flags: string + rules_channel_id: string | undefined + joined_at?: string + large?: boolean + unavailable: boolean + member_count?: number + voice_states?: VoiceStatePayload[] + members?: Member[] + channels?: Channel[] + presences?: PresenceUpdatePayload[] + max_presences?: number | undefined + max_members?: number + vanity_url_code: string | undefined + description: string | undefined + banner: string | undefined + premium_tier: number + premium_subscription_count?: number + preferred_locale: string + public_updates_channel_id: string | undefined + max_video_channel_users?: number + approximate_number_count?: number + approximate_presence_count?: number +} + +interface MemberPayload { + user: User + nick: string | undefined + roles: Role[] + joined_at: string + premium_since?: string | undefined + deaf: boolean + mute: boolean +} + +enum MessageNotification { + ALL_MESSAGES = 0, + ONLY_MENTIONS = 1 +} + +enum ContentFilter { + DISABLED = 0, + MEMBERS_WITHOUT_ROLES = 1, + ALL_MEMBERS = 3 +} + +enum MFA { + NONE = 0, + ELEVATED = 1 +} + +enum Verification { + NONE = 0, + LOW = 1, + MEDIUM = 2, + HIGH = 3, + VERY_HIGH = 4 +} + +enum PremiumTier { + NONE = 0, + TIER_1 = 1, + TIER_2 = 2, + TIER_3 = 3 +} + +enum SystemChannelFlags { + SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0, + SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1 +} + +type GuildFeatures = + | 'INVITE_SPLASH' + | 'VIP_REGIONS' + | 'VANITY_URL' + | 'VERIFIED' + | 'PARTNERED' + | 'PUBLIC' + | 'COMMERCE' + | 'NEWS' + | 'DISCOVERABLE' + | 'FEATURABLE' + | 'ANIMATED_ICON' + | 'BANNER' + +export { MemberPayload, GuildPayload, GuildFeatures } diff --git a/src/types/inviteTypes.ts b/src/types/inviteTypes.ts new file mode 100644 index 0000000..c53aae5 --- /dev/null +++ b/src/types/inviteTypes.ts @@ -0,0 +1,14 @@ +import { Channel } from '../structures/channel.ts' +import { Guild } from '../structures/guild.ts' +import { User } from '../structures/user.ts' + +export interface InvitePayload { + code: string + guild?: Guild + channel: Channel + inviter?: User + target_user?: User + target_user_type?: number + approximate_presence_count?: number + approximate_member_count?: number +} diff --git a/src/types/permissionFlags.ts b/src/types/permissionFlags.ts index 47a6e96..83936a4 100644 --- a/src/types/permissionFlags.ts +++ b/src/types/permissionFlags.ts @@ -1,8 +1,4 @@ // https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags -// 귀하는 해갈 -// 그렇습니다 - -//이거 필요한건가 -와갈드- -UnderC- enum PermissionFlags { CREATE_INSTANT_INVITE = 0x00000001, diff --git a/src/types/presenceTypes.ts b/src/types/presenceTypes.ts new file mode 100644 index 0000000..a2aff4a --- /dev/null +++ b/src/types/presenceTypes.ts @@ -0,0 +1,72 @@ +import { User } from '../structures/user.ts' + +interface PresenceUpdatePayload { + user: User + guild_id: string + status: string + activities: ActivityPayload + client_status: ClientStatus +} + +interface ClientStatus { + desktop?: string + mobile?: string + web?: string +} + +interface ActivityPayload { + name: string + type: 0 | 1 | 2 | 3 | 4 | 5 + url?: string | undefined + created_at: number + timestamps?: ActivityTimestamps + application_id?: string + details?: string | undefined + state?: string | undefined + emoji?: ActivityEmoji + party?: ActivityParty + assets?: ActivityAssets + secrets?: ActivitySecrets + instance?: boolean + flags?: number +} + +interface ActivityTimestamps { + start?: number + end?: number +} + +interface ActivityEmoji { + name: string + id?: string + animated?: boolean +} + +interface ActivityParty { + id?: string + size?: number[] +} + +interface ActivityAssets { + large_image?: string + large_text?: string + small_image?: string + small_text?: string +} + +interface ActivitySecrets { + join?: string + spectate?: string + match?: string +} + +enum ActivityFlags { + INSTANCE = 1 << 0, + JOIN = 1 << 1, + SPECTATE = 1 << 2, + JOIN_REQUEST = 1 << 3, + SYNC = 1 << 4, + PLAY = 1 << 5 +} + +export { ActivityPayload, PresenceUpdatePayload } diff --git a/src/types/roleTypes.ts b/src/types/roleTypes.ts new file mode 100644 index 0000000..78f43d6 --- /dev/null +++ b/src/types/roleTypes.ts @@ -0,0 +1,10 @@ +export interface RolePayload { + id: string + name: string + color: number + hoist: boolean + position: number + permissions: string + managed: boolean + mentionable: boolean +} diff --git a/src/types/snowflake.ts b/src/types/snowflake.ts new file mode 100644 index 0000000..b9409b8 --- /dev/null +++ b/src/types/snowflake.ts @@ -0,0 +1,22 @@ +export class Snowflake { + id: string + constructor (id: string) { + this.id = id + } + + deconstruct () { + const snowflake = BigInt.asUintN(64, BigInt(this.id)) + const res = { + timestamp: ((snowflake << BigInt(22)) + BigInt(1420070400000)).toString(), + workerId: ((snowflake & BigInt(0x3e0000)) >> BigInt(17)).toString(), + processId: ((snowflake & BigInt(0x1f000)) >> BigInt(12)).toString(), + increment: (snowflake & BigInt(0xfff)).toString() + } + return res + } +} + +// BigInt라서 이걸 어케 할까 고심끝에 나온게 toString 읍 +// 엄... + +// deconstruct가 소멸자임? 색 봐서는 아닌거같은데 diff --git a/src/types/templateTypes.ts b/src/types/templateTypes.ts new file mode 100644 index 0000000..b303ef1 --- /dev/null +++ b/src/types/templateTypes.ts @@ -0,0 +1,16 @@ +import { Guild } from '../structures/guild.ts' +import { User } from '../structures/user.ts' + +export interface TemplatePayload { + code: string + name: string + description: string | undefined + usage_count: number + creator_id: string + creator: User + created_at: string + updated_at: string + source_guild_id: string + serialized_source_guild: Guild + is_dirty: boolean | undefined +} diff --git a/src/types/userTypes.ts b/src/types/userTypes.ts new file mode 100644 index 0000000..71fb878 --- /dev/null +++ b/src/types/userTypes.ts @@ -0,0 +1,15 @@ +export interface UserPayload { + id: string + username: string + discriminator: string + avatar?: string + bot?: boolean + system?: boolean + mfa_enabled?: boolean + locale?: string + verified?: boolean + email?: string + flags?: number + premium_type?: 0 | 1 | 2 + public_flags?: number +} diff --git a/src/types/voiceTypes.ts b/src/types/voiceTypes.ts index 7cff333..c098146 100644 --- a/src/types/voiceTypes.ts +++ b/src/types/voiceTypes.ts @@ -1,4 +1,5 @@ // https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice +import { Member } from '../structures/member.ts' enum VoiceOpcodes { // VoiceOpcodes 추가 - UnderC - IDENTIFY = 0, @@ -27,3 +28,18 @@ enum VoiceCloseCodes { VOICE_SERVER_CRASHED = 4015, UNKNOWN_ENCRYPTION_MODE = 4016 } + +export interface VoiceStatePayload { + guild_id?: string + channel_id: string | undefined + user_id: string + member?: Member + session_id: string + deaf: boolean + mute: boolean + self_deaf: boolean + self_mute: boolean + self_stream?: boolean + self_video: boolean + suppress: boolean +} diff --git a/src/types/webhookTypes.ts b/src/types/webhookTypes.ts new file mode 100644 index 0000000..b43c21f --- /dev/null +++ b/src/types/webhookTypes.ts @@ -0,0 +1,13 @@ +import { User } from '../structures/user.ts' + +export interface WebhookPayload { + id: string + type: 1 | 2 + guild_id?: string + channel_id: string + user?: User + name: string | undefined + avatar: string | undefined + token?: string + application_id: string | undefined +} diff --git a/tsconfig.json b/tsconfig.json index 4cbd33c..964ccf7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,15 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ESNEXT", + "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ + "lib": [ + "esnext", + ], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ @@ -25,7 +26,6 @@ // "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'). */ - /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ @@ -36,13 +36,11 @@ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - /* Module Resolution Options */ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ @@ -56,17 +54,14 @@ /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "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. */ - /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */