Merge pull request #40 from DjDeveloperr/main
feat(API): Added new API-methods to structures and more
This commit is contained in:
		
						commit
						5d49762e6e
					
				
					 21 changed files with 1102 additions and 603 deletions
				
			
		
							
								
								
									
										6
									
								
								.prettierrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.prettierrc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | { | ||||||
|  |   "tabWidth": 2, | ||||||
|  |   "useTabs": false, | ||||||
|  |   "semi": false, | ||||||
|  |   "singleQuote": true | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							|  | @ -6,5 +6,6 @@ | ||||||
|   "deno.import_intellisense_origins": { |   "deno.import_intellisense_origins": { | ||||||
|     "https://deno.land": true |     "https://deno.land": true | ||||||
|   }, |   }, | ||||||
|   "editor.tabSize": 2 |   "editor.tabSize": 2, | ||||||
|  |   "editor.formatOnSave": true | ||||||
| } | } | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| export const DISCORD_API_URL: string = 'https://discord.com/api' | export const DISCORD_API_URL: string = 'https://discord.com/api' | ||||||
| 
 | 
 | ||||||
| export const DISCORD_GATEWAY_URL: string = 'wss://gateway.discord.gg' | export const DISCORD_GATEWAY_URL: string = 'wss://gateway.discord.gg' | ||||||
| 
 | 
 | ||||||
| export const DISCORD_CDN_URL: string = 'https://cdn.discordapp.com' | export const DISCORD_CDN_URL: string = 'https://cdn.discordapp.com' | ||||||
| 
 | 
 | ||||||
| export const DISCORD_API_VERSION: number = 8 | export const DISCORD_API_VERSION: number = 8 | ||||||
|  | 
 | ||||||
|  | export const DISCORD_VOICE_VERSION: number = 4 | ||||||
							
								
								
									
										31
									
								
								src/gateway/handlers/guildMembersChunk.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/gateway/handlers/guildMembersChunk.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
|  | import { Guild } from '../../structures/guild.ts' | ||||||
|  | import { GuildMemberChunkPayload } from '../../types/gateway.ts' | ||||||
|  | 
 | ||||||
|  | export const guildMembersChunk: GatewayEventHandler = async ( | ||||||
|  |   gateway: Gateway, | ||||||
|  |   d: GuildMemberChunkPayload | ||||||
|  | ) => { | ||||||
|  |   const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) | ||||||
|  |   // Weird case, shouldn't happen
 | ||||||
|  |   if (guild === undefined) return | ||||||
|  | 
 | ||||||
|  |   for (const member of d.members) { | ||||||
|  |     await guild.members.set(member.user.id, member) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // TODO: Cache Presences
 | ||||||
|  | 
 | ||||||
|  |   gateway.client.emit('guildMembersChunk', guild, { | ||||||
|  |     members: d.members.map((m) => m.user.id), | ||||||
|  |     presences: | ||||||
|  |       d.presences === undefined ? undefined : d.presences.map((p) => p.user.id), | ||||||
|  |     chunkIndex: d.chunk_index, | ||||||
|  |     chunkCount: d.chunk_count, | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   // Guild is now completely chunked. Emit an event for that.
 | ||||||
|  |   if (d.chunk_index >= d.chunk_count - 1) { | ||||||
|  |     gateway.client.emit('guildMembersChunked', guild, d.chunk_count) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -36,6 +36,9 @@ import { Member } from "../../structures/member.ts" | ||||||
| import { Role } from "../../structures/role.ts" | import { Role } from "../../structures/role.ts" | ||||||
| import { Message } from "../../structures/message.ts" | import { Message } from "../../structures/message.ts" | ||||||
| import { Collection } from "../../utils/collection.ts" | import { Collection } from "../../utils/collection.ts" | ||||||
|  | import { voiceServerUpdate } from "./voiceServerUpdate.ts" | ||||||
|  | import { voiceStateUpdate } from "./voiceStateUpdate.ts" | ||||||
|  | import { VoiceState } from "../../structures/voiceState.ts" | ||||||
| 
 | 
 | ||||||
| export const gatewayHandlers: { | export const gatewayHandlers: { | ||||||
|   [eventCode in GatewayEvents]: GatewayEventHandler | undefined |   [eventCode in GatewayEvents]: GatewayEventHandler | undefined | ||||||
|  | @ -74,7 +77,8 @@ export const gatewayHandlers: { | ||||||
|   PRESENCE_UPDATE: undefined, |   PRESENCE_UPDATE: undefined, | ||||||
|   TYPING_START: typingStart, |   TYPING_START: typingStart, | ||||||
|   USER_UPDATE: userUpdate, |   USER_UPDATE: userUpdate, | ||||||
|   VOICE_SERVER_UPDATE: undefined, |   VOICE_STATE_UPDATE: voiceStateUpdate, | ||||||
|  |   VOICE_SERVER_UPDATE: voiceServerUpdate, | ||||||
|   WEBHOOKS_UPDATE: webhooksUpdate |   WEBHOOKS_UPDATE: webhooksUpdate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -82,6 +86,12 @@ export interface EventTypes { | ||||||
|   [name: string]: (...args: any[]) => void |   [name: string]: (...args: any[]) => void | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export interface VoiceServerUpdateData { | ||||||
|  |   token: string | ||||||
|  |   endpoint: string | ||||||
|  |   guild: Guild | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export interface ClientEvents extends EventTypes { | export interface ClientEvents extends EventTypes { | ||||||
|   'ready': () => void |   'ready': () => void | ||||||
|   'reconnect': () => void |   'reconnect': () => void | ||||||
|  | @ -111,5 +121,9 @@ export interface ClientEvents extends EventTypes { | ||||||
|   'messageUpdate': (before: Message, after: Message) => void |   'messageUpdate': (before: Message, after: Message) => void | ||||||
|   'typingStart': (user: User, channel: TextChannel, at: Date, guildData?: TypingStartGuildData) => void |   'typingStart': (user: User, channel: TextChannel, at: Date, guildData?: TypingStartGuildData) => void | ||||||
|   'userUpdate': (before: User, after: User) => void |   'userUpdate': (before: User, after: User) => void | ||||||
|  |   'voiceServerUpdate': (data: VoiceServerUpdateData) => void | ||||||
|  |   'voiceStateAdd': (state: VoiceState) => void | ||||||
|  |   'voiceStateRemove': (state: VoiceState) => void | ||||||
|  |   'voiceStateUpdate': (state: VoiceState, after: VoiceState) => void | ||||||
|   'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void |   'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void | ||||||
| } | } | ||||||
							
								
								
									
										14
									
								
								src/gateway/handlers/voiceServerUpdate.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/gateway/handlers/voiceServerUpdate.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | import { Guild } from "../../structures/guild.ts" | ||||||
|  | import { VoiceServerUpdatePayload } from "../../types/gateway.ts" | ||||||
|  | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
|  | 
 | ||||||
|  | export const voiceServerUpdate: GatewayEventHandler = async ( | ||||||
|  |   gateway: Gateway, | ||||||
|  |   d: VoiceServerUpdatePayload | ||||||
|  | ) => { | ||||||
|  |   gateway.client.emit('voiceServerUpdate', { | ||||||
|  |     token: d.token, | ||||||
|  |     endpoint: d.endpoint, | ||||||
|  |     guild: (await gateway.client.guilds.get(d.guild_id) as unknown) as Guild | ||||||
|  |   }) | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								src/gateway/handlers/voiceStateUpdate.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/gateway/handlers/voiceStateUpdate.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | ||||||
|  | import { Guild } from "../../structures/guild.ts" | ||||||
|  | import { VoiceState } from "../../structures/voiceState.ts" | ||||||
|  | import { VoiceStatePayload } from "../../types/voice.ts" | ||||||
|  | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
|  | 
 | ||||||
|  | export const voiceStateUpdate: GatewayEventHandler = async ( | ||||||
|  |   gateway: Gateway, | ||||||
|  |   d: VoiceStatePayload | ||||||
|  | ) => { | ||||||
|  |   // TODO(DjDeveloperr): Support self-bot here; they can be in DMs (Call)
 | ||||||
|  |   if (d.guild_id === undefined) return | ||||||
|  |   const guild = (await gateway.client.guilds.get(d.guild_id) as unknown) as Guild | ||||||
|  | 
 | ||||||
|  |   const voiceState = await guild.voiceStates.get(d.user_id) | ||||||
|  | 
 | ||||||
|  |   if (d.channel_id === null) { | ||||||
|  |     // No longer in the channel, so delete
 | ||||||
|  |     await guild.voiceStates.delete(d.user_id) | ||||||
|  |     gateway.client.emit('voiceStateRemove', (voiceState as unknown) as VoiceState) | ||||||
|  |     return | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   await guild.voiceStates.set(d.user_id, d) | ||||||
|  |   const newVoiceState = await guild.voiceStates.get(d.user_id) | ||||||
|  |   if (voiceState === undefined) { | ||||||
|  |     gateway.client.emit('voiceStateAdd', (newVoiceState as unknown) as VoiceState) | ||||||
|  |   } else { | ||||||
|  |     gateway.client.emit('voiceStateUpdate', voiceState, (newVoiceState as unknown) as VoiceState) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -2,7 +2,7 @@ import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts' | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { | import { | ||||||
|   DISCORD_GATEWAY_URL, |   DISCORD_GATEWAY_URL, | ||||||
|   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 { | import { | ||||||
|  | @ -10,18 +10,23 @@ import { | ||||||
|   GatewayIntents, |   GatewayIntents, | ||||||
|   GatewayCloseCodes, |   GatewayCloseCodes, | ||||||
|   IdentityPayload, |   IdentityPayload, | ||||||
|   StatusUpdatePayload |   StatusUpdatePayload, | ||||||
| } from '../types/gateway.ts' | } 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 { GatewayCache } from '../managers/gatewayCache.ts' | import { GatewayCache } from '../managers/gatewayCache.ts' | ||||||
| import { delay } from '../utils/delay.ts' | import { delay } from '../utils/delay.ts' | ||||||
| 
 | 
 | ||||||
|  | export interface RequestMembersOptions { | ||||||
|  |   limit?: number | ||||||
|  |   presences?: boolean | ||||||
|  |   query?: string | ||||||
|  |   users?: string[] | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Handles Discord gateway connection. |  * Handles Discord gateway connection. | ||||||
|  * You should not use this and rather use Client class. |  * You should not use this and rather use Client class. | ||||||
|  * |  | ||||||
|  * @beta |  | ||||||
|  */ |  */ | ||||||
| class Gateway { | class Gateway { | ||||||
|   websocket: WebSocket |   websocket: WebSocket | ||||||
|  | @ -38,7 +43,7 @@ class Gateway { | ||||||
|   client: Client |   client: Client | ||||||
|   cache: GatewayCache |   cache: GatewayCache | ||||||
| 
 | 
 | ||||||
|   constructor (client: Client, token: string, intents: GatewayIntents[]) { |   constructor(client: Client, token: string, intents: GatewayIntents[]) { | ||||||
|     this.token = token |     this.token = token | ||||||
|     this.intents = intents |     this.intents = intents | ||||||
|     this.client = client |     this.client = client | ||||||
|  | @ -55,12 +60,12 @@ class Gateway { | ||||||
|     this.websocket.onerror = this.onerror.bind(this) |     this.websocket.onerror = this.onerror.bind(this) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   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> { | ||||||
|     let data = event.data |     let data = event.data | ||||||
|     if (data instanceof ArrayBuffer) { |     if (data instanceof ArrayBuffer) { | ||||||
|       data = new Uint8Array(data) |       data = new Uint8Array(data) | ||||||
|  | @ -144,7 +149,7 @@ class Gateway { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async onclose (event: CloseEvent): Promise<void> { |   private async onclose(event: CloseEvent): Promise<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) { | ||||||
|  | @ -191,12 +196,12 @@ class Gateway { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private onerror (event: Event | ErrorEvent): void { |   private onerror(event: Event | ErrorEvent): void { | ||||||
|     const eventError = event as ErrorEvent |     const eventError = event as ErrorEvent | ||||||
|     console.log(eventError) |     console.log(eventError) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async sendIdentify (forceNewSession?: boolean): Promise<void> { |   private async sendIdentify(forceNewSession?: boolean): Promise<void> { | ||||||
|     if (this.client.bot === true) { |     if (this.client.bot === true) { | ||||||
|       this.debug('Fetching /gateway/bot...') |       this.debug('Fetching /gateway/bot...') | ||||||
|       const info = await this.client.rest.get(GATEWAY_BOT()) |       const info = await this.client.rest.get(GATEWAY_BOT()) | ||||||
|  | @ -225,7 +230,7 @@ class Gateway { | ||||||
|       properties: { |       properties: { | ||||||
|         $os: Deno.build.os, |         $os: Deno.build.os, | ||||||
|         $browser: 'harmony', |         $browser: 'harmony', | ||||||
|         $device: 'harmony' |         $device: 'harmony', | ||||||
|       }, |       }, | ||||||
|       compress: true, |       compress: true, | ||||||
|       shard: [0, 1], // TODO: Make sharding possible
 |       shard: [0, 1], // TODO: Make sharding possible
 | ||||||
|  | @ -233,7 +238,7 @@ class Gateway { | ||||||
|         (previous, current) => previous | current, |         (previous, current) => previous | current, | ||||||
|         0 |         0 | ||||||
|       ), |       ), | ||||||
|       presence: this.client.presence.create() |       presence: this.client.presence.create(), | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (this.client.bot === false) { |     if (this.client.bot === false) { | ||||||
|  | @ -245,17 +250,17 @@ class Gateway { | ||||||
|         $browser: 'Firefox', |         $browser: 'Firefox', | ||||||
|         $device: '', |         $device: '', | ||||||
|         $referrer: '', |         $referrer: '', | ||||||
|         $referring_domain: '' |         $referring_domain: '', | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.send({ |     this.send({ | ||||||
|       op: GatewayOpcodes.IDENTIFY, |       op: GatewayOpcodes.IDENTIFY, | ||||||
|       d: payload |       d: payload, | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   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) { | ||||||
|       const cached = await this.cache.get('seq') |       const cached = await this.cache.get('seq') | ||||||
|  | @ -267,17 +272,37 @@ class Gateway { | ||||||
|       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.send(resumePayload) |     this.send(resumePayload) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   debug (msg: string): void { |   requestMembers(guild: string, options: RequestMembersOptions = {}): string { | ||||||
|  |     if (options.query !== undefined && options.limit === undefined) | ||||||
|  |       throw new Error( | ||||||
|  |         'Missing limit property when specifying query for Requesting Members!' | ||||||
|  |       ) | ||||||
|  |     const nonce = `${guild}_${new Date().getTime()}` | ||||||
|  |     this.send({ | ||||||
|  |       op: GatewayOpcodes.REQUEST_GUILD_MEMBERS, | ||||||
|  |       d: { | ||||||
|  |         guild_id: guild, | ||||||
|  |         query: options.query, | ||||||
|  |         limit: options.limit, | ||||||
|  |         presences: options.presences, | ||||||
|  |         user_ids: options.users, | ||||||
|  |         nonce, | ||||||
|  |       }, | ||||||
|  |     }) | ||||||
|  |     return nonce | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   debug(msg: string): void { | ||||||
|     this.client.debug('Gateway', msg) |     this.client.debug('Gateway', msg) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async reconnect (forceNew?: boolean): Promise<void> { |   async reconnect(forceNew?: boolean): Promise<void> { | ||||||
|     clearInterval(this.heartbeatIntervalID) |     clearInterval(this.heartbeatIntervalID) | ||||||
|     if (forceNew === undefined || !forceNew) |     if (forceNew === undefined || !forceNew) | ||||||
|       await this.cache.delete('session_id') |       await this.cache.delete('session_id') | ||||||
|  | @ -285,7 +310,7 @@ class Gateway { | ||||||
|     this.initWebsocket() |     this.initWebsocket() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   initWebsocket (): void { |   initWebsocket(): void { | ||||||
|     this.websocket = new WebSocket( |     this.websocket = new WebSocket( | ||||||
|       // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
 |       // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
 | ||||||
|       `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, |       `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, | ||||||
|  | @ -298,41 +323,41 @@ class Gateway { | ||||||
|     this.websocket.onerror = this.onerror.bind(this) |     this.websocket.onerror = this.onerror.bind(this) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   close (): void { |   close(): void { | ||||||
|     this.websocket.close(1000) |     this.websocket.close(1000) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   send (data: GatewayResponse): boolean { |   send(data: GatewayResponse): boolean { | ||||||
|     if (this.websocket.readyState !== this.websocket.OPEN) return false |     if (this.websocket.readyState !== this.websocket.OPEN) return false | ||||||
|     this.websocket.send( |     this.websocket.send( | ||||||
|       JSON.stringify({ |       JSON.stringify({ | ||||||
|         op: data.op, |         op: data.op, | ||||||
|         d: data.d, |         d: data.d, | ||||||
|         s: typeof data.s === 'number' ? data.s : null, |         s: typeof data.s === 'number' ? data.s : null, | ||||||
|         t: data.t === undefined ? null : data.t |         t: data.t === undefined ? null : data.t, | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   sendPresence (data: StatusUpdatePayload): void { |   sendPresence(data: StatusUpdatePayload): void { | ||||||
|     this.send({ |     this.send({ | ||||||
|       op: GatewayOpcodes.PRESENCE_UPDATE, |       op: GatewayOpcodes.PRESENCE_UPDATE, | ||||||
|       d: data |       d: data, | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   sendHeartbeat (): void { |   sendHeartbeat(): void { | ||||||
|     const payload = { |     const payload = { | ||||||
|       op: GatewayOpcodes.HEARTBEAT, |       op: GatewayOpcodes.HEARTBEAT, | ||||||
|       d: this.sequenceID ?? null |       d: this.sequenceID ?? null, | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.send(payload) |     this.send(payload) | ||||||
|     this.lastPingTimestamp = Date.now() |     this.lastPingTimestamp = Date.now() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   heartbeat (): void { |   heartbeat(): void { | ||||||
|     if (this.heartbeatServerResponded) { |     if (this.heartbeatServerResponded) { | ||||||
|       this.heartbeatServerResponded = false |       this.heartbeatServerResponded = false | ||||||
|     } else { |     } else { | ||||||
|  |  | ||||||
							
								
								
									
										30
									
								
								src/managers/guildVoiceStates.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/managers/guildVoiceStates.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | ||||||
|  | import { Client } from '../models/client.ts' | ||||||
|  | import { Guild } from "../structures/guild.ts" | ||||||
|  | import { VoiceChannel } from "../structures/guildVoiceChannel.ts" | ||||||
|  | import { User } from "../structures/user.ts" | ||||||
|  | import { VoiceState } from "../structures/voiceState.ts" | ||||||
|  | import { VoiceStatePayload } from "../types/voice.ts" | ||||||
|  | import { BaseManager } from './base.ts' | ||||||
|  | 
 | ||||||
|  | export class GuildVoiceStatesManager extends BaseManager<VoiceStatePayload, VoiceState> { | ||||||
|  |   guild: Guild | ||||||
|  | 
 | ||||||
|  |   async get (key: string): Promise<VoiceState | undefined> { | ||||||
|  |     const raw = await this._get(key) | ||||||
|  |     if (raw === undefined) return | ||||||
|  | 
 | ||||||
|  |     const guild = raw.guild_id === undefined ? undefined : await this.client.guilds.get(raw.guild_id) | ||||||
|  | 
 | ||||||
|  |     return new VoiceState(this.client, raw, { | ||||||
|  |       user: (await this.client.users.get(raw.user_id) as unknown) as User, | ||||||
|  |       channel: raw.channel_id == null ? null : (await this.client.channels.get<VoiceChannel>(raw.channel_id) as unknown) as VoiceChannel, | ||||||
|  |       guild, | ||||||
|  |       member: guild === undefined ? undefined : await guild.members.get(raw.user_id)  | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   constructor (client: Client, guild: Guild) { | ||||||
|  |     super(client, `vs:${guild.id}`, VoiceState) | ||||||
|  |     this.guild = guild | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -1,42 +1,81 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { BaseChildManager } from './baseChild.ts' | import { BaseChildManager } from './baseChild.ts' | ||||||
| import { RolePayload } from "../types/role.ts" | import { RolePayload } from '../types/role.ts' | ||||||
| import { Role } from "../structures/role.ts" | import { Role } from '../structures/role.ts' | ||||||
| import { Member } from "../structures/member.ts" | import { Member } from '../structures/member.ts' | ||||||
| import { RolesManager } from "./roles.ts" | import { RolesManager } from './roles.ts' | ||||||
| import { MemberPayload } from "../types/guild.ts" | import { MemberPayload } from '../types/guild.ts' | ||||||
| 
 | import { GUILD_MEMBER_ROLE } from '../types/endpoint.ts' | ||||||
| export class MemberRolesManager extends BaseChildManager< | 
 | ||||||
|   RolePayload, | export class MemberRolesManager extends BaseChildManager<RolePayload, Role> { | ||||||
|   Role |   member: Member | ||||||
| > { | 
 | ||||||
|   member: Member |   constructor(client: Client, parent: RolesManager, member: Member) { | ||||||
| 
 |     super(client, parent as any) | ||||||
|   constructor (client: Client, parent: RolesManager, member: Member) { |     this.member = member | ||||||
|     super(client, parent as any) |   } | ||||||
|     this.member = member | 
 | ||||||
|   } |   async get(id: string): Promise<Role | undefined> { | ||||||
| 
 |     const res = await this.parent.get(id) | ||||||
|   async get (id: string): Promise<Role | undefined> { |     const mem = (await (this.parent as any).guild.members._get( | ||||||
|     const res = await this.parent.get(id) |       this.member.id | ||||||
|     const mem = await (this.parent as any).guild.members._get(this.member.id) as MemberPayload |     )) as MemberPayload | ||||||
|     if (res !== undefined && (mem.roles.includes(res.id) === true || res.id === this.member.guild.id)) return res |     if ( | ||||||
|     else return undefined |       res !== undefined && | ||||||
|   } |       (mem.roles.includes(res.id) === true || res.id === this.member.guild.id) | ||||||
| 
 |     ) | ||||||
|   async array (): Promise<Role[]> { |       return res | ||||||
|     const arr = (await this.parent.array()) as Role[] |     else return undefined | ||||||
|     const mem = await (this.parent as any).guild.members._get(this.member.id) as MemberPayload |   } | ||||||
|     return arr.filter( | 
 | ||||||
|       (c: any) => mem.roles.includes(c.id) as boolean || c.id === this.member.guild.id |   async array(): Promise<Role[]> { | ||||||
|     ) as any |     const arr = (await this.parent.array()) as Role[] | ||||||
|   } |     const mem = (await (this.parent as any).guild.members._get( | ||||||
| 
 |       this.member.id | ||||||
|   async flush (): Promise<boolean> { |     )) as MemberPayload | ||||||
|     const arr = await this.array() |     return arr.filter( | ||||||
|     for (const elem of arr) { |       (c: any) => | ||||||
|       this.parent.delete(elem.id) |         (mem.roles.includes(c.id) as boolean) || c.id === this.member.guild.id | ||||||
|     } |     ) as any | ||||||
|     return true |   } | ||||||
|   } | 
 | ||||||
| } |   async flush(): Promise<boolean> { | ||||||
|  |     const arr = await this.array() | ||||||
|  |     for (const elem of arr) { | ||||||
|  |       this.parent.delete(elem.id) | ||||||
|  |     } | ||||||
|  |     return true | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async add(role: string | Role): Promise<boolean> { | ||||||
|  |     const res = await this.client.rest.put( | ||||||
|  |       GUILD_MEMBER_ROLE( | ||||||
|  |         this.member.guild.id, | ||||||
|  |         this.member.id, | ||||||
|  |         typeof role === 'string' ? role : role.id | ||||||
|  |       ), | ||||||
|  |       undefined, | ||||||
|  |       undefined, | ||||||
|  |       undefined, | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     return res.status === 204 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async remove(role: string | Role): Promise<boolean> { | ||||||
|  |     const res = await this.client.rest.delete( | ||||||
|  |       GUILD_MEMBER_ROLE( | ||||||
|  |         this.member.guild.id, | ||||||
|  |         this.member.id, | ||||||
|  |         typeof role === 'string' ? role : role.id | ||||||
|  |       ), | ||||||
|  |       undefined, | ||||||
|  |       undefined, | ||||||
|  |       undefined, | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     return res.status === 204 | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,190 +1,190 @@ | ||||||
| import { Guild } from '../structures/guild.ts' | import { Guild } from '../structures/guild.ts' | ||||||
| import { Message } from '../structures/message.ts' | import { Message } from '../structures/message.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 { Collection } from '../utils/collection.ts' | import { Collection } from '../utils/collection.ts' | ||||||
| import { CommandClient } from './commandClient.ts' | import { CommandClient } from './commandClient.ts' | ||||||
| import { Extension } from "./extensions.ts" | import { Extension } from "./extensions.ts" | ||||||
| 
 | 
 | ||||||
| export interface CommandContext { | export interface CommandContext { | ||||||
|   /** The Client object */ |   /** The Client object */ | ||||||
|   client: CommandClient |   client: CommandClient | ||||||
|   /** Message which was parsed for Command */ |   /** Message which was parsed for Command */ | ||||||
|   message: Message |   message: Message | ||||||
|   /** The Author of the Message */ |   /** The Author of the Message */ | ||||||
|   author: User |   author: User | ||||||
|   /** The Channel in which Command was used */ |   /** The Channel in which Command was used */ | ||||||
|   channel: TextChannel |   channel: TextChannel | ||||||
|   /** Prefix which was used */ |   /** Prefix which was used */ | ||||||
|   prefix: string |   prefix: string | ||||||
|   /** Oject of Command which was used */ |   /** Oject of Command which was used */ | ||||||
|   command: Command |   command: Command | ||||||
|   /** Name of Command which was used */ |   /** Name of Command which was used */ | ||||||
|   name: string |   name: string | ||||||
|   /** Array of Arguments used with Command */ |   /** Array of Arguments used with Command */ | ||||||
|   args: string[] |   args: string[] | ||||||
|   /** Complete Raw String of Arguments */ |   /** Complete Raw String of Arguments */ | ||||||
|   argString: string |   argString: string | ||||||
|   /** Guild which the command has called */ |   /** Guild which the command has called */ | ||||||
|   guild?: Guild |   guild?: Guild | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class Command { | export class Command { | ||||||
|   /** Name of the Command */ |   /** Name of the Command */ | ||||||
|   name: string = '' |   name: string = '' | ||||||
|   /** Description of the Command */ |   /** Description of the Command */ | ||||||
|   description?: string |   description?: string | ||||||
|   /** Array of Aliases of Command, or only string */ |   /** Array of Aliases of Command, or only string */ | ||||||
|   aliases?: string | string[] |   aliases?: string | string[] | ||||||
|   /** Extension (Parent) of the Command */ |   /** Extension (Parent) of the Command */ | ||||||
|   extension?: Extension |   extension?: Extension | ||||||
|   /** Usage of Command, only Argument Names */ |   /** Usage of Command, only Argument Names */ | ||||||
|   usage?: string | string[] |   usage?: string | string[] | ||||||
|   /** Usage Example of Command, only Arguments (without Prefix and Name) */ |   /** Usage Example of Command, only Arguments (without Prefix and Name) */ | ||||||
|   examples?: string | string[] |   examples?: string | string[] | ||||||
|   /** Does the Command take Arguments? Maybe number of required arguments? */ |   /** Does the Command take Arguments? Maybe number of required arguments? */ | ||||||
|   args?: number | boolean |   args?: number | boolean | ||||||
|   /** Permission(s) required for using Command */ |   /** Permission(s) required for using Command */ | ||||||
|   permissions?: string | string[] |   permissions?: string | string[] | ||||||
|   /** Permission(s) bot will need in order to execute Command */ |   /** Permission(s) bot will need in order to execute Command */ | ||||||
|   botPermissions?: string | string[] |   botPermissions?: string | string[] | ||||||
|   /** Role(s) user will require in order to use Command. List or one of ID or name */ |   /** Role(s) user will require in order to use Command. List or one of ID or name */ | ||||||
|   roles?: string | string[] |   roles?: string | string[] | ||||||
|   /** Whitelisted Guilds. Only these Guild(s) can execute Command. (List or one of IDs) */ |   /** Whitelisted Guilds. Only these Guild(s) can execute Command. (List or one of IDs) */ | ||||||
|   whitelistedGuilds?: string | string[] |   whitelistedGuilds?: string | string[] | ||||||
|   /** Whitelisted Channels. Command can be executed only in these channels. (List or one of IDs) */ |   /** Whitelisted Channels. Command can be executed only in these channels. (List or one of IDs) */ | ||||||
|   whitelistedChannels?: string | string[] |   whitelistedChannels?: string | string[] | ||||||
|   /** Whitelisted Users. Command can be executed only by these Users (List or one of IDs) */ |   /** Whitelisted Users. Command can be executed only by these Users (List or one of IDs) */ | ||||||
|   whitelistedUsers?: string | string[] |   whitelistedUsers?: string | string[] | ||||||
|   /** Whether the Command can only be used in Guild (if allowed in DMs) */ |   /** Whether the Command can only be used in Guild (if allowed in DMs) */ | ||||||
|   guildOnly?: boolean |   guildOnly?: boolean | ||||||
|   /** Whether the Command can only be used in Bot's DMs (if allowed) */ |   /** Whether the Command can only be used in Bot's DMs (if allowed) */ | ||||||
|   dmOnly?: boolean |   dmOnly?: boolean | ||||||
|   /** Whether the Command can only be used by Bot Owners */ |   /** Whether the Command can only be used by Bot Owners */ | ||||||
|   ownerOnly?: boolean |   ownerOnly?: boolean | ||||||
| 
 | 
 | ||||||
|   /** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */ |   /** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */ | ||||||
|   beforeExecute(ctx: CommandContext): boolean | Promise<boolean> { return true } |   beforeExecute(ctx: CommandContext): boolean | Promise<boolean> { return true } | ||||||
|   /** Actual command code, which is executed when all checks have passed. */ |   /** Actual command code, which is executed when all checks have passed. */ | ||||||
|   execute(ctx: CommandContext): any { } |   execute(ctx: CommandContext): any { } | ||||||
|   /** Method executed after executing command, passes on CommandContext and the value returned by execute too. (optional) */ |   /** Method executed after executing command, passes on CommandContext and the value returned by execute too. (optional) */ | ||||||
|   afterExecute(ctx: CommandContext, executeResult: any): any { } |   afterExecute(ctx: CommandContext, executeResult: any): any { } | ||||||
| 
 | 
 | ||||||
|   toString(): string { |   toString(): string { | ||||||
|     return `Command: ${this.name}${this.extension !== undefined ? ` [${this.extension.name}]` : ''}` |     return `Command: ${this.name}${this.extension !== undefined ? ` [${this.extension.name}]` : ''}` | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class CommandsManager { | export class CommandsManager { | ||||||
|   client: CommandClient |   client: CommandClient | ||||||
|   list: Collection<string, Command> = new Collection() |   list: Collection<string, Command> = new Collection() | ||||||
|   disabled: Set<string> = new Set() |   disabled: Set<string> = new Set() | ||||||
| 
 | 
 | ||||||
|   constructor(client: CommandClient) { |   constructor(client: CommandClient) { | ||||||
|     this.client = client |     this.client = client | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Number of loaded Commands */ |   /** Number of loaded Commands */ | ||||||
|   get count(): number { return this.list.size } |   get count(): number { return this.list.size } | ||||||
| 
 | 
 | ||||||
|   /** Find a Command by name/alias */ |   /** Find a Command by name/alias */ | ||||||
|   find(search: string): Command | undefined { |   find(search: string): Command | undefined { | ||||||
|     if (this.client.caseSensitive === false) search = search.toLowerCase() |     if (this.client.caseSensitive === false) search = search.toLowerCase() | ||||||
|     return this.list.find((cmd: Command): boolean => { |     return this.list.find((cmd: Command): boolean => { | ||||||
|       const name = |       const name = | ||||||
|         this.client.caseSensitive === true ? cmd.name : cmd.name.toLowerCase() |         this.client.caseSensitive === true ? cmd.name : cmd.name.toLowerCase() | ||||||
|       if (name === search) return true |       if (name === search) return true | ||||||
|       else if (cmd.aliases !== undefined) { |       else if (cmd.aliases !== undefined) { | ||||||
|         let aliases: string[] |         let aliases: string[] | ||||||
|         if (typeof cmd.aliases === 'string') aliases = [cmd.aliases] |         if (typeof cmd.aliases === 'string') aliases = [cmd.aliases] | ||||||
|         else aliases = cmd.aliases |         else aliases = cmd.aliases | ||||||
|         if (this.client.caseSensitive === false) |         if (this.client.caseSensitive === false) | ||||||
|           aliases = aliases.map(e => e.toLowerCase()) |           aliases = aliases.map(e => e.toLowerCase()) | ||||||
|         return aliases.includes(search) |         return aliases.includes(search) | ||||||
|       } else return false |       } else return false | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Fetch a Command including disable checks */ |   /** Fetch a Command including disable checks */ | ||||||
|   fetch(name: string, bypassDisable?: boolean): Command | undefined { |   fetch(name: string, bypassDisable?: boolean): Command | undefined { | ||||||
|     const cmd = this.find(name) |     const cmd = this.find(name) | ||||||
|     if (cmd === undefined) return |     if (cmd === undefined) return | ||||||
|     if (this.isDisabled(cmd) && bypassDisable !== true) return |     if (this.isDisabled(cmd) && bypassDisable !== true) return | ||||||
|     return cmd |     return cmd | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Check whether a Command exists or not */ |   /** Check whether a Command exists or not */ | ||||||
|   exists(search: Command | string): boolean { |   exists(search: Command | string): boolean { | ||||||
|     let exists = false |     let exists = false | ||||||
|     if (typeof search === 'string') return this.find(search) !== undefined |     if (typeof search === 'string') return this.find(search) !== undefined | ||||||
|     else { |     else { | ||||||
|       exists = this.find(search.name) !== undefined |       exists = this.find(search.name) !== undefined | ||||||
|       if (search.aliases !== undefined) { |       if (search.aliases !== undefined) { | ||||||
|         const aliases: string[] = |         const aliases: string[] = | ||||||
|           typeof search.aliases === 'string' ? [search.aliases] : search.aliases |           typeof search.aliases === 'string' ? [search.aliases] : search.aliases | ||||||
|         exists = |         exists = | ||||||
|           aliases.map(alias => this.find(alias) !== undefined).find(e => e) ?? |           aliases.map(alias => this.find(alias) !== undefined).find(e => e) ?? | ||||||
|           false |           false | ||||||
|       } |       } | ||||||
|       return exists |       return exists | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Add a Command */ |   /** Add a Command */ | ||||||
|   add(cmd: Command | typeof Command): boolean { |   add(cmd: Command | typeof Command): boolean { | ||||||
|     // eslint-disable-next-line new-cap
 |     // eslint-disable-next-line new-cap
 | ||||||
|     if (!(cmd instanceof Command)) cmd = new cmd() |     if (!(cmd instanceof Command)) cmd = new cmd() | ||||||
|     if (this.exists(cmd)) throw new Error(`Failed to add Command '${cmd.toString()}' with name/alias already exists.`) |     if (this.exists(cmd)) throw new Error(`Failed to add Command '${cmd.toString()}' with name/alias already exists.`) | ||||||
|     this.list.set(cmd.name, cmd) |     this.list.set(cmd.name, cmd) | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Delete a Command */ |   /** Delete a Command */ | ||||||
|   delete(cmd: string | Command): boolean { |   delete(cmd: string | Command): boolean { | ||||||
|     const find = typeof cmd === 'string' ? this.find(cmd) : cmd |     const find = typeof cmd === 'string' ? this.find(cmd) : cmd | ||||||
|     if (find === undefined) return false |     if (find === undefined) return false | ||||||
|     else return this.list.delete(find.name) |     else return this.list.delete(find.name) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Check whether a Command is disabled or not */ |   /** Check whether a Command is disabled or not */ | ||||||
|   isDisabled(name: string | Command): boolean { |   isDisabled(name: string | Command): boolean { | ||||||
|     const cmd = typeof name === "string" ? this.find(name) : name |     const cmd = typeof name === "string" ? this.find(name) : name | ||||||
|     if (cmd === undefined) return false |     if (cmd === undefined) return false | ||||||
|     const exists = this.exists(name) |     const exists = this.exists(name) | ||||||
|     if (!exists) return false |     if (!exists) return false | ||||||
|     return this.disabled.has(cmd.name) |     return this.disabled.has(cmd.name) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Disable a Command */ |   /** Disable a Command */ | ||||||
|   disable(name: string | Command): boolean { |   disable(name: string | Command): boolean { | ||||||
|     const cmd = typeof name === "string" ? this.find(name) : name |     const cmd = typeof name === "string" ? this.find(name) : name | ||||||
|     if (cmd === undefined) return false |     if (cmd === undefined) return false | ||||||
|     if (this.isDisabled(cmd)) return false |     if (this.isDisabled(cmd)) return false | ||||||
|     this.disabled.add(cmd.name) |     this.disabled.add(cmd.name) | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface ParsedCommand { | export interface ParsedCommand { | ||||||
|   name: string |   name: string | ||||||
|   args: string[] |   args: string[] | ||||||
|   argString: string |   argString: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const parseCommand = ( | export const parseCommand = ( | ||||||
|   client: CommandClient, |   client: CommandClient, | ||||||
|   msg: Message, |   msg: Message, | ||||||
|   prefix: string |   prefix: string | ||||||
| ): ParsedCommand => { | ): ParsedCommand => { | ||||||
|   let content = msg.content.slice(prefix.length) |   let content = msg.content.slice(prefix.length) | ||||||
|   if (client.spacesAfterPrefix === true) content = content.trim() |   if (client.spacesAfterPrefix === true) content = content.trim() | ||||||
|   const args = content.split(client.betterArgs === true ? /[\S\s]*/ : / +/) |   const args = content.split(client.betterArgs === true ? /[\S\s]*/ : / +/) | ||||||
|   const name = args.shift() as string |   const name = args.shift() as string | ||||||
|   const argString = content.slice(name.length).trim() |   const argString = content.slice(name.length).trim() | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     name, |     name, | ||||||
|     args, |     args, | ||||||
|     argString |     argString | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,13 @@ import { Client } from './client.ts' | ||||||
| import { getBuildInfo } from '../utils/buildInfo.ts' | import { getBuildInfo } from '../utils/buildInfo.ts' | ||||||
| import { Collection } from '../utils/collection.ts' | import { Collection } from '../utils/collection.ts' | ||||||
| 
 | 
 | ||||||
| export type RequestMethods = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete' | export type RequestMethods = | ||||||
|  |   | 'get' | ||||||
|  |   | 'post' | ||||||
|  |   | 'put' | ||||||
|  |   | 'patch' | ||||||
|  |   | 'head' | ||||||
|  |   | 'delete' | ||||||
| 
 | 
 | ||||||
| export enum HttpResponseCode { | export enum HttpResponseCode { | ||||||
|   Ok = 200, |   Ok = 200, | ||||||
|  | @ -16,7 +22,7 @@ export enum HttpResponseCode { | ||||||
|   NotFound = 404, |   NotFound = 404, | ||||||
|   MethodNotAllowed = 405, |   MethodNotAllowed = 405, | ||||||
|   TooManyRequests = 429, |   TooManyRequests = 429, | ||||||
|   GatewayUnavailable = 502 |   GatewayUnavailable = 502, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface RequestHeaders { | export interface RequestHeaders { | ||||||
|  | @ -30,11 +36,14 @@ export class DiscordAPIError extends Error { | ||||||
| export interface QueuedItem { | export interface QueuedItem { | ||||||
|   bucket?: string | null |   bucket?: string | null | ||||||
|   url: string |   url: string | ||||||
|   onComplete: () => Promise<{ |   onComplete: () => Promise< | ||||||
|     rateLimited: any |     | { | ||||||
|     bucket?: string | null |         rateLimited: any | ||||||
|     before: boolean |         bucket?: string | null | ||||||
|   } | undefined> |         before: boolean | ||||||
|  |       } | ||||||
|  |     | undefined | ||||||
|  |   > | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface RateLimit { | export interface RateLimit { | ||||||
|  | @ -85,16 +94,14 @@ export class RESTManager { | ||||||
|   async processQueue(): Promise<void> { |   async processQueue(): Promise<void> { | ||||||
|     if (Object.keys(this.queues).length !== 0 && !this.globalRateLimit) { |     if (Object.keys(this.queues).length !== 0 && !this.globalRateLimit) { | ||||||
|       await Promise.allSettled( |       await Promise.allSettled( | ||||||
|         Object.values(this.queues).map(async pathQueue => { |         Object.values(this.queues).map(async (pathQueue) => { | ||||||
|           const request = pathQueue.shift() |           const request = pathQueue.shift() | ||||||
|           if (request === undefined) return |           if (request === undefined) return | ||||||
| 
 | 
 | ||||||
|           const rateLimitedURLResetIn = await this.isRateLimited(request.url) |           const rateLimitedURLResetIn = await this.isRateLimited(request.url) | ||||||
| 
 | 
 | ||||||
|           if (typeof request.bucket === 'string') { |           if (typeof request.bucket === 'string') { | ||||||
|             const rateLimitResetIn = await this.isRateLimited( |             const rateLimitResetIn = await this.isRateLimited(request.bucket) | ||||||
|               request.bucket |  | ||||||
|             ) |  | ||||||
|             if (rateLimitResetIn !== false) { |             if (rateLimitResetIn !== false) { | ||||||
|               this.queue(request) |               this.queue(request) | ||||||
|             } else { |             } else { | ||||||
|  | @ -102,7 +109,7 @@ export class RESTManager { | ||||||
|               if (result?.rateLimited !== undefined) { |               if (result?.rateLimited !== undefined) { | ||||||
|                 this.queue({ |                 this.queue({ | ||||||
|                   ...request, |                   ...request, | ||||||
|                   bucket: result.bucket ?? request.bucket |                   bucket: result.bucket ?? request.bucket, | ||||||
|                 }) |                 }) | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|  | @ -114,7 +121,7 @@ export class RESTManager { | ||||||
|               if (result?.rateLimited !== undefined) { |               if (result?.rateLimited !== undefined) { | ||||||
|                 this.queue({ |                 this.queue({ | ||||||
|                   ...request, |                   ...request, | ||||||
|                   bucket: result.bucket ?? request.bucket |                   bucket: result.bucket ?? request.bucket, | ||||||
|                 }) |                 }) | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|  | @ -132,20 +139,18 @@ export class RESTManager { | ||||||
|     } else this.processing = false |     } else this.processing = false | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   prepare( |   prepare(body: any, method: RequestMethods): { [key: string]: any } { | ||||||
|     body: any, |  | ||||||
|     method: RequestMethods |  | ||||||
|   ): { [key: string]: any } { |  | ||||||
| 
 |  | ||||||
|     const headers: RequestHeaders = { |     const headers: RequestHeaders = { | ||||||
|       'User-Agent': `DiscordBot (harmony, https://github.com/harmony-org/harmony)` |       'User-Agent': `DiscordBot (harmony, https://github.com/harmony-org/harmony)`, | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (this.client !== undefined) headers.Authorization = `Bot ${this.client.token}` |     if (this.client !== undefined) | ||||||
|  |       headers.Authorization = `Bot ${this.client.token}` | ||||||
| 
 | 
 | ||||||
|     if (this.client?.token === undefined) delete headers.Authorization |     if (this.client?.token === undefined) delete headers.Authorization | ||||||
| 
 | 
 | ||||||
|     if (method === 'get' || method === 'head' || method === 'delete') body = undefined |     if (method === 'get' || method === 'head' || method === 'delete') | ||||||
|  |       body = undefined | ||||||
| 
 | 
 | ||||||
|     if (body?.reason !== undefined) { |     if (body?.reason !== undefined) { | ||||||
|       headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason) |       headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason) | ||||||
|  | @ -163,7 +168,7 @@ export class RESTManager { | ||||||
|     const data: { [name: string]: any } = { |     const data: { [name: string]: any } = { | ||||||
|       headers, |       headers, | ||||||
|       body: body?.file ?? JSON.stringify(body), |       body: body?.file ?? JSON.stringify(body), | ||||||
|       method: method.toUpperCase() |       method: method.toUpperCase(), | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (this.client?.bot === false) { |     if (this.client?.bot === false) { | ||||||
|  | @ -217,14 +222,14 @@ export class RESTManager { | ||||||
|       this.rateLimits.set(url, { |       this.rateLimits.set(url, { | ||||||
|         url, |         url, | ||||||
|         resetAt: Number(resetAt) * 1000, |         resetAt: Number(resetAt) * 1000, | ||||||
|         bucket |         bucket, | ||||||
|       }) |       }) | ||||||
| 
 | 
 | ||||||
|       if (bucket !== null) { |       if (bucket !== null) { | ||||||
|         this.rateLimits.set(bucket, { |         this.rateLimits.set(bucket, { | ||||||
|           url, |           url, | ||||||
|           resetAt: Number(resetAt) * 1000, |           resetAt: Number(resetAt) * 1000, | ||||||
|           bucket |           bucket, | ||||||
|         }) |         }) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -237,14 +242,14 @@ export class RESTManager { | ||||||
|       this.rateLimits.set('global', { |       this.rateLimits.set('global', { | ||||||
|         url: 'global', |         url: 'global', | ||||||
|         resetAt: reset, |         resetAt: reset, | ||||||
|         bucket |         bucket, | ||||||
|       }) |       }) | ||||||
| 
 | 
 | ||||||
|       if (bucket !== null) { |       if (bucket !== null) { | ||||||
|         this.rateLimits.set(bucket, { |         this.rateLimits.set(bucket, { | ||||||
|           url: 'global', |           url: 'global', | ||||||
|           resetAt: reset, |           resetAt: reset, | ||||||
|           bucket |           bucket, | ||||||
|         }) |         }) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -253,32 +258,46 @@ export class RESTManager { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async handleStatusCode( |   async handleStatusCode( | ||||||
|     response: Response, body: any, data: { [key: string]: any } |     response: Response, | ||||||
|  |     body: any, | ||||||
|  |     data: { [key: string]: any } | ||||||
|   ): Promise<undefined> { |   ): Promise<undefined> { | ||||||
|     const status = response.status |     const status = response.status | ||||||
| 
 | 
 | ||||||
|     if ( |     if ( | ||||||
|       (status >= 200 && status < 400) |       (status >= 200 && status < 400) || | ||||||
|       || status === HttpResponseCode.NoContent |       status === HttpResponseCode.NoContent || | ||||||
|       || status === HttpResponseCode.TooManyRequests |       status === HttpResponseCode.TooManyRequests | ||||||
|     ) return |     ) | ||||||
|  |       return | ||||||
| 
 | 
 | ||||||
|     let text: undefined | string = Deno.inspect(body.errors === undefined ? body : body.errors) |     let text: undefined | string = Deno.inspect( | ||||||
|  |       body.errors === undefined ? body : body.errors | ||||||
|  |     ) | ||||||
|     if (text === 'undefined') text = undefined |     if (text === 'undefined') text = undefined | ||||||
| 
 | 
 | ||||||
|     if (status === HttpResponseCode.Unauthorized) |     if (status === HttpResponseCode.Unauthorized) | ||||||
|       throw new DiscordAPIError(`Request was not successful (Unauthorized). Invalid Token.\n${text}`) |       throw new DiscordAPIError( | ||||||
|  |         `Request was not successful (Unauthorized). Invalid Token.\n${text}` | ||||||
|  |       ) | ||||||
| 
 | 
 | ||||||
|     // At this point we know it is error
 |     // At this point we know it is error
 | ||||||
|     let error = { url: response.url, status, method: data.method, body: data.body } |     let error = { | ||||||
|  |       url: response.url, | ||||||
|  |       status, | ||||||
|  |       method: data.method, | ||||||
|  |       body: data.body, | ||||||
|  |     } | ||||||
|     if (body !== undefined) error = Object.assign(error, body) |     if (body !== undefined) error = Object.assign(error, body) | ||||||
| 
 | 
 | ||||||
|     if ([ |     if ( | ||||||
|       HttpResponseCode.BadRequest, |       [ | ||||||
|       HttpResponseCode.NotFound, |         HttpResponseCode.BadRequest, | ||||||
|       HttpResponseCode.Forbidden, |         HttpResponseCode.NotFound, | ||||||
|       HttpResponseCode.MethodNotAllowed |         HttpResponseCode.Forbidden, | ||||||
|     ].includes(status)) { |         HttpResponseCode.MethodNotAllowed, | ||||||
|  |       ].includes(status) | ||||||
|  |     ) { | ||||||
|       throw new DiscordAPIError(Deno.inspect(error)) |       throw new DiscordAPIError(Deno.inspect(error)) | ||||||
|     } else if (status === HttpResponseCode.GatewayUnavailable) { |     } else if (status === HttpResponseCode.GatewayUnavailable) { | ||||||
|       throw new DiscordAPIError(Deno.inspect(error)) |       throw new DiscordAPIError(Deno.inspect(error)) | ||||||
|  | @ -291,7 +310,7 @@ export class RESTManager { | ||||||
|     body?: unknown, |     body?: unknown, | ||||||
|     maxRetries = 0, |     maxRetries = 0, | ||||||
|     bucket?: string | null, |     bucket?: string | null, | ||||||
|     rawResponse?: boolean, |     rawResponse?: boolean | ||||||
|   ): Promise<any> { |   ): Promise<any> { | ||||||
|     return await new Promise((resolve, reject) => { |     return await new Promise((resolve, reject) => { | ||||||
|       const onComplete = async (): Promise<undefined | any> => { |       const onComplete = async (): Promise<undefined | any> => { | ||||||
|  | @ -301,20 +320,20 @@ export class RESTManager { | ||||||
|             return { |             return { | ||||||
|               rateLimited: rateLimitResetIn, |               rateLimited: rateLimitResetIn, | ||||||
|               before: true, |               before: true, | ||||||
|               bucket |               bucket, | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           const query = |           const query = | ||||||
|             method === 'get' && body !== undefined |             method === 'get' && body !== undefined | ||||||
|               ? Object.entries(body as any) |               ? Object.entries(body as any) | ||||||
|                 .map( |                   .map( | ||||||
|                   ([key, value]) => |                     ([key, value]) => | ||||||
|                     `${encodeURIComponent(key)}=${encodeURIComponent( |                       `${encodeURIComponent(key)}=${encodeURIComponent( | ||||||
|                       value as any |                         value as any | ||||||
|                     )}` |                       )}` | ||||||
|                 ) |                   ) | ||||||
|                 .join('&') |                   .join('&') | ||||||
|               : '' |               : '' | ||||||
|           let urlToUse = |           let urlToUse = | ||||||
|             method === 'get' && query !== '' ? `${url}?${query}` : url |             method === 'get' && query !== '' ? `${url}?${query}` : url | ||||||
|  | @ -329,7 +348,10 @@ export class RESTManager { | ||||||
|           const response = await fetch(urlToUse, requestData) |           const response = await fetch(urlToUse, requestData) | ||||||
|           const bucketFromHeaders = this.processHeaders(url, response.headers) |           const bucketFromHeaders = this.processHeaders(url, response.headers) | ||||||
| 
 | 
 | ||||||
|           if (response.status === 204) return resolve(rawResponse === true ? { response, body: null } : undefined) |           if (response.status === 204) | ||||||
|  |             return resolve( | ||||||
|  |               rawResponse === true ? { response, body: null } : undefined | ||||||
|  |             ) | ||||||
| 
 | 
 | ||||||
|           const json: any = await response.json() |           const json: any = await response.json() | ||||||
|           await this.handleStatusCode(response, json, requestData) |           await this.handleStatusCode(response, json, requestData) | ||||||
|  | @ -345,7 +367,7 @@ export class RESTManager { | ||||||
|             return { |             return { | ||||||
|               rateLimited: json.retry_after, |               rateLimited: json.retry_after, | ||||||
|               before: false, |               before: false, | ||||||
|               bucket: bucketFromHeaders |               bucket: bucketFromHeaders, | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|           return resolve(rawResponse === true ? { response, body: json } : json) |           return resolve(rawResponse === true ? { response, body: json } : json) | ||||||
|  | @ -357,7 +379,7 @@ export class RESTManager { | ||||||
|       this.queue({ |       this.queue({ | ||||||
|         onComplete, |         onComplete, | ||||||
|         bucket, |         bucket, | ||||||
|         url |         url, | ||||||
|       }) |       }) | ||||||
|       if (!this.processing) { |       if (!this.processing) { | ||||||
|         this.processing = true |         this.processing = true | ||||||
|  | @ -376,23 +398,53 @@ export class RESTManager { | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async get(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> { |   async get( | ||||||
|  |     url: string, | ||||||
|  |     body?: unknown, | ||||||
|  |     maxRetries = 0, | ||||||
|  |     bucket?: string | null, | ||||||
|  |     rawResponse?: boolean | ||||||
|  |   ): Promise<any> { | ||||||
|     return await this.make('get', url, body, maxRetries, bucket, rawResponse) |     return await this.make('get', url, body, maxRetries, bucket, rawResponse) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async post(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> { |   async post( | ||||||
|  |     url: string, | ||||||
|  |     body?: unknown, | ||||||
|  |     maxRetries = 0, | ||||||
|  |     bucket?: string | null, | ||||||
|  |     rawResponse?: boolean | ||||||
|  |   ): Promise<any> { | ||||||
|     return await this.make('post', url, body, maxRetries, bucket, rawResponse) |     return await this.make('post', url, body, maxRetries, bucket, rawResponse) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async delete(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> { |   async delete( | ||||||
|  |     url: string, | ||||||
|  |     body?: unknown, | ||||||
|  |     maxRetries = 0, | ||||||
|  |     bucket?: string | null, | ||||||
|  |     rawResponse?: boolean | ||||||
|  |   ): Promise<any> { | ||||||
|     return await this.make('delete', url, body, maxRetries, bucket, rawResponse) |     return await this.make('delete', url, body, maxRetries, bucket, rawResponse) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async patch(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> { |   async patch( | ||||||
|  |     url: string, | ||||||
|  |     body?: unknown, | ||||||
|  |     maxRetries = 0, | ||||||
|  |     bucket?: string | null, | ||||||
|  |     rawResponse?: boolean | ||||||
|  |   ): Promise<any> { | ||||||
|     return await this.make('patch', url, body, maxRetries, bucket, rawResponse) |     return await this.make('patch', url, body, maxRetries, bucket, rawResponse) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async put(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> { |   async put( | ||||||
|  |     url: string, | ||||||
|  |     body?: unknown, | ||||||
|  |     maxRetries = 0, | ||||||
|  |     bucket?: string | null, | ||||||
|  |     rawResponse?: boolean | ||||||
|  |   ): Promise<any> { | ||||||
|     return await this.make('put', url, body, maxRetries, bucket, rawResponse) |     return await this.make('put', url, body, maxRetries, bucket, rawResponse) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,10 +1,26 @@ | ||||||
|  | import { Guild } from "../structures/guild.ts" | ||||||
|  | import { VoiceChannel } from "../structures/guildVoiceChannel.ts" | ||||||
| import { Client } from './client.ts' | import { Client } from './client.ts' | ||||||
| 
 | 
 | ||||||
|  | export interface VoiceOptions { | ||||||
|  |   guild: Guild, | ||||||
|  |   channel: VoiceChannel | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export class VoiceClient { | export class VoiceClient { | ||||||
|   client: Client |   client: Client | ||||||
|  |   ws?: WebSocket | ||||||
|  |   guild: Guild | ||||||
|  |   channel: VoiceChannel | ||||||
| 
 | 
 | ||||||
|   constructor(client: Client) { |   constructor(client: Client, options: VoiceOptions) { | ||||||
|     this.client = client |     this.client = client | ||||||
|      |     this.guild = options.guild | ||||||
|  |     this.channel = options.channel | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async connect(): Promise<VoiceClient> { | ||||||
|  |     // TODO(DjDeveloperr): Actually understand what the hell docs say
 | ||||||
|  |     return this | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { | import { | ||||||
|  |   GuildBanPayload, | ||||||
|   GuildFeatures, |   GuildFeatures, | ||||||
|   GuildIntegrationPayload, |   GuildIntegrationPayload, | ||||||
|   GuildPayload, |   GuildPayload, | ||||||
|   IntegrationAccountPayload, |   IntegrationAccountPayload, | ||||||
|   IntegrationExpireBehavior |   IntegrationExpireBehavior, | ||||||
| } from '../types/guild.ts' | } from '../types/guild.ts' | ||||||
| import { PresenceUpdatePayload } from '../types/gateway.ts' | import { PresenceUpdatePayload } from '../types/gateway.ts' | ||||||
| import { Base } from './base.ts' | import { Base } from './base.ts' | ||||||
| import { VoiceState } from './voiceState.ts' |  | ||||||
| import { RolesManager } from '../managers/roles.ts' | import { RolesManager } from '../managers/roles.ts' | ||||||
| import { GuildChannelsManager } from '../managers/guildChannels.ts' | import { GuildChannelsManager } from '../managers/guildChannels.ts' | ||||||
| import { MembersManager } from '../managers/members.ts' | import { MembersManager } from '../managers/members.ts' | ||||||
|  | @ -17,7 +17,100 @@ import { GuildEmojisManager } from '../managers/guildEmojis.ts' | ||||||
| import { Member } from './member.ts' | import { Member } from './member.ts' | ||||||
| import { User } from './user.ts' | import { User } from './user.ts' | ||||||
| import { Application } from './application.ts' | import { Application } from './application.ts' | ||||||
| import { GUILD_INTEGRATIONS } from '../types/endpoint.ts' | import { GUILD_BAN, GUILD_BANS, GUILD_INTEGRATIONS } from '../types/endpoint.ts' | ||||||
|  | import { GuildVoiceStatesManager } from '../managers/guildVoiceStates.ts' | ||||||
|  | import { RequestMembersOptions } from '../gateway/index.ts' | ||||||
|  | 
 | ||||||
|  | export class GuildBan extends Base { | ||||||
|  |   guild: Guild | ||||||
|  |   reason?: string | ||||||
|  |   user: User | ||||||
|  | 
 | ||||||
|  |   constructor(client: Client, data: GuildBanPayload, guild: Guild) { | ||||||
|  |     super(client, data) | ||||||
|  |     this.guild = guild | ||||||
|  |     this.reason = data.reason === null ? undefined : data.reason | ||||||
|  |     this.user = new User(client, data.user) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class GuildBans { | ||||||
|  |   client: Client | ||||||
|  |   guild: Guild | ||||||
|  | 
 | ||||||
|  |   constructor(client: Client, guild: Guild) { | ||||||
|  |     this.client = client | ||||||
|  |     this.guild = guild | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Get all bans in the Guild. | ||||||
|  |    */ | ||||||
|  |   async all(): Promise<GuildBan[]> { | ||||||
|  |     const res = await this.client.rest.get(GUILD_BANS(this.guild.id)) | ||||||
|  |     if (typeof res !== 'object' || !Array.isArray(res)) | ||||||
|  |       throw new Error('Failed to fetch Guild Bans') | ||||||
|  | 
 | ||||||
|  |     const bans = (res as GuildBanPayload[]).map( | ||||||
|  |       (ban) => new GuildBan(this.client, ban, this.guild) | ||||||
|  |     ) | ||||||
|  |     return bans | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Get ban details of a User if any. | ||||||
|  |    * @param user User to get ban of, ID or User object. | ||||||
|  |    */ | ||||||
|  |   async get(user: string | User): Promise<GuildBan> { | ||||||
|  |     const res = await this.client.rest.get( | ||||||
|  |       GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id) | ||||||
|  |     ) | ||||||
|  |     if (typeof res !== 'object') throw new Error('Failed to fetch Guild Ban') | ||||||
|  |     return new GuildBan(this.client, res, this.guild) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Ban a User. | ||||||
|  |    * @param user User to ban, ID or User object. | ||||||
|  |    * @param reason Reason for the Ban. | ||||||
|  |    * @param deleteMessagesDays Delete Old Messages? If yes, how much days. | ||||||
|  |    */ | ||||||
|  |   async add( | ||||||
|  |     user: string | User, | ||||||
|  |     reason?: string, | ||||||
|  |     deleteMessagesDays?: number | ||||||
|  |   ): Promise<void> { | ||||||
|  |     const res = await this.client.rest.put( | ||||||
|  |       GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id), | ||||||
|  |       { | ||||||
|  |         reason, | ||||||
|  |         delete_message_days: deleteMessagesDays, | ||||||
|  |       }, | ||||||
|  |       undefined, | ||||||
|  |       null, | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     if (res.status !== 204) throw new Error('Failed to Add Guild Ban') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Unban (remove ban from) a User. | ||||||
|  |    * @param user User to unban, ID or User object. | ||||||
|  |    */ | ||||||
|  |   async remove(user: string | User): Promise<boolean> { | ||||||
|  |     const res = await this.client.rest.delete( | ||||||
|  |       GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id), | ||||||
|  |       undefined, | ||||||
|  |       undefined, | ||||||
|  |       null, | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     if (res.status !== 204) return false | ||||||
|  |     else return true | ||||||
|  |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| export class Guild extends Base { | export class Guild extends Base { | ||||||
|   id: string |   id: string | ||||||
|  | @ -49,7 +142,7 @@ export class Guild extends Base { | ||||||
|   large?: boolean |   large?: boolean | ||||||
|   unavailable: boolean |   unavailable: boolean | ||||||
|   memberCount?: number |   memberCount?: number | ||||||
|   voiceStates?: VoiceState[] |   voiceStates: GuildVoiceStatesManager | ||||||
|   members: MembersManager |   members: MembersManager | ||||||
|   channels: GuildChannelsManager |   channels: GuildChannelsManager | ||||||
|   presences?: PresenceUpdatePayload[] |   presences?: PresenceUpdatePayload[] | ||||||
|  | @ -65,12 +158,15 @@ export class Guild extends Base { | ||||||
|   maxVideoChannelUsers?: number |   maxVideoChannelUsers?: number | ||||||
|   approximateNumberCount?: number |   approximateNumberCount?: number | ||||||
|   approximatePresenceCount?: number |   approximatePresenceCount?: number | ||||||
|  |   bans: GuildBans | ||||||
| 
 | 
 | ||||||
|   constructor (client: Client, data: GuildPayload) { |   constructor(client: Client, data: GuildPayload) { | ||||||
|     super(client, data) |     super(client, data) | ||||||
|     this.id = data.id |     this.id = data.id | ||||||
|  |     this.bans = new GuildBans(client, this) | ||||||
|     this.unavailable = data.unavailable |     this.unavailable = data.unavailable | ||||||
|     this.members = new MembersManager(this.client, this) |     this.members = new MembersManager(this.client, this) | ||||||
|  |     this.voiceStates = new GuildVoiceStatesManager(client, this) | ||||||
|     this.channels = new GuildChannelsManager( |     this.channels = new GuildChannelsManager( | ||||||
|       this.client, |       this.client, | ||||||
|       this.client.channels, |       this.client.channels, | ||||||
|  | @ -96,15 +192,6 @@ export class Guild extends Base { | ||||||
|       this.verificationLevel = data.verification_level |       this.verificationLevel = data.verification_level | ||||||
|       this.defaultMessageNotifications = data.default_message_notifications |       this.defaultMessageNotifications = data.default_message_notifications | ||||||
|       this.explicitContentFilter = data.explicit_content_filter |       this.explicitContentFilter = data.explicit_content_filter | ||||||
|       // this.roles = data.roles.map(
 |  | ||||||
|       //   v => cache.get('role', v.id) ?? new Role(client, v)
 |  | ||||||
|       // )
 |  | ||||||
|       // data.roles.forEach(role => {
 |  | ||||||
|       //   this.roles.set(role.id, new Role(client, role))
 |  | ||||||
|       // })
 |  | ||||||
|       // this.emojis = data.emojis.map(
 |  | ||||||
|       //   v => cache.get('emoji', v.id) ?? new Emoji(client, v)
 |  | ||||||
|       // )
 |  | ||||||
|       this.features = data.features |       this.features = data.features | ||||||
|       this.mfaLevel = data.mfa_level |       this.mfaLevel = data.mfa_level | ||||||
|       this.systemChannelID = data.system_channel_id |       this.systemChannelID = data.system_channel_id | ||||||
|  | @ -113,20 +200,6 @@ export class Guild extends Base { | ||||||
|       this.joinedAt = data.joined_at |       this.joinedAt = data.joined_at | ||||||
|       this.large = data.large |       this.large = data.large | ||||||
|       this.memberCount = data.member_count |       this.memberCount = data.member_count | ||||||
|       // TODO: Cache in Gateway Event code
 |  | ||||||
|       // this.voiceStates = data.voice_states?.map(
 |  | ||||||
|       //   v =>
 |  | ||||||
|       //     cache.get('voiceState', `${v.guild_id}:${v.user_id}`) ??
 |  | ||||||
|       //     new VoiceState(client, v)
 |  | ||||||
|       // )
 |  | ||||||
|       // this.members = data.members?.map(
 |  | ||||||
|       //   v =>
 |  | ||||||
|       //     cache.get('member', `${this.id}:${v.user.id}`) ??
 |  | ||||||
|       //     new Member(client, v)
 |  | ||||||
|       // )
 |  | ||||||
|       // this.channels = data.channels?.map(
 |  | ||||||
|       //   v => cache.get('channel', v.id) ?? getChannelByType(this.client, v)
 |  | ||||||
|       // )
 |  | ||||||
|       this.presences = data.presences |       this.presences = data.presences | ||||||
|       this.maxPresences = data.max_presences |       this.maxPresences = data.max_presences | ||||||
|       this.maxMembers = data.max_members |       this.maxMembers = data.max_members | ||||||
|  | @ -143,7 +216,7 @@ export class Guild extends Base { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected readFromData (data: GuildPayload): void { |   protected readFromData(data: GuildPayload): void { | ||||||
|     super.readFromData(data) |     super.readFromData(data) | ||||||
|     this.id = data.id ?? this.id |     this.id = data.id ?? this.id | ||||||
|     this.unavailable = data.unavailable ?? this.unavailable |     this.unavailable = data.unavailable ?? this.unavailable | ||||||
|  | @ -167,14 +240,6 @@ export class Guild extends Base { | ||||||
|         data.default_message_notifications ?? this.defaultMessageNotifications |         data.default_message_notifications ?? this.defaultMessageNotifications | ||||||
|       this.explicitContentFilter = |       this.explicitContentFilter = | ||||||
|         data.explicit_content_filter ?? this.explicitContentFilter |         data.explicit_content_filter ?? this.explicitContentFilter | ||||||
|       // this.roles =
 |  | ||||||
|       //   data.roles.map(
 |  | ||||||
|       //     v => cache.get('role', v.id) ?? new Role(this.client, v)
 |  | ||||||
|       //   ) ?? this.roles
 |  | ||||||
|       // this.emojis =
 |  | ||||||
|       //   data.emojis.map(
 |  | ||||||
|       //     v => cache.get('emoji', v.id) ?? new Emoji(this.client, v)
 |  | ||||||
|       //   ) ?? this.emojis
 |  | ||||||
|       this.features = data.features ?? this.features |       this.features = data.features ?? this.features | ||||||
|       this.mfaLevel = data.mfa_level ?? this.mfaLevel |       this.mfaLevel = data.mfa_level ?? this.mfaLevel | ||||||
|       this.systemChannelID = data.system_channel_id ?? this.systemChannelID |       this.systemChannelID = data.system_channel_id ?? this.systemChannelID | ||||||
|  | @ -184,22 +249,6 @@ export class Guild extends Base { | ||||||
|       this.joinedAt = data.joined_at ?? this.joinedAt |       this.joinedAt = data.joined_at ?? this.joinedAt | ||||||
|       this.large = data.large ?? this.large |       this.large = data.large ?? this.large | ||||||
|       this.memberCount = data.member_count ?? this.memberCount |       this.memberCount = data.member_count ?? this.memberCount | ||||||
|       // this.voiceStates =
 |  | ||||||
|       //   data.voice_states?.map(
 |  | ||||||
|       //     v =>
 |  | ||||||
|       //       cache.get('voiceState', `${v.guild_id}:${v.user_id}`) ??
 |  | ||||||
|       //       new VoiceState(this.client, v)
 |  | ||||||
|       //   ) ?? this.voiceStates
 |  | ||||||
|       // this.members =
 |  | ||||||
|       //   data.members?.map(
 |  | ||||||
|       //     v =>
 |  | ||||||
|       //       cache.get('member', `${this.id}:${v.user.id}`) ??
 |  | ||||||
|       //       new Member(this.client, v)
 |  | ||||||
|       //   ) ?? this.members
 |  | ||||||
|       // this.channels =
 |  | ||||||
|       //   data.channels?.map(
 |  | ||||||
|       //     v => cache.get('channel', v.id) ?? getChannelByType(this.client, v, this)
 |  | ||||||
|       //   ) ?? this.members
 |  | ||||||
|       this.presences = data.presences ?? this.presences |       this.presences = data.presences ?? this.presences | ||||||
|       this.maxPresences = data.max_presences ?? this.maxPresences |       this.maxPresences = data.max_presences ?? this.maxPresences | ||||||
|       this.maxMembers = data.max_members ?? this.maxMembers |       this.maxMembers = data.max_members ?? this.maxMembers | ||||||
|  | @ -221,22 +270,65 @@ export class Guild extends Base { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async getEveryoneRole (): Promise<Role> { |   /** | ||||||
|  |    * Get Everyone role of the Guild | ||||||
|  |    */ | ||||||
|  |   async getEveryoneRole(): Promise<Role> { | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 |     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||||
|     return (await this.roles.get(this.id)) as Role |     return (await this.roles.get(this.id)) as Role | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async me (): Promise<Member> { |   /** | ||||||
|  |    * Get current client's member in the Guild | ||||||
|  |    */ | ||||||
|  |   async me(): Promise<Member> { | ||||||
|     const get = await this.members.get(this.client.user?.id as string) |     const get = await this.members.get(this.client.user?.id as string) | ||||||
|     if (get === undefined) throw new Error('Guild#me is not cached') |     if (get === undefined) throw new Error('Guild#me is not cached') | ||||||
|     return get |     return get | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async fetchIntegrations (): Promise<GuildIntegration[]> { |   /** | ||||||
|  |    * Fetch Guild's Integrations (Webhooks, Bots, etc.) | ||||||
|  |    */ | ||||||
|  |   async fetchIntegrations(): Promise<GuildIntegration[]> { | ||||||
|     const raw = (await this.client.rest.get( |     const raw = (await this.client.rest.get( | ||||||
|       GUILD_INTEGRATIONS(this.id) |       GUILD_INTEGRATIONS(this.id) | ||||||
|     )) as GuildIntegrationPayload[] |     )) as GuildIntegrationPayload[] | ||||||
|     return raw.map(e => new GuildIntegration(this.client, e)) |     return raw.map((e) => new GuildIntegration(this.client, e)) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Chunk the Guild Members, i.e. cache them. | ||||||
|  |    * @param options Options regarding the Members Request | ||||||
|  |    * @param wait Whether to wait for all Members to come before resolving Promise or not. | ||||||
|  |    * @param timeout Configurable timeout to cancel the wait to safely remove listener. | ||||||
|  |    */ | ||||||
|  |   async chunk( | ||||||
|  |     options: RequestMembersOptions, | ||||||
|  |     wait: boolean = false, | ||||||
|  |     timeout: number = 60000 | ||||||
|  |   ): Promise<Guild> { | ||||||
|  |     return await new Promise((resolve, reject) => { | ||||||
|  |       this.client.gateway?.requestMembers(this.id, options) | ||||||
|  |       if (!wait) return resolve(this) | ||||||
|  |       else { | ||||||
|  |         let chunked = false | ||||||
|  |         const listener = (guild: Guild): void => { | ||||||
|  |           if (guild.id === this.id) { | ||||||
|  |             chunked = true | ||||||
|  |             this.client.removeListener('guildMembersChunked', listener) | ||||||
|  |             resolve(this) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         this.client.on('guildMembersChunked', listener) | ||||||
|  |         setTimeout(() => { | ||||||
|  |           if (!chunked) { | ||||||
|  |             this.client.removeListener('guildMembersChunked', listener) | ||||||
|  |           } | ||||||
|  |         }, timeout) | ||||||
|  |       } | ||||||
|  |       resolve(this) | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -257,7 +349,7 @@ export class GuildIntegration extends Base { | ||||||
|   revoked?: boolean |   revoked?: boolean | ||||||
|   application?: Application |   application?: Application | ||||||
| 
 | 
 | ||||||
|   constructor (client: Client, data: GuildIntegrationPayload) { |   constructor(client: Client, data: GuildIntegrationPayload) { | ||||||
|     super(client, data) |     super(client, data) | ||||||
| 
 | 
 | ||||||
|     this.id = data.id |     this.id = data.id | ||||||
|  |  | ||||||
|  | @ -1,44 +1,173 @@ | ||||||
| import { MemberRolesManager } from "../managers/memberRoles.ts" | import { MemberRolesManager } from '../managers/memberRoles.ts' | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { MemberPayload } from '../types/guild.ts' | import { GUILD_MEMBER } from '../types/endpoint.ts' | ||||||
| import { Permissions } from "../utils/permissions.ts" | import { MemberPayload } from '../types/guild.ts' | ||||||
| import { Base } from './base.ts' | import { Permissions } from '../utils/permissions.ts' | ||||||
| import { Guild } from "./guild.ts" | import { Base } from './base.ts' | ||||||
| import { User } from './user.ts' | import { Guild } from './guild.ts' | ||||||
| 
 | import { Role } from './role.ts' | ||||||
| export class Member extends Base { | import { User } from './user.ts' | ||||||
|   id: string | 
 | ||||||
|   user: User | export interface MemberData { | ||||||
|   nick?: string |   nick?: string | null | ||||||
|   roles: MemberRolesManager |   roles?: Array<Role | string> | ||||||
|   joinedAt: string |   deaf?: boolean | ||||||
|   premiumSince?: string |   mute?: boolean | ||||||
|   deaf: boolean | } | ||||||
|   mute: boolean | 
 | ||||||
|   guild: Guild | export class Member extends Base { | ||||||
|   permissions: Permissions |   id: string | ||||||
| 
 |   user: User | ||||||
|   constructor (client: Client, data: MemberPayload, user: User, guild: Guild, perms?: Permissions) { |   nick?: string | ||||||
|     super(client) |   roles: MemberRolesManager | ||||||
|     this.id = data.user.id |   joinedAt: string | ||||||
|     this.user = user |   premiumSince?: string | ||||||
|     this.nick = data.nick |   deaf: boolean | ||||||
|     this.guild = guild |   mute: boolean | ||||||
|     this.roles = new MemberRolesManager(this.client, this.guild.roles, this) |   guild: Guild | ||||||
|     this.joinedAt = data.joined_at |   permissions: Permissions | ||||||
|     this.premiumSince = data.premium_since | 
 | ||||||
|     this.deaf = data.deaf |   constructor( | ||||||
|     this.mute = data.mute |     client: Client, | ||||||
|     if (perms !== undefined) this.permissions = perms |     data: MemberPayload, | ||||||
|     else this.permissions = new Permissions(Permissions.DEFAULT) |     user: User, | ||||||
|   } |     guild: Guild, | ||||||
| 
 |     perms?: Permissions | ||||||
|   protected readFromData (data: MemberPayload): void { |   ) { | ||||||
|     super.readFromData(data.user) |     super(client) | ||||||
|     this.nick = data.nick ?? this.nick |     this.id = data.user.id | ||||||
|     this.joinedAt = data.joined_at ?? this.joinedAt |     this.user = user | ||||||
|     this.premiumSince = data.premium_since ?? this.premiumSince |     this.nick = data.nick | ||||||
|     this.deaf = data.deaf ?? this.deaf |     this.guild = guild | ||||||
|     this.mute = data.mute ?? this.mute |     this.roles = new MemberRolesManager(this.client, this.guild.roles, this) | ||||||
|   } |     this.joinedAt = data.joined_at | ||||||
| } |     this.premiumSince = data.premium_since | ||||||
|  |     this.deaf = data.deaf | ||||||
|  |     this.mute = data.mute | ||||||
|  |     if (perms !== undefined) this.permissions = perms | ||||||
|  |     else this.permissions = new Permissions(Permissions.DEFAULT) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get displayName(): string { | ||||||
|  |     return this.nick !== undefined ? this.nick : this.user.username | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   toString(): string { | ||||||
|  |     return this.user.nickMention | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected readFromData(data: MemberPayload): void { | ||||||
|  |     super.readFromData(data.user) | ||||||
|  |     this.nick = data.nick ?? this.nick | ||||||
|  |     this.joinedAt = data.joined_at ?? this.joinedAt | ||||||
|  |     this.premiumSince = data.premium_since ?? this.premiumSince | ||||||
|  |     this.deaf = data.deaf ?? this.deaf | ||||||
|  |     this.mute = data.mute ?? this.mute | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Update the Member data in cache (and this object). | ||||||
|  |    */ | ||||||
|  |   async fetch(): Promise<Member> { | ||||||
|  |     const raw = await this.client.rest.get(this.id) | ||||||
|  |     if (typeof raw !== 'object') throw new Error('Member not found') | ||||||
|  |     await this.guild.members.set(this.id, raw) | ||||||
|  |     this.readFromData(raw) | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Edit the Member | ||||||
|  |    * @param data Data to apply | ||||||
|  |    */ | ||||||
|  |   async edit(data: MemberData): Promise<Member> { | ||||||
|  |     const payload = { | ||||||
|  |       nick: data.nick, | ||||||
|  |       roles: data.roles?.map((e) => (typeof e === 'string' ? e : e.id)), | ||||||
|  |       deaf: data.deaf, | ||||||
|  |       mute: data.mute, | ||||||
|  |     } | ||||||
|  |     const res = await this.client.rest.patch( | ||||||
|  |       GUILD_MEMBER(this.guild.id, this.id), | ||||||
|  |       payload, | ||||||
|  |       undefined, | ||||||
|  |       null, | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  |     if (res.ok === true) { | ||||||
|  |       if (data.nick !== undefined) | ||||||
|  |         this.nick = data.nick === null ? undefined : data.nick | ||||||
|  |       if (data.deaf !== undefined) this.deaf = data.deaf | ||||||
|  |       if (data.mute !== undefined) this.mute = data.mute | ||||||
|  |     } | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * New nickname to set. If empty, nick is reset | ||||||
|  |    * @param nick New nickname | ||||||
|  |    */ | ||||||
|  |   async setNickname(nick?: string): Promise<Member> { | ||||||
|  |     return await this.edit({ | ||||||
|  |       nick: nick === undefined ? null : nick, | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Reset nickname of the Member | ||||||
|  |    */ | ||||||
|  |   async resetNickname(): Promise<Member> { | ||||||
|  |     return await this.setNickname() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Set mute of a Member in VC | ||||||
|  |    * @param mute Value to set | ||||||
|  |    */ | ||||||
|  |   async setMute(mute?: boolean): Promise<Member> { | ||||||
|  |     return await this.edit({ | ||||||
|  |       mute: mute === undefined ? false : mute, | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Set deaf of a Member in VC | ||||||
|  |    * @param deaf Value to set | ||||||
|  |    */ | ||||||
|  |   async setDeaf(deaf?: boolean): Promise<Member> { | ||||||
|  |     return await this.edit({ | ||||||
|  |       deaf: deaf === undefined ? false : deaf, | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Unmute the Member from VC. | ||||||
|  |    */ | ||||||
|  |   async unmute(): Promise<Member> { | ||||||
|  |     return await this.setMute(false) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Kick the member. | ||||||
|  |    */ | ||||||
|  |   async kick(): Promise<boolean> { | ||||||
|  |     const resp = await this.client.rest.delete( | ||||||
|  |       GUILD_MEMBER(this.guild.id, this.id), | ||||||
|  |       undefined, | ||||||
|  |       undefined, | ||||||
|  |       null, | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  |     if (resp.ok !== true) return false | ||||||
|  |     else return true | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Ban the Member. | ||||||
|  |    * @param reason Reason for the Ban. | ||||||
|  |    * @param deleteMessagesDays Delete Old Messages? If yes, how much days. | ||||||
|  |    */ | ||||||
|  |   async ban(reason?: string, deleteOldMessages?: number): Promise<void> { | ||||||
|  |     return this.guild.bans.add(this.id, reason, deleteOldMessages) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,49 +1,53 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { MemberPayload } from '../types/guild.ts' | import { VoiceStatePayload } from '../types/voice.ts' | ||||||
| import { VoiceStatePayload } from '../types/voice.ts' | import { Base } from './base.ts' | ||||||
| import { Base } from './base.ts' | import { Guild } from "./guild.ts" | ||||||
| 
 | import { VoiceChannel } from "./guildVoiceChannel.ts" | ||||||
| export class VoiceState extends Base { | import { Member } from "./member.ts" | ||||||
|   guildID?: string | import { User } from "./user.ts" | ||||||
|   channelID?: string | 
 | ||||||
|   userID: string | export class VoiceState extends Base { | ||||||
|   member?: MemberPayload |   guild?: Guild | ||||||
|   sessionID: string |   channel: VoiceChannel | null | ||||||
|   deaf: boolean |   user: User | ||||||
|   mute: boolean |   member?: Member | ||||||
|   selfDeaf: boolean |   sessionID: string | ||||||
|   selfMute: boolean |   deaf: boolean | ||||||
|   selfStream?: boolean |   mute: boolean | ||||||
|   selfVideo: boolean |   stream?: boolean | ||||||
|   suppress: boolean |   video: boolean | ||||||
| 
 |   suppress: boolean | ||||||
|   constructor (client: Client, data: VoiceStatePayload) { | 
 | ||||||
|     super(client, data) |   constructor (client: Client, data: VoiceStatePayload, _data: { | ||||||
|     this.channelID = data.channel_id |     user: User, | ||||||
|     this.sessionID = data.session_id |     channel: VoiceChannel | null, | ||||||
|     this.userID = data.user_id |     member?: Member, | ||||||
|     this.deaf = data.deaf |     guild?: Guild | ||||||
|     this.mute = data.mute |   }) { | ||||||
|     this.selfDeaf = data.self_deaf |     super(client, data) | ||||||
|     this.selfMute = data.self_mute |     this.channel = _data.channel | ||||||
|     this.selfStream = data.self_stream |     this.sessionID = data.session_id | ||||||
|     this.selfVideo = data.self_video |     this.user = _data.user | ||||||
|     this.suppress = data.suppress |     this.member = _data.member | ||||||
|     // TODO: Cache in Gateway Event Code
 |     this.guild = _data.guild | ||||||
|     // cache.set('voiceState', `${this.guildID}:${this.userID}`, this)
 |     this.deaf = data.deaf | ||||||
|   } |     this.mute = data.mute | ||||||
| 
 |     this.deaf = data.self_deaf | ||||||
|   protected readFromData (data: VoiceStatePayload): void { |     this.mute = data.self_mute | ||||||
|     super.readFromData(data) |     this.stream = data.self_stream | ||||||
|     this.channelID = data.channel_id ?? this.channelID |     this.video = data.self_video | ||||||
|     this.sessionID = data.session_id ?? this.sessionID |     this.suppress = data.suppress | ||||||
|     this.userID = data.user_id ?? this.userID |   } | ||||||
|     this.deaf = data.deaf ?? this.deaf | 
 | ||||||
|     this.mute = data.mute ?? this.mute |   protected readFromData (data: VoiceStatePayload): void { | ||||||
|     this.selfDeaf = data.self_deaf ?? this.selfDeaf |     super.readFromData(data) | ||||||
|     this.selfMute = data.self_mute ?? this.selfMute |     this.sessionID = data.session_id ?? this.sessionID | ||||||
|     this.selfStream = data.self_stream ?? this.selfStream |     this.deaf = data.deaf ?? this.deaf | ||||||
|     this.selfVideo = data.self_video ?? this.selfVideo |     this.mute = data.mute ?? this.mute | ||||||
|     this.suppress = data.suppress ?? this.suppress |     this.deaf = data.self_deaf ?? this.deaf | ||||||
|   } |     this.mute = data.self_mute ?? this.mute | ||||||
| } |     this.stream = data.self_stream ?? this.stream | ||||||
|  |     this.video = data.self_video ?? this.video | ||||||
|  |     this.suppress = data.suppress ?? this.suppress | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -92,6 +92,14 @@ client.on('typingStart', (user, channel, at, guildData) => { | ||||||
|   console.log(`${user.tag} started typing in ${channel.id} at ${at}${guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : ''}`) |   console.log(`${user.tag} started typing in ${channel.id} at ${at}${guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : ''}`) | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
|  | client.on('voiceStateAdd', (state) => { | ||||||
|  |   console.log('VC Join', state) | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | client.on('voiceStateRemove', (state) => { | ||||||
|  |   console.log('VC Leave', state) | ||||||
|  | }) | ||||||
|  | 
 | ||||||
| // client.on('raw', (evt: string) => console.log(`EVENT: ${evt}`))
 | // client.on('raw', (evt: string) => console.log(`EVENT: ${evt}`))
 | ||||||
| 
 | 
 | ||||||
| const files = Deno.readDirSync('./src/test/cmds') | const files = Deno.readDirSync('./src/test/cmds') | ||||||
|  |  | ||||||
|  | @ -104,6 +104,7 @@ export enum GatewayEvents { | ||||||
|   Typing_Start = 'TYPING_START', |   Typing_Start = 'TYPING_START', | ||||||
|   User_Update = 'USER_UPDATE', |   User_Update = 'USER_UPDATE', | ||||||
|   Voice_Server_Update = 'VOICE_SERVER_UPDATE', |   Voice_Server_Update = 'VOICE_SERVER_UPDATE', | ||||||
|  |   Voice_State_Update = 'VOICE_STATE_UPDATE', | ||||||
|   Webhooks_Update = 'WEBHOOKS_UPDATE' |   Webhooks_Update = 'WEBHOOKS_UPDATE' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import { ApplicationPayload } from "./application.ts" | import { ApplicationPayload } from './application.ts' | ||||||
| import { ChannelPayload } from './channel.ts' | import { ChannelPayload } from './channel.ts' | ||||||
| import { EmojiPayload } from './emoji.ts' | import { EmojiPayload } from './emoji.ts' | ||||||
| import { PresenceUpdatePayload } from './gateway.ts' | import { PresenceUpdatePayload } from './gateway.ts' | ||||||
|  | @ -66,18 +66,18 @@ export interface MemberPayload { | ||||||
| 
 | 
 | ||||||
| export enum MessageNotification { | export enum MessageNotification { | ||||||
|   ALL_MESSAGES = 0, |   ALL_MESSAGES = 0, | ||||||
|   ONLY_MENTIONS = 1 |   ONLY_MENTIONS = 1, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum ContentFilter { | export enum ContentFilter { | ||||||
|   DISABLED = 0, |   DISABLED = 0, | ||||||
|   MEMBERS_WITHOUT_ROLES = 1, |   MEMBERS_WITHOUT_ROLES = 1, | ||||||
|   ALL_MEMBERS = 3 |   ALL_MEMBERS = 3, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum MFA { | export enum MFA { | ||||||
|   NONE = 0, |   NONE = 0, | ||||||
|   ELEVATED = 1 |   ELEVATED = 1, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum Verification { | export enum Verification { | ||||||
|  | @ -85,19 +85,19 @@ export enum Verification { | ||||||
|   LOW = 1, |   LOW = 1, | ||||||
|   MEDIUM = 2, |   MEDIUM = 2, | ||||||
|   HIGH = 3, |   HIGH = 3, | ||||||
|   VERY_HIGH = 4 |   VERY_HIGH = 4, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum PremiumTier { | export enum PremiumTier { | ||||||
|   NONE = 0, |   NONE = 0, | ||||||
|   TIER_1 = 1, |   TIER_1 = 1, | ||||||
|   TIER_2 = 2, |   TIER_2 = 2, | ||||||
|   TIER_3 = 3 |   TIER_3 = 3, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum SystemChannelFlags { | export enum SystemChannelFlags { | ||||||
|   SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0, |   SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0, | ||||||
|   SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1 |   SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type GuildFeatures = | export type GuildFeatures = | ||||||
|  | @ -116,7 +116,7 @@ export type GuildFeatures = | ||||||
| 
 | 
 | ||||||
| export enum IntegrationExpireBehavior { | export enum IntegrationExpireBehavior { | ||||||
|   REMOVE_ROLE = 0, |   REMOVE_ROLE = 0, | ||||||
|   KICK = 1 |   KICK = 1, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface IntegrationAccountPayload { | export interface IntegrationAccountPayload { | ||||||
|  | @ -140,4 +140,9 @@ export interface GuildIntegrationPayload { | ||||||
|   subscriber_count?: number |   subscriber_count?: number | ||||||
|   revoked?: boolean |   revoked?: boolean | ||||||
|   application?: ApplicationPayload |   application?: ApplicationPayload | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export interface GuildBanPayload { | ||||||
|  |   reason: string | null | ||||||
|  |   user: UserPayload | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,45 +1,45 @@ | ||||||
| // https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice
 | // https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice
 | ||||||
| import { MemberPayload } from './guild.ts' | import { MemberPayload } from './guild.ts' | ||||||
| 
 | 
 | ||||||
| export enum VoiceOpcodes { // VoiceOpcodes 추가 - UnderC -
 | export enum VoiceOpcodes { // VoiceOpcodes 추가 - UnderC -
 | ||||||
|   IDENTIFY = 0, |   IDENTIFY = 0, | ||||||
|   SELECT_PROTOCOL = 1, |   SELECT_PROTOCOL = 1, | ||||||
|   READY = 2, |   READY = 2, | ||||||
|   HEARTBEAT = 3, |   HEARTBEAT = 3, | ||||||
|   SESSION_DESCRIPTION = 4, |   SESSION_DESCRIPTION = 4, | ||||||
|   SPEAKING = 6, |   SPEAKING = 6, | ||||||
|   HEARTBEAT_ACK = 6, |   HEARTBEAT_ACK = 6, | ||||||
|   RESUME = 7, |   RESUME = 7, | ||||||
|   HELLO = 8, |   HELLO = 8, | ||||||
|   RESUMED = 9, |   RESUMED = 9, | ||||||
|   CLIENT_DISCONNECT = 13 |   CLIENT_DISCONNECT = 13 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum VoiceCloseCodes { | export enum VoiceCloseCodes { | ||||||
|   UNKNOWN_OPCODE = 4001, |   UNKNOWN_OPCODE = 4001, | ||||||
|   NOT_AUTHENTICATED = 4003, |   NOT_AUTHENTICATED = 4003, | ||||||
|   AUTHENTICATION_FAILED = 4004, |   AUTHENTICATION_FAILED = 4004, | ||||||
|   ALREADY_AUTHENTICATED = 4005, |   ALREADY_AUTHENTICATED = 4005, | ||||||
|   SESSION_NO_LONGER_VALID = 4006, |   SESSION_NO_LONGER_VALID = 4006, | ||||||
|   SESSION_TIMEOUT = 4009, |   SESSION_TIMEOUT = 4009, | ||||||
|   SERVER_NOT_FOUNT = 4011, |   SERVER_NOT_FOUNT = 4011, | ||||||
|   UNKNOWN_PROTOCOL = 4012, |   UNKNOWN_PROTOCOL = 4012, | ||||||
|   DISCONNECTED = 4014, |   DISCONNECTED = 4014, | ||||||
|   VOICE_SERVER_CRASHED = 4015, |   VOICE_SERVER_CRASHED = 4015, | ||||||
|   UNKNOWN_ENCRYPTION_MODE = 4016 |   UNKNOWN_ENCRYPTION_MODE = 4016 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface VoiceStatePayload { | export interface VoiceStatePayload { | ||||||
|   guild_id?: string |   guild_id?: string | ||||||
|   channel_id?: string |   channel_id: string | null | ||||||
|   user_id: string |   user_id: string | ||||||
|   member?: MemberPayload |   member?: MemberPayload | ||||||
|   session_id: string |   session_id: string | ||||||
|   deaf: boolean |   deaf: boolean | ||||||
|   mute: boolean |   mute: boolean | ||||||
|   self_deaf: boolean |   self_deaf: boolean | ||||||
|   self_mute: boolean |   self_mute: boolean | ||||||
|   self_stream?: boolean |   self_stream?: boolean | ||||||
|   self_video: boolean |   self_video: boolean | ||||||
|   suppress: boolean |   suppress: boolean | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										140
									
								
								tsconfig.json
									
										
									
									
									
								
							
							
						
						
									
										140
									
								
								tsconfig.json
									
										
									
									
									
								
							|  | @ -1,70 +1,70 @@ | ||||||
| { | { | ||||||
|   "compilerOptions": { |   "compilerOptions": { | ||||||
|     /* Visit https://aka.ms/tsconfig.json to read more about this file */ |     /* Visit https://aka.ms/tsconfig.json to read more about this file */ | ||||||
|     /* Basic Options */ |     /* Basic Options */ | ||||||
|     // "incremental": true,                   /* Enable incremental compilation */ |     // "incremental": true,                   /* Enable incremental compilation */ | ||||||
|     "target": "ESNext", |     "target": "ESNext", | ||||||
|     /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ |     /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ | ||||||
|     "module": "ESNext", |     "module": "ESNext", | ||||||
|     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ |     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ | ||||||
|     "lib": [ |     "lib": [ | ||||||
|       "esnext" |       "esnext" | ||||||
|     ] /* Specify library files to be included in the compilation. */, |     ] /* Specify library files to be included in the compilation. */, | ||||||
|     // "allowJs": true,                       /* Allow javascript files to be compiled. */ |     // "allowJs": true,                       /* Allow javascript files to be compiled. */ | ||||||
|     // "checkJs": true,                       /* Report errors in .js files. */ |     // "checkJs": true,                       /* Report errors in .js files. */ | ||||||
|     // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ |     // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ | ||||||
|     // "declaration": true,                   /* Generates corresponding '.d.ts' file. */ |     // "declaration": true,                   /* Generates corresponding '.d.ts' file. */ | ||||||
|     // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */ |     // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */ | ||||||
|     // "sourceMap": true,                     /* Generates corresponding '.map' file. */ |     // "sourceMap": true,                     /* Generates corresponding '.map' file. */ | ||||||
|     // "outFile": "./",                       /* Concatenate and emit output to single file. */ |     // "outFile": "./",                       /* Concatenate and emit output to single file. */ | ||||||
|     // "outDir": "./",                        /* Redirect output structure to the directory. */ |     // "outDir": "./",                        /* Redirect output structure to the directory. */ | ||||||
|     // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ |     // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ | ||||||
|     // "composite": true,                     /* Enable project compilation */ |     // "composite": true,                     /* Enable project compilation */ | ||||||
|     // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */ |     // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */ | ||||||
|     // "removeComments": true,                /* Do not emit comments to output. */ |     // "removeComments": true,                /* Do not emit comments to output. */ | ||||||
|     // "noEmit": true,                        /* Do not emit outputs. */ |     // "noEmit": true,                        /* Do not emit outputs. */ | ||||||
|     // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */ |     // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */ | ||||||
|     // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ |     // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | ||||||
|     "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, |     "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, | ||||||
|     /* Strict Type-Checking Options */ |     /* Strict Type-Checking Options */ | ||||||
|     "strict": true, |     "strict": true, | ||||||
|     /* Enable all strict type-checking options. */ |     /* Enable all strict type-checking options. */ | ||||||
|     // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */ |     // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */ | ||||||
|     // "strictNullChecks": true,              /* Enable strict null checks. */ |     // "strictNullChecks": true,              /* Enable strict null checks. */ | ||||||
|     // "strictFunctionTypes": true,           /* Enable strict checking of function types. */ |     // "strictFunctionTypes": true,           /* Enable strict checking of function types. */ | ||||||
|     // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ |     // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ | ||||||
|     // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */ |     // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */ | ||||||
|     // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */ |     // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */ | ||||||
|     // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */ |     // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */ | ||||||
|     /* Additional Checks */ |     /* Additional Checks */ | ||||||
|     // "noUnusedLocals": true,                /* Report errors on unused locals. */ |     // "noUnusedLocals": true,                /* Report errors on unused locals. */ | ||||||
|     // "noUnusedParameters": true,            /* Report errors on unused parameters. */ |     // "noUnusedParameters": true,            /* Report errors on unused parameters. */ | ||||||
|     // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */ |     // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */ | ||||||
|     // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */ |     // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */ | ||||||
|     /* Module Resolution Options */ |     /* Module Resolution Options */ | ||||||
|     "moduleResolution": "node", |     "moduleResolution": "node", | ||||||
|     /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ |     /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ | ||||||
|     // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */ |     // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */ | ||||||
|     // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ |     // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ | ||||||
|     // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */ |     // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */ | ||||||
|     // "typeRoots": [],                       /* List of folders to include type definitions from. */ |     // "typeRoots": [],                       /* List of folders to include type definitions from. */ | ||||||
|     // "types": [],                           /* Type declaration files to be included in compilation. */ |     // "types": [],                           /* Type declaration files to be included in compilation. */ | ||||||
|     // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ |     // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||||||
|     "esModuleInterop": true, |     "esModuleInterop": true, | ||||||
|     /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ |     /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||||||
|     // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */ |     // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */ | ||||||
|     // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */ |     // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */ | ||||||
|     /* Source Map Options */ |     /* Source Map Options */ | ||||||
|     // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */ |     // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | ||||||
|     // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */ |     // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */ | ||||||
|     // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */ |     // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */ | ||||||
|     // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ |     // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | ||||||
|     /* Experimental Options */ |     /* Experimental Options */ | ||||||
|     // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */ |     "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */ | ||||||
|     "emitDecoratorMetadata": false /* Enables experimental support for emitting type metadata for decorators. */, |     "emitDecoratorMetadata": false /* Enables experimental support for emitting type metadata for decorators. */, | ||||||
|     /* Advanced Options */ |     /* Advanced Options */ | ||||||
|     "skipLibCheck": true, |     "skipLibCheck": true, | ||||||
|     /* Skip type checking of declaration files. */ |     /* Skip type checking of declaration files. */ | ||||||
|     "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ |     "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue