Merge pull request #40 from DjDeveloperr/main

feat(API): Added new API-methods to structures and more
This commit is contained in:
Helloyunho 2020-12-02 16:48:28 +09:00 committed by GitHub
commit 5d49762e6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1102 additions and 603 deletions

6
.prettierrc Normal file
View file

@ -0,0 +1,6 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true
}

View file

@ -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
} }

View file

@ -5,3 +5,5 @@ 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

View 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)
}
}

View file

@ -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
} }

View 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
})
}

View 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)
}
}

View file

@ -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
@ -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,13 +250,13 @@ 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,
}) })
} }
@ -267,12 +272,32 @@ 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)
} }
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 { debug(msg: string): void {
this.client.debug('Gateway', msg) this.client.debug('Gateway', msg)
} }
@ -309,7 +334,7 @@ class Gateway {
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
@ -318,14 +343,14 @@ class Gateway {
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)

View 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
}
}

View file

@ -1,15 +1,13 @@
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< export class MemberRolesManager extends BaseChildManager<RolePayload, Role> {
RolePayload,
Role
> {
member: Member member: Member
constructor(client: Client, parent: RolesManager, member: Member) { constructor(client: Client, parent: RolesManager, member: Member) {
@ -19,16 +17,25 @@ export class MemberRolesManager extends BaseChildManager<
async get(id: string): Promise<Role | undefined> { async get(id: string): Promise<Role | undefined> {
const res = await this.parent.get(id) const res = await this.parent.get(id)
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(
if (res !== undefined && (mem.roles.includes(res.id) === true || res.id === this.member.guild.id)) return res 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 else return undefined
} }
async array(): Promise<Role[]> { async array(): Promise<Role[]> {
const arr = (await this.parent.array()) as 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( 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 ) as any
} }
@ -39,4 +46,36 @@ export class MemberRolesManager extends BaseChildManager<
} }
return true 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
}
} }

View file

@ -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 rateLimited: any
bucket?: string | null bucket?: string | null
before: boolean before: boolean
} | undefined> }
| 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.BadRequest,
HttpResponseCode.NotFound, HttpResponseCode.NotFound,
HttpResponseCode.Forbidden, HttpResponseCode.Forbidden,
HttpResponseCode.MethodNotAllowed HttpResponseCode.MethodNotAllowed,
].includes(status)) { ].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,7 +320,7 @@ export class RESTManager {
return { return {
rateLimited: rateLimitResetIn, rateLimited: rateLimitResetIn,
before: true, before: true,
bucket bucket,
} }
} }
@ -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)
} }
} }

View file

@ -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
} }
} }

View file

@ -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
@ -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 {
} }
} }
/**
* Get Everyone role of the Guild
*/
async getEveryoneRole(): Promise<Role> { 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
} }
/**
* Get current client's member in the Guild
*/
async me(): Promise<Member> { 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
} }
/**
* Fetch Guild's Integrations (Webhooks, Bots, etc.)
*/
async fetchIntegrations(): Promise<GuildIntegration[]> { 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)
})
} }
} }

View file

@ -1,11 +1,20 @@
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 { GUILD_MEMBER } from '../types/endpoint.ts'
import { MemberPayload } from '../types/guild.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 { Base } from './base.ts'
import { Guild } from "./guild.ts" import { Guild } from './guild.ts'
import { Role } from './role.ts'
import { User } from './user.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 { export class Member extends Base {
id: string id: string
user: User user: User
@ -18,7 +27,13 @@ export class Member extends Base {
guild: Guild guild: Guild
permissions: Permissions 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) super(client)
this.id = data.user.id this.id = data.user.id
this.user = user this.user = user
@ -33,6 +48,14 @@ export class Member extends Base {
else this.permissions = new Permissions(Permissions.DEFAULT) 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 { protected readFromData(data: MemberPayload): void {
super.readFromData(data.user) super.readFromData(data.user)
this.nick = data.nick ?? this.nick this.nick = data.nick ?? this.nick
@ -41,4 +64,110 @@ export class Member extends Base {
this.deaf = data.deaf ?? this.deaf this.deaf = data.deaf ?? this.deaf
this.mute = data.mute ?? this.mute 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)
}
} }

View file

@ -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"
import { Member } from "./member.ts"
import { User } from "./user.ts"
export class VoiceState extends Base { export class VoiceState extends Base {
guildID?: string guild?: Guild
channelID?: string channel: VoiceChannel | null
userID: string user: User
member?: MemberPayload member?: Member
sessionID: string sessionID: string
deaf: boolean deaf: boolean
mute: boolean mute: boolean
selfDeaf: boolean stream?: boolean
selfMute: boolean video: boolean
selfStream?: boolean
selfVideo: boolean
suppress: boolean suppress: boolean
constructor (client: Client, data: VoiceStatePayload) { constructor (client: Client, data: VoiceStatePayload, _data: {
user: User,
channel: VoiceChannel | null,
member?: Member,
guild?: Guild
}) {
super(client, data) super(client, data)
this.channelID = data.channel_id this.channel = _data.channel
this.sessionID = data.session_id this.sessionID = data.session_id
this.userID = data.user_id this.user = _data.user
this.member = _data.member
this.guild = _data.guild
this.deaf = data.deaf this.deaf = data.deaf
this.mute = data.mute this.mute = data.mute
this.selfDeaf = data.self_deaf this.deaf = data.self_deaf
this.selfMute = data.self_mute this.mute = data.self_mute
this.selfStream = data.self_stream this.stream = data.self_stream
this.selfVideo = data.self_video this.video = data.self_video
this.suppress = data.suppress this.suppress = data.suppress
// TODO: Cache in Gateway Event Code
// cache.set('voiceState', `${this.guildID}:${this.userID}`, this)
} }
protected readFromData (data: VoiceStatePayload): void { protected readFromData (data: VoiceStatePayload): void {
super.readFromData(data) super.readFromData(data)
this.channelID = data.channel_id ?? this.channelID
this.sessionID = data.session_id ?? this.sessionID this.sessionID = data.session_id ?? this.sessionID
this.userID = data.user_id ?? this.userID
this.deaf = data.deaf ?? this.deaf this.deaf = data.deaf ?? this.deaf
this.mute = data.mute ?? this.mute this.mute = data.mute ?? this.mute
this.selfDeaf = data.self_deaf ?? this.selfDeaf this.deaf = data.self_deaf ?? this.deaf
this.selfMute = data.self_mute ?? this.selfMute this.mute = data.self_mute ?? this.mute
this.selfStream = data.self_stream ?? this.selfStream this.stream = data.self_stream ?? this.stream
this.selfVideo = data.self_video ?? this.selfVideo this.video = data.self_video ?? this.video
this.suppress = data.suppress ?? this.suppress this.suppress = data.suppress ?? this.suppress
} }
} }

View file

@ -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')

View file

@ -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'
} }

View file

@ -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 {
@ -141,3 +141,8 @@ export interface GuildIntegrationPayload {
revoked?: boolean revoked?: boolean
application?: ApplicationPayload application?: ApplicationPayload
} }
export interface GuildBanPayload {
reason: string | null
user: UserPayload
}

View file

@ -31,7 +31,7 @@ export enum VoiceCloseCodes {
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

View file

@ -60,7 +60,7 @@
// "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,