Merge branch 'main' into main
This commit is contained in:
		
						commit
						2af8e15452
					
				
					 40 changed files with 813 additions and 1080 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -110,3 +110,6 @@ yarn.lock | |||
| # PRIVACY XDDDD | ||||
| src/test/config.ts | ||||
| .vscode | ||||
| 
 | ||||
| # macOS is shit xD | ||||
| **/.DS_Store | ||||
|  |  | |||
							
								
								
									
										33
									
								
								CONTRIBUTING.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								CONTRIBUTING.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| # Welcome! | ||||
| 
 | ||||
| This document is for people who want to contribute to this repository! | ||||
| 
 | ||||
| ## Code Style | ||||
| 
 | ||||
| We use [standard.js](https://standardjs.org) with [eslint](https://eslint.org) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint). | ||||
| So please don't make as lint errors as possible. There're many rules in standard.js but the highlight things are: | ||||
| 
 | ||||
| - Use `camelCase` for function names, variables, etc. | ||||
| - Use `PascalCase` for class names. | ||||
| - Add return types on function. Ex: | ||||
| 
 | ||||
| ```ts | ||||
| const example = (): void => {} | ||||
| ``` | ||||
| 
 | ||||
| - Do not make unused variables or unused imports. | ||||
| 
 | ||||
| These are not on standard.js but we want you to follow. | ||||
| 
 | ||||
| - Make names to simple but understandable for someone whose English is not a primary language. | ||||
| 
 | ||||
| ## File Name Style | ||||
| 
 | ||||
| Nothing much, but please make it as simple as possible, and in `camelCase`. | ||||
| 
 | ||||
| ## Submitting PR | ||||
| 
 | ||||
| When submitting PR, please make the title as simple as possible. Ex: `[Feature improvement]: Cache can now be loaded much faster` | ||||
| Also, please make it understandable for someone whose English is not a primary language. | ||||
| 
 | ||||
| Thanks! | ||||
							
								
								
									
										53
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										53
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,3 +1,52 @@ | |||
| # discord.deno | ||||
| # discord-deno | ||||
| 
 | ||||
| ## Feature | ||||
|  | ||||
| 
 | ||||
| [](https://github.com/RichardLitt/standard-readme) | ||||
| 
 | ||||
| Discord Deno API that is easy to use | ||||
| 
 | ||||
| ## Table of Contents | ||||
| 
 | ||||
| - [Usage](#usage) | ||||
| - [Docs](#docs) | ||||
| - [Maintainers](#maintainers) | ||||
| - [Contributing](#contributing) | ||||
| - [License](#license) | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| ```ts | ||||
| import { Client } from 'https://deno.land/x/discord-deno/models/client.ts' | ||||
| import { Message } from 'https://deno.land/x/discord-deno/structures/message.ts' | ||||
| 
 | ||||
| const bot = new Client() | ||||
| 
 | ||||
| bot.on('messageCreate', (msg: Message): void => { | ||||
|   if (msg.content === '!ping') { | ||||
|     msg.channel.send(`Pong! ping: ${bot.ping}`) | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| bot.connect(TOKEN, [GatewayIntents.GUILD_MESSAGES]) | ||||
| ``` | ||||
| 
 | ||||
| ## Docs | ||||
| 
 | ||||
| Not made yet | ||||
| 
 | ||||
| ## Maintainers | ||||
| 
 | ||||
| [@Helloyunho](https://github.com/Helloyunho) | ||||
| 
 | ||||
| ## Contributing | ||||
| 
 | ||||
| See [the contributing file](CONTRIBUTING.md)! | ||||
| 
 | ||||
| PRs accepted. | ||||
| 
 | ||||
| Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| MIT © 2020 Helloyunho | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| import getChannelByType from '../../utils/getChannelByType.ts' | ||||
| import { ChannelPayload } from '../../types/channel.ts' | ||||
| 
 | ||||
| export const channelCreate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: any | ||||
|   d: ChannelPayload | ||||
| ) => { | ||||
|   const channel = getChannelByType(gateway.client, d) | ||||
|   if (channel !== undefined) { | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| import { Channel } from '../../structures/channel.ts' | ||||
| import { ChannelPayload } from '../../types/channel.ts' | ||||
| 
 | ||||
| export const channelDelete: GatewayEventHandler = async( | ||||
| export const channelDelete: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: any | ||||
|   d: ChannelPayload | ||||
| ) => { | ||||
|   const channel: Channel | void = await gateway.client.channels.get(d.id) | ||||
|   if (channel !== undefined) { | ||||
|  |  | |||
|  | @ -1,19 +1,21 @@ | |||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| import cache from '../../models/cache.ts' | ||||
| import { TextChannel } from '../../structures/textChannel.ts' | ||||
| import { ChannelPayload } from "../../types/channel.ts" | ||||
| import { ChannelPinsUpdatePayload } from '../../types/gateway.ts' | ||||
| 
 | ||||
| export const channelPinsUpdate: GatewayEventHandler = async( | ||||
| export const channelPinsUpdate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: any | ||||
|   d: ChannelPinsUpdatePayload | ||||
| ) => { | ||||
|   const after: TextChannel | void = await gateway.client.channels.get<TextChannel>(d.channel_id) | ||||
|   if (after !== undefined) { | ||||
|     const before = after.refreshFromData({ | ||||
|       last_pin_timestamp: d.last_pin_timestamp | ||||
|     }) | ||||
|     const raw = await gateway.client.channels._get(d.channel_id) ; | ||||
|     await gateway.client.channels.set(after.id, Object.assign(raw, { last_pin_timestamp: d.last_pin_timestamp })) | ||||
|     const raw = await gateway.client.channels._get(d.channel_id) | ||||
|     await gateway.client.channels.set( | ||||
|       after.id, | ||||
|       Object.assign(raw, { last_pin_timestamp: d.last_pin_timestamp }) | ||||
|     ) | ||||
|     gateway.client.emit('channelPinsUpdate', before, after) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,11 +1,12 @@ | |||
| import { Channel } from '../../structures/channel.ts' | ||||
| import { Guild } from "../../structures/guild.ts" | ||||
| import { ChannelPayload } from '../../types/channel.ts' | ||||
| import getChannelByType from '../../utils/getChannelByType.ts' | ||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| 
 | ||||
| export const channelUpdate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: any | ||||
|   d: ChannelPayload | ||||
| ) => { | ||||
|   const oldChannel: Channel | void = await gateway.client.channels.get(d.id) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,14 +1,19 @@ | |||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| import cache from '../../models/cache.ts' | ||||
| import { Guild } from '../../structures/guild.ts' | ||||
| import { User } from '../../structures/user.ts' | ||||
| import { GuildBanAddPayload } from '../../types/gateway.ts' | ||||
| 
 | ||||
| export const guildBanAdd: GatewayEventHandler = (gateway: Gateway, d: any) => { | ||||
|   const guild: Guild = cache.get('guild', d.guild_id) | ||||
| export const guildBanAdd: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: GuildBanAddPayload | ||||
| ) => { | ||||
|   const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) | ||||
|   const user: User = | ||||
|     cache.get('user', d.user.id) ?? new User(gateway.client, d.user) | ||||
|     (await gateway.client.users.get(d.user.id)) ?? | ||||
|     new User(gateway.client, d.user) | ||||
| 
 | ||||
|   if (guild !== undefined) { | ||||
|     guild.members = guild.members?.filter(member => member.id !== d.user.id) | ||||
|     gateway.client.emit('guildBanAdd', guild, user) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -2,10 +2,11 @@ import { Gateway, GatewayEventHandler } from '../index.ts' | |||
| import cache from '../../models/cache.ts' | ||||
| import { Guild } from '../../structures/guild.ts' | ||||
| import { User } from '../../structures/user.ts' | ||||
| import { GuildBanRemovePayload } from '../../types/gateway.ts' | ||||
| 
 | ||||
| export const guildBanRemove: GatewayEventHandler = ( | ||||
|   gateway: Gateway, | ||||
|   d: any | ||||
|   d: GuildBanRemovePayload | ||||
| ) => { | ||||
|   const guild: Guild = cache.get('guild', d.guild_id) | ||||
|   const user: User = | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import { RolePayload } from "../../types/role.ts" | |||
| import { RolesManager } from "../../managers/RolesManager.ts" | ||||
| 
 | ||||
| export const guildCreate: GatewayEventHandler = async(gateway: Gateway, d: GuildPayload) => { | ||||
|   let guild: Guild | void = await gateway.client.guilds.get(d.id) | ||||
|   let guild: Guild | undefined = await gateway.client.guilds.get(d.id) | ||||
|   if (guild !== undefined) { | ||||
|     // It was just lazy load, so we don't fire the event as its gonna fire for every guild bot is in
 | ||||
|     await gateway.client.guilds.set(d.id, d) | ||||
|  | @ -47,6 +47,7 @@ export const guildCreate: GatewayEventHandler = async(gateway: Gateway, d: Guild | |||
|       guild.roles = roles | ||||
|     } | ||||
|     await guild.roles.fromPayload(d.roles) | ||||
|     guild = new Guild(gateway.client, d) | ||||
|     gateway.client.emit('guildCreate', guild) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,12 @@ | |||
| import { Guild } from '../../structures/guild.ts' | ||||
| import { GuildPayload } from '../../types/guild.ts' | ||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| 
 | ||||
| export const guildDelte: GatewayEventHandler = async (gateway: Gateway, d: any) => { | ||||
|   const guild: Guild | void = await gateway.client.guilds.get(d.id) | ||||
| export const guildDelte: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: GuildPayload | ||||
| ) => { | ||||
|   const guild: Guild | undefined = await gateway.client.guilds.get(d.id) | ||||
| 
 | ||||
|   if (guild !== undefined) { | ||||
|     guild.refreshFromData(d) | ||||
|  |  | |||
							
								
								
									
										14
									
								
								src/gateway/handlers/guildEmojiUpdate.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/gateway/handlers/guildEmojiUpdate.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| import cache from '../../models/cache.ts' | ||||
| import { Guild } from '../../structures/guild.ts' | ||||
| import { GuildEmojiUpdatePayload } from '../../types/gatewayTypes.ts' | ||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| 
 | ||||
| const guildEmojiUpdate: GatewayEventHandler = ( | ||||
|   gateway: Gateway, | ||||
|   d: GuildEmojiUpdatePayload | ||||
| ) => { | ||||
|   const guild: Guild = cache.get('guild', d.guild_id) | ||||
|   if (guild !== undefined) { | ||||
|     const emojis = guild.emojis | ||||
|   } | ||||
| } | ||||
|  | @ -1,10 +1,14 @@ | |||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| import { Guild } from '../../structures/guild.ts' | ||||
| import { GuildPayload } from '../../types/guild.ts' | ||||
| 
 | ||||
| export const guildUpdate: GatewayEventHandler = async(gateway: Gateway, d: any) => { | ||||
|   const before: Guild | void = await gateway.client.guilds.get(d.id) | ||||
|   if(!before) return | ||||
| export const guildUpdate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: GuildPayload | ||||
| ) => { | ||||
|   const after: Guild | undefined = await gateway.client.guilds.get(d.id) | ||||
|   if (after === undefined) return | ||||
|   const before = after.refreshFromData(d) | ||||
|   await gateway.client.guilds.set(d.id, d) | ||||
|   const after: Guild | void = await gateway.client.guilds.get(d.id) | ||||
|   gateway.client.emit('guildUpdate', before, after) | ||||
| } | ||||
|  |  | |||
|  | @ -1,19 +1,20 @@ | |||
| import { Channel } from "../../structures/channel.ts" | ||||
| import { Message } from "../../structures/message.ts" | ||||
| import { MessageMentions } from "../../structures/MessageMentions.ts" | ||||
| import { TextChannel } from "../../structures/textChannel.ts" | ||||
| import { User } from "../../structures/user.ts" | ||||
| import { MessagePayload } from "../../types/channel.ts" | ||||
| import { Channel } from '../../structures/channel.ts' | ||||
| import { Message } from '../../structures/message.ts' | ||||
| import { MessageMentions } from '../../structures/MessageMentions.ts' | ||||
| import { TextChannel } from '../../structures/textChannel.ts' | ||||
| import { User } from '../../structures/user.ts' | ||||
| import { MessagePayload } from '../../types/channel.ts' | ||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| 
 | ||||
| export const messageCreate: GatewayEventHandler = async( | ||||
| export const messageCreate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: MessagePayload | ||||
| ) => { | ||||
|   let channel = await gateway.client.channels.get<TextChannel>(d.channel_id) | ||||
|   // Fetch the channel if not cached
 | ||||
|   if(!channel) channel = (await gateway.client.channels.fetch(d.channel_id) as any) as TextChannel | ||||
|   let user = new User(gateway.client, d.author) | ||||
|   if (channel === undefined) | ||||
|     channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel | ||||
|   const user = new User(gateway.client, d.author) | ||||
|   await gateway.client.users.set(d.author.id, d.author) | ||||
|   let guild | ||||
|   if(d.guild_id) { | ||||
|  |  | |||
|  | @ -5,7 +5,11 @@ import { | |||
|   DISCORD_API_VERSION | ||||
| } from '../consts/urlsAndVersions.ts' | ||||
| import { GatewayResponse } from '../types/gatewayResponse.ts' | ||||
| import { GatewayOpcodes, GatewayIntents, GatewayCloseCodes } from '../types/gateway.ts' | ||||
| import { | ||||
|   GatewayOpcodes, | ||||
|   GatewayIntents, | ||||
|   GatewayCloseCodes | ||||
| } from '../types/gateway.ts' | ||||
| import { gatewayHandlers } from './handlers/index.ts' | ||||
| import { GATEWAY_BOT } from '../types/endpoint.ts' | ||||
| import { GatewayBotPayload } from "../types/gatewayBot.ts" | ||||
|  | @ -52,7 +56,7 @@ class Gateway { | |||
| 
 | ||||
|   private onopen(): void { | ||||
|     this.connected = true | ||||
|     this.debug("Connected to Gateway!") | ||||
|     this.debug('Connected to Gateway!') | ||||
|   } | ||||
| 
 | ||||
|   private async onmessage(event: MessageEvent): Promise<void> { | ||||
|  | @ -70,12 +74,15 @@ class Gateway { | |||
|     switch (op) { | ||||
|       case GatewayOpcodes.HELLO: | ||||
|         this.heartbeatInterval = d.heartbeat_interval | ||||
|         this.debug(`Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}`) | ||||
|         this.debug( | ||||
|           `Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}` | ||||
|         ) | ||||
|         this.heartbeatIntervalID = setInterval(() => { | ||||
|           if (this.heartbeatServerResponded) { | ||||
|             this.heartbeatServerResponded = false | ||||
|           } else { | ||||
|             clearInterval(this.heartbeatIntervalID) | ||||
|             // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|             this.reconnect() | ||||
|             return | ||||
|           } | ||||
|  | @ -91,7 +98,8 @@ class Gateway { | |||
|           this.sendIdentify(this.client.forceNewSession) | ||||
|           this.initialized = true | ||||
|         } else { | ||||
|           console.log("Calling Resume") | ||||
|           console.log('Calling Resume') | ||||
|           // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|           this.sendResume() | ||||
|         } | ||||
|         break | ||||
|  | @ -99,13 +107,16 @@ class Gateway { | |||
|       case GatewayOpcodes.HEARTBEAT_ACK: | ||||
|         this.heartbeatServerResponded = true | ||||
|         this.client.ping = Date.now() - this.lastPingTimestamp | ||||
|         this.debug(`Received Heartbeat Ack. Ping Recognized: ${this.client.ping}ms`) | ||||
|         this.debug( | ||||
|           `Received Heartbeat Ack. Ping Recognized: ${this.client.ping}ms` | ||||
|         ) | ||||
|         break | ||||
| 
 | ||||
|       case GatewayOpcodes.INVALID_SESSION: | ||||
|         // Because we know this gonna be bool
 | ||||
|         this.debug(`Invalid Session! Identifying with forced new session`) | ||||
|         // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
 | ||||
|         // eslint-disable-next-line @typescript-eslint/promise-function-async
 | ||||
|         setTimeout(() => this.sendIdentify(true), 3000) | ||||
|         break | ||||
| 
 | ||||
|  | @ -113,7 +124,7 @@ class Gateway { | |||
|         this.heartbeatServerResponded = true | ||||
|         if (s !== null) { | ||||
|           this.sequenceID = s | ||||
|           await this.cache.set("seq", s) | ||||
|           await this.cache.set('seq', s) | ||||
|         } | ||||
|         if (t !== null && t !== undefined) { | ||||
|           const handler = gatewayHandlers[t] | ||||
|  | @ -128,11 +139,12 @@ class Gateway { | |||
|         // this.token = d.token
 | ||||
|         this.sessionID = d.session_id | ||||
|         this.sequenceID = d.seq | ||||
|         await this.cache.set("seq", d.seq) | ||||
|         await this.cache.set("session_id", this.sessionID) | ||||
|         await this.cache.set('seq', d.seq) | ||||
|         await this.cache.set('session_id', this.sessionID) | ||||
|         break | ||||
|       } | ||||
|       case GatewayOpcodes.RECONNECT: { | ||||
|         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|         this.reconnect() | ||||
|         break | ||||
|       } | ||||
|  | @ -141,41 +153,46 @@ class Gateway { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private onclose(event: CloseEvent): void { | ||||
|     this.debug("Connection Closed with code: " + event.code) | ||||
|   private onclose (event: CloseEvent): void { | ||||
|     this.debug(`Connection Closed with code: ${event.code}`) | ||||
| 
 | ||||
|     if (event.code == GatewayCloseCodes.UNKNOWN_ERROR) { | ||||
|       this.debug("API has encountered Unknown Error. Reconnecting...") | ||||
|     if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) { | ||||
|       this.debug('API has encountered Unknown Error. Reconnecting...') | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.reconnect() | ||||
|     } else if (event.code == GatewayCloseCodes.UNKNOWN_OPCODE) { | ||||
|     } else if (event.code === GatewayCloseCodes.UNKNOWN_OPCODE) { | ||||
|       throw new Error("Unknown OP Code was sent. This shouldn't happen!") | ||||
|     } else if (event.code == GatewayCloseCodes.DECODE_ERROR) { | ||||
|     } else if (event.code === GatewayCloseCodes.DECODE_ERROR) { | ||||
|       throw new Error("Invalid Payload was sent. This shouldn't happen!") | ||||
|     } else if (event.code == GatewayCloseCodes.NOT_AUTHENTICATED) { | ||||
|       throw new Error("Not Authorized: Payload was sent before Identifying.") | ||||
|     } else if (event.code == GatewayCloseCodes.AUTHENTICATION_FAILED) { | ||||
|       throw new Error("Invalid Token provided!") | ||||
|     } else if (event.code == GatewayCloseCodes.INVALID_SEQ) { | ||||
|       this.debug("Invalid Seq was sent. Reconnecting.") | ||||
|     } else if (event.code === GatewayCloseCodes.NOT_AUTHENTICATED) { | ||||
|       throw new Error('Not Authorized: Payload was sent before Identifying.') | ||||
|     } else if (event.code === GatewayCloseCodes.AUTHENTICATION_FAILED) { | ||||
|       throw new Error('Invalid Token provided!') | ||||
|     } else if (event.code === GatewayCloseCodes.INVALID_SEQ) { | ||||
|       this.debug('Invalid Seq was sent. Reconnecting.') | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.reconnect() | ||||
|     } else if (event.code == GatewayCloseCodes.RATE_LIMITED) { | ||||
|     } else if (event.code === GatewayCloseCodes.RATE_LIMITED) { | ||||
|       throw new Error("You're ratelimited. Calm down.") | ||||
|     } else if (event.code == GatewayCloseCodes.SESSION_TIMED_OUT) { | ||||
|       this.debug("Session Timeout. Reconnecting.") | ||||
|     } else if (event.code === GatewayCloseCodes.SESSION_TIMED_OUT) { | ||||
|       this.debug('Session Timeout. Reconnecting.') | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.reconnect(true) | ||||
|     } else if (event.code == GatewayCloseCodes.INVALID_SHARD) { | ||||
|       this.debug("Invalid Shard was sent. Reconnecting.") | ||||
|     } else if (event.code === GatewayCloseCodes.INVALID_SHARD) { | ||||
|       this.debug('Invalid Shard was sent. Reconnecting.') | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.reconnect() | ||||
|     } else if (event.code == GatewayCloseCodes.SHARDING_REQUIRED) { | ||||
|     } else if (event.code === GatewayCloseCodes.SHARDING_REQUIRED) { | ||||
|       throw new Error("Couldn't connect. Sharding is requried!") | ||||
|     } else if (event.code == GatewayCloseCodes.INVALID_API_VERSION) { | ||||
|     } else if (event.code === GatewayCloseCodes.INVALID_API_VERSION) { | ||||
|       throw new Error("Invalid API Version was used. This shouldn't happen!") | ||||
|     } else if (event.code == GatewayCloseCodes.INVALID_INTENTS) { | ||||
|       throw new Error("Invalid Intents") | ||||
|     } else if (event.code == GatewayCloseCodes.DISALLOWED_INTENTS) { | ||||
|     } else if (event.code === GatewayCloseCodes.INVALID_INTENTS) { | ||||
|       throw new Error('Invalid Intents') | ||||
|     } else if (event.code === GatewayCloseCodes.DISALLOWED_INTENTS) { | ||||
|       throw new Error("Given Intents aren't allowed") | ||||
|     } else { | ||||
|       this.debug("Unknown Close code, probably connection error. Reconnecting.") | ||||
|       this.debug('Unknown Close code, probably connection error. Reconnecting.') | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.reconnect() | ||||
|     } | ||||
|   } | ||||
|  | @ -185,20 +202,25 @@ class Gateway { | |||
|     console.log(eventError) | ||||
|   } | ||||
| 
 | ||||
|   private async sendIdentify(forceNewSession?: boolean) { | ||||
|     this.debug("Fetching /gateway/bot...") | ||||
|     const info = await this.client.rest.get(GATEWAY_BOT()) as GatewayBotPayload | ||||
|     if (info.session_start_limit.remaining == 0) throw new Error("Session Limit Reached. Retry After " + info.session_start_limit.reset_after + "ms") | ||||
|     this.debug("Recommended Shards: " + info.shards) | ||||
|     this.debug("=== Session Limit Info ===") | ||||
|     this.debug(`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`) | ||||
|   private async sendIdentify (forceNewSession?: boolean): Promise<void> { | ||||
|     this.debug('Fetching /gateway/bot...') | ||||
|     const info = await this.client.rest.get(GATEWAY_BOT()) | ||||
|     if (info.session_start_limit.remaining === 0) | ||||
|       throw new Error( | ||||
|         `Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms` | ||||
|       ) | ||||
|     this.debug(`Recommended Shards: ${info.shards}`) | ||||
|     this.debug('=== Session Limit Info ===') | ||||
|     this.debug( | ||||
|       `Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}` | ||||
|     ) | ||||
|     this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`) | ||||
|     if (!forceNewSession) { | ||||
|       let sessionIDCached = await this.cache.get("session_id") | ||||
|       if (sessionIDCached) { | ||||
|         this.debug("Found Cached SessionID: " + sessionIDCached) | ||||
|     if (forceNewSession === undefined || !forceNewSession) { | ||||
|       const sessionIDCached = await this.cache.get('session_id') | ||||
|       if (sessionIDCached !== undefined) { | ||||
|         this.debug(`Found Cached SessionID: ${sessionIDCached}`) | ||||
|         this.sessionID = sessionIDCached | ||||
|         return this.sendResume() | ||||
|         return await this.sendResume() | ||||
|       } | ||||
|     } | ||||
|     this.send({ | ||||
|  | @ -224,27 +246,29 @@ class Gateway { | |||
|   private async sendResume(): Promise<void> { | ||||
|     this.debug(`Preparing to resume with Session: ${this.sessionID}`) | ||||
|     if (this.sequenceID === undefined) { | ||||
|       let cached = await this.cache.get("seq") | ||||
|       if (cached) this.sequenceID = typeof cached == "string" ? parseInt(cached) : cached | ||||
|       const cached = await this.cache.get('seq') | ||||
|       if (cached !== undefined) | ||||
|         this.sequenceID = typeof cached === 'string' ? parseInt(cached) : cached | ||||
|     } | ||||
|     const resumePayload = { | ||||
|       op: GatewayOpcodes.RESUME, | ||||
|       d: { | ||||
|         token: this.token, | ||||
|         session_id: this.sessionID, | ||||
|         seq: this.sequenceID || null | ||||
|         seq: this.sequenceID ?? null | ||||
|       } | ||||
|     } | ||||
|     this.send(resumePayload) | ||||
|   } | ||||
| 
 | ||||
|   debug(msg: string) { | ||||
|     this.client.debug("Gateway", msg) | ||||
|   debug (msg: string): void { | ||||
|     this.client.debug('Gateway', msg) | ||||
|   } | ||||
| 
 | ||||
|   async reconnect(forceNew?: boolean) { | ||||
|   async reconnect (forceNew?: boolean): Promise<void> { | ||||
|     clearInterval(this.heartbeatIntervalID) | ||||
|     if (forceNew) await this.cache.delete("session_id") | ||||
|     if (forceNew === undefined || !forceNew) | ||||
|       await this.cache.delete('session_id') | ||||
|     this.close() | ||||
|     this.initWebsocket() | ||||
|   } | ||||
|  |  | |||
|  | @ -4,29 +4,29 @@ import { Collection } from "../utils/collection.ts"; | |||
| export class BaseManager<T, T2> { | ||||
|   client: Client | ||||
|   cacheName: string | ||||
|   dataType: any | ||||
|   DataType: any | ||||
| 
 | ||||
|   constructor(client: Client, cacheName: string, dataType: any) { | ||||
|   constructor (client: Client, cacheName: string, DataType: any) { | ||||
|     this.client = client | ||||
|     this.cacheName = cacheName | ||||
|     this.dataType = dataType | ||||
|     this.DataType = DataType | ||||
|   } | ||||
| 
 | ||||
|   _get(key: string): Promise<T> { | ||||
|     return this.client.cache.get(this.cacheName, key) as Promise<T> | ||||
|   async _get (key: string): Promise<T | undefined> { | ||||
|     return this.client.cache.get(this.cacheName, key) | ||||
|   } | ||||
| 
 | ||||
|   async get(key: string): Promise<T2 | void> { | ||||
|   async get (key: string): Promise<T2 | undefined> { | ||||
|     const raw = await this._get(key) | ||||
|     if(!raw) return | ||||
|     return new this.dataType(this.client, raw) as any | ||||
|     if (raw === undefined) return | ||||
|     return new this.DataType(this.client, raw) | ||||
|   } | ||||
| 
 | ||||
|   set(key: string, value: T) { | ||||
|   async set (key: string, value: T): Promise<any> { | ||||
|     return this.client.cache.set(this.cacheName, key, value) | ||||
|   } | ||||
| 
 | ||||
|   delete(key: string) { | ||||
|   async delete (key: string): Promise<boolean> { | ||||
|     return this.client.cache.delete(this.cacheName, key) | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,20 +1,23 @@ | |||
| import { Client } from "../models/client.ts"; | ||||
| import { Emoji } from "../structures/emoji.ts"; | ||||
| import { EmojiPayload } from "../types/emoji.ts"; | ||||
| import { CHANNEL } from "../types/endpoint.ts"; | ||||
| import { BaseManager } from "./BaseManager.ts"; | ||||
| import { Client } from '../models/client.ts' | ||||
| import { Emoji } from '../structures/emoji.ts' | ||||
| import { EmojiPayload } from '../types/emoji.ts' | ||||
| import { CHANNEL } from '../types/endpoint.ts' | ||||
| import { BaseManager } from './BaseManager.ts' | ||||
| 
 | ||||
| export class EmojisManager extends BaseManager<EmojiPayload, Emoji> { | ||||
|   constructor(client: Client) { | ||||
|     super(client, "emojis", Emoji) | ||||
|   constructor (client: Client) { | ||||
|     super(client, 'emojis', Emoji) | ||||
|   } | ||||
| 
 | ||||
|   fetch(id: string) { | ||||
|     return new Promise((res, rej) => { | ||||
|       this.client.rest.get(CHANNEL(id)).then(data => { | ||||
|   async fetch (id: string): Promise<Emoji> { | ||||
|     return await new Promise((resolve, reject) => { | ||||
|       this.client.rest | ||||
|         .get(CHANNEL(id)) | ||||
|         .then(data => { | ||||
|           this.set(id, data as EmojiPayload) | ||||
|         res(new Emoji(this.client, data as EmojiPayload)) | ||||
|       }).catch(e => rej(e)) | ||||
|           resolve(new Emoji(this.client, data as EmojiPayload)) | ||||
|         }) | ||||
|         .catch(e => reject(e)) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|  | @ -1,23 +1,27 @@ | |||
| import { Client } from "../models/client.ts"; | ||||
| import { Client } from '../models/client.ts' | ||||
| 
 | ||||
| export class GatewayCache { | ||||
|   client: Client | ||||
|     cacheName: string = "discord_gateway_cache" | ||||
|   cacheName: string = 'discord_gateway_cache' | ||||
| 
 | ||||
|     constructor(client: Client, cacheName?: string) { | ||||
|   constructor (client: Client, cacheName?: string) { | ||||
|     this.client = client | ||||
|         if(cacheName) this.cacheName = cacheName | ||||
|     if (cacheName !== undefined) this.cacheName = cacheName | ||||
|   } | ||||
| 
 | ||||
|     get(key: string) { | ||||
|         return this.client.cache.get(this.cacheName, key) | ||||
|   async get (key: string): Promise<undefined | any> { | ||||
|     const result = await this.client.cache.get(this.cacheName, key) | ||||
|     return result | ||||
|   } | ||||
| 
 | ||||
|     set(key: string, value: any) { | ||||
|         return this.client.cache.set(this.cacheName, key, value) | ||||
|   async set (key: string, value: any): Promise<any> { | ||||
|     const result = await this.client.cache.set(this.cacheName, key, value) | ||||
|     return result | ||||
|   } | ||||
| 
 | ||||
|     delete(key: string) { | ||||
|         return this.client.cache.delete(this.cacheName, key) | ||||
|   async delete (key: string): Promise<boolean> { | ||||
|     console.log(`[GatewayCache] DEL ${key}`) | ||||
|     const result = await this.client.cache.delete(this.cacheName, key) | ||||
|     return result | ||||
|   } | ||||
| } | ||||
|  | @ -7,8 +7,8 @@ import { BaseManager } from "./BaseManager.ts"; | |||
| import { MembersManager } from "./MembersManager.ts"; | ||||
| 
 | ||||
| export class GuildManager extends BaseManager<GuildPayload, Guild> { | ||||
|   constructor(client: Client) { | ||||
|     super(client, "guilds", Guild) | ||||
|   constructor (client: Client) { | ||||
|     super(client, 'guilds', Guild) | ||||
|   } | ||||
| 
 | ||||
|   fetch(id: string) { | ||||
|  |  | |||
|  | @ -9,8 +9,8 @@ import { UserPayload } from "../types/user.ts"; | |||
| import { BaseManager } from "./BaseManager.ts"; | ||||
| 
 | ||||
| export class MessagesManager extends BaseManager<MessagePayload, Message> { | ||||
|   constructor(client: Client) { | ||||
|     super(client, "messages", Message) | ||||
|   constructor (client: Client) { | ||||
|     super(client, 'messages', Message) | ||||
|   } | ||||
| 
 | ||||
|   async get(key: string): Promise<Message | void> { | ||||
|  |  | |||
|  | @ -1,24 +1,27 @@ | |||
| import { Client } from "../models/client.ts"; | ||||
| import { Guild } from "../structures/guild.ts"; | ||||
| import { Role } from "../structures/role.ts"; | ||||
| import { GUILD_ROLE } from "../types/endpoint.ts"; | ||||
| import { RolePayload } from "../types/role.ts"; | ||||
| import { BaseManager } from "./BaseManager.ts"; | ||||
| import { Client } from '../models/client.ts' | ||||
| import { Guild } from '../structures/guild.ts' | ||||
| import { Role } from '../structures/role.ts' | ||||
| import { GUILD_ROLE } from '../types/endpoint.ts' | ||||
| import { RolePayload } from '../types/role.ts' | ||||
| import { BaseManager } from './BaseManager.ts' | ||||
| 
 | ||||
| export class RolesManager extends BaseManager<RolePayload, Role> { | ||||
|   guild: Guild | ||||
| 
 | ||||
|   constructor(client: Client, guild: Guild) { | ||||
|     super(client, "roles:" + guild.id, Role) | ||||
|   constructor (client: Client, guild: Guild) { | ||||
|     super(client, `roles:${guild.id}`, Role) | ||||
|     this.guild = guild | ||||
|   } | ||||
| 
 | ||||
|   fetch(id: string) { | ||||
|     return new Promise((res, rej) => { | ||||
|       this.client.rest.get(GUILD_ROLE(this.guild.id, id)).then(data => { | ||||
|   async fetch (id: string): Promise<Role> { | ||||
|     return await new Promise((resolve, reject) => { | ||||
|       this.client.rest | ||||
|         .get(GUILD_ROLE(this.guild.id, id)) | ||||
|         .then(data => { | ||||
|           this.set(id, data as RolePayload) | ||||
|         res(new Role(this.client, data as RolePayload)) | ||||
|       }).catch(e => rej(e)) | ||||
|           resolve(new Role(this.client, data as RolePayload)) | ||||
|         }) | ||||
|         .catch(e => reject(e)) | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,20 +1,23 @@ | |||
| import { Client } from "../models/client.ts"; | ||||
| import { User } from "../structures/user.ts"; | ||||
| import { USER } from "../types/endpoint.ts"; | ||||
| import { UserPayload } from "../types/user.ts"; | ||||
| import { BaseManager } from "./BaseManager.ts"; | ||||
| import { Client } from '../models/client.ts' | ||||
| import { User } from '../structures/user.ts' | ||||
| import { USER } from '../types/endpoint.ts' | ||||
| import { UserPayload } from '../types/user.ts' | ||||
| import { BaseManager } from './BaseManager.ts' | ||||
| 
 | ||||
| export class UserManager extends BaseManager<UserPayload, User> { | ||||
|   constructor(client: Client) { | ||||
|     super(client, "users", User) | ||||
|   constructor (client: Client) { | ||||
|     super(client, 'users', User) | ||||
|   } | ||||
| 
 | ||||
|   fetch(id: string) { | ||||
|     return new Promise((res, rej) => { | ||||
|       this.client.rest.get(USER(id)).then(data => { | ||||
|   async fetch (id: string): Promise<User> { | ||||
|     return await new Promise((resolve, reject) => { | ||||
|       this.client.rest | ||||
|         .get(USER(id)) | ||||
|         .then(data => { | ||||
|           this.set(id, data as UserPayload) | ||||
|         res(new User(this.client, data as UserPayload)) | ||||
|       }).catch(e => rej(e)) | ||||
|           resolve(new User(this.client, data as UserPayload)) | ||||
|         }) | ||||
|         .catch(e => reject(e)) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|  | @ -1,13 +1,17 @@ | |||
| import { Collection } from "../utils/collection.ts"; | ||||
| import { Client } from "./client.ts"; | ||||
| import { connect, Redis, RedisConnectOptions } from "https://denopkg.com/keroxp/deno-redis/mod.ts"; | ||||
| import { Collection } from '../utils/collection.ts' | ||||
| import { Client } from './client.ts' | ||||
| import { | ||||
|   connect, | ||||
|   Redis, | ||||
|   RedisConnectOptions | ||||
| } from 'https://denopkg.com/keroxp/deno-redis/mod.ts' | ||||
| 
 | ||||
| export interface ICacheAdapter { | ||||
|   client: Client | ||||
|   get: (cacheName: string, key: string) => Promise<any> | any | ||||
|   set: (cacheName: string, key: string, value: any) => Promise<any> | any | ||||
|   delete: (cacheName: string, key: string) => Promise<boolean> | boolean | ||||
|   array: (cacheName: string) => void | any[] | Promise<any[] | void> | ||||
|   array: (cacheName: string) => undefined | any[] | Promise<any[] | undefined> | ||||
|   deleteCache: (cacheName: string) => any | ||||
| } | ||||
| 
 | ||||
|  | @ -17,34 +21,34 @@ export class DefaultCacheAdapter implements ICacheAdapter { | |||
|     [name: string]: Collection<string, any> | ||||
|   } = {} | ||||
| 
 | ||||
|   constructor(client: Client) { | ||||
|   constructor (client: Client) { | ||||
|     this.client = client | ||||
|   } | ||||
| 
 | ||||
|   async get(cacheName: string, key: string) { | ||||
|   async get (cacheName: string, key: string): Promise<undefined | any> { | ||||
|     const cache = this.data[cacheName] | ||||
|     if (!cache) return; | ||||
|     if (cache === undefined) return | ||||
|     return cache.get(key) | ||||
|   } | ||||
| 
 | ||||
|   async set(cacheName: string, key: string, value: any) { | ||||
|   async set (cacheName: string, key: string, value: any): Promise<any> { | ||||
|     let cache = this.data[cacheName] | ||||
|     if (!cache) { | ||||
|     if (cache === undefined) { | ||||
|       this.data[cacheName] = new Collection() | ||||
|       cache = this.data[cacheName] | ||||
|     } | ||||
|     cache.set(key, value) | ||||
|     return cache.set(key, value) | ||||
|   } | ||||
| 
 | ||||
|   async delete(cacheName: string, key: string) { | ||||
|   async delete (cacheName: string, key: string): Promise<boolean> { | ||||
|     const cache = this.data[cacheName] | ||||
|     if (!cache) return false | ||||
|     if (cache === undefined) return false | ||||
|     return cache.delete(key) | ||||
|   } | ||||
| 
 | ||||
|   async array(cacheName: string) { | ||||
|   async array (cacheName: string): Promise<any[] | undefined> { | ||||
|     const cache = this.data[cacheName] | ||||
|     if (!cache) return [] | ||||
|     if (cache === undefined) return | ||||
|     return cache.array() | ||||
|   } | ||||
| 
 | ||||
|  | @ -59,45 +63,60 @@ export class RedisCacheAdapter implements ICacheAdapter { | |||
|   redis?: Redis | ||||
|   ready: boolean = false | ||||
| 
 | ||||
|   constructor(client: Client, options: RedisConnectOptions) { | ||||
|   constructor (client: Client, options: RedisConnectOptions) { | ||||
|     this.client = client | ||||
|     this._redis = connect(options) | ||||
|     this._redis.then(redis => { | ||||
|     this._redis.then( | ||||
|       redis => { | ||||
|         this.redis = redis | ||||
|         this.ready = true | ||||
|     }) | ||||
|       }, | ||||
|       () => { | ||||
|         // TODO: Make error for this
 | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   async _checkReady() { | ||||
|     if(!this.ready) return await this._redis; | ||||
|     else return; | ||||
|   async _checkReady (): Promise<void> { | ||||
|     if (!this.ready) await this._redis | ||||
|   } | ||||
| 
 | ||||
|   async get(cacheName: string, key: string) { | ||||
|   async get (cacheName: string, key: string): Promise<string | undefined> { | ||||
|     await this._checkReady() | ||||
|     let cache = await this.redis?.hget(cacheName, key) | ||||
|     if(!cache) return | ||||
|     const cache = await this.redis?.hget(cacheName, key) | ||||
|     if (cache === undefined) return | ||||
|     try { | ||||
|       return JSON.parse(cache as string) | ||||
|     } catch(e) { return cache } | ||||
|       return JSON.parse(cache) | ||||
|     } catch (e) { | ||||
|       return cache | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async set(cacheName: string, key: string, value: any) { | ||||
|   async set ( | ||||
|     cacheName: string, | ||||
|     key: string, | ||||
|     value: any | ||||
|   ): Promise<number | undefined> { | ||||
|     await this._checkReady() | ||||
|     return await this.redis?.hset(cacheName, key, typeof value === "object" ? JSON.stringify(value) : value) | ||||
|     const result = await this.redis?.hset( | ||||
|       cacheName, | ||||
|       key, | ||||
|       typeof value === 'object' ? JSON.stringify(value) : value | ||||
|     ) | ||||
|     return result | ||||
|   } | ||||
| 
 | ||||
|   async delete(cacheName: string, key: string) { | ||||
|   async delete (cacheName: string, key: string): Promise<boolean> { | ||||
|     await this._checkReady() | ||||
|     let exists = await this.redis?.hexists(cacheName, key) | ||||
|     if(!exists) return false | ||||
|     const exists = await this.redis?.hexists(cacheName, key) | ||||
|     if (exists === 0) return false | ||||
|     await this.redis?.hdel(cacheName, key) | ||||
|     return true | ||||
|   } | ||||
| 
 | ||||
|   async array(cacheName: string) { | ||||
|   async array (cacheName: string): Promise<any[] | undefined> { | ||||
|     await this._checkReady() | ||||
|     let data = await this.redis?.hvals(cacheName) | ||||
|     const data = await this.redis?.hvals(cacheName) | ||||
|     return data?.map((e: string) => JSON.parse(e)) | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,7 +32,6 @@ export class Client extends EventEmitter { | |||
|   cache: ICacheAdapter = new DefaultCacheAdapter(this) | ||||
|   intents?: GatewayIntents[] | ||||
|   forceNewSession?: boolean | ||||
|    | ||||
|   users: UserManager = new UserManager(this) | ||||
|   guilds: GuildManager = new GuildManager(this) | ||||
|   channels: ChannelsManager = new ChannelsManager(this) | ||||
|  | @ -46,11 +45,11 @@ export class Client extends EventEmitter { | |||
|     this.token = options.token | ||||
|     this.intents = options.intents | ||||
|     this.forceNewSession = options.forceNewSession | ||||
|     if(options.cache) this.cache = options.cache | ||||
|     if(options.presence) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence) | ||||
|     if (options.cache !== undefined) this.cache = options.cache | ||||
|     if (options.presence !== undefined) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence) | ||||
|   } | ||||
| 
 | ||||
|   setAdapter(adapter: ICacheAdapter) { | ||||
|   setAdapter (adapter: ICacheAdapter): Client { | ||||
|     this.cache = adapter | ||||
|     return this | ||||
|   } | ||||
|  | @ -72,16 +71,15 @@ export class Client extends EventEmitter { | |||
|    * @param intents Gateway intents in array. This is required. | ||||
|    */ | ||||
|   connect (token?: string, intents?: GatewayIntents[]): void { | ||||
|     if(!token && this.token) token = this.token | ||||
|     else if(!this.token && token) { | ||||
|     if (token === undefined && this.token !== undefined) token = this.token | ||||
|     else if (this.token === undefined && token !== undefined) { | ||||
|       this.token = token | ||||
|     } | ||||
|     else throw new Error("No Token Provided") | ||||
|     if(!intents && this.intents) intents = this.intents | ||||
|     else if(intents && !this.intents) { | ||||
|     } else throw new Error('No Token Provided') | ||||
|     if (intents === undefined && this.intents !== undefined) | ||||
|       intents = this.intents | ||||
|     else if (intents !== undefined && this.intents === undefined) { | ||||
|       this.intents = intents | ||||
|     } | ||||
|     else throw new Error("No Gateway Intents were provided") | ||||
|     } else throw new Error('No Gateway Intents were provided') | ||||
|     this.gateway = new Gateway(this, token, intents) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { delay } from "../utils/index.ts"; | ||||
| import * as baseEndpoints from "../consts/urlsAndVersions.ts"; | ||||
| import { Client } from "./client.ts"; | ||||
| import { delay } from '../utils/index.ts' | ||||
| import * as baseEndpoints from '../consts/urlsAndVersions.ts' | ||||
| import { Client } from './client.ts' | ||||
| 
 | ||||
| export enum HttpResponseCode { | ||||
|   Ok = 200, | ||||
|  | @ -17,265 +17,290 @@ export enum HttpResponseCode { | |||
| } | ||||
| 
 | ||||
| export type RequestMethods = | ||||
|   | "get" | ||||
|   | "post" | ||||
|   | "put" | ||||
|   | "patch" | ||||
|   | "head" | ||||
|   | "delete"; | ||||
|   | 'get' | ||||
|   | 'post' | ||||
|   | 'put' | ||||
|   | 'patch' | ||||
|   | 'head' | ||||
|   | 'delete' | ||||
| 
 | ||||
| export interface QueuedRequest { | ||||
|   callback: () => Promise< | ||||
| 		void | { | ||||
| 			rateLimited: any; | ||||
| 			beforeFetch: boolean; | ||||
| 			bucketID?: string | null; | ||||
|     | { | ||||
|         rateLimited: any | ||||
|         beforeFetch: boolean | ||||
|         bucketID?: string | null | ||||
|       } | ||||
| 	>; | ||||
| 	bucketID?: string | null; | ||||
| 	url: string; | ||||
|     | undefined | ||||
|   > | ||||
|   bucketID?: string | null | ||||
|   url: string | ||||
| } | ||||
| 
 | ||||
| export interface RateLimitedPath { | ||||
| 	url: string; | ||||
| 	resetTimestamp: number; | ||||
| 	bucketID: string | null; | ||||
|   url: string | ||||
|   resetTimestamp: number | ||||
|   bucketID: string | null | ||||
| } | ||||
| 
 | ||||
| export class RESTManager { | ||||
| 	client: Client; | ||||
| 	globallyRateLimited: boolean = false; | ||||
| 	queueInProcess: boolean = false; | ||||
| 	pathQueues: { [key: string]: QueuedRequest[] } = {}; | ||||
| 	ratelimitedPaths = new Map<string, RateLimitedPath>(); | ||||
|   client: Client | ||||
|   globallyRateLimited: boolean = false | ||||
|   queueInProcess: boolean = false | ||||
|   pathQueues: { [key: string]: QueuedRequest[] } = {} | ||||
|   ratelimitedPaths = new Map<string, RateLimitedPath>() | ||||
| 
 | ||||
| 	constructor(client: Client) { | ||||
| 		this.client = client; | ||||
|   constructor (client: Client) { | ||||
|     this.client = client | ||||
|     setTimeout(this.processRateLimitedPaths, 1000) | ||||
|   } | ||||
| 
 | ||||
| 	async processRateLimitedPaths() { | ||||
| 		const now = Date.now(); | ||||
|   async processRateLimitedPaths (): Promise<void> { | ||||
|     const now = Date.now() | ||||
|     this.ratelimitedPaths.forEach((value, key) => { | ||||
| 			if (value.resetTimestamp > now) return; | ||||
| 			this.ratelimitedPaths.delete(key); | ||||
| 			if (key === "global") this.globallyRateLimited = false; | ||||
| 		}); | ||||
| 
 | ||||
| 		await delay(1000); | ||||
| 		this.processRateLimitedPaths(); | ||||
|       if (value.resetTimestamp > now) return | ||||
|       this.ratelimitedPaths.delete(key) | ||||
|       if (key === 'global') this.globallyRateLimited = false | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
| 	addToQueue(request: QueuedRequest) { | ||||
| 		const route = request.url.substring(baseEndpoints.DISCORD_API_URL.length + 1); | ||||
| 		const parts = route.split("/"); | ||||
|   addToQueue (request: QueuedRequest): void { | ||||
|     const route = request.url.substring( | ||||
|       // eslint seriously?
 | ||||
|       // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
 | ||||
|       baseEndpoints.DISCORD_API_URL.length + 1 | ||||
|     ) | ||||
|     const parts = route.split('/') | ||||
|     // Remove the major param
 | ||||
| 		parts.shift(); | ||||
| 		const [id] = parts; | ||||
|     parts.shift() | ||||
|     const [id] = parts | ||||
| 
 | ||||
| 		if (this.pathQueues[id]) { | ||||
| 			this.pathQueues[id].push(request); | ||||
|     if (this.pathQueues[id] !== undefined) { | ||||
|       this.pathQueues[id].push(request) | ||||
|     } else { | ||||
| 			this.pathQueues[id] = [request]; | ||||
|       this.pathQueues[id] = [request] | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 	async cleanupQueues() { | ||||
| 		Object.entries(this.pathQueues).map(([key, value]) => { | ||||
| 			if (!value.length) { | ||||
|   async cleanupQueues (): Promise<void> { | ||||
|     Object.entries(this.pathQueues).forEach(([key, value]) => { | ||||
|       if (value.length === 0) { | ||||
|         // Remove it entirely
 | ||||
| 				delete this.pathQueues[key]; | ||||
|         // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
 | ||||
|         delete this.pathQueues[key] | ||||
|       } | ||||
| 		}); | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
| 	async processQueue() { | ||||
|   async processQueue (): Promise<void> { | ||||
|     if ( | ||||
| 			(Object.keys(this.pathQueues).length) && !this.globallyRateLimited | ||||
|       Object.keys(this.pathQueues).length !== 0 && | ||||
|       !this.globallyRateLimited | ||||
|     ) { | ||||
|       await Promise.allSettled( | ||||
| 				Object.values(this.pathQueues).map(async (pathQueue) => { | ||||
| 					const request = pathQueue.shift(); | ||||
| 					if (!request) return; | ||||
|         Object.values(this.pathQueues).map(async pathQueue => { | ||||
|           const request = pathQueue.shift() | ||||
|           if (request === undefined) return | ||||
| 
 | ||||
| 					const rateLimitedURLResetIn = await this.checkRatelimits(request.url); | ||||
|           const rateLimitedURLResetIn = await this.checkRatelimits(request.url) | ||||
| 
 | ||||
| 					if (request.bucketID) { | ||||
| 						const rateLimitResetIn = await this.checkRatelimits(request.bucketID); | ||||
| 						if (rateLimitResetIn) { | ||||
| 							// This request is still rate limited readd to queue
 | ||||
| 							this.addToQueue(request); | ||||
| 						} else if (rateLimitedURLResetIn) { | ||||
| 							// This URL is rate limited readd to queue
 | ||||
| 							this.addToQueue(request); | ||||
|           if (typeof request.bucketID === 'string') { | ||||
|             const rateLimitResetIn = await this.checkRatelimits( | ||||
|               request.bucketID | ||||
|             ) | ||||
|             if (rateLimitResetIn !== false) { | ||||
|               // This request is still rate limited read to queue
 | ||||
|               this.addToQueue(request) | ||||
|             } else { | ||||
|               // This request is not rate limited so it should be run
 | ||||
| 							const result = await request.callback(); | ||||
| 							if (result && result.rateLimited) { | ||||
| 								this.addToQueue( | ||||
| 									{ ...request, bucketID: result.bucketID || request.bucketID }, | ||||
| 								); | ||||
|               const result = await request.callback() | ||||
|               if (result?.rateLimited !== undefined) { | ||||
|                 this.addToQueue({ | ||||
|                   ...request, | ||||
|                   bucketID: result.bucketID ?? request.bucketID | ||||
|                 }) | ||||
|               } | ||||
|             } | ||||
|           } else { | ||||
| 						if (rateLimitedURLResetIn) { | ||||
|             if (rateLimitedURLResetIn !== false) { | ||||
|               // This URL is rate limited readd to queue
 | ||||
| 							this.addToQueue(request); | ||||
|               this.addToQueue(request) | ||||
|             } else { | ||||
|               // This request has no bucket id so it should be processed
 | ||||
| 							const result = await request.callback(); | ||||
| 							if (request && result && result.rateLimited) { | ||||
| 								this.addToQueue( | ||||
| 									{ ...request, bucketID: result.bucketID || request.bucketID }, | ||||
| 								); | ||||
|               const result = await request.callback() | ||||
|               if (result?.rateLimited !== undefined) { | ||||
|                 this.addToQueue({ | ||||
|                   ...request, | ||||
|                   bucketID: result.bucketID ?? request.bucketID | ||||
|                 }) | ||||
|               } | ||||
|             } | ||||
|           } | ||||
| 				}), | ||||
| 			); | ||||
|         }) | ||||
|       ) | ||||
|     } | ||||
| 
 | ||||
| 		if (Object.keys(this.pathQueues).length) { | ||||
| 			await delay(1000); | ||||
| 			this.processQueue(); | ||||
| 			this.cleanupQueues(); | ||||
| 		} else this.queueInProcess = false; | ||||
|     if (Object.keys(this.pathQueues).length !== 0) { | ||||
|       await delay(1000) | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.processQueue() | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.cleanupQueues() | ||||
|     } else this.queueInProcess = false | ||||
|   } | ||||
| 
 | ||||
| 	createRequestBody(body: any, method: RequestMethods) { | ||||
|   createRequestBody ( | ||||
|     body: any, | ||||
|     method: RequestMethods | ||||
|   ): { [key: string]: any } { | ||||
|     const headers: { [key: string]: string } = { | ||||
|       Authorization: `Bot ${this.client.token}`, | ||||
| 			"User-Agent": | ||||
| 				`DiscordBot (discord.deno)`, | ||||
| 		}; | ||||
| 
 | ||||
| 		if(!this.client.token) delete headers.Authorization; | ||||
| 	 | ||||
| 		if (method === "get") body = undefined; | ||||
| 	 | ||||
| 		if (body?.reason) { | ||||
| 			headers["X-Audit-Log-Reason"] = encodeURIComponent(body.reason); | ||||
|       'User-Agent': `DiscordBot (discord.deno)` | ||||
|     } | ||||
| 
 | ||||
| 		if (body?.file) { | ||||
| 			const form = new FormData(); | ||||
| 			form.append("file", body.file.blob, body.file.name); | ||||
| 			form.append("payload_json", JSON.stringify({ ...body, file: undefined })); | ||||
| 			body.file = form; | ||||
| 		} else if ( | ||||
| 			body && !["get", "delete"].includes(method) | ||||
| 		) { | ||||
| 			headers["Content-Type"] = "application/json"; | ||||
|     if (this.client.token !== undefined) delete headers.Authorization | ||||
| 
 | ||||
|     if (method === 'get') body = undefined | ||||
| 
 | ||||
|     if (body?.reason !== undefined) { | ||||
|       headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason) | ||||
|     } | ||||
| 
 | ||||
|     if (body?.file !== undefined) { | ||||
|       const form = new FormData() | ||||
|       form.append('file', body.file.blob, body.file.name) | ||||
|       form.append('payload_json', JSON.stringify({ ...body, file: undefined })) | ||||
|       body.file = form | ||||
|     } else if (body !== undefined && !['get', 'delete'].includes(method)) { | ||||
|       headers['Content-Type'] = 'application/json' | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|       headers, | ||||
| 			body: body?.file || JSON.stringify(body), | ||||
| 			method: method.toUpperCase(), | ||||
| 		}; | ||||
|       body: body?.file ?? JSON.stringify(body), | ||||
|       method: method.toUpperCase() | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 	async checkRatelimits(url: string) { | ||||
| 		const ratelimited = this.ratelimitedPaths.get(url); | ||||
| 		const global = this.ratelimitedPaths.get("global"); | ||||
| 		const now = Date.now(); | ||||
|   async checkRatelimits (url: string): Promise<number | false> { | ||||
|     const ratelimited = this.ratelimitedPaths.get(url) | ||||
|     const global = this.ratelimitedPaths.get('global') | ||||
|     const now = Date.now() | ||||
| 
 | ||||
| 		if (ratelimited && now < ratelimited.resetTimestamp) { | ||||
| 			return ratelimited.resetTimestamp - now; | ||||
|     if (ratelimited !== undefined && now < ratelimited.resetTimestamp) { | ||||
|       return ratelimited.resetTimestamp - now | ||||
|     } | ||||
| 		if (global && now < global.resetTimestamp) { | ||||
| 			return global.resetTimestamp - now; | ||||
|     if (global !== undefined && now < global.resetTimestamp) { | ||||
|       return global.resetTimestamp - now | ||||
|     } | ||||
| 
 | ||||
| 		return false; | ||||
|     return false | ||||
|   } | ||||
| 
 | ||||
| 	async runMethod( | ||||
|   async runMethod ( | ||||
|     method: RequestMethods, | ||||
|     url: string, | ||||
|     body?: unknown, | ||||
|     retryCount = 0, | ||||
| 		bucketID?: string | null, | ||||
| 	) { | ||||
| 		const errorStack = new Error("Location In Your Files:"); | ||||
| 		Error.captureStackTrace(errorStack); | ||||
|     bucketID?: string | null | ||||
|   ): Promise<any> { | ||||
|     const errorStack = new Error('Location In Your Files:') | ||||
|     Error.captureStackTrace(errorStack) | ||||
| 
 | ||||
|     return await new Promise((resolve, reject) => { | ||||
| 			const callback = async () => { | ||||
|       const callback = async (): Promise<undefined | any> => { | ||||
|         try { | ||||
| 					const rateLimitResetIn = await this.checkRatelimits(url); | ||||
| 					if (rateLimitResetIn) { | ||||
| 						return { rateLimited: rateLimitResetIn, beforeFetch: true, bucketID }; | ||||
|           const rateLimitResetIn = await this.checkRatelimits(url) | ||||
|           if (rateLimitResetIn !== false) { | ||||
|             return { | ||||
|               rateLimited: rateLimitResetIn, | ||||
|               beforeFetch: true, | ||||
|               bucketID | ||||
|             } | ||||
|           } | ||||
| 
 | ||||
| 					const query = method === "get" && body | ||||
| 						? Object.entries(body as any).map(([key, value]) => | ||||
| 							`${encodeURIComponent(key)}=${encodeURIComponent(value as any)}` | ||||
|           const query = | ||||
|             method === 'get' && body !== undefined | ||||
|               ? Object.entries(body as any) | ||||
|                   .map( | ||||
|                     ([key, value]) => | ||||
|                       `${encodeURIComponent(key)}=${encodeURIComponent( | ||||
|                         value as any | ||||
|                       )}` | ||||
|                   ) | ||||
| 							.join("&") | ||||
| 						: ""; | ||||
| 					const urlToUse = method === "get" && query ? `${url}?${query}` : url; | ||||
|                   .join('&') | ||||
|               : '' | ||||
|           const urlToUse = | ||||
|             method === 'get' && query !== '' ? `${url}?${query}` : url | ||||
| 
 | ||||
| 					const response = await fetch(urlToUse, this.createRequestBody(body, method)); | ||||
|           const bucketIDFromHeaders = this.processHeaders(url, response.headers); | ||||
|           const response = await fetch( | ||||
|             urlToUse, | ||||
|             this.createRequestBody(body, method) | ||||
|           ) | ||||
|           const bucketIDFromHeaders = this.processHeaders(url, response.headers) | ||||
|           this.handleStatusCode(response, errorStack) | ||||
| 
 | ||||
|           // Sometimes Discord returns an empty 204 response that can't be made to JSON.
 | ||||
| 					if (response.status === 204) return resolve(); | ||||
|           if (response.status === 204) return resolve(undefined) | ||||
| 
 | ||||
| 					this.handleStatusCode(response, errorStack); | ||||
| 	 | ||||
| 					const json = await response.json(); | ||||
|           const json = await response.json() | ||||
|           if ( | ||||
| 						json.retry_after || | ||||
| 						json.message === "You are being rate limited." | ||||
|             json.retry_after !== undefined || | ||||
|             json.message === 'You are being rate limited.' | ||||
|           ) { | ||||
|             if (retryCount > 10) { | ||||
| 							throw new Error("Max RateLimit Retries hit"); | ||||
|               throw new Error('Max RateLimit Retries hit') | ||||
|             } | ||||
| 
 | ||||
|             return { | ||||
|               rateLimited: json.retry_after, | ||||
|               beforeFetch: false, | ||||
| 							bucketID: bucketIDFromHeaders, | ||||
| 						}; | ||||
|               bucketID: bucketIDFromHeaders | ||||
|             } | ||||
| 					return resolve(json); | ||||
|           } | ||||
|           return resolve(json) | ||||
|         } catch (error) { | ||||
| 					return reject(error); | ||||
|           return reject(error) | ||||
|         } | ||||
|       } | ||||
| 			}; | ||||
| 
 | ||||
|       this.addToQueue({ | ||||
|         callback, | ||||
|         bucketID, | ||||
| 				url, | ||||
| 			}); | ||||
|         url | ||||
|       }) | ||||
|       if (!this.queueInProcess) { | ||||
| 				this.queueInProcess = true; | ||||
| 				this.processQueue(); | ||||
|         this.queueInProcess = true | ||||
|         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|         this.processQueue() | ||||
|       } | ||||
| 		}); | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
| 	async logErrors(response: Response, errorStack?: unknown) { | ||||
|   async logErrors (response: Response, errorStack?: unknown): Promise<void> { | ||||
|     try { | ||||
| 			const error = await response.json(); | ||||
| 			console.error(error); | ||||
|       const error = await response.json() | ||||
|       console.error(error) | ||||
|     } catch { | ||||
| 			console.error(response); | ||||
|       console.error(response) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 	async handleStatusCode(response: Response, errorStack?: unknown) { | ||||
|   handleStatusCode ( | ||||
|     response: Response, | ||||
|     errorStack?: unknown | ||||
|   ): undefined | boolean { | ||||
|     const status = response.status | ||||
| 
 | ||||
|     if ( | ||||
|       (status >= 200 && status < 400) || | ||||
|       status === HttpResponseCode.TooManyRequests | ||||
|     ) { | ||||
| 			return true; | ||||
|       return true | ||||
|     } | ||||
| 
 | ||||
| 		this.logErrors(response, errorStack); | ||||
|     // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|     this.logErrors(response, errorStack) | ||||
| 
 | ||||
|     switch (status) { | ||||
|       case HttpResponseCode.BadRequest: | ||||
|  | @ -283,84 +308,85 @@ export class RESTManager { | |||
|       case HttpResponseCode.Forbidden: | ||||
|       case HttpResponseCode.NotFound: | ||||
|       case HttpResponseCode.MethodNotAllowed: | ||||
| 				throw new Error("Request Client Error. Code: " + status); | ||||
|         throw new Error('Request Client Error') | ||||
|       case HttpResponseCode.GatewayUnavailable: | ||||
| 				throw new Error("Request Server Error. Code: " + status); | ||||
|         throw new Error('Request Server Error') | ||||
|     } | ||||
| 
 | ||||
|     // left are all unknown
 | ||||
| 		throw new Error("Request Unknown Error"); | ||||
|     throw new Error('Request Unknown Error') | ||||
|   } | ||||
| 
 | ||||
| 	processHeaders(url: string, headers: Headers) { | ||||
| 		let ratelimited = false; | ||||
|   processHeaders (url: string, headers: Headers): string | null | undefined { | ||||
|     let ratelimited = false | ||||
| 
 | ||||
|     // Get all useful headers
 | ||||
| 		const remaining = headers.get("x-ratelimit-remaining"); | ||||
| 		const resetTimestamp = headers.get("x-ratelimit-reset"); | ||||
| 		const retryAfter = headers.get("retry-after"); | ||||
| 		const global = headers.get("x-ratelimit-global"); | ||||
| 		const bucketID = headers.get("x-ratelimit-bucket"); | ||||
|     const remaining = headers.get('x-ratelimit-remaining') | ||||
|     const resetTimestamp = headers.get('x-ratelimit-reset') | ||||
|     const retryAfter = headers.get('retry-after') | ||||
|     const global = headers.get('x-ratelimit-global') | ||||
|     const bucketID = headers.get('x-ratelimit-bucket') | ||||
| 
 | ||||
|     // If there is no remaining rate limit for this endpoint, we save it in cache
 | ||||
| 		if (remaining && remaining === "0") { | ||||
| 			ratelimited = true; | ||||
|     if (remaining !== null && remaining === '0') { | ||||
|       ratelimited = true | ||||
| 
 | ||||
|       this.ratelimitedPaths.set(url, { | ||||
|         url, | ||||
|         resetTimestamp: Number(resetTimestamp) * 1000, | ||||
| 				bucketID, | ||||
| 			}); | ||||
|         bucketID | ||||
|       }) | ||||
| 
 | ||||
| 			if (bucketID) { | ||||
|       if (bucketID !== null) { | ||||
|         this.ratelimitedPaths.set(bucketID, { | ||||
|           url, | ||||
|           resetTimestamp: Number(resetTimestamp) * 1000, | ||||
| 					bucketID, | ||||
| 				}); | ||||
|           bucketID | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| 		if (global) { | ||||
| 			const reset = Date.now() + Number(retryAfter); | ||||
| 			this.globallyRateLimited = true; | ||||
| 			ratelimited = true; | ||||
|     // If there is no remaining global limit, we save it in cache
 | ||||
|     if (global !== null) { | ||||
|       const reset = Date.now() + Number(retryAfter) | ||||
|       this.globallyRateLimited = true | ||||
|       ratelimited = true | ||||
| 
 | ||||
| 			this.ratelimitedPaths.set("global", { | ||||
| 				url: "global", | ||||
|       this.ratelimitedPaths.set('global', { | ||||
|         url: 'global', | ||||
|         resetTimestamp: reset, | ||||
| 				bucketID, | ||||
| 			}); | ||||
|         bucketID | ||||
|       }) | ||||
| 
 | ||||
| 			if (bucketID) { | ||||
|       if (bucketID !== null) { | ||||
|         this.ratelimitedPaths.set(bucketID, { | ||||
| 					url: "global", | ||||
|           url: 'global', | ||||
|           resetTimestamp: reset, | ||||
| 					bucketID, | ||||
| 				}); | ||||
|           bucketID | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| 		return ratelimited ? bucketID : undefined; | ||||
|     return ratelimited ? bucketID : undefined | ||||
|   } | ||||
| 
 | ||||
| 	get(url: string, body?: unknown) { | ||||
|     return this.runMethod("get", url, body); | ||||
|   async get (url: string, body?: unknown): Promise<any> { | ||||
|     return await this.runMethod('get', url, body) | ||||
|   } | ||||
| 
 | ||||
|   post(url: string, body?: unknown) { | ||||
|     return this.runMethod("post", url, body); | ||||
|   async post (url: string, body?: unknown): Promise<any> { | ||||
|     return await this.runMethod('post', url, body) | ||||
|   } | ||||
| 
 | ||||
|   delete(url: string, body?: unknown) { | ||||
|     return this.runMethod("delete", url, body); | ||||
|   async delete (url: string, body?: unknown): Promise<any> { | ||||
|     return await this.runMethod('delete', url, body) | ||||
|   } | ||||
| 
 | ||||
|   patch(url: string, body?: unknown) { | ||||
|     return this.runMethod("patch", url, body); | ||||
|   async patch (url: string, body?: unknown): Promise<any> { | ||||
|     return await this.runMethod('patch', url, body) | ||||
|   } | ||||
| 
 | ||||
|   put(url: string, body?: unknown) { | ||||
|     return this.runMethod("put", url, body); | ||||
|   async put (url: string, body?: unknown): Promise<any> { | ||||
|     return await this.runMethod('put', url, body) | ||||
|   } | ||||
| } | ||||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { ChannelPayload, ChannelTypes } from '../types/channel.ts' | ||||
| import { Base } from './base.ts' | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { DMChannelPayload } from '../types/channel.ts' | ||||
| import { UserPayload } from '../types/user.ts' | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { GroupDMChannelPayload } from '../types/channel.ts' | ||||
| import { Channel } from './channel.ts' | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { GuildFeatures, GuildPayload } from '../types/guild.ts' | ||||
| import { PresenceUpdatePayload } from '../types/presence.ts' | ||||
| import { PresenceUpdatePayload } from '../types/gateway.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { Channel } from './channel.ts' | ||||
| import { Emoji } from './emoji.ts' | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { GuildVoiceChannelPayload, Overwrite } from '../types/channel.ts' | ||||
| import { Channel } from './channel.ts' | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ import { User } from './user.ts' | |||
| import { Member } from './member.ts' | ||||
| import { Embed } from './embed.ts' | ||||
| import { CHANNEL_MESSAGE } from '../types/endpoint.ts' | ||||
| import cache from '../models/cache.ts' | ||||
| import { Channel } from "./channel.ts" | ||||
| import { MessageMentions } from "./MessageMentions.ts" | ||||
| import { TextChannel } from "./textChannel.ts" | ||||
|  | @ -51,7 +50,13 @@ export class Message extends Base { | |||
|   messageReference?: MessageReference | ||||
|   flags?: number | ||||
| 
 | ||||
|   constructor (client: Client, data: MessagePayload, channel: TextChannel, author: User, mentions: MessageMentions) { | ||||
|   constructor ( | ||||
|     client: Client, | ||||
|     data: MessagePayload, | ||||
|     channel: TextChannel, | ||||
|     author: User, | ||||
|     mentions: MessageMentions | ||||
|   ) { | ||||
|     super(client) | ||||
|     this.data = data | ||||
|     this.id = data.id | ||||
|  | @ -120,7 +125,7 @@ export class Message extends Base { | |||
|   } | ||||
| 
 | ||||
|   edit (text?: string, option?: MessageOption): Promise<Message> { | ||||
|     return (this.channel as TextChannel).edit(this.id, text, option)   | ||||
|     return this.channel.edit(this.id, text, option)   | ||||
|   } | ||||
| 
 | ||||
|   reply(text: string, options?: MessageOption) { | ||||
|  | @ -129,7 +134,7 @@ export class Message extends Base { | |||
|     return this.channel.send(`${this.author.mention}, ${text}`, options) | ||||
|   } | ||||
| 
 | ||||
|   delete (): Promise<void> { | ||||
|     return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) as any | ||||
|   async delete (): Promise<void> { | ||||
|     return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { RolePayload } from '../types/role.ts' | ||||
|  |  | |||
|  | @ -1,11 +1,9 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { MessageOption, MessagePayload, TextChannelPayload } from '../types/channel.ts' | ||||
| import { MessageOption, TextChannelPayload } from '../types/channel.ts' | ||||
| import { CHANNEL_MESSAGE, CHANNEL_MESSAGES } from '../types/endpoint.ts' | ||||
| import { Channel } from './channel.ts' | ||||
| import { Message } from './message.ts' | ||||
| import { MessageMentions } from "./MessageMentions.ts" | ||||
| import { User } from "./user.ts" | ||||
| import { MessageMentions } from './MessageMentions.ts' | ||||
| 
 | ||||
| export class TextChannel extends Channel { | ||||
|   lastMessageID?: string | ||||
|  | @ -37,7 +35,7 @@ export class TextChannel extends Channel { | |||
|         allowed_mentions: option?.allowedMention | ||||
|     }) | ||||
| 
 | ||||
|     return new Message(this.client, resp as any, this, this.client.user as User, new MessageMentions()) | ||||
|     return new Message(this.client, resp as any, this, this.client.user, new MessageMentions()) | ||||
|   } | ||||
| 
 | ||||
|   async edit ( | ||||
|  | @ -45,21 +43,31 @@ export class TextChannel extends Channel { | |||
|     text?: string, | ||||
|     option?: MessageOption | ||||
|   ): Promise<Message> { | ||||
|     if (text !== undefined && option !== undefined) { | ||||
|     if (text === undefined && option === undefined) { | ||||
|       throw new Error('Either text or option is necessary.') | ||||
|     } | ||||
| 
 | ||||
|     let newMsg = await this.client.rest.patch(CHANNEL_MESSAGE(this.id, typeof message == "string" ? message : message.id), { | ||||
|     if (this.client.user === undefined) { | ||||
|       throw new Error('Client user has not initialized.') | ||||
|     } | ||||
| 
 | ||||
|     const newMsg = await this.client.rest.patch( | ||||
|       CHANNEL_MESSAGE( | ||||
|         this.id, | ||||
|         typeof message === 'string' ? message : message.id | ||||
|       ), | ||||
|       { | ||||
|         content: text, | ||||
|         embed: option?.embed.toJSON(), | ||||
|         file: option?.file, | ||||
|         tts: option?.tts, | ||||
|         allowed_mentions: option?.allowedMention | ||||
|     }) as MessagePayload | ||||
|       } | ||||
|     ) | ||||
| 
 | ||||
|     // TODO: Actually construct this object
 | ||||
|     let mentions = new MessageMentions() | ||||
|     const mentions = new MessageMentions() | ||||
| 
 | ||||
|     return new Message(this.client, newMsg, this, this.client.user as User, mentions) | ||||
|     return new Message(this.client, newMsg, this, this.client.user, mentions) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { UserPayload } from '../types/user.ts' | ||||
| import { Base } from './base.ts' | ||||
|  | @ -18,8 +17,8 @@ export class User extends Base { | |||
|   premiumType?: 0 | 1 | 2 | ||||
|   publicFlags?: number | ||||
| 
 | ||||
|   get tag(): string { | ||||
|     return `${this.username}#${this.discriminator}`; | ||||
|   get tag (): string { | ||||
|     return `${this.username}#${this.discriminator}` | ||||
|   } | ||||
| 
 | ||||
|   get nickMention (): string { | ||||
|  | @ -65,7 +64,7 @@ export class User extends Base { | |||
|     this.publicFlags = data.public_flags ?? this.publicFlags | ||||
|   } | ||||
| 
 | ||||
|   toString() { | ||||
|     return this.mention; | ||||
|   toString (): string { | ||||
|     return this.mention | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import cache from '../models/cache.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { MemberPayload } from '../types/guild.ts' | ||||
| import { VoiceStatePayload } from '../types/voice.ts' | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| // https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
 | ||||
| import { EmojiPayload } from './emoji.ts' | ||||
| import { MemberPayload } from './guild.ts' | ||||
| import { ActivityPayload, PresenceUpdatePayload } from './presence.ts' | ||||
| import { ActivityPayload } from './presence.ts' | ||||
| import { RolePayload } from './role.ts' | ||||
| import { UserPayload } from './user.ts' | ||||
| 
 | ||||
|  | @ -100,7 +100,7 @@ enum GatewayEvents { | |||
|   Webhooks_Update = 'WEBHOOKS_UPDATE' | ||||
| } | ||||
| 
 | ||||
| interface IdentityPayload { | ||||
| export interface IdentityPayload { | ||||
|   token: string | ||||
|   properties: IdentityConnection | ||||
|   compress?: boolean | ||||
|  | @ -119,19 +119,19 @@ enum UpdateStatus { | |||
|   offline = 'offline' | ||||
| } | ||||
| 
 | ||||
| interface IdentityConnection { | ||||
| export interface IdentityConnection { | ||||
|   $os: 'darwin' | 'windows' | 'linux' | 'custom os' | ||||
|   $browser: 'discord.deno' | ||||
|   $device: 'discord.deno' | ||||
| } | ||||
| 
 | ||||
| interface Resume { | ||||
| export interface Resume { | ||||
|   token: string | ||||
|   session_id: string | ||||
|   seq: number | ||||
| } | ||||
| 
 | ||||
| interface GuildRequestMembers { | ||||
| export interface GuildRequestMembers { | ||||
|   guild_id: string | string[] | ||||
|   query?: string | ||||
|   limit: number | ||||
|  | @ -140,25 +140,25 @@ interface GuildRequestMembers { | |||
|   nonce?: string | ||||
| } | ||||
| 
 | ||||
| interface GatewayVoiceStateUpdate { | ||||
| export interface GatewayVoiceStateUpdate { | ||||
|   guild_id: string | ||||
|   channel_id: string | ||||
|   self_mute: boolean | ||||
|   self_deaf: boolean | ||||
| } | ||||
| 
 | ||||
| interface GatewayStatusUpdate { | ||||
| export interface GatewayStatusUpdate { | ||||
|   since: number | undefined | ||||
|   activities: ActivityPayload[] | ||||
|   status: string | ||||
|   afk: boolean | ||||
| } | ||||
| 
 | ||||
| interface Hello { | ||||
| export interface Hello { | ||||
|   heartbeat_interval: number | ||||
| } | ||||
| 
 | ||||
| interface ReadyEvent { | ||||
| export interface ReadyEvent { | ||||
|   v: number | ||||
|   user: UserPayload | ||||
|   privateChannels: [] | ||||
|  | @ -167,40 +167,40 @@ interface ReadyEvent { | |||
|   shard?: number[] | ||||
| } | ||||
| 
 | ||||
| interface ChannelPinsUpdate { | ||||
| export interface ChannelPinsUpdatePayload { | ||||
|   guild_id?: string | ||||
|   channel_id: string | ||||
|   last_pin_timestamp?: string | ||||
| } | ||||
| 
 | ||||
| interface GuildBanAdd { | ||||
| export interface GuildBanAddPayload { | ||||
|   guild_id: string | ||||
|   user: UserPayload | ||||
| } | ||||
| 
 | ||||
| interface GuildBanRemove { | ||||
| export interface GuildBanRemovePayload { | ||||
|   guild_id: string | ||||
|   user: UserPayload | ||||
| } | ||||
| 
 | ||||
| interface GuildEmojiUpdate { | ||||
| export interface GuildEmojiUpdatePayload { | ||||
|   guild_id: string | ||||
|   emojis: [] | ||||
| } | ||||
| 
 | ||||
| interface GuildIntegrationsUpdate { | ||||
| export interface GuildIntegrationsUpdatePayload { | ||||
|   guild_id: string | ||||
| } | ||||
| 
 | ||||
| interface GuildMemberAddExtra { | ||||
| export interface GuildMemberAddPayload { | ||||
|   guild_id: string | ||||
| } | ||||
| 
 | ||||
| interface GuildMemberRemove { | ||||
| export interface GuildMemberRemovePayload { | ||||
|   guild_id: string | ||||
|   user: UserPayload | ||||
| } | ||||
| interface GuildMemberUpdate { | ||||
| export interface GuildMemberUpdatePayload { | ||||
|   guild_id: string | ||||
|   roles: string[] | ||||
|   user: UserPayload | ||||
|  | @ -209,7 +209,7 @@ interface GuildMemberUpdate { | |||
|   premium_since?: string | undefined | ||||
| } | ||||
| 
 | ||||
| interface GuildMemberChunk { | ||||
| export interface GuildMemberChunkPayload { | ||||
|   guild_id: string | ||||
|   members: MemberPayload[] | ||||
|   chunk_index: number | ||||
|  | @ -219,22 +219,22 @@ interface GuildMemberChunk { | |||
|   nonce?: string | ||||
| } | ||||
| 
 | ||||
| interface GuildRoleCreate { | ||||
| export interface GuildRoleCreatePayload { | ||||
|   guild_id: string | ||||
|   role: RolePayload | ||||
| } | ||||
| 
 | ||||
| interface GuildRoleUpdate { | ||||
| export interface GuildRoleUpdatePayload { | ||||
|   guild_id: string | ||||
|   role: RolePayload | ||||
| } | ||||
| 
 | ||||
| interface GuildRoleDelete { | ||||
| export interface GuildRoleDeletePayload { | ||||
|   guild_id: string | ||||
|   role_id: string | ||||
| } | ||||
| 
 | ||||
| interface InviteCreate { | ||||
| export interface InviteCreatePayload { | ||||
|   channel_id: string | ||||
|   code: string | ||||
|   created_at: string | ||||
|  | @ -248,25 +248,25 @@ interface InviteCreate { | |||
|   uses: number | ||||
| } | ||||
| 
 | ||||
| interface InviteDelete { | ||||
| export interface InviteDeletePayload { | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
|   code: string | ||||
| } | ||||
| 
 | ||||
| interface MessageDelete { | ||||
| export interface MessageDeletePayload { | ||||
|   id: string | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
| } | ||||
| 
 | ||||
| interface MessageDeleteBulk { | ||||
| export interface MessageDeleteBulkPayload { | ||||
|   ids: string[] | ||||
|   channel_id: string | ||||
|   guild_id: string | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionAdd { | ||||
| export interface MessageReactionAddPayload { | ||||
|   user_id: string | ||||
|   channel_id: string | ||||
|   message_id: string | ||||
|  | @ -274,7 +274,7 @@ interface MessageReactionAdd { | |||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionRemove { | ||||
| export interface MessageReactionRemovePayload { | ||||
|   user_id: string | ||||
|   channel_id: string | ||||
|   message_id: string | ||||
|  | @ -282,21 +282,13 @@ interface MessageReactionRemove { | |||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionRemoveAll { | ||||
| export interface MessageReactionRemoveAllPayload { | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
|   message_id: string | ||||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionRemove { | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
|   message_id: string | ||||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface PresenceUpdate { | ||||
| export interface PresenceUpdatePayload { | ||||
|   user: UserPayload | ||||
|   guild_id: string | ||||
|   status: string | ||||
|  | @ -304,76 +296,7 @@ interface PresenceUpdate { | |||
|   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?: EmojiPayload | 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 { | ||||
| export interface TypeStart { | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
|   user_id: string | ||||
|  | @ -381,16 +304,22 @@ interface TypeStart { | |||
|   member?: MemberPayload | ||||
| } | ||||
| 
 | ||||
| interface VoiceServerUpdate { | ||||
| export interface VoiceServerUpdatePayload { | ||||
|   token: string | ||||
|   guild_id: string | ||||
|   endpoint: string | ||||
| } | ||||
| 
 | ||||
| interface WebhooksUpdate { | ||||
| export interface WebhooksUpdatePayload { | ||||
|   guild_id: string | ||||
|   channel_id: string | ||||
| } | ||||
| 
 | ||||
| // https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields
 | ||||
| export { GatewayCloseCodes, GatewayOpcodes, GatewayIntents, GatewayEvents } | ||||
| export { | ||||
|   GatewayCloseCodes, | ||||
|   GatewayOpcodes, | ||||
|   GatewayIntents, | ||||
|   GatewayEvents, | ||||
|   UpdateStatus | ||||
| } | ||||
|  |  | |||
|  | @ -1,396 +0,0 @@ | |||
| // https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway
 | ||||
| // https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
 | ||||
| import { EmojiPayload } from './emoji.ts' | ||||
| import { MemberPayload } from './guild.ts' | ||||
| import { ActivityPayload, PresenceUpdatePayload } from './presence.ts' | ||||
| import { RolePayload } from './role.ts' | ||||
| import { UserPayload } from './user.ts' | ||||
| 
 | ||||
| /** | ||||
|  * Gateway OPcodes from Discord docs. | ||||
|  */ | ||||
| enum GatewayOpcodes { // 문서를 확인해본 결과 Opcode 5번은 비어있다. - UnderC -
 | ||||
|   DISPATCH = 0, | ||||
|   HEARTBEAT = 1, | ||||
|   IDENTIFY = 2, | ||||
|   PRESENCE_UPDATE = 3, | ||||
|   VOICE_STATE_UPDATE = 4, | ||||
|   RESUME = 6, | ||||
|   RECONNECT = 7, | ||||
|   REQUEST_GUILD_MEMBERS = 8, | ||||
|   INVALID_SESSION = 9, | ||||
|   HELLO = 10, | ||||
|   HEARTBEAT_ACK = 11 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Gateway Close Codes from Discord docs. | ||||
|  */ | ||||
| enum GatewayCloseCodes { | ||||
|   UNKNOWN_ERROR = 4000, | ||||
|   UNKNOWN_OPCODE = 4001, | ||||
|   DECODE_ERROR = 4002, | ||||
|   NOT_AUTHENTICATED = 4003, | ||||
|   AUTHENTICATION_FAILED = 4004, | ||||
|   ALREADY_AUTHENTICATED = 4005, | ||||
|   INVALID_SEQ = 4007, | ||||
|   RATE_LIMITED = 4008, | ||||
|   SESSION_TIMED_OUT = 4009, | ||||
|   INVALID_SHARD = 4010, | ||||
|   SHARDING_REQUIRED = 4011, | ||||
|   INVALID_API_VERSION = 4012, | ||||
|   INVALID_INTENTS = 4013, | ||||
|   DISALLOWED_INTENTS = 4014 | ||||
| } | ||||
| 
 | ||||
| enum GatewayIntents { | ||||
|   GUILDS = 1 << 0, | ||||
|   GUILD_MEMBERS = 1 << 1, | ||||
|   GUILD_BANS = 1 << 2, | ||||
|   GUILD_EMOJIS = 1 << 3, | ||||
|   GUILD_INTEGRATIONS = 1 << 4, | ||||
|   GUILD_WEBHOOKS = 1 << 5, | ||||
|   GUILD_INVITES = 1 << 6, | ||||
|   GUILD_VOICE_STATES = 1 << 7, | ||||
|   GUILD_PRESENCES = 1 << 8, | ||||
|   GUILD_MESSAGES = 1 << 9, | ||||
|   GUILD_MESSAGE_REACTIONS = 1 << 10, | ||||
|   GUILD_MESSAGE_TYPING = 1 << 11, | ||||
|   DIRECT_MESSAGES = 1 << 12, | ||||
|   DIRECT_MESSAGE_REACTIONS = 1 << 13, | ||||
|   DIRECT_MESSAGE_TYPING = 1 << 13 | ||||
| } | ||||
| 
 | ||||
| enum GatewayEvents { | ||||
|   Ready = 'READY', | ||||
|   Resumed = 'RESUMED', | ||||
|   Reconnect = 'RECONNECT', | ||||
|   Channel_Create = 'CHANNEL_CREATE', | ||||
|   Channel_Update = 'CHANNEL_UPDATE', | ||||
|   Channel_Delete = 'CHANNEL_DELETE', | ||||
|   Channel_Pins_Update = 'CHANNEL_PINS_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 = 'MESSAGE_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_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: 'darwin' | 'windows' | 'linux' | 'custom os' | ||||
|   $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 | ||||
|   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: UserPayload | ||||
|   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: UserPayload | ||||
| } | ||||
| 
 | ||||
| interface GuildBanRemove { | ||||
|   guild_id: string | ||||
|   user: UserPayload | ||||
| } | ||||
| 
 | ||||
| interface GuildEmojiUpdate { | ||||
|   guild_id: string | ||||
|   emojis: [] | ||||
| } | ||||
| 
 | ||||
| interface GuildIntegrationsUpdate { | ||||
|   guild_id: string | ||||
| } | ||||
| 
 | ||||
| interface GuildMemberAddExtra { | ||||
|   guild_id: string | ||||
| } | ||||
| 
 | ||||
| interface GuildMemberRemove { | ||||
|   guild_id: string | ||||
|   user: UserPayload | ||||
| } | ||||
| interface GuildMemberUpdate { | ||||
|   guild_id: string | ||||
|   roles: string[] | ||||
|   user: UserPayload | ||||
|   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: RolePayload | ||||
| } | ||||
| 
 | ||||
| interface GuildRoleUpdate { | ||||
|   guild_id: string | ||||
|   role: RolePayload | ||||
| } | ||||
| 
 | ||||
| interface GuildRoleDelete { | ||||
|   guild_id: string | ||||
|   role_id: string | ||||
| } | ||||
| 
 | ||||
| interface InviteCreate { | ||||
|   channel_id: string | ||||
|   code: string | ||||
|   created_at: string | ||||
|   guild_id?: string | ||||
|   inviter?: UserPayload | ||||
|   max_age: number | ||||
|   max_uses: number | ||||
|   target_user?: UserPayload | ||||
|   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: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionRemove { | ||||
|   user_id: string | ||||
|   channel_id: string | ||||
|   message_id: string | ||||
|   guild_id?: string | ||||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionRemoveAll { | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
|   message_id: string | ||||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface MessageReactionRemove { | ||||
|   channel_id: string | ||||
|   guild_id?: string | ||||
|   message_id: string | ||||
|   emoji: EmojiPayload | ||||
| } | ||||
| 
 | ||||
| interface PresenceUpdate { | ||||
|   user: UserPayload | ||||
|   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?: EmojiPayload | 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?: MemberPayload | ||||
| } | ||||
| 
 | ||||
| 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 } | ||||
|  | @ -39,3 +39,5 @@ enum PermissionFlags { | |||
|   MANAGE_WEBHOOKS = 0x20000000, | ||||
|   MANAGE_EMOJIS = 0x40000000 | ||||
| } | ||||
| 
 | ||||
| export { PermissionFlags } | ||||
|  |  | |||
|  | @ -1,14 +1,4 @@ | |||
| import { UserPayload } from './user.ts' | ||||
| 
 | ||||
| export interface PresenceUpdatePayload { | ||||
|   user: UserPayload | ||||
|   guild_id: string | ||||
|   status: string | ||||
|   activities: ActivityPayload | ||||
|   client_status: ClientStatus | ||||
| } | ||||
| 
 | ||||
| interface ClientStatus { | ||||
| export interface ClientStatus { | ||||
|   desktop?: string | ||||
|   mobile?: string | ||||
|   web?: string | ||||
|  | @ -31,30 +21,30 @@ export interface ActivityPayload { | |||
|   flags?: number | ||||
| } | ||||
| 
 | ||||
| interface ActivityTimestamps { | ||||
| export interface ActivityTimestamps { | ||||
|   start?: number | ||||
|   end?: number | ||||
| } | ||||
| 
 | ||||
| interface ActivityEmoji { | ||||
| export interface ActivityEmoji { | ||||
|   name: string | ||||
|   id?: string | ||||
|   animated?: boolean | ||||
| } | ||||
| 
 | ||||
| interface ActivityParty { | ||||
| export interface ActivityParty { | ||||
|   id?: string | ||||
|   size?: number[] | ||||
| } | ||||
| 
 | ||||
| interface ActivityAssets { | ||||
| export interface ActivityAssets { | ||||
|   large_image?: string | ||||
|   large_text?: string | ||||
|   small_image?: string | ||||
|   small_text?: string | ||||
| } | ||||
| 
 | ||||
| interface ActivitySecrets { | ||||
| export interface ActivitySecrets { | ||||
|   join?: string | ||||
|   spectate?: string | ||||
|   match?: string | ||||
|  | @ -68,3 +58,5 @@ enum ActivityFlags { | |||
|   SYNC = 1 << 4, | ||||
|   PLAY = 1 << 5 | ||||
| } | ||||
| 
 | ||||
| export { ActivityFlags } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue