feat(api): add new api-related methods
This commit is contained in:
		
							parent
							
								
									50d1da11e6
								
							
						
					
					
						commit
						b748086ae7
					
				
					 10 changed files with 618 additions and 239 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": { | ||||
|     "https://deno.land": true | ||||
|   }, | ||||
|   "editor.tabSize": 2 | ||||
|   "editor.tabSize": 2, | ||||
|   "editor.formatOnSave": true | ||||
| } | ||||
							
								
								
									
										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) | ||||
|   } | ||||
| } | ||||
|  | @ -2,7 +2,7 @@ import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts' | |||
| import { Client } from '../models/client.ts' | ||||
| import { | ||||
|   DISCORD_GATEWAY_URL, | ||||
|   DISCORD_API_VERSION | ||||
|   DISCORD_API_VERSION, | ||||
| } from '../consts/urlsAndVersions.ts' | ||||
| import { GatewayResponse } from '../types/gatewayResponse.ts' | ||||
| import { | ||||
|  | @ -10,18 +10,23 @@ import { | |||
|   GatewayIntents, | ||||
|   GatewayCloseCodes, | ||||
|   IdentityPayload, | ||||
|   StatusUpdatePayload | ||||
|   StatusUpdatePayload, | ||||
| } from '../types/gateway.ts' | ||||
| import { gatewayHandlers } from './handlers/index.ts' | ||||
| import { GATEWAY_BOT } from '../types/endpoint.ts' | ||||
| import { GatewayCache } from '../managers/gatewayCache.ts' | ||||
| import { delay } from '../utils/delay.ts' | ||||
| 
 | ||||
| export interface RequestMembersOptions { | ||||
|   limit?: number | ||||
|   presences?: boolean | ||||
|   query?: string | ||||
|   users?: string[] | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Handles Discord gateway connection. | ||||
|  * You should not use this and rather use Client class. | ||||
|  * | ||||
|  * @beta | ||||
|  */ | ||||
| class Gateway { | ||||
|   websocket: WebSocket | ||||
|  | @ -38,7 +43,7 @@ class Gateway { | |||
|   client: Client | ||||
|   cache: GatewayCache | ||||
| 
 | ||||
|   constructor (client: Client, token: string, intents: GatewayIntents[]) { | ||||
|   constructor(client: Client, token: string, intents: GatewayIntents[]) { | ||||
|     this.token = token | ||||
|     this.intents = intents | ||||
|     this.client = client | ||||
|  | @ -55,12 +60,12 @@ class Gateway { | |||
|     this.websocket.onerror = this.onerror.bind(this) | ||||
|   } | ||||
| 
 | ||||
|   private onopen (): void { | ||||
|   private onopen(): void { | ||||
|     this.connected = true | ||||
|     this.debug('Connected to Gateway!') | ||||
|   } | ||||
| 
 | ||||
|   private async onmessage (event: MessageEvent): Promise<void> { | ||||
|   private async onmessage(event: MessageEvent): Promise<void> { | ||||
|     let data = event.data | ||||
|     if (data instanceof ArrayBuffer) { | ||||
|       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}`) | ||||
| 
 | ||||
|     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 | ||||
|     console.log(eventError) | ||||
|   } | ||||
| 
 | ||||
|   private async sendIdentify (forceNewSession?: boolean): Promise<void> { | ||||
|   private async sendIdentify(forceNewSession?: boolean): Promise<void> { | ||||
|     if (this.client.bot === true) { | ||||
|       this.debug('Fetching /gateway/bot...') | ||||
|       const info = await this.client.rest.get(GATEWAY_BOT()) | ||||
|  | @ -225,7 +230,7 @@ class Gateway { | |||
|       properties: { | ||||
|         $os: Deno.build.os, | ||||
|         $browser: 'harmony', | ||||
|         $device: 'harmony' | ||||
|         $device: 'harmony', | ||||
|       }, | ||||
|       compress: true, | ||||
|       shard: [0, 1], // TODO: Make sharding possible
 | ||||
|  | @ -233,7 +238,7 @@ class Gateway { | |||
|         (previous, current) => previous | current, | ||||
|         0 | ||||
|       ), | ||||
|       presence: this.client.presence.create() | ||||
|       presence: this.client.presence.create(), | ||||
|     } | ||||
| 
 | ||||
|     if (this.client.bot === false) { | ||||
|  | @ -245,17 +250,17 @@ class Gateway { | |||
|         $browser: 'Firefox', | ||||
|         $device: '', | ||||
|         $referrer: '', | ||||
|         $referring_domain: '' | ||||
|         $referring_domain: '', | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     this.send({ | ||||
|       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}`) | ||||
|     if (this.sequenceID === undefined) { | ||||
|       const cached = await this.cache.get('seq') | ||||
|  | @ -267,17 +272,37 @@ class Gateway { | |||
|       d: { | ||||
|         token: this.token, | ||||
|         session_id: this.sessionID, | ||||
|         seq: this.sequenceID ?? null | ||||
|       } | ||||
|         seq: this.sequenceID ?? null, | ||||
|       }, | ||||
|     } | ||||
|     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) | ||||
|   } | ||||
| 
 | ||||
|   async reconnect (forceNew?: boolean): Promise<void> { | ||||
|   async reconnect(forceNew?: boolean): Promise<void> { | ||||
|     clearInterval(this.heartbeatIntervalID) | ||||
|     if (forceNew === undefined || !forceNew) | ||||
|       await this.cache.delete('session_id') | ||||
|  | @ -285,7 +310,7 @@ class Gateway { | |||
|     this.initWebsocket() | ||||
|   } | ||||
| 
 | ||||
|   initWebsocket (): void { | ||||
|   initWebsocket(): void { | ||||
|     this.websocket = new WebSocket( | ||||
|       // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
 | ||||
|       `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, | ||||
|  | @ -298,41 +323,41 @@ class Gateway { | |||
|     this.websocket.onerror = this.onerror.bind(this) | ||||
|   } | ||||
| 
 | ||||
|   close (): void { | ||||
|   close(): void { | ||||
|     this.websocket.close(1000) | ||||
|   } | ||||
| 
 | ||||
|   send (data: GatewayResponse): boolean { | ||||
|   send(data: GatewayResponse): boolean { | ||||
|     if (this.websocket.readyState !== this.websocket.OPEN) return false | ||||
|     this.websocket.send( | ||||
|       JSON.stringify({ | ||||
|         op: data.op, | ||||
|         d: data.d, | ||||
|         s: typeof data.s === 'number' ? data.s : null, | ||||
|         t: data.t === undefined ? null : data.t | ||||
|         t: data.t === undefined ? null : data.t, | ||||
|       }) | ||||
|     ) | ||||
|     return true | ||||
|   } | ||||
| 
 | ||||
|   sendPresence (data: StatusUpdatePayload): void { | ||||
|   sendPresence(data: StatusUpdatePayload): void { | ||||
|     this.send({ | ||||
|       op: GatewayOpcodes.PRESENCE_UPDATE, | ||||
|       d: data | ||||
|       d: data, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   sendHeartbeat (): void { | ||||
|   sendHeartbeat(): void { | ||||
|     const payload = { | ||||
|       op: GatewayOpcodes.HEARTBEAT, | ||||
|       d: this.sequenceID ?? null | ||||
|       d: this.sequenceID ?? null, | ||||
|     } | ||||
| 
 | ||||
|     this.send(payload) | ||||
|     this.lastPingTimestamp = Date.now() | ||||
|   } | ||||
| 
 | ||||
|   heartbeat (): void { | ||||
|   heartbeat(): void { | ||||
|     if (this.heartbeatServerResponded) { | ||||
|       this.heartbeatServerResponded = false | ||||
|     } else { | ||||
|  |  | |||
|  | @ -1,42 +1,81 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { BaseChildManager } from './baseChild.ts' | ||||
| import { RolePayload } from "../types/role.ts" | ||||
| import { Role } from "../structures/role.ts" | ||||
| import { Member } from "../structures/member.ts" | ||||
| import { RolesManager } from "./roles.ts" | ||||
| import { MemberPayload } from "../types/guild.ts" | ||||
| import { RolePayload } from '../types/role.ts' | ||||
| import { Role } from '../structures/role.ts' | ||||
| import { Member } from '../structures/member.ts' | ||||
| import { RolesManager } from './roles.ts' | ||||
| import { MemberPayload } from '../types/guild.ts' | ||||
| import { GUILD_MEMBER_ROLE } from '../types/endpoint.ts' | ||||
| 
 | ||||
| export class MemberRolesManager extends BaseChildManager< | ||||
|   RolePayload, | ||||
|   Role | ||||
| > { | ||||
| export class MemberRolesManager extends BaseChildManager<RolePayload, Role> { | ||||
|   member: Member | ||||
| 
 | ||||
|   constructor (client: Client, parent: RolesManager, member: Member) { | ||||
|   constructor(client: Client, parent: RolesManager, member: Member) { | ||||
|     super(client, parent as any) | ||||
|     this.member = member | ||||
|   } | ||||
| 
 | ||||
|   async get (id: string): Promise<Role | undefined> { | ||||
|   async get(id: string): Promise<Role | undefined> { | ||||
|     const res = await this.parent.get(id) | ||||
|     const mem = await (this.parent as any).guild.members._get(this.member.id) as MemberPayload | ||||
|     if (res !== undefined && (mem.roles.includes(res.id) === true || res.id === this.member.guild.id)) return res | ||||
|     const mem = (await (this.parent as any).guild.members._get( | ||||
|       this.member.id | ||||
|     )) as MemberPayload | ||||
|     if ( | ||||
|       res !== undefined && | ||||
|       (mem.roles.includes(res.id) === true || res.id === this.member.guild.id) | ||||
|     ) | ||||
|       return res | ||||
|     else return undefined | ||||
|   } | ||||
| 
 | ||||
|   async array (): Promise<Role[]> { | ||||
|   async array(): Promise<Role[]> { | ||||
|     const arr = (await this.parent.array()) as Role[] | ||||
|     const mem = await (this.parent as any).guild.members._get(this.member.id) as MemberPayload | ||||
|     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 | ||||
|       (c: any) => | ||||
|         (mem.roles.includes(c.id) as boolean) || c.id === this.member.guild.id | ||||
|     ) as any | ||||
|   } | ||||
| 
 | ||||
|   async flush (): Promise<boolean> { | ||||
|   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 | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,13 @@ import { Client } from './client.ts' | |||
| import { getBuildInfo } from '../utils/buildInfo.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 { | ||||
|   Ok = 200, | ||||
|  | @ -16,7 +22,7 @@ export enum HttpResponseCode { | |||
|   NotFound = 404, | ||||
|   MethodNotAllowed = 405, | ||||
|   TooManyRequests = 429, | ||||
|   GatewayUnavailable = 502 | ||||
|   GatewayUnavailable = 502, | ||||
| } | ||||
| 
 | ||||
| export interface RequestHeaders { | ||||
|  | @ -30,11 +36,14 @@ export class DiscordAPIError extends Error { | |||
| export interface QueuedItem { | ||||
|   bucket?: string | null | ||||
|   url: string | ||||
|   onComplete: () => Promise<{ | ||||
|     rateLimited: any | ||||
|     bucket?: string | null | ||||
|     before: boolean | ||||
|   } | undefined> | ||||
|   onComplete: () => Promise< | ||||
|     | { | ||||
|         rateLimited: any | ||||
|         bucket?: string | null | ||||
|         before: boolean | ||||
|       } | ||||
|     | undefined | ||||
|   > | ||||
| } | ||||
| 
 | ||||
| export interface RateLimit { | ||||
|  | @ -85,16 +94,14 @@ export class RESTManager { | |||
|   async processQueue(): Promise<void> { | ||||
|     if (Object.keys(this.queues).length !== 0 && !this.globalRateLimit) { | ||||
|       await Promise.allSettled( | ||||
|         Object.values(this.queues).map(async pathQueue => { | ||||
|         Object.values(this.queues).map(async (pathQueue) => { | ||||
|           const request = pathQueue.shift() | ||||
|           if (request === undefined) return | ||||
| 
 | ||||
|           const rateLimitedURLResetIn = await this.isRateLimited(request.url) | ||||
| 
 | ||||
|           if (typeof request.bucket === 'string') { | ||||
|             const rateLimitResetIn = await this.isRateLimited( | ||||
|               request.bucket | ||||
|             ) | ||||
|             const rateLimitResetIn = await this.isRateLimited(request.bucket) | ||||
|             if (rateLimitResetIn !== false) { | ||||
|               this.queue(request) | ||||
|             } else { | ||||
|  | @ -102,7 +109,7 @@ export class RESTManager { | |||
|               if (result?.rateLimited !== undefined) { | ||||
|                 this.queue({ | ||||
|                   ...request, | ||||
|                   bucket: result.bucket ?? request.bucket | ||||
|                   bucket: result.bucket ?? request.bucket, | ||||
|                 }) | ||||
|               } | ||||
|             } | ||||
|  | @ -114,7 +121,7 @@ export class RESTManager { | |||
|               if (result?.rateLimited !== undefined) { | ||||
|                 this.queue({ | ||||
|                   ...request, | ||||
|                   bucket: result.bucket ?? request.bucket | ||||
|                   bucket: result.bucket ?? request.bucket, | ||||
|                 }) | ||||
|               } | ||||
|             } | ||||
|  | @ -132,20 +139,18 @@ export class RESTManager { | |||
|     } else this.processing = false | ||||
|   } | ||||
| 
 | ||||
|   prepare( | ||||
|     body: any, | ||||
|     method: RequestMethods | ||||
|   ): { [key: string]: any } { | ||||
| 
 | ||||
|   prepare(body: any, method: RequestMethods): { [key: string]: any } { | ||||
|     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 (method === 'get' || method === 'head' || method === 'delete') body = undefined | ||||
|     if (method === 'get' || method === 'head' || method === 'delete') | ||||
|       body = undefined | ||||
| 
 | ||||
|     if (body?.reason !== undefined) { | ||||
|       headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason) | ||||
|  | @ -163,7 +168,7 @@ export class RESTManager { | |||
|     const data: { [name: string]: any } = { | ||||
|       headers, | ||||
|       body: body?.file ?? JSON.stringify(body), | ||||
|       method: method.toUpperCase() | ||||
|       method: method.toUpperCase(), | ||||
|     } | ||||
| 
 | ||||
|     if (this.client?.bot === false) { | ||||
|  | @ -217,14 +222,14 @@ export class RESTManager { | |||
|       this.rateLimits.set(url, { | ||||
|         url, | ||||
|         resetAt: Number(resetAt) * 1000, | ||||
|         bucket | ||||
|         bucket, | ||||
|       }) | ||||
| 
 | ||||
|       if (bucket !== null) { | ||||
|         this.rateLimits.set(bucket, { | ||||
|           url, | ||||
|           resetAt: Number(resetAt) * 1000, | ||||
|           bucket | ||||
|           bucket, | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|  | @ -237,14 +242,14 @@ export class RESTManager { | |||
|       this.rateLimits.set('global', { | ||||
|         url: 'global', | ||||
|         resetAt: reset, | ||||
|         bucket | ||||
|         bucket, | ||||
|       }) | ||||
| 
 | ||||
|       if (bucket !== null) { | ||||
|         this.rateLimits.set(bucket, { | ||||
|           url: 'global', | ||||
|           resetAt: reset, | ||||
|           bucket | ||||
|           bucket, | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|  | @ -253,32 +258,46 @@ export class RESTManager { | |||
|   } | ||||
| 
 | ||||
|   async handleStatusCode( | ||||
|     response: Response, body: any, data: { [key: string]: any } | ||||
|     response: Response, | ||||
|     body: any, | ||||
|     data: { [key: string]: any } | ||||
|   ): Promise<undefined> { | ||||
|     const status = response.status | ||||
| 
 | ||||
|     if ( | ||||
|       (status >= 200 && status < 400) | ||||
|       || status === HttpResponseCode.NoContent | ||||
|       || status === HttpResponseCode.TooManyRequests | ||||
|     ) return | ||||
|       (status >= 200 && status < 400) || | ||||
|       status === HttpResponseCode.NoContent || | ||||
|       status === HttpResponseCode.TooManyRequests | ||||
|     ) | ||||
|       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 (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
 | ||||
|     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 ([ | ||||
|       HttpResponseCode.BadRequest, | ||||
|       HttpResponseCode.NotFound, | ||||
|       HttpResponseCode.Forbidden, | ||||
|       HttpResponseCode.MethodNotAllowed | ||||
|     ].includes(status)) { | ||||
|     if ( | ||||
|       [ | ||||
|         HttpResponseCode.BadRequest, | ||||
|         HttpResponseCode.NotFound, | ||||
|         HttpResponseCode.Forbidden, | ||||
|         HttpResponseCode.MethodNotAllowed, | ||||
|       ].includes(status) | ||||
|     ) { | ||||
|       throw new DiscordAPIError(Deno.inspect(error)) | ||||
|     } else if (status === HttpResponseCode.GatewayUnavailable) { | ||||
|       throw new DiscordAPIError(Deno.inspect(error)) | ||||
|  | @ -291,7 +310,7 @@ export class RESTManager { | |||
|     body?: unknown, | ||||
|     maxRetries = 0, | ||||
|     bucket?: string | null, | ||||
|     rawResponse?: boolean, | ||||
|     rawResponse?: boolean | ||||
|   ): Promise<any> { | ||||
|     return await new Promise((resolve, reject) => { | ||||
|       const onComplete = async (): Promise<undefined | any> => { | ||||
|  | @ -301,20 +320,20 @@ export class RESTManager { | |||
|             return { | ||||
|               rateLimited: rateLimitResetIn, | ||||
|               before: true, | ||||
|               bucket | ||||
|               bucket, | ||||
|             } | ||||
|           } | ||||
| 
 | ||||
|           const query = | ||||
|             method === 'get' && body !== undefined | ||||
|               ? Object.entries(body as any) | ||||
|                 .map( | ||||
|                   ([key, value]) => | ||||
|                     `${encodeURIComponent(key)}=${encodeURIComponent( | ||||
|                       value as any | ||||
|                     )}` | ||||
|                 ) | ||||
|                 .join('&') | ||||
|                   .map( | ||||
|                     ([key, value]) => | ||||
|                       `${encodeURIComponent(key)}=${encodeURIComponent( | ||||
|                         value as any | ||||
|                       )}` | ||||
|                   ) | ||||
|                   .join('&') | ||||
|               : '' | ||||
|           let urlToUse = | ||||
|             method === 'get' && query !== '' ? `${url}?${query}` : url | ||||
|  | @ -329,7 +348,10 @@ export class RESTManager { | |||
|           const response = await fetch(urlToUse, requestData) | ||||
|           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() | ||||
|           await this.handleStatusCode(response, json, requestData) | ||||
|  | @ -345,7 +367,7 @@ export class RESTManager { | |||
|             return { | ||||
|               rateLimited: json.retry_after, | ||||
|               before: false, | ||||
|               bucket: bucketFromHeaders | ||||
|               bucket: bucketFromHeaders, | ||||
|             } | ||||
|           } | ||||
|           return resolve(rawResponse === true ? { response, body: json } : json) | ||||
|  | @ -357,7 +379,7 @@ export class RESTManager { | |||
|       this.queue({ | ||||
|         onComplete, | ||||
|         bucket, | ||||
|         url | ||||
|         url, | ||||
|       }) | ||||
|       if (!this.processing) { | ||||
|         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) | ||||
|   } | ||||
| 
 | ||||
|   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) | ||||
|   } | ||||
| 
 | ||||
|   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) | ||||
|   } | ||||
| 
 | ||||
|   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) | ||||
|   } | ||||
| 
 | ||||
|   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) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ export class VoiceClient { | |||
|   } | ||||
| 
 | ||||
|   async connect(): Promise<VoiceClient> { | ||||
|      | ||||
|     // TODO(DjDeveloperr): Actually understand what the hell docs say
 | ||||
|     return this | ||||
|   } | ||||
| } | ||||
|  | @ -1,10 +1,11 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { | ||||
|   GuildBanPayload, | ||||
|   GuildFeatures, | ||||
|   GuildIntegrationPayload, | ||||
|   GuildPayload, | ||||
|   IntegrationAccountPayload, | ||||
|   IntegrationExpireBehavior | ||||
|   IntegrationExpireBehavior, | ||||
| } from '../types/guild.ts' | ||||
| import { PresenceUpdatePayload } from '../types/gateway.ts' | ||||
| import { Base } from './base.ts' | ||||
|  | @ -16,8 +17,100 @@ import { GuildEmojisManager } from '../managers/guildEmojis.ts' | |||
| import { Member } from './member.ts' | ||||
| import { User } from './user.ts' | ||||
| import { Application } from './application.ts' | ||||
| import { GUILD_INTEGRATIONS } from '../types/endpoint.ts' | ||||
| import { GuildVoiceStatesManager } from "../managers/guildVoiceStates.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 { | ||||
|   id: string | ||||
|  | @ -65,10 +158,12 @@ export class Guild extends Base { | |||
|   maxVideoChannelUsers?: number | ||||
|   approximateNumberCount?: number | ||||
|   approximatePresenceCount?: number | ||||
|   bans: GuildBans | ||||
| 
 | ||||
|   constructor (client: Client, data: GuildPayload) { | ||||
|   constructor(client: Client, data: GuildPayload) { | ||||
|     super(client, data) | ||||
|     this.id = data.id | ||||
|     this.bans = new GuildBans(client, this) | ||||
|     this.unavailable = data.unavailable | ||||
|     this.members = new MembersManager(this.client, this) | ||||
|     this.voiceStates = new GuildVoiceStatesManager(client, this) | ||||
|  | @ -97,15 +192,6 @@ export class Guild extends Base { | |||
|       this.verificationLevel = data.verification_level | ||||
|       this.defaultMessageNotifications = data.default_message_notifications | ||||
|       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.mfaLevel = data.mfa_level | ||||
|       this.systemChannelID = data.system_channel_id | ||||
|  | @ -114,20 +200,6 @@ export class Guild extends Base { | |||
|       this.joinedAt = data.joined_at | ||||
|       this.large = data.large | ||||
|       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.maxPresences = data.max_presences | ||||
|       this.maxMembers = data.max_members | ||||
|  | @ -144,7 +216,7 @@ export class Guild extends Base { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: GuildPayload): void { | ||||
|   protected readFromData(data: GuildPayload): void { | ||||
|     super.readFromData(data) | ||||
|     this.id = data.id ?? this.id | ||||
|     this.unavailable = data.unavailable ?? this.unavailable | ||||
|  | @ -168,14 +240,6 @@ export class Guild extends Base { | |||
|         data.default_message_notifications ?? this.defaultMessageNotifications | ||||
|       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.mfaLevel = data.mfa_level ?? this.mfaLevel | ||||
|       this.systemChannelID = data.system_channel_id ?? this.systemChannelID | ||||
|  | @ -185,22 +249,6 @@ export class Guild extends Base { | |||
|       this.joinedAt = data.joined_at ?? this.joinedAt | ||||
|       this.large = data.large ?? this.large | ||||
|       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.maxPresences = data.max_presences ?? this.maxPresences | ||||
|       this.maxMembers = data.max_members ?? this.maxMembers | ||||
|  | @ -222,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
 | ||||
|     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) | ||||
|     if (get === undefined) throw new Error('Guild#me is not cached') | ||||
|     return get | ||||
|   } | ||||
| 
 | ||||
|   async fetchIntegrations (): Promise<GuildIntegration[]> { | ||||
|   /** | ||||
|    * Fetch Guild's Integrations (Webhooks, Bots, etc.) | ||||
|    */ | ||||
|   async fetchIntegrations(): Promise<GuildIntegration[]> { | ||||
|     const raw = (await this.client.rest.get( | ||||
|       GUILD_INTEGRATIONS(this.id) | ||||
|     )) 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) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -258,7 +349,7 @@ export class GuildIntegration extends Base { | |||
|   revoked?: boolean | ||||
|   application?: Application | ||||
| 
 | ||||
|   constructor (client: Client, data: GuildIntegrationPayload) { | ||||
|   constructor(client: Client, data: GuildIntegrationPayload) { | ||||
|     super(client, data) | ||||
| 
 | ||||
|     this.id = data.id | ||||
|  |  | |||
|  | @ -1,11 +1,20 @@ | |||
| import { MemberRolesManager } from "../managers/memberRoles.ts" | ||||
| import { MemberRolesManager } from '../managers/memberRoles.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| import { GUILD_MEMBER } from '../types/endpoint.ts' | ||||
| import { MemberPayload } from '../types/guild.ts' | ||||
| import { Permissions } from "../utils/permissions.ts" | ||||
| import { Permissions } from '../utils/permissions.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { Guild } from "./guild.ts" | ||||
| import { Guild } from './guild.ts' | ||||
| import { Role } from './role.ts' | ||||
| import { User } from './user.ts' | ||||
| 
 | ||||
| export interface MemberData { | ||||
|   nick?: string | null | ||||
|   roles?: Array<Role | string> | ||||
|   deaf?: boolean | ||||
|   mute?: boolean | ||||
| } | ||||
| 
 | ||||
| export class Member extends Base { | ||||
|   id: string | ||||
|   user: User | ||||
|  | @ -18,7 +27,13 @@ export class Member extends Base { | |||
|   guild: Guild | ||||
|   permissions: Permissions | ||||
| 
 | ||||
|   constructor (client: Client, data: MemberPayload, user: User, guild: Guild, perms?: Permissions) { | ||||
|   constructor( | ||||
|     client: Client, | ||||
|     data: MemberPayload, | ||||
|     user: User, | ||||
|     guild: Guild, | ||||
|     perms?: Permissions | ||||
|   ) { | ||||
|     super(client) | ||||
|     this.id = data.user.id | ||||
|     this.user = user | ||||
|  | @ -33,7 +48,15 @@ export class Member extends Base { | |||
|     else this.permissions = new Permissions(Permissions.DEFAULT) | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: MemberPayload): void { | ||||
|   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 | ||||
|  | @ -41,4 +64,110 @@ export class Member extends Base { | |||
|     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,4 +1,4 @@ | |||
| import { ApplicationPayload } from "./application.ts" | ||||
| import { ApplicationPayload } from './application.ts' | ||||
| import { ChannelPayload } from './channel.ts' | ||||
| import { EmojiPayload } from './emoji.ts' | ||||
| import { PresenceUpdatePayload } from './gateway.ts' | ||||
|  | @ -66,18 +66,18 @@ export interface MemberPayload { | |||
| 
 | ||||
| export enum MessageNotification { | ||||
|   ALL_MESSAGES = 0, | ||||
|   ONLY_MENTIONS = 1 | ||||
|   ONLY_MENTIONS = 1, | ||||
| } | ||||
| 
 | ||||
| export enum ContentFilter { | ||||
|   DISABLED = 0, | ||||
|   MEMBERS_WITHOUT_ROLES = 1, | ||||
|   ALL_MEMBERS = 3 | ||||
|   ALL_MEMBERS = 3, | ||||
| } | ||||
| 
 | ||||
| export enum MFA { | ||||
|   NONE = 0, | ||||
|   ELEVATED = 1 | ||||
|   ELEVATED = 1, | ||||
| } | ||||
| 
 | ||||
| export enum Verification { | ||||
|  | @ -85,19 +85,19 @@ export enum Verification { | |||
|   LOW = 1, | ||||
|   MEDIUM = 2, | ||||
|   HIGH = 3, | ||||
|   VERY_HIGH = 4 | ||||
|   VERY_HIGH = 4, | ||||
| } | ||||
| 
 | ||||
| export enum PremiumTier { | ||||
|   NONE = 0, | ||||
|   TIER_1 = 1, | ||||
|   TIER_2 = 2, | ||||
|   TIER_3 = 3 | ||||
|   TIER_3 = 3, | ||||
| } | ||||
| 
 | ||||
| export enum SystemChannelFlags { | ||||
|   SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0, | ||||
|   SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1 | ||||
|   SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1, | ||||
| } | ||||
| 
 | ||||
| export type GuildFeatures = | ||||
|  | @ -116,7 +116,7 @@ export type GuildFeatures = | |||
| 
 | ||||
| export enum IntegrationExpireBehavior { | ||||
|   REMOVE_ROLE = 0, | ||||
|   KICK = 1 | ||||
|   KICK = 1, | ||||
| } | ||||
| 
 | ||||
| export interface IntegrationAccountPayload { | ||||
|  | @ -141,3 +141,8 @@ export interface GuildIntegrationPayload { | |||
|   revoked?: boolean | ||||
|   application?: ApplicationPayload | ||||
| } | ||||
| 
 | ||||
| export interface GuildBanPayload { | ||||
|   reason: string | null | ||||
|   user: UserPayload | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue