Fix some lint errors and make more runtime errors(for now)
This commit is contained in:
		
							parent
							
								
									98b202fb30
								
							
						
					
					
						commit
						00baa2759e
					
				
					 40 changed files with 853 additions and 1179 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -110,3 +110,6 @@ yarn.lock | ||||||
| # PRIVACY XDDDD | # PRIVACY XDDDD | ||||||
| src/test/config.ts | src/test/config.ts | ||||||
| .vscode | .vscode | ||||||
|  | 
 | ||||||
|  | # macOS is shit xD | ||||||
|  | **/.DS_Store | ||||||
|  |  | ||||||
|  | @ -1,9 +1,10 @@ | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import getChannelByType from '../../utils/getChannelByType.ts' | import getChannelByType from '../../utils/getChannelByType.ts' | ||||||
|  | import { ChannelPayload } from '../../types/channel.ts' | ||||||
| 
 | 
 | ||||||
| export const channelCreate: GatewayEventHandler = async ( | export const channelCreate: GatewayEventHandler = async ( | ||||||
|   gateway: Gateway, |   gateway: Gateway, | ||||||
|   d: any |   d: ChannelPayload | ||||||
| ) => { | ) => { | ||||||
|   const channel = getChannelByType(gateway.client, d) |   const channel = getChannelByType(gateway.client, d) | ||||||
|   if (channel !== undefined) { |   if (channel !== undefined) { | ||||||
|  |  | ||||||
|  | @ -1,9 +1,10 @@ | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import { Channel } from '../../structures/channel.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, |   gateway: Gateway, | ||||||
|   d: any |   d: ChannelPayload | ||||||
| ) => { | ) => { | ||||||
|   const channel: Channel = await gateway.client.channels.get(d.id) |   const channel: Channel = await gateway.client.channels.get(d.id) | ||||||
|   if (channel !== undefined) { |   if (channel !== undefined) { | ||||||
|  |  | ||||||
|  | @ -1,19 +1,21 @@ | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import cache from '../../models/cache.ts' |  | ||||||
| import { TextChannel } from '../../structures/textChannel.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, |   gateway: Gateway, | ||||||
|   d: any |   d: ChannelPinsUpdatePayload | ||||||
| ) => { | ) => { | ||||||
|   const after: TextChannel = await gateway.client.channels.get(d.channel_id) |   const after: TextChannel = await gateway.client.channels.get(d.channel_id) | ||||||
|   if (after !== undefined) { |   if (after !== undefined) { | ||||||
|     const before = after.refreshFromData({ |     const before = after.refreshFromData({ | ||||||
|       last_pin_timestamp: d.last_pin_timestamp |       last_pin_timestamp: d.last_pin_timestamp | ||||||
|     }) |     }) | ||||||
|     const raw = await gateway.client.channels._get(d.channel_id) ; |     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 })) |     await gateway.client.channels.set( | ||||||
|  |       after.id, | ||||||
|  |       Object.assign(raw, { last_pin_timestamp: d.last_pin_timestamp }) | ||||||
|  |     ) | ||||||
|     gateway.client.emit('channelPinsUpdate', before, after) |     gateway.client.emit('channelPinsUpdate', before, after) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,10 +1,11 @@ | ||||||
| import { Channel } from '../../structures/channel.ts' | import { Channel } from '../../structures/channel.ts' | ||||||
|  | import { ChannelPayload } from '../../types/channel.ts' | ||||||
| import getChannelByType from '../../utils/getChannelByType.ts' | import getChannelByType from '../../utils/getChannelByType.ts' | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| 
 | 
 | ||||||
| export const channelUpdate: GatewayEventHandler = async ( | export const channelUpdate: GatewayEventHandler = async ( | ||||||
|   gateway: Gateway, |   gateway: Gateway, | ||||||
|   d: any |   d: ChannelPayload | ||||||
| ) => { | ) => { | ||||||
|   const oldChannel: Channel = await gateway.client.channels.get(d.id) |   const oldChannel: Channel = await gateway.client.channels.get(d.id) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,14 +1,19 @@ | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import cache from '../../models/cache.ts' |  | ||||||
| import { Guild } from '../../structures/guild.ts' | import { Guild } from '../../structures/guild.ts' | ||||||
| import { User } from '../../structures/user.ts' | import { User } from '../../structures/user.ts' | ||||||
|  | import { GuildBanAddPayload } from '../../types/gateway.ts' | ||||||
| 
 | 
 | ||||||
| export const guildBanAdd: GatewayEventHandler = (gateway: Gateway, d: any) => { | export const guildBanAdd: GatewayEventHandler = async ( | ||||||
|   const guild: Guild = cache.get('guild', d.guild_id) |   gateway: Gateway, | ||||||
|  |   d: GuildBanAddPayload | ||||||
|  | ) => { | ||||||
|  |   const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) | ||||||
|   const user: User = |   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) { |   if (guild !== undefined) { | ||||||
|  |     guild.members = guild.members?.filter(member => member.id !== d.user.id) | ||||||
|     gateway.client.emit('guildBanAdd', guild, user) |     gateway.client.emit('guildBanAdd', guild, user) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,10 +2,11 @@ import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import cache from '../../models/cache.ts' | import cache from '../../models/cache.ts' | ||||||
| import { Guild } from '../../structures/guild.ts' | import { Guild } from '../../structures/guild.ts' | ||||||
| import { User } from '../../structures/user.ts' | import { User } from '../../structures/user.ts' | ||||||
|  | import { GuildBanRemovePayload } from '../../types/gateway.ts' | ||||||
| 
 | 
 | ||||||
| export const guildBanRemove: GatewayEventHandler = ( | export const guildBanRemove: GatewayEventHandler = ( | ||||||
|   gateway: Gateway, |   gateway: Gateway, | ||||||
|   d: any |   d: GuildBanRemovePayload | ||||||
| ) => { | ) => { | ||||||
|   const guild: Guild = cache.get('guild', d.guild_id) |   const guild: Guild = cache.get('guild', d.guild_id) | ||||||
|   const user: User = |   const user: User = | ||||||
|  |  | ||||||
|  | @ -1,16 +1,19 @@ | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import { Guild } from '../../structures/guild.ts' | import { Guild } from '../../structures/guild.ts' | ||||||
| import { GuildPayload } from "../../types/guild.ts" | import { GuildPayload } from '../../types/guild.ts' | ||||||
| 
 | 
 | ||||||
| export const guildCreate: GatewayEventHandler = async(gateway: Gateway, d: any) => { | export const guildCreate: GatewayEventHandler = async ( | ||||||
|   let guild: Guild | void = await gateway.client.guilds.get(d.id) |   gateway: Gateway, | ||||||
|  |   d: GuildPayload | ||||||
|  | ) => { | ||||||
|  |   let guild: Guild | undefined = await gateway.client.guilds.get(d.id) | ||||||
|   if (guild !== undefined) { |   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
 |     // 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) |     await gateway.client.guilds.set(d.id, d) | ||||||
|     guild.refreshFromData(d) |     guild.refreshFromData(d) | ||||||
|   } else { |   } else { | ||||||
|     await gateway.client.guilds.set(d.id, d) |     await gateway.client.guilds.set(d.id, d) | ||||||
|     guild = new Guild(gateway.client, d as GuildPayload) |     guild = new Guild(gateway.client, d) | ||||||
|     gateway.client.emit('guildCreate', guild) |     gateway.client.emit('guildCreate', guild) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,12 @@ | ||||||
| import { Guild } from '../../structures/guild.ts' | import { Guild } from '../../structures/guild.ts' | ||||||
|  | import { GuildPayload } from '../../types/guild.ts' | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| 
 | 
 | ||||||
| export const guildDelte: GatewayEventHandler = async (gateway: Gateway, d: any) => { | export const guildDelte: GatewayEventHandler = async ( | ||||||
|   const guild: Guild | void = await gateway.client.guilds.get(d.id) |   gateway: Gateway, | ||||||
|  |   d: GuildPayload | ||||||
|  | ) => { | ||||||
|  |   const guild: Guild | undefined = await gateway.client.guilds.get(d.id) | ||||||
| 
 | 
 | ||||||
|   if (guild !== undefined) { |   if (guild !== undefined) { | ||||||
|     guild.refreshFromData(d) |     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 { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import { Guild } from '../../structures/guild.ts' | import { Guild } from '../../structures/guild.ts' | ||||||
|  | import { GuildPayload } from '../../types/guild.ts' | ||||||
| 
 | 
 | ||||||
| export const guildUpdate: GatewayEventHandler = async(gateway: Gateway, d: any) => { | export const guildUpdate: GatewayEventHandler = async ( | ||||||
|   const before: Guild | void = await gateway.client.guilds.get(d.id) |   gateway: Gateway, | ||||||
|   if(!before) return |   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) |   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) |   gateway.client.emit('guildUpdate', before, after) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,21 +1,22 @@ | ||||||
| import { Channel } from "../../structures/channel.ts" | import { Channel } from '../../structures/channel.ts' | ||||||
| import { Message } from "../../structures/message.ts" | import { Message } from '../../structures/message.ts' | ||||||
| import { MessageMentions } from "../../structures/MessageMentions.ts" | import { MessageMentions } from '../../structures/MessageMentions.ts' | ||||||
| import { TextChannel } from "../../structures/textChannel.ts" | import { TextChannel } from '../../structures/textChannel.ts' | ||||||
| import { User } from "../../structures/user.ts" | import { User } from '../../structures/user.ts' | ||||||
| import { MessagePayload } from "../../types/channel.ts" | import { MessagePayload } from '../../types/channel.ts' | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| 
 | 
 | ||||||
| export const messageCreate: GatewayEventHandler = async( | export const messageCreate: GatewayEventHandler = async ( | ||||||
|   gateway: Gateway, |   gateway: Gateway, | ||||||
|   d: MessagePayload |   d: MessagePayload | ||||||
| ) => { | ) => { | ||||||
|   let channel = await gateway.client.channels.get(d.channel_id) |   let channel = (await gateway.client.channels.get(d.channel_id)) as TextChannel | ||||||
|   // Fetch the channel if not cached
 |   // Fetch the channel if not cached
 | ||||||
|   if(!channel) channel = (await gateway.client.channels.fetch(d.channel_id) as any) as TextChannel |   if (channel === undefined) | ||||||
|   let user = new User(gateway.client, d.author) |     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) |   await gateway.client.users.set(d.author.id, d.author) | ||||||
|   let mentions = new MessageMentions() |   const mentions = new MessageMentions() | ||||||
|   let message = new Message(gateway.client, d, channel, user, mentions) |   const message = new Message(gateway.client, d, channel, user, mentions) | ||||||
|   gateway.client.emit('messageCreate', message) |   gateway.client.emit('messageCreate', message) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,11 +5,14 @@ import { | ||||||
|   DISCORD_API_VERSION |   DISCORD_API_VERSION | ||||||
| } from '../consts/urlsAndVersions.ts' | } from '../consts/urlsAndVersions.ts' | ||||||
| import { GatewayResponse } from '../types/gatewayResponse.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 { gatewayHandlers } from './handlers/index.ts' | ||||||
| import { GATEWAY_BOT } from '../types/endpoint.ts' | import { GATEWAY_BOT } from '../types/endpoint.ts' | ||||||
| import { GatewayBotPayload } from "../types/gatewayBot.ts" | import { GatewayCache } from '../managers/GatewayCache.ts' | ||||||
| import { GatewayCache } from "../managers/GatewayCache.ts" |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Handles Discord gateway connection. |  * Handles Discord gateway connection. | ||||||
|  | @ -51,7 +54,7 @@ class Gateway { | ||||||
| 
 | 
 | ||||||
|   private onopen (): void { |   private onopen (): void { | ||||||
|     this.connected = true |     this.connected = true | ||||||
|     this.debug("Connected to Gateway!") |     this.debug('Connected to Gateway!') | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async onmessage (event: MessageEvent): Promise<void> { |   private async onmessage (event: MessageEvent): Promise<void> { | ||||||
|  | @ -69,12 +72,15 @@ class Gateway { | ||||||
|     switch (op) { |     switch (op) { | ||||||
|       case GatewayOpcodes.HELLO: |       case GatewayOpcodes.HELLO: | ||||||
|         this.heartbeatInterval = d.heartbeat_interval |         this.heartbeatInterval = d.heartbeat_interval | ||||||
|         this.debug(`Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}`) |         this.debug( | ||||||
|  |           `Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}` | ||||||
|  |         ) | ||||||
|         this.heartbeatIntervalID = setInterval(() => { |         this.heartbeatIntervalID = setInterval(() => { | ||||||
|           if (this.heartbeatServerResponded) { |           if (this.heartbeatServerResponded) { | ||||||
|             this.heartbeatServerResponded = false |             this.heartbeatServerResponded = false | ||||||
|           } else { |           } else { | ||||||
|             clearInterval(this.heartbeatIntervalID) |             clearInterval(this.heartbeatIntervalID) | ||||||
|  |             // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|             this.reconnect() |             this.reconnect() | ||||||
|             return |             return | ||||||
|           } |           } | ||||||
|  | @ -89,10 +95,12 @@ class Gateway { | ||||||
|         }, this.heartbeatInterval) |         }, this.heartbeatInterval) | ||||||
| 
 | 
 | ||||||
|         if (!this.initialized) { |         if (!this.initialized) { | ||||||
|  |           // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|           this.sendIdentify() |           this.sendIdentify() | ||||||
|           this.initialized = true |           this.initialized = true | ||||||
|         } else { |         } else { | ||||||
|           console.log("Calling Resume") |           console.log('Calling Resume') | ||||||
|  |           // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|           this.sendResume() |           this.sendResume() | ||||||
|         } |         } | ||||||
|         break |         break | ||||||
|  | @ -100,13 +108,16 @@ class Gateway { | ||||||
|       case GatewayOpcodes.HEARTBEAT_ACK: |       case GatewayOpcodes.HEARTBEAT_ACK: | ||||||
|         this.heartbeatServerResponded = true |         this.heartbeatServerResponded = true | ||||||
|         this.client.ping = Date.now() - this.lastPingTimestamp |         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 |         break | ||||||
| 
 | 
 | ||||||
|       case GatewayOpcodes.INVALID_SESSION: |       case GatewayOpcodes.INVALID_SESSION: | ||||||
|         // Because we know this gonna be bool
 |         // Because we know this gonna be bool
 | ||||||
|         this.debug(`Invalid Session! Identifying with forced new session`) |         this.debug(`Invalid Session! Identifying with forced new session`) | ||||||
|         // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
 |         // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
 | ||||||
|  |         // eslint-disable-next-line @typescript-eslint/promise-function-async
 | ||||||
|         setTimeout(() => this.sendIdentify(true), 3000) |         setTimeout(() => this.sendIdentify(true), 3000) | ||||||
|         break |         break | ||||||
| 
 | 
 | ||||||
|  | @ -114,7 +125,7 @@ class Gateway { | ||||||
|         this.heartbeatServerResponded = true |         this.heartbeatServerResponded = true | ||||||
|         if (s !== null) { |         if (s !== null) { | ||||||
|           this.sequenceID = s |           this.sequenceID = s | ||||||
|           await this.cache.set("seq", s) |           await this.cache.set('seq', s) | ||||||
|         } |         } | ||||||
|         if (t !== null && t !== undefined) { |         if (t !== null && t !== undefined) { | ||||||
|           const handler = gatewayHandlers[t] |           const handler = gatewayHandlers[t] | ||||||
|  | @ -129,11 +140,12 @@ class Gateway { | ||||||
|         // this.token = d.token
 |         // this.token = d.token
 | ||||||
|         this.sessionID = d.session_id |         this.sessionID = d.session_id | ||||||
|         this.sequenceID = d.seq |         this.sequenceID = d.seq | ||||||
|         await this.cache.set("seq", d.seq) |         await this.cache.set('seq', d.seq) | ||||||
|         await this.cache.set("session_id", this.sessionID) |         await this.cache.set('session_id', this.sessionID) | ||||||
|         break |         break | ||||||
|       } |       } | ||||||
|       case GatewayOpcodes.RECONNECT: { |       case GatewayOpcodes.RECONNECT: { | ||||||
|  |         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|         this.reconnect() |         this.reconnect() | ||||||
|         break |         break | ||||||
|       } |       } | ||||||
|  | @ -143,40 +155,45 @@ class Gateway { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private onclose (event: CloseEvent): void { |   private onclose (event: CloseEvent): void { | ||||||
|     this.debug("Connection Closed with code: " + event.code) |     this.debug(`Connection Closed with code: ${event.code}`) | ||||||
| 
 | 
 | ||||||
|     if(event.code == GatewayCloseCodes.UNKNOWN_ERROR) { |     if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) { | ||||||
|       this.debug("API has encountered Unknown Error. Reconnecting...") |       this.debug('API has encountered Unknown Error. Reconnecting...') | ||||||
|  |       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       this.reconnect() |       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!") |       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!") |       throw new Error("Invalid Payload was sent. This shouldn't happen!") | ||||||
|     } else if(event.code == GatewayCloseCodes.NOT_AUTHENTICATED) { |     } else if (event.code === GatewayCloseCodes.NOT_AUTHENTICATED) { | ||||||
|       throw new Error("Not Authorized: Payload was sent before Identifying.") |       throw new Error('Not Authorized: Payload was sent before Identifying.') | ||||||
|     } else if(event.code == GatewayCloseCodes.AUTHENTICATION_FAILED) { |     } else if (event.code === GatewayCloseCodes.AUTHENTICATION_FAILED) { | ||||||
|       throw new Error("Invalid Token provided!") |       throw new Error('Invalid Token provided!') | ||||||
|     } else if(event.code == GatewayCloseCodes.INVALID_SEQ) { |     } else if (event.code === GatewayCloseCodes.INVALID_SEQ) { | ||||||
|       this.debug("Invalid Seq was sent. Reconnecting.") |       this.debug('Invalid Seq was sent. Reconnecting.') | ||||||
|  |       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       this.reconnect() |       this.reconnect() | ||||||
|     } else if(event.code == GatewayCloseCodes.RATE_LIMITED) { |     } else if (event.code === GatewayCloseCodes.RATE_LIMITED) { | ||||||
|       throw new Error("You're ratelimited. Calm down.") |       throw new Error("You're ratelimited. Calm down.") | ||||||
|     } else if(event.code == GatewayCloseCodes.SESSION_TIMED_OUT) { |     } else if (event.code === GatewayCloseCodes.SESSION_TIMED_OUT) { | ||||||
|       this.debug("Session Timeout. Reconnecting.") |       this.debug('Session Timeout. Reconnecting.') | ||||||
|  |       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       this.reconnect(true) |       this.reconnect(true) | ||||||
|     } else if(event.code == GatewayCloseCodes.INVALID_SHARD) { |     } else if (event.code === GatewayCloseCodes.INVALID_SHARD) { | ||||||
|       this.debug("Invalid Shard was sent. Reconnecting.") |       this.debug('Invalid Shard was sent. Reconnecting.') | ||||||
|  |       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       this.reconnect() |       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!") |       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!") |       throw new Error("Invalid API Version was used. This shouldn't happen!") | ||||||
|     } else if(event.code == GatewayCloseCodes.INVALID_INTENTS) { |     } else if (event.code === GatewayCloseCodes.INVALID_INTENTS) { | ||||||
|       throw new Error("Invalid Intents") |       throw new Error('Invalid Intents') | ||||||
|     } else if(event.code == GatewayCloseCodes.DISALLOWED_INTENTS) { |     } else if (event.code === GatewayCloseCodes.DISALLOWED_INTENTS) { | ||||||
|       throw new Error("Given Intents aren't allowed") |       throw new Error("Given Intents aren't allowed") | ||||||
|     } else { |     } 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() |       this.reconnect() | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -186,20 +203,25 @@ class Gateway { | ||||||
|     console.log(eventError) |     console.log(eventError) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async sendIdentify (forceNewSession?: boolean) { |   private async sendIdentify (forceNewSession?: boolean): Promise<void> { | ||||||
|     this.debug("Fetching /gateway/bot...") |     this.debug('Fetching /gateway/bot...') | ||||||
|     const info = await this.client.rest.get(GATEWAY_BOT()) as GatewayBotPayload |     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") |     if (info.session_start_limit.remaining === 0) | ||||||
|     this.debug("Recommended Shards: " + info.shards) |       throw new Error( | ||||||
|     this.debug("=== Session Limit Info ===") |         `Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms` | ||||||
|     this.debug(`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`) |       ) | ||||||
|  |     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`) |     this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`) | ||||||
|     if(!forceNewSession) { |     if (forceNewSession === undefined || !forceNewSession) { | ||||||
|       let sessionIDCached = await this.cache.get("session_id") |       const sessionIDCached = await this.cache.get('session_id') | ||||||
|       if(sessionIDCached) { |       if (sessionIDCached !== undefined) { | ||||||
|         this.debug("Found Cached SessionID: " + sessionIDCached) |         this.debug(`Found Cached SessionID: ${sessionIDCached}`) | ||||||
|         this.sessionID = sessionIDCached |         this.sessionID = sessionIDCached | ||||||
|         return this.sendResume() |         return await this.sendResume() | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this.websocket.send( |     this.websocket.send( | ||||||
|  | @ -231,30 +253,30 @@ class Gateway { | ||||||
| 
 | 
 | ||||||
|   private async sendResume (): Promise<void> { |   private async sendResume (): Promise<void> { | ||||||
|     this.debug(`Preparing to resume with Session: ${this.sessionID}`) |     this.debug(`Preparing to resume with Session: ${this.sessionID}`) | ||||||
|     if(this.sequenceID === undefined) { |     if (this.sequenceID === undefined) { | ||||||
|       let cached = await this.cache.get("seq") |       const cached = await this.cache.get('seq') | ||||||
|       if(cached) this.sequenceID = typeof cached == "string" ? parseInt(cached) : cached |       if (cached !== undefined) | ||||||
|  |         this.sequenceID = typeof cached === 'string' ? parseInt(cached) : cached | ||||||
|     } |     } | ||||||
|     const resumePayload = { |     const resumePayload = { | ||||||
|       op: GatewayOpcodes.RESUME, |       op: GatewayOpcodes.RESUME, | ||||||
|       d: { |       d: { | ||||||
|         token: this.token, |         token: this.token, | ||||||
|         session_id: this.sessionID, |         session_id: this.sessionID, | ||||||
|         seq: this.sequenceID || null |         seq: this.sequenceID ?? null | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this.websocket.send( |     this.websocket.send(JSON.stringify(resumePayload)) | ||||||
|       JSON.stringify(resumePayload) |  | ||||||
|     ) |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   debug(msg: string) { |   debug (msg: string): void { | ||||||
|     this.client.debug("Gateway", msg) |     this.client.debug('Gateway', msg) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async reconnect(forceNew?: boolean) { |   async reconnect (forceNew?: boolean): Promise<void> { | ||||||
|     clearInterval(this.heartbeatIntervalID) |     clearInterval(this.heartbeatIntervalID) | ||||||
|     if(forceNew) await this.cache.delete("session_id") |     if (forceNew === undefined || !forceNew) | ||||||
|  |       await this.cache.delete('session_id') | ||||||
|     this.close() |     this.close() | ||||||
|     this.initWebsocket() |     this.initWebsocket() | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -1,32 +1,31 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { Base } from "../structures/base.ts"; |  | ||||||
| 
 | 
 | ||||||
| export class BaseManager<T, T2> { | export class BaseManager<T, T2> { | ||||||
|   client: Client |   client: Client | ||||||
|   cacheName: string |   cacheName: string | ||||||
|   dataType: any |   DataType: any | ||||||
| 
 | 
 | ||||||
|   constructor(client: Client, cacheName: string, dataType: any) { |   constructor (client: Client, cacheName: string, DataType: any) { | ||||||
|     this.client = client |     this.client = client | ||||||
|     this.cacheName = cacheName |     this.cacheName = cacheName | ||||||
|     this.dataType = dataType |     this.DataType = DataType | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _get(key: string): Promise<T> { |   async _get (key: string): Promise<T | undefined> { | ||||||
|     return this.client.cache.get(this.cacheName, key) as Promise<T> |     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) |     const raw = await this._get(key) | ||||||
|     if(!raw) return |     if (raw === undefined) return | ||||||
|     return new this.dataType(this.client, raw) as any |     return new this.DataType(this.client, raw) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async set(key: string, value: T) { |   async set (key: string, value: T): Promise<any> { | ||||||
|     return this.client.cache.set(this.cacheName, key, value) |     return this.client.cache.set(this.cacheName, key, value) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async delete(key: string) { |   async delete (key: string): Promise<boolean> { | ||||||
|     return this.client.cache.delete(this.cacheName, key) |     return this.client.cache.delete(this.cacheName, key) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,26 +1,29 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { Channel } from "../structures/channel.ts"; | import { Channel } from '../structures/channel.ts' | ||||||
| import { User } from "../structures/user.ts"; | import { User } from '../structures/user.ts' | ||||||
| import { ChannelPayload } from "../types/channel.ts"; | import { ChannelPayload } from '../types/channel.ts' | ||||||
| import { CHANNEL } from "../types/endpoint.ts"; | import { CHANNEL } from '../types/endpoint.ts' | ||||||
| import { BaseManager } from "./BaseManager.ts"; | import { BaseManager } from './BaseManager.ts' | ||||||
| 
 | 
 | ||||||
| export class ChannelsManager extends BaseManager<ChannelPayload, Channel> { | export class ChannelsManager extends BaseManager<ChannelPayload, Channel> { | ||||||
|   constructor(client: Client) { |   constructor (client: Client) { | ||||||
|     super(client, "channels", User) |     super(client, 'channels', User) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Override get method as Generic
 |   // Override get method as Generic
 | ||||||
|   async get<T = Channel>(key: string): Promise<T> { |   async get<T = Channel> (key: string): Promise<T> { | ||||||
|     return new this.dataType(this.client, this._get(key)) as any |     return new this.DataType(this.client, this._get(key)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fetch(id: string): Promise<Channel> { |   async fetch (id: string): Promise<Channel> { | ||||||
|     return new Promise((res, rej) => { |     return await new Promise((resolve, reject) => { | ||||||
|       this.client.rest.get(CHANNEL(id)).then(data => { |       this.client.rest | ||||||
|         this.set(id, data as ChannelPayload) |         .get(CHANNEL(id)) | ||||||
|         res(new Channel(this.client, data as ChannelPayload)) |         .then(data => { | ||||||
|       }).catch(e => rej(e)) |           this.set(id, data as ChannelPayload) | ||||||
|  |           resolve(new Channel(this.client, data as ChannelPayload)) | ||||||
|  |         }) | ||||||
|  |         .catch(e => reject(e)) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,20 +1,23 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { Emoji } from "../structures/emoji.ts"; | import { Emoji } from '../structures/emoji.ts' | ||||||
| import { EmojiPayload } from "../types/emoji.ts"; | import { EmojiPayload } from '../types/emoji.ts' | ||||||
| import { CHANNEL } from "../types/endpoint.ts"; | import { CHANNEL } from '../types/endpoint.ts' | ||||||
| import { BaseManager } from "./BaseManager.ts"; | import { BaseManager } from './BaseManager.ts' | ||||||
| 
 | 
 | ||||||
| export class EmojisManager extends BaseManager<EmojiPayload, Emoji> { | export class EmojisManager extends BaseManager<EmojiPayload, Emoji> { | ||||||
|   constructor(client: Client) { |   constructor (client: Client) { | ||||||
|     super(client, "emojis", Emoji) |     super(client, 'emojis', Emoji) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fetch(id: string) { |   async fetch (id: string): Promise<Emoji> { | ||||||
|     return new Promise((res, rej) => { |     return await new Promise((resolve, reject) => { | ||||||
|       this.client.rest.get(CHANNEL(id)).then(data => { |       this.client.rest | ||||||
|         this.set(id, data as EmojiPayload) |         .get(CHANNEL(id)) | ||||||
|         res(new Emoji(this.client, data as EmojiPayload)) |         .then(data => { | ||||||
|       }).catch(e => rej(e)) |           this.set(id, data as EmojiPayload) | ||||||
|  |           resolve(new Emoji(this.client, data as EmojiPayload)) | ||||||
|  |         }) | ||||||
|  |         .catch(e => reject(e)) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,24 +1,27 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| 
 | 
 | ||||||
| export class GatewayCache { | export class GatewayCache { | ||||||
|     client: Client |   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 |     this.client = client | ||||||
|         if(cacheName) this.cacheName = cacheName |     if (cacheName !== undefined) this.cacheName = cacheName | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
|     get(key: string) { |   async get (key: string): Promise<undefined | any> { | ||||||
|         return this.client.cache.get(this.cacheName, key) |     const result = await this.client.cache.get(this.cacheName, key) | ||||||
|     } |     return result | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     set(key: string, value: any) { |   async set (key: string, value: any): Promise<any> { | ||||||
|         return this.client.cache.set(this.cacheName, key, value) |     const result = await this.client.cache.set(this.cacheName, key, value) | ||||||
|     } |     return result | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     delete(key: string) { |   async delete (key: string): Promise<boolean> { | ||||||
|         console.log(`[GatewayCache] DEL ${key}`) |     console.log(`[GatewayCache] DEL ${key}`) | ||||||
|         return this.client.cache.delete(this.cacheName, key) |     const result = await this.client.cache.delete(this.cacheName, key) | ||||||
|     } |     return result | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | @ -1,20 +1,23 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { Guild } from "../structures/guild.ts"; | import { Guild } from '../structures/guild.ts' | ||||||
| import { GUILD } from "../types/endpoint.ts"; | import { GUILD } from '../types/endpoint.ts' | ||||||
| import { GuildPayload } from "../types/guild.ts"; | import { GuildPayload } from '../types/guild.ts' | ||||||
| import { BaseManager } from "./BaseManager.ts"; | import { BaseManager } from './BaseManager.ts' | ||||||
| 
 | 
 | ||||||
| export class GuildManager extends BaseManager<GuildPayload, Guild> { | export class GuildManager extends BaseManager<GuildPayload, Guild> { | ||||||
|   constructor(client: Client) { |   constructor (client: Client) { | ||||||
|     super(client, "guilds", Guild) |     super(client, 'guilds', Guild) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fetch(id: string) { |   async fetch (id: string): Promise<Guild> { | ||||||
|     return new Promise((res, rej) => { |     return await new Promise((resolve, reject) => { | ||||||
|       this.client.rest.get(GUILD(id)).then(data => { |       this.client.rest | ||||||
|         this.set(id, data as GuildPayload) |         .get(GUILD(id)) | ||||||
|         res(new Guild(this.client, data as GuildPayload)) |         .then(data => { | ||||||
|       }).catch(e => rej(e)) |           this.set(id, data as GuildPayload) | ||||||
|  |           resolve(new Guild(this.client, data as GuildPayload)) | ||||||
|  |         }) | ||||||
|  |         .catch(e => reject(e)) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,29 +1,43 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { Message } from "../structures/message.ts"; | import { Message } from '../structures/message.ts' | ||||||
| import { MessageMentions } from "../structures/MessageMentions.ts"; | import { MessageMentions } from '../structures/MessageMentions.ts' | ||||||
| import { User } from "../structures/user.ts"; | import { User } from '../structures/user.ts' | ||||||
| import { MessagePayload } from "../types/channel.ts"; | import { MessagePayload } from '../types/channel.ts' | ||||||
| import { CHANNEL_MESSAGE } from "../types/endpoint.ts"; | import { CHANNEL_MESSAGE } from '../types/endpoint.ts' | ||||||
| import { UserPayload } from "../types/user.ts"; | import { BaseManager } from './BaseManager.ts' | ||||||
| import { BaseManager } from "./BaseManager.ts"; |  | ||||||
| 
 | 
 | ||||||
| export class MessagesManager extends BaseManager<MessagePayload, Message> { | export class MessagesManager extends BaseManager<MessagePayload, Message> { | ||||||
|   constructor(client: Client) { |   constructor (client: Client) { | ||||||
|     super(client, "messages", Message) |     super(client, 'messages', Message) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fetch(channelID: string, id: string) { |   async fetch (channelID: string, id: string): Promise<Message> { | ||||||
|     return new Promise((res, rej) => { |     return await new Promise((resolve, reject) => { | ||||||
|       this.client.rest.get(CHANNEL_MESSAGE(channelID, id)).then(async data => { |       this.client.rest | ||||||
|         this.set(id, data as MessagePayload) |         .get(CHANNEL_MESSAGE(channelID, id)) | ||||||
|         let channel = await this.client.channels.get(channelID) |         .then(async data => { | ||||||
|         if(!channel) channel = await this.client.channels.fetch(channelID) |           this.set(id, data as MessagePayload) | ||||||
|         let author = new User(this.client, (data as MessagePayload).author as UserPayload) |           let channel = await this.client.channels.get(channelID) | ||||||
|         await this.client.users.set(author.id, (data as MessagePayload).author) |           if (channel === undefined) | ||||||
|         // TODO: Make this thing work (MessageMentions)
 |             channel = await this.client.channels.fetch(channelID) | ||||||
|         let mentions = new MessageMentions() |           const author = new User(this.client, (data as MessagePayload).author) | ||||||
|         res(new Message(this.client, data as MessagePayload, channel, author, mentions)) |           await this.client.users.set( | ||||||
|       }).catch(e => rej(e)) |             author.id, | ||||||
|  |             (data as MessagePayload).author | ||||||
|  |           ) | ||||||
|  |           // TODO: Make this thing work (MessageMentions)
 | ||||||
|  |           const mentions = new MessageMentions() | ||||||
|  |           resolve( | ||||||
|  |             new Message( | ||||||
|  |               this.client, | ||||||
|  |               data as MessagePayload, | ||||||
|  |               channel, | ||||||
|  |               author, | ||||||
|  |               mentions | ||||||
|  |             ) | ||||||
|  |           ) | ||||||
|  |         }) | ||||||
|  |         .catch(e => reject(e)) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,25 +1,27 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { Guild } from "../structures/guild.ts"; | import { Guild } from '../structures/guild.ts' | ||||||
| import { Role } from "../structures/role.ts"; | import { Role } from '../structures/role.ts' | ||||||
| import { User } from "../structures/user.ts"; | import { GUILD_ROLE } from '../types/endpoint.ts' | ||||||
| import { GUILD_ROLE } from "../types/endpoint.ts"; | import { RolePayload } from '../types/role.ts' | ||||||
| import { RolePayload } from "../types/role.ts"; | import { BaseManager } from './BaseManager.ts' | ||||||
| import { BaseManager } from "./BaseManager.ts"; |  | ||||||
| 
 | 
 | ||||||
| export class RolesManager extends BaseManager<RolePayload, Role> { | export class RolesManager extends BaseManager<RolePayload, Role> { | ||||||
|   guild: Guild |   guild: Guild | ||||||
| 
 | 
 | ||||||
|   constructor(client: Client, guild: Guild) { |   constructor (client: Client, guild: Guild) { | ||||||
|     super(client, "roles:" + guild.id, Role) |     super(client, `roles:${guild.id}`, Role) | ||||||
|     this.guild = guild |     this.guild = guild | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fetch(id: string) { |   async fetch (id: string): Promise<Role> { | ||||||
|     return new Promise((res, rej) => { |     return await new Promise((resolve, reject) => { | ||||||
|       this.client.rest.get(GUILD_ROLE(this.guild.id, id)).then(data => { |       this.client.rest | ||||||
|         this.set(id, data as RolePayload) |         .get(GUILD_ROLE(this.guild.id, id)) | ||||||
|         res(new Role(this.client, data as RolePayload)) |         .then(data => { | ||||||
|       }).catch(e => rej(e)) |           this.set(id, data as RolePayload) | ||||||
|  |           resolve(new Role(this.client, data as RolePayload)) | ||||||
|  |         }) | ||||||
|  |         .catch(e => reject(e)) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,20 +1,23 @@ | ||||||
| import { Client } from "../models/client.ts"; | import { Client } from '../models/client.ts' | ||||||
| import { User } from "../structures/user.ts"; | import { User } from '../structures/user.ts' | ||||||
| import { USER } from "../types/endpoint.ts"; | import { USER } from '../types/endpoint.ts' | ||||||
| import { UserPayload } from "../types/user.ts"; | import { UserPayload } from '../types/user.ts' | ||||||
| import { BaseManager } from "./BaseManager.ts"; | import { BaseManager } from './BaseManager.ts' | ||||||
| 
 | 
 | ||||||
| export class UserManager extends BaseManager<UserPayload, User> { | export class UserManager extends BaseManager<UserPayload, User> { | ||||||
|   constructor(client: Client) { |   constructor (client: Client) { | ||||||
|     super(client, "users", User) |     super(client, 'users', User) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fetch(id: string) { |   async fetch (id: string): Promise<User> { | ||||||
|     return new Promise((res, rej) => { |     return await new Promise((resolve, reject) => { | ||||||
|       this.client.rest.get(USER(id)).then(data => { |       this.client.rest | ||||||
|         this.set(id, data as UserPayload) |         .get(USER(id)) | ||||||
|         res(new User(this.client, data as UserPayload)) |         .then(data => { | ||||||
|       }).catch(e => rej(e)) |           this.set(id, data as UserPayload) | ||||||
|  |           resolve(new User(this.client, data as UserPayload)) | ||||||
|  |         }) | ||||||
|  |         .catch(e => reject(e)) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,13 +1,17 @@ | ||||||
| import { Collection } from "../utils/collection.ts"; | import { Collection } from '../utils/collection.ts' | ||||||
| import { Client } from "./client.ts"; | import { Client } from './client.ts' | ||||||
| import { connect, Redis, RedisConnectOptions } from "https://denopkg.com/keroxp/deno-redis/mod.ts"; | import { | ||||||
|  |   connect, | ||||||
|  |   Redis, | ||||||
|  |   RedisConnectOptions | ||||||
|  | } from 'https://denopkg.com/keroxp/deno-redis/mod.ts' | ||||||
| 
 | 
 | ||||||
| export interface ICacheAdapter { | export interface ICacheAdapter { | ||||||
|   client: Client |   client: Client | ||||||
|   get: (cacheName: string, key: string) => Promise<any> | any |   get: (cacheName: string, key: string) => Promise<undefined | any> | ||||||
|   set: (cacheName: string, key: string, value: any) => Promise<any> | any |   set: (cacheName: string, key: string, value: any) => Promise<any> | ||||||
|   delete: (cacheName: string, key: string) => Promise<boolean> | boolean |   delete: (cacheName: string, key: string) => Promise<boolean> | ||||||
|   array: (cacheName: string) => void | any[] | Promise<any[] | void> |   array: (cacheName: string) => Promise<any[] | undefined> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class DefaultCacheAdapter implements ICacheAdapter { | export class DefaultCacheAdapter implements ICacheAdapter { | ||||||
|  | @ -16,34 +20,34 @@ export class DefaultCacheAdapter implements ICacheAdapter { | ||||||
|     [name: string]: Collection<string, any> |     [name: string]: Collection<string, any> | ||||||
|   } = {} |   } = {} | ||||||
| 
 | 
 | ||||||
|   constructor(client: Client) { |   constructor (client: Client) { | ||||||
|     this.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] |     const cache = this.data[cacheName] | ||||||
|     if (!cache) return; |     if (cache === undefined) return | ||||||
|     return cache.get(key) |     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] |     let cache = this.data[cacheName] | ||||||
|     if (!cache) { |     if (cache === undefined) { | ||||||
|       this.data[cacheName] = new Collection() |       this.data[cacheName] = new Collection() | ||||||
|       cache = this.data[cacheName] |       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] |     const cache = this.data[cacheName] | ||||||
|     if (!cache) return false |     if (cache === undefined) return false | ||||||
|     return cache.delete(key) |     return cache.delete(key) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async array(cacheName: string) { |   async array (cacheName: string): Promise<any[] | undefined> { | ||||||
|     const cache = this.data[cacheName] |     const cache = this.data[cacheName] | ||||||
|     if (!cache) return |     if (cache === undefined) return | ||||||
|     return cache.array() |     return cache.array() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -54,45 +58,60 @@ export class RedisCacheAdapter implements ICacheAdapter { | ||||||
|   redis?: Redis |   redis?: Redis | ||||||
|   ready: boolean = false |   ready: boolean = false | ||||||
| 
 | 
 | ||||||
|   constructor(client: Client, options: RedisConnectOptions) { |   constructor (client: Client, options: RedisConnectOptions) { | ||||||
|     this.client = client |     this.client = client | ||||||
|     this._redis = connect(options) |     this._redis = connect(options) | ||||||
|     this._redis.then(redis => { |     this._redis.then( | ||||||
|       this.redis = redis |       redis => { | ||||||
|       this.ready = true |         this.redis = redis | ||||||
|     }) |         this.ready = true | ||||||
|  |       }, | ||||||
|  |       () => { | ||||||
|  |         // TODO: Make error for this
 | ||||||
|  |       } | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async _checkReady() { |   async _checkReady (): Promise<void> { | ||||||
|     if(!this.ready) return await this._redis; |     if (!this.ready) await this._redis | ||||||
|     else return; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async get(cacheName: string, key: string) { |   async get (cacheName: string, key: string): Promise<string | undefined> { | ||||||
|     await this._checkReady() |     await this._checkReady() | ||||||
|     let cache = await this.redis?.hget(cacheName, key) |     const cache = await this.redis?.hget(cacheName, key) | ||||||
|     if(!cache) return |     if (cache === undefined) return | ||||||
|     try { |     try { | ||||||
|       return JSON.parse(cache as string) |       return JSON.parse(cache) | ||||||
|     } catch(e) { return 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() |     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() |     await this._checkReady() | ||||||
|     let exists = await this.redis?.hexists(cacheName, key) |     const exists = await this.redis?.hexists(cacheName, key) | ||||||
|     if(!exists) return false |     if (exists === 0) return false | ||||||
|     await this.redis?.hdel(cacheName, key) |     await this.redis?.hdel(cacheName, key) | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async array(cacheName: string) { |   async array (cacheName: string): Promise<any[] | undefined> { | ||||||
|     await this._checkReady() |     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)) |     return data?.map((e: string) => JSON.parse(e)) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -3,12 +3,12 @@ import { GatewayIntents } from '../types/gateway.ts' | ||||||
| import { Gateway } from '../gateway/index.ts' | import { Gateway } from '../gateway/index.ts' | ||||||
| import { RESTManager } from './rest.ts' | import { RESTManager } from './rest.ts' | ||||||
| import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' | import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' | ||||||
| import { DefaultCacheAdapter, ICacheAdapter } from "./CacheAdapter.ts" | import { DefaultCacheAdapter, ICacheAdapter } from './CacheAdapter.ts' | ||||||
| import { UserManager } from "../managers/UsersManager.ts" | import { UserManager } from '../managers/UsersManager.ts' | ||||||
| import { GuildManager } from "../managers/GuildsManager.ts" | import { GuildManager } from '../managers/GuildsManager.ts' | ||||||
| import { EmojisManager } from "../managers/EmojisManager.ts" | import { EmojisManager } from '../managers/EmojisManager.ts' | ||||||
| import { ChannelsManager } from "../managers/ChannelsManager.ts" | import { ChannelsManager } from '../managers/ChannelsManager.ts' | ||||||
| import { MessagesManager } from "../managers/MessagesManager.ts" | import { MessagesManager } from '../managers/MessagesManager.ts' | ||||||
| 
 | 
 | ||||||
| /** Some Client Options to modify behaviour */ | /** Some Client Options to modify behaviour */ | ||||||
| export interface ClientOptions { | export interface ClientOptions { | ||||||
|  | @ -39,16 +39,16 @@ export class Client extends EventEmitter { | ||||||
|     super() |     super() | ||||||
|     this.token = options.token |     this.token = options.token | ||||||
|     this.intents = options.intents |     this.intents = options.intents | ||||||
|     if(options.cache) this.cache = options.cache |     if (options.cache !== undefined) this.cache = options.cache | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setAdapter(adapter: ICacheAdapter) { |   setAdapter (adapter: ICacheAdapter): Client { | ||||||
|     this.cache = adapter |     this.cache = adapter | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   debug(tag: string, msg: string) { |   debug (tag: string, msg: string): void { | ||||||
|     this.emit("debug", `[${tag}] ${msg}`) |     this.emit('debug', `[${tag}] ${msg}`) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|  | @ -57,16 +57,15 @@ export class Client extends EventEmitter { | ||||||
|    * @param intents Gateway intents in array. This is required. |    * @param intents Gateway intents in array. This is required. | ||||||
|    */ |    */ | ||||||
|   connect (token?: string, intents?: GatewayIntents[]): void { |   connect (token?: string, intents?: GatewayIntents[]): void { | ||||||
|     if(!token && this.token) token = this.token |     if (token === undefined && this.token !== undefined) token = this.token | ||||||
|     else if(!this.token && token) { |     else if (this.token === undefined && token !== undefined) { | ||||||
|       this.token = token |       this.token = token | ||||||
|     } |     } else throw new Error('No Token Provided') | ||||||
|     else throw new Error("No Token Provided") |     if (intents === undefined && this.intents !== undefined) | ||||||
|     if(!intents && this.intents) intents = this.intents |       intents = this.intents | ||||||
|     else if(intents && !this.intents) { |     else if (intents !== undefined && this.intents === undefined) { | ||||||
|       this.intents = intents |       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) |     this.gateway = new Gateway(this, token, intents) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import { delay } from "../utils/index.ts"; | import { delay } from '../utils/index.ts' | ||||||
| import * as baseEndpoints from "../consts/urlsAndVersions.ts"; | import * as baseEndpoints from '../consts/urlsAndVersions.ts' | ||||||
| import { Client } from "./client.ts"; | import { Client } from './client.ts' | ||||||
| 
 | 
 | ||||||
| export enum HttpResponseCode { | export enum HttpResponseCode { | ||||||
|   Ok = 200, |   Ok = 200, | ||||||
|  | @ -17,351 +17,376 @@ export enum HttpResponseCode { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type RequestMethods = | export type RequestMethods = | ||||||
|   | "get" |   | 'get' | ||||||
|   | "post" |   | 'post' | ||||||
|   | "put" |   | 'put' | ||||||
|   | "patch" |   | 'patch' | ||||||
|   | "head" |   | 'head' | ||||||
|   | "delete"; |   | 'delete' | ||||||
| 
 | 
 | ||||||
| export interface QueuedRequest { | export interface QueuedRequest { | ||||||
| 	callback: () => Promise< |   callback: () => Promise< | ||||||
| 		void | { |     | { | ||||||
| 			rateLimited: any; |         rateLimited: any | ||||||
| 			beforeFetch: boolean; |         beforeFetch: boolean | ||||||
| 			bucketID?: string | null; |         bucketID?: string | null | ||||||
| 		} |       } | ||||||
| 	>; |     | undefined | ||||||
| 	bucketID?: string | null; |   > | ||||||
| 	url: string; |   bucketID?: string | null | ||||||
|  |   url: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface RateLimitedPath { | export interface RateLimitedPath { | ||||||
| 	url: string; |   url: string | ||||||
| 	resetTimestamp: number; |   resetTimestamp: number | ||||||
| 	bucketID: string | null; |   bucketID: string | null | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class RESTManager { | export class RESTManager { | ||||||
| 	client: Client; |   client: Client | ||||||
| 	globallyRateLimited: boolean = false; |   globallyRateLimited: boolean = false | ||||||
| 	queueInProcess: boolean = false; |   queueInProcess: boolean = false | ||||||
| 	pathQueues: { [key: string]: QueuedRequest[] } = {}; |   pathQueues: { [key: string]: QueuedRequest[] } = {} | ||||||
| 	ratelimitedPaths = new Map<string, RateLimitedPath>(); |   ratelimitedPaths = new Map<string, RateLimitedPath>() | ||||||
| 
 | 
 | ||||||
| 	constructor(client: Client) { |   constructor (client: Client) { | ||||||
| 		this.client = client; |     this.client = client | ||||||
| 	} |     setTimeout(this.processRateLimitedPaths, 1000) | ||||||
| 
 |  | ||||||
| 	async processRateLimitedPaths() { |  | ||||||
| 		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(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	addToQueue(request: QueuedRequest) { |  | ||||||
| 		const route = request.url.substring(baseEndpoints.DISCORD_API_URL.length + 1); |  | ||||||
| 		const parts = route.split("/"); |  | ||||||
| 		// Remove the major param
 |  | ||||||
| 		parts.shift(); |  | ||||||
| 		const [id] = parts; |  | ||||||
| 	 |  | ||||||
| 		if (this.pathQueues[id]) { |  | ||||||
| 			this.pathQueues[id].push(request); |  | ||||||
| 		} else { |  | ||||||
| 			this.pathQueues[id] = [request]; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	async cleanupQueues() { |  | ||||||
| 		Object.entries(this.pathQueues).map(([key, value]) => { |  | ||||||
| 			if (!value.length) { |  | ||||||
| 				// Remove it entirely
 |  | ||||||
| 				delete this.pathQueues[key]; |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	async processQueue() { |  | ||||||
| 		if ( |  | ||||||
| 			(Object.keys(this.pathQueues).length) && !this.globallyRateLimited |  | ||||||
| 		) { |  | ||||||
| 			await Promise.allSettled( |  | ||||||
| 				Object.values(this.pathQueues).map(async (pathQueue) => { |  | ||||||
| 					const request = pathQueue.shift(); |  | ||||||
| 					if (!request) return; |  | ||||||
| 	 |  | ||||||
| 					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); |  | ||||||
| 						} 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 }, |  | ||||||
| 								); |  | ||||||
| 							} |  | ||||||
| 						} |  | ||||||
| 					} else { |  | ||||||
| 						if (rateLimitedURLResetIn) { |  | ||||||
| 							// This URL is rate limited readd to queue
 |  | ||||||
| 							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 }, |  | ||||||
| 								); |  | ||||||
| 							} |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				}), |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		if (Object.keys(this.pathQueues).length) { |  | ||||||
| 			await delay(1000); |  | ||||||
| 			this.processQueue(); |  | ||||||
| 			this.cleanupQueues(); |  | ||||||
| 		} else this.queueInProcess = false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	createRequestBody(body: any, method: RequestMethods) { |  | ||||||
| 		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); |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		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"; |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		return { |  | ||||||
| 			headers, |  | ||||||
| 			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(); |  | ||||||
| 	 |  | ||||||
| 		if (ratelimited && now < ratelimited.resetTimestamp) { |  | ||||||
| 			return ratelimited.resetTimestamp - now; |  | ||||||
| 		} |  | ||||||
| 		if (global && now < global.resetTimestamp) { |  | ||||||
| 			return global.resetTimestamp - now; |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	async runMethod( |  | ||||||
| 		method: RequestMethods, |  | ||||||
| 		url: string, |  | ||||||
| 		body?: unknown, |  | ||||||
| 		retryCount = 0, |  | ||||||
| 		bucketID?: string | null, |  | ||||||
| 	) { |  | ||||||
| 	 |  | ||||||
| 		const errorStack = new Error("Location In Your Files:"); |  | ||||||
| 		Error.captureStackTrace(errorStack); |  | ||||||
| 	 |  | ||||||
| 		return await new Promise((resolve, reject) => { |  | ||||||
| 			const callback = async () => { |  | ||||||
| 				try { |  | ||||||
| 					const rateLimitResetIn = await this.checkRatelimits(url); |  | ||||||
| 					if (rateLimitResetIn) { |  | ||||||
| 						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)}` |  | ||||||
| 						) |  | ||||||
| 							.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); |  | ||||||
| 					this.handleStatusCode(response, errorStack); |  | ||||||
| 	 |  | ||||||
| 					// Sometimes Discord returns an empty 204 response that can't be made to JSON.
 |  | ||||||
| 					if (response.status === 204) return resolve(); |  | ||||||
| 	 |  | ||||||
| 					const json = await response.json(); |  | ||||||
| 					if ( |  | ||||||
| 						json.retry_after || |  | ||||||
| 						json.message === "You are being rate limited." |  | ||||||
| 					) { |  | ||||||
| 						if (retryCount > 10) { |  | ||||||
| 							throw new Error("Max RateLimit Retries hit"); |  | ||||||
| 						} |  | ||||||
| 	 |  | ||||||
| 						return { |  | ||||||
| 							rateLimited: json.retry_after, |  | ||||||
| 							beforeFetch: false, |  | ||||||
| 							bucketID: bucketIDFromHeaders, |  | ||||||
| 						}; |  | ||||||
| 					} |  | ||||||
| 					return resolve(json); |  | ||||||
| 				} catch (error) { |  | ||||||
| 					return reject(error); |  | ||||||
| 				} |  | ||||||
| 			}; |  | ||||||
| 	 |  | ||||||
| 			this.addToQueue({ |  | ||||||
| 				callback, |  | ||||||
| 				bucketID, |  | ||||||
| 				url, |  | ||||||
| 			}); |  | ||||||
| 			if (!this.queueInProcess) { |  | ||||||
| 				this.queueInProcess = true; |  | ||||||
| 				this.processQueue(); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	async logErrors(response: Response, errorStack?: unknown) { |  | ||||||
| 		try { |  | ||||||
| 			const error = await response.json(); |  | ||||||
| 			console.error(error); |  | ||||||
| 		} catch { |  | ||||||
| 			console.error(response); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	handleStatusCode(response: Response, errorStack?: unknown) { |  | ||||||
| 		const status = response.status; |  | ||||||
| 	 |  | ||||||
| 		if ( |  | ||||||
| 			(status >= 200 && status < 400) || |  | ||||||
| 			status === HttpResponseCode.TooManyRequests |  | ||||||
| 		) { |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		this.logErrors(response, errorStack); |  | ||||||
| 	 |  | ||||||
| 		switch (status) { |  | ||||||
| 			case HttpResponseCode.BadRequest: |  | ||||||
| 			case HttpResponseCode.Unauthorized: |  | ||||||
| 			case HttpResponseCode.Forbidden: |  | ||||||
| 			case HttpResponseCode.NotFound: |  | ||||||
| 			case HttpResponseCode.MethodNotAllowed: |  | ||||||
| 				throw new Error("Request Client Error"); |  | ||||||
| 			case HttpResponseCode.GatewayUnavailable: |  | ||||||
| 				throw new Error("Request Server Error"); |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		// left are all unknown
 |  | ||||||
| 		throw new Error("Request Unknown Error"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	processHeaders(url: string, headers: Headers) { |  | ||||||
| 		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"); |  | ||||||
| 	 |  | ||||||
| 		// If there is no remaining rate limit for this endpoint, we save it in cache
 |  | ||||||
| 		if (remaining && remaining === "0") { |  | ||||||
| 			ratelimited = true; |  | ||||||
| 	 |  | ||||||
| 			this.ratelimitedPaths.set(url, { |  | ||||||
| 				url, |  | ||||||
| 				resetTimestamp: Number(resetTimestamp) * 1000, |  | ||||||
| 				bucketID, |  | ||||||
| 			}); |  | ||||||
| 	 |  | ||||||
| 			if (bucketID) { |  | ||||||
| 				this.ratelimitedPaths.set(bucketID, { |  | ||||||
| 					url, |  | ||||||
| 					resetTimestamp: Number(resetTimestamp) * 1000, |  | ||||||
| 					bucketID, |  | ||||||
| 				}); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		// If there is no remaining global limit, we save it in cache
 |  | ||||||
| 		if (global) { |  | ||||||
| 			const reset = Date.now() + Number(retryAfter); |  | ||||||
| 			this.globallyRateLimited = true; |  | ||||||
| 			ratelimited = true; |  | ||||||
| 	 |  | ||||||
| 			this.ratelimitedPaths.set("global", { |  | ||||||
| 				url: "global", |  | ||||||
| 				resetTimestamp: reset, |  | ||||||
| 				bucketID, |  | ||||||
| 			}); |  | ||||||
| 	 |  | ||||||
| 			if (bucketID) { |  | ||||||
| 				this.ratelimitedPaths.set(bucketID, { |  | ||||||
| 					url: "global", |  | ||||||
| 					resetTimestamp: reset, |  | ||||||
| 					bucketID, |  | ||||||
| 				}); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	 |  | ||||||
| 		return ratelimited ? bucketID : undefined; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	get(url: string, body?: unknown) { |  | ||||||
|     return this.runMethod("get", url, body); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   post(url: string, body?: unknown) { |   async processRateLimitedPaths (): Promise<void> { | ||||||
|     return this.runMethod("post", url, body); |     const now = Date.now() | ||||||
|  |     this.ratelimitedPaths.forEach((value, key) => { | ||||||
|  |       if (value.resetTimestamp > now) return | ||||||
|  |       this.ratelimitedPaths.delete(key) | ||||||
|  |       if (key === 'global') this.globallyRateLimited = false | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   delete(url: string, body?: unknown) { |   addToQueue (request: QueuedRequest): void { | ||||||
|     return this.runMethod("delete", url, body); |     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 | ||||||
|  | 
 | ||||||
|  |     if (this.pathQueues[id] !== undefined) { | ||||||
|  |       this.pathQueues[id].push(request) | ||||||
|  |     } else { | ||||||
|  |       this.pathQueues[id] = [request] | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   patch(url: string, body?: unknown) { |   async cleanupQueues (): Promise<void> { | ||||||
|     return this.runMethod("patch", url, body); |     Object.entries(this.pathQueues).forEach(([key, value]) => { | ||||||
|  |       if (value.length === 0) { | ||||||
|  |         // Remove it entirely
 | ||||||
|  |         // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
 | ||||||
|  |         delete this.pathQueues[key] | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   put(url: string, body?: unknown) { |   async processQueue (): Promise<void> { | ||||||
|     return this.runMethod("put", url, body); |     if ( | ||||||
|  |       Object.keys(this.pathQueues).length !== 0 && | ||||||
|  |       !this.globallyRateLimited | ||||||
|  |     ) { | ||||||
|  |       await Promise.allSettled( | ||||||
|  |         Object.values(this.pathQueues).map(async pathQueue => { | ||||||
|  |           const request = pathQueue.shift() | ||||||
|  |           if (request === undefined) return | ||||||
|  | 
 | ||||||
|  |           const rateLimitedURLResetIn = await this.checkRatelimits(request.url) | ||||||
|  | 
 | ||||||
|  |           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?.rateLimited !== undefined) { | ||||||
|  |                 this.addToQueue({ | ||||||
|  |                   ...request, | ||||||
|  |                   bucketID: result.bucketID ?? request.bucketID | ||||||
|  |                 }) | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             if (rateLimitedURLResetIn !== false) { | ||||||
|  |               // This URL is rate limited readd to queue
 | ||||||
|  |               this.addToQueue(request) | ||||||
|  |             } else { | ||||||
|  |               // This request has no bucket id so it should be processed
 | ||||||
|  |               const result = await request.callback() | ||||||
|  |               if (result?.rateLimited !== undefined) { | ||||||
|  |                 this.addToQueue({ | ||||||
|  |                   ...request, | ||||||
|  |                   bucketID: result.bucketID ?? request.bucketID | ||||||
|  |                 }) | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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 | ||||||
|  |   ): { [key: string]: any } { | ||||||
|  |     const headers: { [key: string]: string } = { | ||||||
|  |       Authorization: `Bot ${this.client.token}`, | ||||||
|  |       'User-Agent': `DiscordBot (discord.deno)` | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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 !== undefined && now < ratelimited.resetTimestamp) { | ||||||
|  |       return ratelimited.resetTimestamp - now | ||||||
|  |     } | ||||||
|  |     if (global !== undefined && now < global.resetTimestamp) { | ||||||
|  |       return global.resetTimestamp - now | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async runMethod ( | ||||||
|  |     method: RequestMethods, | ||||||
|  |     url: string, | ||||||
|  |     body?: unknown, | ||||||
|  |     retryCount = 0, | ||||||
|  |     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 (): Promise<undefined | any> => { | ||||||
|  |         try { | ||||||
|  |           const rateLimitResetIn = await this.checkRatelimits(url) | ||||||
|  |           if (rateLimitResetIn !== false) { | ||||||
|  |             return { | ||||||
|  |               rateLimited: rateLimitResetIn, | ||||||
|  |               beforeFetch: true, | ||||||
|  |               bucketID | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           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 | ||||||
|  | 
 | ||||||
|  |           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(undefined) | ||||||
|  | 
 | ||||||
|  |           const json = await response.json() | ||||||
|  |           if ( | ||||||
|  |             json.retry_after !== undefined || | ||||||
|  |             json.message === 'You are being rate limited.' | ||||||
|  |           ) { | ||||||
|  |             if (retryCount > 10) { | ||||||
|  |               throw new Error('Max RateLimit Retries hit') | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |               rateLimited: json.retry_after, | ||||||
|  |               beforeFetch: false, | ||||||
|  |               bucketID: bucketIDFromHeaders | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           return resolve(json) | ||||||
|  |         } catch (error) { | ||||||
|  |           return reject(error) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.addToQueue({ | ||||||
|  |         callback, | ||||||
|  |         bucketID, | ||||||
|  |         url | ||||||
|  |       }) | ||||||
|  |       if (!this.queueInProcess) { | ||||||
|  |         this.queueInProcess = true | ||||||
|  |         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|  |         this.processQueue() | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async logErrors (response: Response, errorStack?: unknown): Promise<void> { | ||||||
|  |     try { | ||||||
|  |       const error = await response.json() | ||||||
|  |       console.error(error) | ||||||
|  |     } catch { | ||||||
|  |       console.error(response) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleStatusCode ( | ||||||
|  |     response: Response, | ||||||
|  |     errorStack?: unknown | ||||||
|  |   ): undefined | boolean { | ||||||
|  |     const status = response.status | ||||||
|  | 
 | ||||||
|  |     if ( | ||||||
|  |       (status >= 200 && status < 400) || | ||||||
|  |       status === HttpResponseCode.TooManyRequests | ||||||
|  |     ) { | ||||||
|  |       return true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|  |     this.logErrors(response, errorStack) | ||||||
|  | 
 | ||||||
|  |     switch (status) { | ||||||
|  |       case HttpResponseCode.BadRequest: | ||||||
|  |       case HttpResponseCode.Unauthorized: | ||||||
|  |       case HttpResponseCode.Forbidden: | ||||||
|  |       case HttpResponseCode.NotFound: | ||||||
|  |       case HttpResponseCode.MethodNotAllowed: | ||||||
|  |         throw new Error('Request Client Error') | ||||||
|  |       case HttpResponseCode.GatewayUnavailable: | ||||||
|  |         throw new Error('Request Server Error') | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // left are all unknown
 | ||||||
|  |     throw new Error('Request Unknown Error') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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') | ||||||
|  | 
 | ||||||
|  |     // If there is no remaining rate limit for this endpoint, we save it in cache
 | ||||||
|  |     if (remaining !== null && remaining === '0') { | ||||||
|  |       ratelimited = true | ||||||
|  | 
 | ||||||
|  |       this.ratelimitedPaths.set(url, { | ||||||
|  |         url, | ||||||
|  |         resetTimestamp: Number(resetTimestamp) * 1000, | ||||||
|  |         bucketID | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       if (bucketID !== null) { | ||||||
|  |         this.ratelimitedPaths.set(bucketID, { | ||||||
|  |           url, | ||||||
|  |           resetTimestamp: Number(resetTimestamp) * 1000, | ||||||
|  |           bucketID | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 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', | ||||||
|  |         resetTimestamp: reset, | ||||||
|  |         bucketID | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       if (bucketID !== null) { | ||||||
|  |         this.ratelimitedPaths.set(bucketID, { | ||||||
|  |           url: 'global', | ||||||
|  |           resetTimestamp: reset, | ||||||
|  |           bucketID | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ratelimited ? bucketID : undefined | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async get (url: string, body?: unknown): Promise<any> { | ||||||
|  |     return await this.runMethod('get', url, body) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async post (url: string, body?: unknown): Promise<any> { | ||||||
|  |     return await this.runMethod('post', url, body) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async delete (url: string, body?: unknown): Promise<any> { | ||||||
|  |     return await this.runMethod('delete', url, body) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async patch (url: string, body?: unknown): Promise<any> { | ||||||
|  |     return await this.runMethod('patch', 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 { Client } from '../models/client.ts' | ||||||
| import { ChannelPayload, ChannelTypes } from '../types/channel.ts' | import { ChannelPayload, ChannelTypes } from '../types/channel.ts' | ||||||
| import { Base } from './base.ts' | import { Base } from './base.ts' | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { DMChannelPayload } from '../types/channel.ts' | import { DMChannelPayload } from '../types/channel.ts' | ||||||
| import { UserPayload } from '../types/user.ts' | import { UserPayload } from '../types/user.ts' | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { GroupDMChannelPayload } from '../types/channel.ts' | import { GroupDMChannelPayload } from '../types/channel.ts' | ||||||
| import { Channel } from './channel.ts' | import { Channel } from './channel.ts' | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { GuildFeatures, GuildPayload } from '../types/guild.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 { Base } from './base.ts' | ||||||
| import { Channel } from './channel.ts' | import { Channel } from './channel.ts' | ||||||
| import { Emoji } from './emoji.ts' | import { Emoji } from './emoji.ts' | ||||||
|  | @ -8,8 +8,7 @@ import { Member } from './member.ts' | ||||||
| import { VoiceState } from './voiceState.ts' | import { VoiceState } from './voiceState.ts' | ||||||
| import cache from '../models/cache.ts' | import cache from '../models/cache.ts' | ||||||
| import getChannelByType from '../utils/getChannelByType.ts' | import getChannelByType from '../utils/getChannelByType.ts' | ||||||
| import { RolesManager } from "../managers/RolesManager.ts" | import { RolesManager } from '../managers/RolesManager.ts' | ||||||
| import { Role } from "./role.ts" |  | ||||||
| 
 | 
 | ||||||
| export class Guild extends Base { | export class Guild extends Base { | ||||||
|   id: string |   id: string | ||||||
|  |  | ||||||
|  | @ -1,10 +1,6 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { Channel } from './channel.ts' | import { Channel } from './channel.ts' | ||||||
| import { | import { GuildChannelCategoryPayload, Overwrite } from '../types/channel.ts' | ||||||
|   GuildChannelCategoryPayload, |  | ||||||
|   Overwrite |  | ||||||
| } from '../types/channel.ts' |  | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| 
 | 
 | ||||||
| export class CategoryChannel extends Channel { | export class CategoryChannel extends Channel { | ||||||
|   guildID: string |   guildID: string | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { GuildTextChannelPayload, Overwrite } from '../types/channel.ts' | import { GuildTextChannelPayload, Overwrite } from '../types/channel.ts' | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| import { TextChannel } from './textChannel.ts' | import { TextChannel } from './textChannel.ts' | ||||||
| 
 | 
 | ||||||
| export class GuildTextChannel extends TextChannel { | export class GuildTextChannel extends TextChannel { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { GuildVoiceChannelPayload, Overwrite } from '../types/channel.ts' | import { GuildVoiceChannelPayload, Overwrite } from '../types/channel.ts' | ||||||
| import { Channel } from './channel.ts' | import { Channel } from './channel.ts' | ||||||
|  |  | ||||||
|  | @ -14,10 +14,9 @@ import { User } from './user.ts' | ||||||
| import { Member } from './member.ts' | import { Member } from './member.ts' | ||||||
| import { Embed } from './embed.ts' | import { Embed } from './embed.ts' | ||||||
| import { CHANNEL_MESSAGE } from '../types/endpoint.ts' | import { CHANNEL_MESSAGE } from '../types/endpoint.ts' | ||||||
| import cache from '../models/cache.ts' | import { Channel } from './channel.ts' | ||||||
| import { Channel } from "./channel.ts" | import { MessageMentions } from './MessageMentions.ts' | ||||||
| import { MessageMentions } from "./MessageMentions.ts" | import { TextChannel } from './textChannel.ts' | ||||||
| import { TextChannel } from "./textChannel.ts" |  | ||||||
| 
 | 
 | ||||||
| export class Message extends Base { | export class Message extends Base { | ||||||
|   // eslint-disable-next-line @typescript-eslint/prefer-readonly
 |   // eslint-disable-next-line @typescript-eslint/prefer-readonly
 | ||||||
|  | @ -48,7 +47,13 @@ export class Message extends Base { | ||||||
|   messageReference?: MessageReference |   messageReference?: MessageReference | ||||||
|   flags?: number |   flags?: number | ||||||
| 
 | 
 | ||||||
|   constructor (client: Client, data: MessagePayload, channel: Channel, author: User, mentions: MessageMentions) { |   constructor ( | ||||||
|  |     client: Client, | ||||||
|  |     data: MessagePayload, | ||||||
|  |     channel: Channel, | ||||||
|  |     author: User, | ||||||
|  |     mentions: MessageMentions | ||||||
|  |   ) { | ||||||
|     super(client) |     super(client) | ||||||
|     this.data = data |     this.data = data | ||||||
|     this.id = data.id |     this.id = data.id | ||||||
|  | @ -116,11 +121,13 @@ export class Message extends Base { | ||||||
|     this.flags = data.flags ?? this.flags |     this.flags = data.flags ?? this.flags | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   edit (text?: string, option?: MessageOption): Promise<Message> { |   async edit (text?: string, option?: MessageOption): Promise<Message> { | ||||||
|  |     // Seriously eslint?
 | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||||
|     return (this.channel as TextChannel).editMessage(this.id, text, option) |     return (this.channel as TextChannel).editMessage(this.id, text, option) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   delete (): Promise<void> { |   async delete (): Promise<void> { | ||||||
|     return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) as any |     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 { Client } from '../models/client.ts' | ||||||
| import { Base } from './base.ts' | import { Base } from './base.ts' | ||||||
| import { RolePayload } from '../types/role.ts' | import { RolePayload } from '../types/role.ts' | ||||||
|  |  | ||||||
|  | @ -1,11 +1,9 @@ | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| import { Client } from '../models/client.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_MESSAGE, CHANNEL_MESSAGES } from '../types/endpoint.ts' | ||||||
| import { Channel } from './channel.ts' | import { Channel } from './channel.ts' | ||||||
| import { Message } from './message.ts' | import { Message } from './message.ts' | ||||||
| import { MessageMentions } from "./MessageMentions.ts" | import { MessageMentions } from './MessageMentions.ts' | ||||||
| import { User } from "./user.ts" |  | ||||||
| 
 | 
 | ||||||
| export class TextChannel extends Channel { | export class TextChannel extends Channel { | ||||||
|   lastMessageID?: string |   lastMessageID?: string | ||||||
|  | @ -29,6 +27,10 @@ export class TextChannel extends Channel { | ||||||
|     if (text !== undefined && option !== undefined) { |     if (text !== undefined && option !== undefined) { | ||||||
|       throw new Error('Either text or option is necessary.') |       throw new Error('Either text or option is necessary.') | ||||||
|     } |     } | ||||||
|  |     if (this.client.user === undefined) { | ||||||
|  |       throw new Error('Client user has not initialized.') | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const resp = await fetch(CHANNEL_MESSAGES(this.id), { |     const resp = await fetch(CHANNEL_MESSAGES(this.id), { | ||||||
|       headers: { |       headers: { | ||||||
|         Authorization: `Bot ${this.client.token}`, |         Authorization: `Bot ${this.client.token}`, | ||||||
|  | @ -44,7 +46,13 @@ export class TextChannel extends Channel { | ||||||
|       }) |       }) | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|     return new Message(this.client, await resp.json(), this, this.client.user as User, new MessageMentions()) |     return new Message( | ||||||
|  |       this.client, | ||||||
|  |       await resp.json(), | ||||||
|  |       this, | ||||||
|  |       this.client.user, | ||||||
|  |       new MessageMentions() | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async editMessage ( |   async editMessage ( | ||||||
|  | @ -52,21 +60,31 @@ export class TextChannel extends Channel { | ||||||
|     text?: string, |     text?: string, | ||||||
|     option?: MessageOption |     option?: MessageOption | ||||||
|   ): Promise<Message> { |   ): Promise<Message> { | ||||||
|     if (text !== undefined && option !== undefined) { |     if (text === undefined && option === undefined) { | ||||||
|       throw new Error('Either text or option is necessary.') |       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) { | ||||||
|       content: text, |       throw new Error('Client user has not initialized.') | ||||||
|       embed: option?.embed.toJSON(), |     } | ||||||
|       file: option?.file, | 
 | ||||||
|       tts: option?.tts, |     const newMsg = await this.client.rest.patch( | ||||||
|       allowed_mentions: option?.allowedMention |       CHANNEL_MESSAGE( | ||||||
|     }) as MessagePayload |         this.id, | ||||||
|  |         typeof message === 'string' ? message : message.id | ||||||
|  |       ), | ||||||
|  |       { | ||||||
|  |         content: text, | ||||||
|  |         embed: option?.embed.toJSON(), | ||||||
|  |         file: option?.file, | ||||||
|  |         tts: option?.tts, | ||||||
|  |         allowed_mentions: option?.allowedMention | ||||||
|  |       } | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     // TODO: Actually construct this object
 |     // 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 { Client } from '../models/client.ts' | ||||||
| import { UserPayload } from '../types/user.ts' | import { UserPayload } from '../types/user.ts' | ||||||
| import { Base } from './base.ts' | import { Base } from './base.ts' | ||||||
|  | @ -18,8 +17,8 @@ export class User extends Base { | ||||||
|   premiumType?: 0 | 1 | 2 |   premiumType?: 0 | 1 | 2 | ||||||
|   publicFlags?: number |   publicFlags?: number | ||||||
| 
 | 
 | ||||||
|   get tag(): string { |   get tag (): string { | ||||||
|     return `${this.username}#${this.discriminator}`; |     return `${this.username}#${this.discriminator}` | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get nickMention (): string { |   get nickMention (): string { | ||||||
|  | @ -65,7 +64,7 @@ export class User extends Base { | ||||||
|     this.publicFlags = data.public_flags ?? this.publicFlags |     this.publicFlags = data.public_flags ?? this.publicFlags | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toString() { |   toString (): string { | ||||||
|     return this.mention; |     return this.mention | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import cache from '../models/cache.ts' |  | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { MemberPayload } from '../types/guild.ts' | import { MemberPayload } from '../types/guild.ts' | ||||||
| import { VoiceStatePayload } from '../types/voice.ts' | import { VoiceStatePayload } from '../types/voice.ts' | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
 | // https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
 | ||||||
| import { EmojiPayload } from './emoji.ts' | import { EmojiPayload } from './emoji.ts' | ||||||
| import { MemberPayload } from './guild.ts' | import { MemberPayload } from './guild.ts' | ||||||
| import { ActivityPayload, PresenceUpdatePayload } from './presence.ts' | import { ActivityPayload } from './presence.ts' | ||||||
| import { RolePayload } from './role.ts' | import { RolePayload } from './role.ts' | ||||||
| import { UserPayload } from './user.ts' | import { UserPayload } from './user.ts' | ||||||
| 
 | 
 | ||||||
|  | @ -100,7 +100,7 @@ enum GatewayEvents { | ||||||
|   Webhooks_Update = 'WEBHOOKS_UPDATE' |   Webhooks_Update = 'WEBHOOKS_UPDATE' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface IdentityPayload { | export interface IdentityPayload { | ||||||
|   token: string |   token: string | ||||||
|   properties: IdentityConnection |   properties: IdentityConnection | ||||||
|   compress?: boolean |   compress?: boolean | ||||||
|  | @ -119,19 +119,19 @@ enum UpdateStatus { | ||||||
|   offline = 'offline' |   offline = 'offline' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface IdentityConnection { | export interface IdentityConnection { | ||||||
|   $os: 'darwin' | 'windows' | 'linux' | 'custom os' |   $os: 'darwin' | 'windows' | 'linux' | 'custom os' | ||||||
|   $browser: 'discord.deno' |   $browser: 'discord.deno' | ||||||
|   $device: 'discord.deno' |   $device: 'discord.deno' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface Resume { | export interface Resume { | ||||||
|   token: string |   token: string | ||||||
|   session_id: string |   session_id: string | ||||||
|   seq: number |   seq: number | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildRequestMembers { | export interface GuildRequestMembers { | ||||||
|   guild_id: string | string[] |   guild_id: string | string[] | ||||||
|   query?: string |   query?: string | ||||||
|   limit: number |   limit: number | ||||||
|  | @ -140,25 +140,25 @@ interface GuildRequestMembers { | ||||||
|   nonce?: string |   nonce?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GatewayVoiceStateUpdate { | export interface GatewayVoiceStateUpdate { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   self_mute: boolean |   self_mute: boolean | ||||||
|   self_deaf: boolean |   self_deaf: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GatewayStatusUpdate { | export interface GatewayStatusUpdate { | ||||||
|   since: number | undefined |   since: number | undefined | ||||||
|   activities: ActivityPayload[] |   activities: ActivityPayload[] | ||||||
|   status: string |   status: string | ||||||
|   afk: boolean |   afk: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface Hello { | export interface Hello { | ||||||
|   heartbeat_interval: number |   heartbeat_interval: number | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ReadyEvent { | export interface ReadyEvent { | ||||||
|   v: number |   v: number | ||||||
|   user: UserPayload |   user: UserPayload | ||||||
|   privateChannels: [] |   privateChannels: [] | ||||||
|  | @ -167,40 +167,40 @@ interface ReadyEvent { | ||||||
|   shard?: number[] |   shard?: number[] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ChannelPinsUpdate { | export interface ChannelPinsUpdatePayload { | ||||||
|   guild_id?: string |   guild_id?: string | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   last_pin_timestamp?: string |   last_pin_timestamp?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildBanAdd { | export interface GuildBanAddPayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   user: UserPayload |   user: UserPayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildBanRemove { | export interface GuildBanRemovePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   user: UserPayload |   user: UserPayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildEmojiUpdate { | export interface GuildEmojiUpdatePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   emojis: [] |   emojis: [] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildIntegrationsUpdate { | export interface GuildIntegrationsUpdatePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildMemberAddExtra { | export interface GuildMemberAddPayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildMemberRemove { | export interface GuildMemberRemovePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   user: UserPayload |   user: UserPayload | ||||||
| } | } | ||||||
| interface GuildMemberUpdate { | export interface GuildMemberUpdatePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   roles: string[] |   roles: string[] | ||||||
|   user: UserPayload |   user: UserPayload | ||||||
|  | @ -209,7 +209,7 @@ interface GuildMemberUpdate { | ||||||
|   premium_since?: string | undefined |   premium_since?: string | undefined | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildMemberChunk { | export interface GuildMemberChunkPayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   members: MemberPayload[] |   members: MemberPayload[] | ||||||
|   chunk_index: number |   chunk_index: number | ||||||
|  | @ -219,22 +219,22 @@ interface GuildMemberChunk { | ||||||
|   nonce?: string |   nonce?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildRoleCreate { | export interface GuildRoleCreatePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   role: RolePayload |   role: RolePayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildRoleUpdate { | export interface GuildRoleUpdatePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   role: RolePayload |   role: RolePayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface GuildRoleDelete { | export interface GuildRoleDeletePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   role_id: string |   role_id: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface InviteCreate { | export interface InviteCreatePayload { | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   code: string |   code: string | ||||||
|   created_at: string |   created_at: string | ||||||
|  | @ -248,25 +248,25 @@ interface InviteCreate { | ||||||
|   uses: number |   uses: number | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface InviteDelete { | export interface InviteDeletePayload { | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   guild_id?: string |   guild_id?: string | ||||||
|   code: string |   code: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface MessageDelete { | export interface MessageDeletePayload { | ||||||
|   id: string |   id: string | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   guild_id?: string |   guild_id?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface MessageDeleteBulk { | export interface MessageDeleteBulkPayload { | ||||||
|   ids: string[] |   ids: string[] | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   guild_id: string |   guild_id: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface MessageReactionAdd { | export interface MessageReactionAddPayload { | ||||||
|   user_id: string |   user_id: string | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   message_id: string |   message_id: string | ||||||
|  | @ -274,7 +274,7 @@ interface MessageReactionAdd { | ||||||
|   emoji: EmojiPayload |   emoji: EmojiPayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface MessageReactionRemove { | export interface MessageReactionRemovePayload { | ||||||
|   user_id: string |   user_id: string | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   message_id: string |   message_id: string | ||||||
|  | @ -282,21 +282,13 @@ interface MessageReactionRemove { | ||||||
|   emoji: EmojiPayload |   emoji: EmojiPayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface MessageReactionRemoveAll { | export interface MessageReactionRemoveAllPayload { | ||||||
|   channel_id: string |   channel_id: string | ||||||
|   guild_id?: string |   guild_id?: string | ||||||
|   message_id: string |   message_id: string | ||||||
|   emoji: EmojiPayload |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface MessageReactionRemove { | export interface PresenceUpdatePayload { | ||||||
|   channel_id: string |  | ||||||
|   guild_id?: string |  | ||||||
|   message_id: string |  | ||||||
|   emoji: EmojiPayload |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface PresenceUpdate { |  | ||||||
|   user: UserPayload |   user: UserPayload | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   status: string |   status: string | ||||||
|  | @ -304,76 +296,7 @@ interface PresenceUpdate { | ||||||
|   client_status: UpdateStatus[] |   client_status: UpdateStatus[] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface CilentStatus { | export interface TypeStart { | ||||||
|   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 |   channel_id: string | ||||||
|   guild_id?: string |   guild_id?: string | ||||||
|   user_id: string |   user_id: string | ||||||
|  | @ -381,16 +304,22 @@ interface TypeStart { | ||||||
|   member?: MemberPayload |   member?: MemberPayload | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface VoiceServerUpdate { | export interface VoiceServerUpdatePayload { | ||||||
|   token: string |   token: string | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   endpoint: string |   endpoint: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface WebhooksUpdate { | export interface WebhooksUpdatePayload { | ||||||
|   guild_id: string |   guild_id: string | ||||||
|   channel_id: string |   channel_id: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields
 | // https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields
 | ||||||
| export { GatewayCloseCodes, GatewayOpcodes, GatewayIntents, GatewayEvents } | 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_WEBHOOKS = 0x20000000, | ||||||
|   MANAGE_EMOJIS = 0x40000000 |   MANAGE_EMOJIS = 0x40000000 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export { PermissionFlags } | ||||||
|  |  | ||||||
|  | @ -1,14 +1,4 @@ | ||||||
| import { UserPayload } from './user.ts' | export interface ClientStatus { | ||||||
| 
 |  | ||||||
| export interface PresenceUpdatePayload { |  | ||||||
|   user: UserPayload |  | ||||||
|   guild_id: string |  | ||||||
|   status: string |  | ||||||
|   activities: ActivityPayload |  | ||||||
|   client_status: ClientStatus |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface ClientStatus { |  | ||||||
|   desktop?: string |   desktop?: string | ||||||
|   mobile?: string |   mobile?: string | ||||||
|   web?: string |   web?: string | ||||||
|  | @ -31,30 +21,30 @@ export interface ActivityPayload { | ||||||
|   flags?: number |   flags?: number | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ActivityTimestamps { | export interface ActivityTimestamps { | ||||||
|   start?: number |   start?: number | ||||||
|   end?: number |   end?: number | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ActivityEmoji { | export interface ActivityEmoji { | ||||||
|   name: string |   name: string | ||||||
|   id?: string |   id?: string | ||||||
|   animated?: boolean |   animated?: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ActivityParty { | export interface ActivityParty { | ||||||
|   id?: string |   id?: string | ||||||
|   size?: number[] |   size?: number[] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ActivityAssets { | export interface ActivityAssets { | ||||||
|   large_image?: string |   large_image?: string | ||||||
|   large_text?: string |   large_text?: string | ||||||
|   small_image?: string |   small_image?: string | ||||||
|   small_text?: string |   small_text?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface ActivitySecrets { | export interface ActivitySecrets { | ||||||
|   join?: string |   join?: string | ||||||
|   spectate?: string |   spectate?: string | ||||||
|   match?: string |   match?: string | ||||||
|  | @ -68,3 +58,5 @@ enum ActivityFlags { | ||||||
|   SYNC = 1 << 4, |   SYNC = 1 << 4, | ||||||
|   PLAY = 1 << 5 |   PLAY = 1 << 5 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export { ActivityFlags } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue