Merge pull request #40 from DjDeveloperr/main
feat(API): Added new API-methods to structures and more
This commit is contained in:
commit
5d49762e6e
21 changed files with 1102 additions and 603 deletions
6
.prettierrc
Normal file
6
.prettierrc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -6,5 +6,6 @@
|
||||||
"deno.import_intellisense_origins": {
|
"deno.import_intellisense_origins": {
|
||||||
"https://deno.land": true
|
"https://deno.land": true
|
||||||
},
|
},
|
||||||
"editor.tabSize": 2
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnSave": true
|
||||||
}
|
}
|
|
@ -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
|
31
src/gateway/handlers/guildMembersChunk.ts
Normal file
31
src/gateway/handlers/guildMembersChunk.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||||
|
import { Guild } from '../../structures/guild.ts'
|
||||||
|
import { GuildMemberChunkPayload } from '../../types/gateway.ts'
|
||||||
|
|
||||||
|
export const guildMembersChunk: GatewayEventHandler = async (
|
||||||
|
gateway: Gateway,
|
||||||
|
d: GuildMemberChunkPayload
|
||||||
|
) => {
|
||||||
|
const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id)
|
||||||
|
// Weird case, shouldn't happen
|
||||||
|
if (guild === undefined) return
|
||||||
|
|
||||||
|
for (const member of d.members) {
|
||||||
|
await guild.members.set(member.user.id, member)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Cache Presences
|
||||||
|
|
||||||
|
gateway.client.emit('guildMembersChunk', guild, {
|
||||||
|
members: d.members.map((m) => m.user.id),
|
||||||
|
presences:
|
||||||
|
d.presences === undefined ? undefined : d.presences.map((p) => p.user.id),
|
||||||
|
chunkIndex: d.chunk_index,
|
||||||
|
chunkCount: d.chunk_count,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Guild is now completely chunked. Emit an event for that.
|
||||||
|
if (d.chunk_index >= d.chunk_count - 1) {
|
||||||
|
gateway.client.emit('guildMembersChunked', guild, d.chunk_count)
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,9 @@ import { Member } from "../../structures/member.ts"
|
||||||
import { Role } from "../../structures/role.ts"
|
import { Role } from "../../structures/role.ts"
|
||||||
import { Message } from "../../structures/message.ts"
|
import { Message } from "../../structures/message.ts"
|
||||||
import { Collection } from "../../utils/collection.ts"
|
import { Collection } from "../../utils/collection.ts"
|
||||||
|
import { voiceServerUpdate } from "./voiceServerUpdate.ts"
|
||||||
|
import { voiceStateUpdate } from "./voiceStateUpdate.ts"
|
||||||
|
import { VoiceState } from "../../structures/voiceState.ts"
|
||||||
|
|
||||||
export const gatewayHandlers: {
|
export const gatewayHandlers: {
|
||||||
[eventCode in GatewayEvents]: GatewayEventHandler | undefined
|
[eventCode in GatewayEvents]: GatewayEventHandler | undefined
|
||||||
|
@ -74,7 +77,8 @@ export const gatewayHandlers: {
|
||||||
PRESENCE_UPDATE: undefined,
|
PRESENCE_UPDATE: undefined,
|
||||||
TYPING_START: typingStart,
|
TYPING_START: typingStart,
|
||||||
USER_UPDATE: userUpdate,
|
USER_UPDATE: userUpdate,
|
||||||
VOICE_SERVER_UPDATE: undefined,
|
VOICE_STATE_UPDATE: voiceStateUpdate,
|
||||||
|
VOICE_SERVER_UPDATE: voiceServerUpdate,
|
||||||
WEBHOOKS_UPDATE: webhooksUpdate
|
WEBHOOKS_UPDATE: webhooksUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +86,12 @@ export interface EventTypes {
|
||||||
[name: string]: (...args: any[]) => void
|
[name: string]: (...args: any[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VoiceServerUpdateData {
|
||||||
|
token: string
|
||||||
|
endpoint: string
|
||||||
|
guild: Guild
|
||||||
|
}
|
||||||
|
|
||||||
export interface ClientEvents extends EventTypes {
|
export interface ClientEvents extends EventTypes {
|
||||||
'ready': () => void
|
'ready': () => void
|
||||||
'reconnect': () => void
|
'reconnect': () => void
|
||||||
|
@ -111,5 +121,9 @@ export interface ClientEvents extends EventTypes {
|
||||||
'messageUpdate': (before: Message, after: Message) => void
|
'messageUpdate': (before: Message, after: Message) => void
|
||||||
'typingStart': (user: User, channel: TextChannel, at: Date, guildData?: TypingStartGuildData) => void
|
'typingStart': (user: User, channel: TextChannel, at: Date, guildData?: TypingStartGuildData) => void
|
||||||
'userUpdate': (before: User, after: User) => void
|
'userUpdate': (before: User, after: User) => void
|
||||||
|
'voiceServerUpdate': (data: VoiceServerUpdateData) => void
|
||||||
|
'voiceStateAdd': (state: VoiceState) => void
|
||||||
|
'voiceStateRemove': (state: VoiceState) => void
|
||||||
|
'voiceStateUpdate': (state: VoiceState, after: VoiceState) => void
|
||||||
'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void
|
'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void
|
||||||
}
|
}
|
14
src/gateway/handlers/voiceServerUpdate.ts
Normal file
14
src/gateway/handlers/voiceServerUpdate.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { Guild } from "../../structures/guild.ts"
|
||||||
|
import { VoiceServerUpdatePayload } from "../../types/gateway.ts"
|
||||||
|
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||||
|
|
||||||
|
export const voiceServerUpdate: GatewayEventHandler = async (
|
||||||
|
gateway: Gateway,
|
||||||
|
d: VoiceServerUpdatePayload
|
||||||
|
) => {
|
||||||
|
gateway.client.emit('voiceServerUpdate', {
|
||||||
|
token: d.token,
|
||||||
|
endpoint: d.endpoint,
|
||||||
|
guild: (await gateway.client.guilds.get(d.guild_id) as unknown) as Guild
|
||||||
|
})
|
||||||
|
}
|
30
src/gateway/handlers/voiceStateUpdate.ts
Normal file
30
src/gateway/handlers/voiceStateUpdate.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { Guild } from "../../structures/guild.ts"
|
||||||
|
import { VoiceState } from "../../structures/voiceState.ts"
|
||||||
|
import { VoiceStatePayload } from "../../types/voice.ts"
|
||||||
|
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||||
|
|
||||||
|
export const voiceStateUpdate: GatewayEventHandler = async (
|
||||||
|
gateway: Gateway,
|
||||||
|
d: VoiceStatePayload
|
||||||
|
) => {
|
||||||
|
// TODO(DjDeveloperr): Support self-bot here; they can be in DMs (Call)
|
||||||
|
if (d.guild_id === undefined) return
|
||||||
|
const guild = (await gateway.client.guilds.get(d.guild_id) as unknown) as Guild
|
||||||
|
|
||||||
|
const voiceState = await guild.voiceStates.get(d.user_id)
|
||||||
|
|
||||||
|
if (d.channel_id === null) {
|
||||||
|
// No longer in the channel, so delete
|
||||||
|
await guild.voiceStates.delete(d.user_id)
|
||||||
|
gateway.client.emit('voiceStateRemove', (voiceState as unknown) as VoiceState)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await guild.voiceStates.set(d.user_id, d)
|
||||||
|
const newVoiceState = await guild.voiceStates.get(d.user_id)
|
||||||
|
if (voiceState === undefined) {
|
||||||
|
gateway.client.emit('voiceStateAdd', (newVoiceState as unknown) as VoiceState)
|
||||||
|
} else {
|
||||||
|
gateway.client.emit('voiceStateUpdate', voiceState, (newVoiceState as unknown) as VoiceState)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts'
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import {
|
import {
|
||||||
DISCORD_GATEWAY_URL,
|
DISCORD_GATEWAY_URL,
|
||||||
DISCORD_API_VERSION
|
DISCORD_API_VERSION,
|
||||||
} from '../consts/urlsAndVersions.ts'
|
} from '../consts/urlsAndVersions.ts'
|
||||||
import { GatewayResponse } from '../types/gatewayResponse.ts'
|
import { GatewayResponse } from '../types/gatewayResponse.ts'
|
||||||
import {
|
import {
|
||||||
|
@ -10,18 +10,23 @@ import {
|
||||||
GatewayIntents,
|
GatewayIntents,
|
||||||
GatewayCloseCodes,
|
GatewayCloseCodes,
|
||||||
IdentityPayload,
|
IdentityPayload,
|
||||||
StatusUpdatePayload
|
StatusUpdatePayload,
|
||||||
} from '../types/gateway.ts'
|
} from '../types/gateway.ts'
|
||||||
import { gatewayHandlers } from './handlers/index.ts'
|
import { gatewayHandlers } from './handlers/index.ts'
|
||||||
import { GATEWAY_BOT } from '../types/endpoint.ts'
|
import { GATEWAY_BOT } from '../types/endpoint.ts'
|
||||||
import { GatewayCache } from '../managers/gatewayCache.ts'
|
import { GatewayCache } from '../managers/gatewayCache.ts'
|
||||||
import { delay } from '../utils/delay.ts'
|
import { delay } from '../utils/delay.ts'
|
||||||
|
|
||||||
|
export interface RequestMembersOptions {
|
||||||
|
limit?: number
|
||||||
|
presences?: boolean
|
||||||
|
query?: string
|
||||||
|
users?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles Discord gateway connection.
|
* Handles Discord gateway connection.
|
||||||
* You should not use this and rather use Client class.
|
* You should not use this and rather use Client class.
|
||||||
*
|
|
||||||
* @beta
|
|
||||||
*/
|
*/
|
||||||
class Gateway {
|
class Gateway {
|
||||||
websocket: WebSocket
|
websocket: WebSocket
|
||||||
|
@ -38,7 +43,7 @@ class Gateway {
|
||||||
client: Client
|
client: Client
|
||||||
cache: GatewayCache
|
cache: GatewayCache
|
||||||
|
|
||||||
constructor (client: Client, token: string, intents: GatewayIntents[]) {
|
constructor(client: Client, token: string, intents: GatewayIntents[]) {
|
||||||
this.token = token
|
this.token = token
|
||||||
this.intents = intents
|
this.intents = intents
|
||||||
this.client = client
|
this.client = client
|
||||||
|
@ -55,12 +60,12 @@ class Gateway {
|
||||||
this.websocket.onerror = this.onerror.bind(this)
|
this.websocket.onerror = this.onerror.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private onopen (): void {
|
private onopen(): void {
|
||||||
this.connected = true
|
this.connected = true
|
||||||
this.debug('Connected to Gateway!')
|
this.debug('Connected to Gateway!')
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onmessage (event: MessageEvent): Promise<void> {
|
private async onmessage(event: MessageEvent): Promise<void> {
|
||||||
let data = event.data
|
let data = event.data
|
||||||
if (data instanceof ArrayBuffer) {
|
if (data instanceof ArrayBuffer) {
|
||||||
data = new Uint8Array(data)
|
data = new Uint8Array(data)
|
||||||
|
@ -144,7 +149,7 @@ class Gateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onclose (event: CloseEvent): Promise<void> {
|
private async onclose(event: CloseEvent): Promise<void> {
|
||||||
this.debug(`Connection Closed with code: ${event.code}`)
|
this.debug(`Connection Closed with code: ${event.code}`)
|
||||||
|
|
||||||
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
|
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
|
||||||
|
@ -191,12 +196,12 @@ class Gateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onerror (event: Event | ErrorEvent): void {
|
private onerror(event: Event | ErrorEvent): void {
|
||||||
const eventError = event as ErrorEvent
|
const eventError = event as ErrorEvent
|
||||||
console.log(eventError)
|
console.log(eventError)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendIdentify (forceNewSession?: boolean): Promise<void> {
|
private async sendIdentify(forceNewSession?: boolean): Promise<void> {
|
||||||
if (this.client.bot === true) {
|
if (this.client.bot === true) {
|
||||||
this.debug('Fetching /gateway/bot...')
|
this.debug('Fetching /gateway/bot...')
|
||||||
const info = await this.client.rest.get(GATEWAY_BOT())
|
const info = await this.client.rest.get(GATEWAY_BOT())
|
||||||
|
@ -225,7 +230,7 @@ class Gateway {
|
||||||
properties: {
|
properties: {
|
||||||
$os: Deno.build.os,
|
$os: Deno.build.os,
|
||||||
$browser: 'harmony',
|
$browser: 'harmony',
|
||||||
$device: 'harmony'
|
$device: 'harmony',
|
||||||
},
|
},
|
||||||
compress: true,
|
compress: true,
|
||||||
shard: [0, 1], // TODO: Make sharding possible
|
shard: [0, 1], // TODO: Make sharding possible
|
||||||
|
@ -233,7 +238,7 @@ class Gateway {
|
||||||
(previous, current) => previous | current,
|
(previous, current) => previous | current,
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
presence: this.client.presence.create()
|
presence: this.client.presence.create(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client.bot === false) {
|
if (this.client.bot === false) {
|
||||||
|
@ -245,17 +250,17 @@ class Gateway {
|
||||||
$browser: 'Firefox',
|
$browser: 'Firefox',
|
||||||
$device: '',
|
$device: '',
|
||||||
$referrer: '',
|
$referrer: '',
|
||||||
$referring_domain: ''
|
$referring_domain: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.send({
|
this.send({
|
||||||
op: GatewayOpcodes.IDENTIFY,
|
op: GatewayOpcodes.IDENTIFY,
|
||||||
d: payload
|
d: payload,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendResume (): Promise<void> {
|
private async sendResume(): Promise<void> {
|
||||||
this.debug(`Preparing to resume with Session: ${this.sessionID}`)
|
this.debug(`Preparing to resume with Session: ${this.sessionID}`)
|
||||||
if (this.sequenceID === undefined) {
|
if (this.sequenceID === undefined) {
|
||||||
const cached = await this.cache.get('seq')
|
const cached = await this.cache.get('seq')
|
||||||
|
@ -267,17 +272,37 @@ class Gateway {
|
||||||
d: {
|
d: {
|
||||||
token: this.token,
|
token: this.token,
|
||||||
session_id: this.sessionID,
|
session_id: this.sessionID,
|
||||||
seq: this.sequenceID ?? null
|
seq: this.sequenceID ?? null,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
this.send(resumePayload)
|
this.send(resumePayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug (msg: string): void {
|
requestMembers(guild: string, options: RequestMembersOptions = {}): string {
|
||||||
|
if (options.query !== undefined && options.limit === undefined)
|
||||||
|
throw new Error(
|
||||||
|
'Missing limit property when specifying query for Requesting Members!'
|
||||||
|
)
|
||||||
|
const nonce = `${guild}_${new Date().getTime()}`
|
||||||
|
this.send({
|
||||||
|
op: GatewayOpcodes.REQUEST_GUILD_MEMBERS,
|
||||||
|
d: {
|
||||||
|
guild_id: guild,
|
||||||
|
query: options.query,
|
||||||
|
limit: options.limit,
|
||||||
|
presences: options.presences,
|
||||||
|
user_ids: options.users,
|
||||||
|
nonce,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(msg: string): void {
|
||||||
this.client.debug('Gateway', msg)
|
this.client.debug('Gateway', msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
async reconnect (forceNew?: boolean): Promise<void> {
|
async reconnect(forceNew?: boolean): Promise<void> {
|
||||||
clearInterval(this.heartbeatIntervalID)
|
clearInterval(this.heartbeatIntervalID)
|
||||||
if (forceNew === undefined || !forceNew)
|
if (forceNew === undefined || !forceNew)
|
||||||
await this.cache.delete('session_id')
|
await this.cache.delete('session_id')
|
||||||
|
@ -285,7 +310,7 @@ class Gateway {
|
||||||
this.initWebsocket()
|
this.initWebsocket()
|
||||||
}
|
}
|
||||||
|
|
||||||
initWebsocket (): void {
|
initWebsocket(): void {
|
||||||
this.websocket = new WebSocket(
|
this.websocket = new WebSocket(
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
`${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`,
|
`${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`,
|
||||||
|
@ -298,41 +323,41 @@ class Gateway {
|
||||||
this.websocket.onerror = this.onerror.bind(this)
|
this.websocket.onerror = this.onerror.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
close (): void {
|
close(): void {
|
||||||
this.websocket.close(1000)
|
this.websocket.close(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
send (data: GatewayResponse): boolean {
|
send(data: GatewayResponse): boolean {
|
||||||
if (this.websocket.readyState !== this.websocket.OPEN) return false
|
if (this.websocket.readyState !== this.websocket.OPEN) return false
|
||||||
this.websocket.send(
|
this.websocket.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
op: data.op,
|
op: data.op,
|
||||||
d: data.d,
|
d: data.d,
|
||||||
s: typeof data.s === 'number' ? data.s : null,
|
s: typeof data.s === 'number' ? data.s : null,
|
||||||
t: data.t === undefined ? null : data.t
|
t: data.t === undefined ? null : data.t,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPresence (data: StatusUpdatePayload): void {
|
sendPresence(data: StatusUpdatePayload): void {
|
||||||
this.send({
|
this.send({
|
||||||
op: GatewayOpcodes.PRESENCE_UPDATE,
|
op: GatewayOpcodes.PRESENCE_UPDATE,
|
||||||
d: data
|
d: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sendHeartbeat (): void {
|
sendHeartbeat(): void {
|
||||||
const payload = {
|
const payload = {
|
||||||
op: GatewayOpcodes.HEARTBEAT,
|
op: GatewayOpcodes.HEARTBEAT,
|
||||||
d: this.sequenceID ?? null
|
d: this.sequenceID ?? null,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.send(payload)
|
this.send(payload)
|
||||||
this.lastPingTimestamp = Date.now()
|
this.lastPingTimestamp = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
heartbeat (): void {
|
heartbeat(): void {
|
||||||
if (this.heartbeatServerResponded) {
|
if (this.heartbeatServerResponded) {
|
||||||
this.heartbeatServerResponded = false
|
this.heartbeatServerResponded = false
|
||||||
} else {
|
} else {
|
||||||
|
|
30
src/managers/guildVoiceStates.ts
Normal file
30
src/managers/guildVoiceStates.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { Client } from '../models/client.ts'
|
||||||
|
import { Guild } from "../structures/guild.ts"
|
||||||
|
import { VoiceChannel } from "../structures/guildVoiceChannel.ts"
|
||||||
|
import { User } from "../structures/user.ts"
|
||||||
|
import { VoiceState } from "../structures/voiceState.ts"
|
||||||
|
import { VoiceStatePayload } from "../types/voice.ts"
|
||||||
|
import { BaseManager } from './base.ts'
|
||||||
|
|
||||||
|
export class GuildVoiceStatesManager extends BaseManager<VoiceStatePayload, VoiceState> {
|
||||||
|
guild: Guild
|
||||||
|
|
||||||
|
async get (key: string): Promise<VoiceState | undefined> {
|
||||||
|
const raw = await this._get(key)
|
||||||
|
if (raw === undefined) return
|
||||||
|
|
||||||
|
const guild = raw.guild_id === undefined ? undefined : await this.client.guilds.get(raw.guild_id)
|
||||||
|
|
||||||
|
return new VoiceState(this.client, raw, {
|
||||||
|
user: (await this.client.users.get(raw.user_id) as unknown) as User,
|
||||||
|
channel: raw.channel_id == null ? null : (await this.client.channels.get<VoiceChannel>(raw.channel_id) as unknown) as VoiceChannel,
|
||||||
|
guild,
|
||||||
|
member: guild === undefined ? undefined : await guild.members.get(raw.user_id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (client: Client, guild: Guild) {
|
||||||
|
super(client, `vs:${guild.id}`, VoiceState)
|
||||||
|
this.guild = guild
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,42 +1,81 @@
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import { BaseChildManager } from './baseChild.ts'
|
import { BaseChildManager } from './baseChild.ts'
|
||||||
import { RolePayload } from "../types/role.ts"
|
import { RolePayload } from '../types/role.ts'
|
||||||
import { Role } from "../structures/role.ts"
|
import { Role } from '../structures/role.ts'
|
||||||
import { Member } from "../structures/member.ts"
|
import { Member } from '../structures/member.ts'
|
||||||
import { RolesManager } from "./roles.ts"
|
import { RolesManager } from './roles.ts'
|
||||||
import { MemberPayload } from "../types/guild.ts"
|
import { MemberPayload } from '../types/guild.ts'
|
||||||
|
import { GUILD_MEMBER_ROLE } from '../types/endpoint.ts'
|
||||||
|
|
||||||
export class MemberRolesManager extends BaseChildManager<
|
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) {
|
||||||
super(client, parent as any)
|
super(client, parent as any)
|
||||||
this.member = member
|
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 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
|
||||||
}
|
}
|
||||||
|
|
||||||
async flush (): Promise<boolean> {
|
async flush(): Promise<boolean> {
|
||||||
const arr = await this.array()
|
const arr = await this.array()
|
||||||
for (const elem of arr) {
|
for (const elem of arr) {
|
||||||
this.parent.delete(elem.id)
|
this.parent.delete(elem.id)
|
||||||
}
|
}
|
||||||
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,13 @@ import { Client } from './client.ts'
|
||||||
import { getBuildInfo } from '../utils/buildInfo.ts'
|
import { getBuildInfo } from '../utils/buildInfo.ts'
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
|
|
||||||
export type RequestMethods = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete'
|
export type RequestMethods =
|
||||||
|
| 'get'
|
||||||
|
| 'post'
|
||||||
|
| 'put'
|
||||||
|
| 'patch'
|
||||||
|
| 'head'
|
||||||
|
| 'delete'
|
||||||
|
|
||||||
export enum HttpResponseCode {
|
export enum HttpResponseCode {
|
||||||
Ok = 200,
|
Ok = 200,
|
||||||
|
@ -16,7 +22,7 @@ export enum HttpResponseCode {
|
||||||
NotFound = 404,
|
NotFound = 404,
|
||||||
MethodNotAllowed = 405,
|
MethodNotAllowed = 405,
|
||||||
TooManyRequests = 429,
|
TooManyRequests = 429,
|
||||||
GatewayUnavailable = 502
|
GatewayUnavailable = 502,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestHeaders {
|
export interface RequestHeaders {
|
||||||
|
@ -30,11 +36,14 @@ export class DiscordAPIError extends Error {
|
||||||
export interface QueuedItem {
|
export interface QueuedItem {
|
||||||
bucket?: string | null
|
bucket?: string | null
|
||||||
url: string
|
url: string
|
||||||
onComplete: () => Promise<{
|
onComplete: () => Promise<
|
||||||
rateLimited: any
|
| {
|
||||||
bucket?: string | null
|
rateLimited: any
|
||||||
before: boolean
|
bucket?: string | null
|
||||||
} | undefined>
|
before: boolean
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RateLimit {
|
export interface RateLimit {
|
||||||
|
@ -85,16 +94,14 @@ export class RESTManager {
|
||||||
async processQueue(): Promise<void> {
|
async processQueue(): Promise<void> {
|
||||||
if (Object.keys(this.queues).length !== 0 && !this.globalRateLimit) {
|
if (Object.keys(this.queues).length !== 0 && !this.globalRateLimit) {
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
Object.values(this.queues).map(async pathQueue => {
|
Object.values(this.queues).map(async (pathQueue) => {
|
||||||
const request = pathQueue.shift()
|
const request = pathQueue.shift()
|
||||||
if (request === undefined) return
|
if (request === undefined) return
|
||||||
|
|
||||||
const rateLimitedURLResetIn = await this.isRateLimited(request.url)
|
const rateLimitedURLResetIn = await this.isRateLimited(request.url)
|
||||||
|
|
||||||
if (typeof request.bucket === 'string') {
|
if (typeof request.bucket === 'string') {
|
||||||
const rateLimitResetIn = await this.isRateLimited(
|
const rateLimitResetIn = await this.isRateLimited(request.bucket)
|
||||||
request.bucket
|
|
||||||
)
|
|
||||||
if (rateLimitResetIn !== false) {
|
if (rateLimitResetIn !== false) {
|
||||||
this.queue(request)
|
this.queue(request)
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,7 +109,7 @@ export class RESTManager {
|
||||||
if (result?.rateLimited !== undefined) {
|
if (result?.rateLimited !== undefined) {
|
||||||
this.queue({
|
this.queue({
|
||||||
...request,
|
...request,
|
||||||
bucket: result.bucket ?? request.bucket
|
bucket: result.bucket ?? request.bucket,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +121,7 @@ export class RESTManager {
|
||||||
if (result?.rateLimited !== undefined) {
|
if (result?.rateLimited !== undefined) {
|
||||||
this.queue({
|
this.queue({
|
||||||
...request,
|
...request,
|
||||||
bucket: result.bucket ?? request.bucket
|
bucket: result.bucket ?? request.bucket,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,20 +139,18 @@ export class RESTManager {
|
||||||
} else this.processing = false
|
} else this.processing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare(
|
prepare(body: any, method: RequestMethods): { [key: string]: any } {
|
||||||
body: any,
|
|
||||||
method: RequestMethods
|
|
||||||
): { [key: string]: any } {
|
|
||||||
|
|
||||||
const headers: RequestHeaders = {
|
const headers: RequestHeaders = {
|
||||||
'User-Agent': `DiscordBot (harmony, https://github.com/harmony-org/harmony)`
|
'User-Agent': `DiscordBot (harmony, https://github.com/harmony-org/harmony)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client !== undefined) headers.Authorization = `Bot ${this.client.token}`
|
if (this.client !== undefined)
|
||||||
|
headers.Authorization = `Bot ${this.client.token}`
|
||||||
|
|
||||||
if (this.client?.token === undefined) delete headers.Authorization
|
if (this.client?.token === undefined) delete headers.Authorization
|
||||||
|
|
||||||
if (method === 'get' || method === 'head' || method === 'delete') body = undefined
|
if (method === 'get' || method === 'head' || method === 'delete')
|
||||||
|
body = undefined
|
||||||
|
|
||||||
if (body?.reason !== undefined) {
|
if (body?.reason !== undefined) {
|
||||||
headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason)
|
headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason)
|
||||||
|
@ -163,7 +168,7 @@ export class RESTManager {
|
||||||
const data: { [name: string]: any } = {
|
const data: { [name: string]: any } = {
|
||||||
headers,
|
headers,
|
||||||
body: body?.file ?? JSON.stringify(body),
|
body: body?.file ?? JSON.stringify(body),
|
||||||
method: method.toUpperCase()
|
method: method.toUpperCase(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client?.bot === false) {
|
if (this.client?.bot === false) {
|
||||||
|
@ -217,14 +222,14 @@ export class RESTManager {
|
||||||
this.rateLimits.set(url, {
|
this.rateLimits.set(url, {
|
||||||
url,
|
url,
|
||||||
resetAt: Number(resetAt) * 1000,
|
resetAt: Number(resetAt) * 1000,
|
||||||
bucket
|
bucket,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (bucket !== null) {
|
if (bucket !== null) {
|
||||||
this.rateLimits.set(bucket, {
|
this.rateLimits.set(bucket, {
|
||||||
url,
|
url,
|
||||||
resetAt: Number(resetAt) * 1000,
|
resetAt: Number(resetAt) * 1000,
|
||||||
bucket
|
bucket,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,14 +242,14 @@ export class RESTManager {
|
||||||
this.rateLimits.set('global', {
|
this.rateLimits.set('global', {
|
||||||
url: 'global',
|
url: 'global',
|
||||||
resetAt: reset,
|
resetAt: reset,
|
||||||
bucket
|
bucket,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (bucket !== null) {
|
if (bucket !== null) {
|
||||||
this.rateLimits.set(bucket, {
|
this.rateLimits.set(bucket, {
|
||||||
url: 'global',
|
url: 'global',
|
||||||
resetAt: reset,
|
resetAt: reset,
|
||||||
bucket
|
bucket,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,32 +258,46 @@ export class RESTManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleStatusCode(
|
async handleStatusCode(
|
||||||
response: Response, body: any, data: { [key: string]: any }
|
response: Response,
|
||||||
|
body: any,
|
||||||
|
data: { [key: string]: any }
|
||||||
): Promise<undefined> {
|
): Promise<undefined> {
|
||||||
const status = response.status
|
const status = response.status
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(status >= 200 && status < 400)
|
(status >= 200 && status < 400) ||
|
||||||
|| status === HttpResponseCode.NoContent
|
status === HttpResponseCode.NoContent ||
|
||||||
|| status === HttpResponseCode.TooManyRequests
|
status === HttpResponseCode.TooManyRequests
|
||||||
) return
|
)
|
||||||
|
return
|
||||||
|
|
||||||
let text: undefined | string = Deno.inspect(body.errors === undefined ? body : body.errors)
|
let text: undefined | string = Deno.inspect(
|
||||||
|
body.errors === undefined ? body : body.errors
|
||||||
|
)
|
||||||
if (text === 'undefined') text = undefined
|
if (text === 'undefined') text = undefined
|
||||||
|
|
||||||
if (status === HttpResponseCode.Unauthorized)
|
if (status === HttpResponseCode.Unauthorized)
|
||||||
throw new DiscordAPIError(`Request was not successful (Unauthorized). Invalid Token.\n${text}`)
|
throw new DiscordAPIError(
|
||||||
|
`Request was not successful (Unauthorized). Invalid Token.\n${text}`
|
||||||
|
)
|
||||||
|
|
||||||
// At this point we know it is error
|
// At this point we know it is error
|
||||||
let error = { url: response.url, status, method: data.method, body: data.body }
|
let error = {
|
||||||
|
url: response.url,
|
||||||
|
status,
|
||||||
|
method: data.method,
|
||||||
|
body: data.body,
|
||||||
|
}
|
||||||
if (body !== undefined) error = Object.assign(error, body)
|
if (body !== undefined) error = Object.assign(error, body)
|
||||||
|
|
||||||
if ([
|
if (
|
||||||
HttpResponseCode.BadRequest,
|
[
|
||||||
HttpResponseCode.NotFound,
|
HttpResponseCode.BadRequest,
|
||||||
HttpResponseCode.Forbidden,
|
HttpResponseCode.NotFound,
|
||||||
HttpResponseCode.MethodNotAllowed
|
HttpResponseCode.Forbidden,
|
||||||
].includes(status)) {
|
HttpResponseCode.MethodNotAllowed,
|
||||||
|
].includes(status)
|
||||||
|
) {
|
||||||
throw new DiscordAPIError(Deno.inspect(error))
|
throw new DiscordAPIError(Deno.inspect(error))
|
||||||
} else if (status === HttpResponseCode.GatewayUnavailable) {
|
} else if (status === HttpResponseCode.GatewayUnavailable) {
|
||||||
throw new DiscordAPIError(Deno.inspect(error))
|
throw new DiscordAPIError(Deno.inspect(error))
|
||||||
|
@ -291,7 +310,7 @@ export class RESTManager {
|
||||||
body?: unknown,
|
body?: unknown,
|
||||||
maxRetries = 0,
|
maxRetries = 0,
|
||||||
bucket?: string | null,
|
bucket?: string | null,
|
||||||
rawResponse?: boolean,
|
rawResponse?: boolean
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
const onComplete = async (): Promise<undefined | any> => {
|
const onComplete = async (): Promise<undefined | any> => {
|
||||||
|
@ -301,20 +320,20 @@ export class RESTManager {
|
||||||
return {
|
return {
|
||||||
rateLimited: rateLimitResetIn,
|
rateLimited: rateLimitResetIn,
|
||||||
before: true,
|
before: true,
|
||||||
bucket
|
bucket,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const query =
|
const query =
|
||||||
method === 'get' && body !== undefined
|
method === 'get' && body !== undefined
|
||||||
? Object.entries(body as any)
|
? Object.entries(body as any)
|
||||||
.map(
|
.map(
|
||||||
([key, value]) =>
|
([key, value]) =>
|
||||||
`${encodeURIComponent(key)}=${encodeURIComponent(
|
`${encodeURIComponent(key)}=${encodeURIComponent(
|
||||||
value as any
|
value as any
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
.join('&')
|
.join('&')
|
||||||
: ''
|
: ''
|
||||||
let urlToUse =
|
let urlToUse =
|
||||||
method === 'get' && query !== '' ? `${url}?${query}` : url
|
method === 'get' && query !== '' ? `${url}?${query}` : url
|
||||||
|
@ -329,7 +348,10 @@ export class RESTManager {
|
||||||
const response = await fetch(urlToUse, requestData)
|
const response = await fetch(urlToUse, requestData)
|
||||||
const bucketFromHeaders = this.processHeaders(url, response.headers)
|
const bucketFromHeaders = this.processHeaders(url, response.headers)
|
||||||
|
|
||||||
if (response.status === 204) return resolve(rawResponse === true ? { response, body: null } : undefined)
|
if (response.status === 204)
|
||||||
|
return resolve(
|
||||||
|
rawResponse === true ? { response, body: null } : undefined
|
||||||
|
)
|
||||||
|
|
||||||
const json: any = await response.json()
|
const json: any = await response.json()
|
||||||
await this.handleStatusCode(response, json, requestData)
|
await this.handleStatusCode(response, json, requestData)
|
||||||
|
@ -345,7 +367,7 @@ export class RESTManager {
|
||||||
return {
|
return {
|
||||||
rateLimited: json.retry_after,
|
rateLimited: json.retry_after,
|
||||||
before: false,
|
before: false,
|
||||||
bucket: bucketFromHeaders
|
bucket: bucketFromHeaders,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resolve(rawResponse === true ? { response, body: json } : json)
|
return resolve(rawResponse === true ? { response, body: json } : json)
|
||||||
|
@ -357,7 +379,7 @@ export class RESTManager {
|
||||||
this.queue({
|
this.queue({
|
||||||
onComplete,
|
onComplete,
|
||||||
bucket,
|
bucket,
|
||||||
url
|
url,
|
||||||
})
|
})
|
||||||
if (!this.processing) {
|
if (!this.processing) {
|
||||||
this.processing = true
|
this.processing = true
|
||||||
|
@ -376,23 +398,53 @@ export class RESTManager {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> {
|
async get(
|
||||||
|
url: string,
|
||||||
|
body?: unknown,
|
||||||
|
maxRetries = 0,
|
||||||
|
bucket?: string | null,
|
||||||
|
rawResponse?: boolean
|
||||||
|
): Promise<any> {
|
||||||
return await this.make('get', url, body, maxRetries, bucket, rawResponse)
|
return await this.make('get', url, body, maxRetries, bucket, rawResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
async post(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> {
|
async post(
|
||||||
|
url: string,
|
||||||
|
body?: unknown,
|
||||||
|
maxRetries = 0,
|
||||||
|
bucket?: string | null,
|
||||||
|
rawResponse?: boolean
|
||||||
|
): Promise<any> {
|
||||||
return await this.make('post', url, body, maxRetries, bucket, rawResponse)
|
return await this.make('post', url, body, maxRetries, bucket, rawResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> {
|
async delete(
|
||||||
|
url: string,
|
||||||
|
body?: unknown,
|
||||||
|
maxRetries = 0,
|
||||||
|
bucket?: string | null,
|
||||||
|
rawResponse?: boolean
|
||||||
|
): Promise<any> {
|
||||||
return await this.make('delete', url, body, maxRetries, bucket, rawResponse)
|
return await this.make('delete', url, body, maxRetries, bucket, rawResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
async patch(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> {
|
async patch(
|
||||||
|
url: string,
|
||||||
|
body?: unknown,
|
||||||
|
maxRetries = 0,
|
||||||
|
bucket?: string | null,
|
||||||
|
rawResponse?: boolean
|
||||||
|
): Promise<any> {
|
||||||
return await this.make('patch', url, body, maxRetries, bucket, rawResponse)
|
return await this.make('patch', url, body, maxRetries, bucket, rawResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(url: string, body?: unknown, maxRetries = 0, bucket?: string | null, rawResponse?: boolean): Promise<any> {
|
async put(
|
||||||
|
url: string,
|
||||||
|
body?: unknown,
|
||||||
|
maxRetries = 0,
|
||||||
|
bucket?: string | null,
|
||||||
|
rawResponse?: boolean
|
||||||
|
): Promise<any> {
|
||||||
return await this.make('put', url, body, maxRetries, bucket, rawResponse)
|
return await this.make('put', url, body, maxRetries, bucket, rawResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
|
import { Guild } from "../structures/guild.ts"
|
||||||
|
import { VoiceChannel } from "../structures/guildVoiceChannel.ts"
|
||||||
import { Client } from './client.ts'
|
import { Client } from './client.ts'
|
||||||
|
|
||||||
|
export interface VoiceOptions {
|
||||||
|
guild: Guild,
|
||||||
|
channel: VoiceChannel
|
||||||
|
}
|
||||||
|
|
||||||
export class VoiceClient {
|
export class VoiceClient {
|
||||||
client: Client
|
client: Client
|
||||||
|
ws?: WebSocket
|
||||||
|
guild: Guild
|
||||||
|
channel: VoiceChannel
|
||||||
|
|
||||||
constructor(client: Client) {
|
constructor(client: Client, options: VoiceOptions) {
|
||||||
this.client = client
|
this.client = client
|
||||||
|
this.guild = options.guild
|
||||||
|
this.channel = options.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(): Promise<VoiceClient> {
|
||||||
|
// TODO(DjDeveloperr): Actually understand what the hell docs say
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import {
|
import {
|
||||||
|
GuildBanPayload,
|
||||||
GuildFeatures,
|
GuildFeatures,
|
||||||
GuildIntegrationPayload,
|
GuildIntegrationPayload,
|
||||||
GuildPayload,
|
GuildPayload,
|
||||||
IntegrationAccountPayload,
|
IntegrationAccountPayload,
|
||||||
IntegrationExpireBehavior
|
IntegrationExpireBehavior,
|
||||||
} from '../types/guild.ts'
|
} from '../types/guild.ts'
|
||||||
import { PresenceUpdatePayload } from '../types/gateway.ts'
|
import { PresenceUpdatePayload } from '../types/gateway.ts'
|
||||||
import { Base } from './base.ts'
|
import { Base } from './base.ts'
|
||||||
import { VoiceState } from './voiceState.ts'
|
|
||||||
import { RolesManager } from '../managers/roles.ts'
|
import { RolesManager } from '../managers/roles.ts'
|
||||||
import { GuildChannelsManager } from '../managers/guildChannels.ts'
|
import { GuildChannelsManager } from '../managers/guildChannels.ts'
|
||||||
import { MembersManager } from '../managers/members.ts'
|
import { MembersManager } from '../managers/members.ts'
|
||||||
|
@ -17,7 +17,100 @@ import { GuildEmojisManager } from '../managers/guildEmojis.ts'
|
||||||
import { Member } from './member.ts'
|
import { Member } from './member.ts'
|
||||||
import { User } from './user.ts'
|
import { User } from './user.ts'
|
||||||
import { Application } from './application.ts'
|
import { Application } from './application.ts'
|
||||||
import { GUILD_INTEGRATIONS } from '../types/endpoint.ts'
|
import { GUILD_BAN, GUILD_BANS, GUILD_INTEGRATIONS } from '../types/endpoint.ts'
|
||||||
|
import { GuildVoiceStatesManager } from '../managers/guildVoiceStates.ts'
|
||||||
|
import { RequestMembersOptions } from '../gateway/index.ts'
|
||||||
|
|
||||||
|
export class GuildBan extends Base {
|
||||||
|
guild: Guild
|
||||||
|
reason?: string
|
||||||
|
user: User
|
||||||
|
|
||||||
|
constructor(client: Client, data: GuildBanPayload, guild: Guild) {
|
||||||
|
super(client, data)
|
||||||
|
this.guild = guild
|
||||||
|
this.reason = data.reason === null ? undefined : data.reason
|
||||||
|
this.user = new User(client, data.user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GuildBans {
|
||||||
|
client: Client
|
||||||
|
guild: Guild
|
||||||
|
|
||||||
|
constructor(client: Client, guild: Guild) {
|
||||||
|
this.client = client
|
||||||
|
this.guild = guild
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all bans in the Guild.
|
||||||
|
*/
|
||||||
|
async all(): Promise<GuildBan[]> {
|
||||||
|
const res = await this.client.rest.get(GUILD_BANS(this.guild.id))
|
||||||
|
if (typeof res !== 'object' || !Array.isArray(res))
|
||||||
|
throw new Error('Failed to fetch Guild Bans')
|
||||||
|
|
||||||
|
const bans = (res as GuildBanPayload[]).map(
|
||||||
|
(ban) => new GuildBan(this.client, ban, this.guild)
|
||||||
|
)
|
||||||
|
return bans
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ban details of a User if any.
|
||||||
|
* @param user User to get ban of, ID or User object.
|
||||||
|
*/
|
||||||
|
async get(user: string | User): Promise<GuildBan> {
|
||||||
|
const res = await this.client.rest.get(
|
||||||
|
GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id)
|
||||||
|
)
|
||||||
|
if (typeof res !== 'object') throw new Error('Failed to fetch Guild Ban')
|
||||||
|
return new GuildBan(this.client, res, this.guild)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ban a User.
|
||||||
|
* @param user User to ban, ID or User object.
|
||||||
|
* @param reason Reason for the Ban.
|
||||||
|
* @param deleteMessagesDays Delete Old Messages? If yes, how much days.
|
||||||
|
*/
|
||||||
|
async add(
|
||||||
|
user: string | User,
|
||||||
|
reason?: string,
|
||||||
|
deleteMessagesDays?: number
|
||||||
|
): Promise<void> {
|
||||||
|
const res = await this.client.rest.put(
|
||||||
|
GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id),
|
||||||
|
{
|
||||||
|
reason,
|
||||||
|
delete_message_days: deleteMessagesDays,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.status !== 204) throw new Error('Failed to Add Guild Ban')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unban (remove ban from) a User.
|
||||||
|
* @param user User to unban, ID or User object.
|
||||||
|
*/
|
||||||
|
async remove(user: string | User): Promise<boolean> {
|
||||||
|
const res = await this.client.rest.delete(
|
||||||
|
GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id),
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.status !== 204) return false
|
||||||
|
else return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Guild extends Base {
|
export class Guild extends Base {
|
||||||
id: string
|
id: string
|
||||||
|
@ -49,7 +142,7 @@ export class Guild extends Base {
|
||||||
large?: boolean
|
large?: boolean
|
||||||
unavailable: boolean
|
unavailable: boolean
|
||||||
memberCount?: number
|
memberCount?: number
|
||||||
voiceStates?: VoiceState[]
|
voiceStates: GuildVoiceStatesManager
|
||||||
members: MembersManager
|
members: MembersManager
|
||||||
channels: GuildChannelsManager
|
channels: GuildChannelsManager
|
||||||
presences?: PresenceUpdatePayload[]
|
presences?: PresenceUpdatePayload[]
|
||||||
|
@ -65,12 +158,15 @@ export class Guild extends Base {
|
||||||
maxVideoChannelUsers?: number
|
maxVideoChannelUsers?: number
|
||||||
approximateNumberCount?: number
|
approximateNumberCount?: number
|
||||||
approximatePresenceCount?: number
|
approximatePresenceCount?: number
|
||||||
|
bans: GuildBans
|
||||||
|
|
||||||
constructor (client: Client, data: GuildPayload) {
|
constructor(client: Client, data: GuildPayload) {
|
||||||
super(client, data)
|
super(client, data)
|
||||||
this.id = data.id
|
this.id = data.id
|
||||||
|
this.bans = new GuildBans(client, this)
|
||||||
this.unavailable = data.unavailable
|
this.unavailable = data.unavailable
|
||||||
this.members = new MembersManager(this.client, this)
|
this.members = new MembersManager(this.client, this)
|
||||||
|
this.voiceStates = new GuildVoiceStatesManager(client, this)
|
||||||
this.channels = new GuildChannelsManager(
|
this.channels = new GuildChannelsManager(
|
||||||
this.client,
|
this.client,
|
||||||
this.client.channels,
|
this.client.channels,
|
||||||
|
@ -96,15 +192,6 @@ export class Guild extends Base {
|
||||||
this.verificationLevel = data.verification_level
|
this.verificationLevel = data.verification_level
|
||||||
this.defaultMessageNotifications = data.default_message_notifications
|
this.defaultMessageNotifications = data.default_message_notifications
|
||||||
this.explicitContentFilter = data.explicit_content_filter
|
this.explicitContentFilter = data.explicit_content_filter
|
||||||
// this.roles = data.roles.map(
|
|
||||||
// v => cache.get('role', v.id) ?? new Role(client, v)
|
|
||||||
// )
|
|
||||||
// data.roles.forEach(role => {
|
|
||||||
// this.roles.set(role.id, new Role(client, role))
|
|
||||||
// })
|
|
||||||
// this.emojis = data.emojis.map(
|
|
||||||
// v => cache.get('emoji', v.id) ?? new Emoji(client, v)
|
|
||||||
// )
|
|
||||||
this.features = data.features
|
this.features = data.features
|
||||||
this.mfaLevel = data.mfa_level
|
this.mfaLevel = data.mfa_level
|
||||||
this.systemChannelID = data.system_channel_id
|
this.systemChannelID = data.system_channel_id
|
||||||
|
@ -113,20 +200,6 @@ export class Guild extends Base {
|
||||||
this.joinedAt = data.joined_at
|
this.joinedAt = data.joined_at
|
||||||
this.large = data.large
|
this.large = data.large
|
||||||
this.memberCount = data.member_count
|
this.memberCount = data.member_count
|
||||||
// TODO: Cache in Gateway Event code
|
|
||||||
// this.voiceStates = data.voice_states?.map(
|
|
||||||
// v =>
|
|
||||||
// cache.get('voiceState', `${v.guild_id}:${v.user_id}`) ??
|
|
||||||
// new VoiceState(client, v)
|
|
||||||
// )
|
|
||||||
// this.members = data.members?.map(
|
|
||||||
// v =>
|
|
||||||
// cache.get('member', `${this.id}:${v.user.id}`) ??
|
|
||||||
// new Member(client, v)
|
|
||||||
// )
|
|
||||||
// this.channels = data.channels?.map(
|
|
||||||
// v => cache.get('channel', v.id) ?? getChannelByType(this.client, v)
|
|
||||||
// )
|
|
||||||
this.presences = data.presences
|
this.presences = data.presences
|
||||||
this.maxPresences = data.max_presences
|
this.maxPresences = data.max_presences
|
||||||
this.maxMembers = data.max_members
|
this.maxMembers = data.max_members
|
||||||
|
@ -143,7 +216,7 @@ export class Guild extends Base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readFromData (data: GuildPayload): void {
|
protected readFromData(data: GuildPayload): void {
|
||||||
super.readFromData(data)
|
super.readFromData(data)
|
||||||
this.id = data.id ?? this.id
|
this.id = data.id ?? this.id
|
||||||
this.unavailable = data.unavailable ?? this.unavailable
|
this.unavailable = data.unavailable ?? this.unavailable
|
||||||
|
@ -167,14 +240,6 @@ export class Guild extends Base {
|
||||||
data.default_message_notifications ?? this.defaultMessageNotifications
|
data.default_message_notifications ?? this.defaultMessageNotifications
|
||||||
this.explicitContentFilter =
|
this.explicitContentFilter =
|
||||||
data.explicit_content_filter ?? this.explicitContentFilter
|
data.explicit_content_filter ?? this.explicitContentFilter
|
||||||
// this.roles =
|
|
||||||
// data.roles.map(
|
|
||||||
// v => cache.get('role', v.id) ?? new Role(this.client, v)
|
|
||||||
// ) ?? this.roles
|
|
||||||
// this.emojis =
|
|
||||||
// data.emojis.map(
|
|
||||||
// v => cache.get('emoji', v.id) ?? new Emoji(this.client, v)
|
|
||||||
// ) ?? this.emojis
|
|
||||||
this.features = data.features ?? this.features
|
this.features = data.features ?? this.features
|
||||||
this.mfaLevel = data.mfa_level ?? this.mfaLevel
|
this.mfaLevel = data.mfa_level ?? this.mfaLevel
|
||||||
this.systemChannelID = data.system_channel_id ?? this.systemChannelID
|
this.systemChannelID = data.system_channel_id ?? this.systemChannelID
|
||||||
|
@ -184,22 +249,6 @@ export class Guild extends Base {
|
||||||
this.joinedAt = data.joined_at ?? this.joinedAt
|
this.joinedAt = data.joined_at ?? this.joinedAt
|
||||||
this.large = data.large ?? this.large
|
this.large = data.large ?? this.large
|
||||||
this.memberCount = data.member_count ?? this.memberCount
|
this.memberCount = data.member_count ?? this.memberCount
|
||||||
// this.voiceStates =
|
|
||||||
// data.voice_states?.map(
|
|
||||||
// v =>
|
|
||||||
// cache.get('voiceState', `${v.guild_id}:${v.user_id}`) ??
|
|
||||||
// new VoiceState(this.client, v)
|
|
||||||
// ) ?? this.voiceStates
|
|
||||||
// this.members =
|
|
||||||
// data.members?.map(
|
|
||||||
// v =>
|
|
||||||
// cache.get('member', `${this.id}:${v.user.id}`) ??
|
|
||||||
// new Member(this.client, v)
|
|
||||||
// ) ?? this.members
|
|
||||||
// this.channels =
|
|
||||||
// data.channels?.map(
|
|
||||||
// v => cache.get('channel', v.id) ?? getChannelByType(this.client, v, this)
|
|
||||||
// ) ?? this.members
|
|
||||||
this.presences = data.presences ?? this.presences
|
this.presences = data.presences ?? this.presences
|
||||||
this.maxPresences = data.max_presences ?? this.maxPresences
|
this.maxPresences = data.max_presences ?? this.maxPresences
|
||||||
this.maxMembers = data.max_members ?? this.maxMembers
|
this.maxMembers = data.max_members ?? this.maxMembers
|
||||||
|
@ -221,22 +270,65 @@ export class Guild extends Base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEveryoneRole (): Promise<Role> {
|
/**
|
||||||
|
* Get Everyone role of the Guild
|
||||||
|
*/
|
||||||
|
async getEveryoneRole(): Promise<Role> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
return (await this.roles.get(this.id)) as Role
|
return (await this.roles.get(this.id)) as Role
|
||||||
}
|
}
|
||||||
|
|
||||||
async me (): Promise<Member> {
|
/**
|
||||||
|
* Get current client's member in the Guild
|
||||||
|
*/
|
||||||
|
async me(): Promise<Member> {
|
||||||
const get = await this.members.get(this.client.user?.id as string)
|
const get = await this.members.get(this.client.user?.id as string)
|
||||||
if (get === undefined) throw new Error('Guild#me is not cached')
|
if (get === undefined) throw new Error('Guild#me is not cached')
|
||||||
return get
|
return get
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchIntegrations (): Promise<GuildIntegration[]> {
|
/**
|
||||||
|
* Fetch Guild's Integrations (Webhooks, Bots, etc.)
|
||||||
|
*/
|
||||||
|
async fetchIntegrations(): Promise<GuildIntegration[]> {
|
||||||
const raw = (await this.client.rest.get(
|
const raw = (await this.client.rest.get(
|
||||||
GUILD_INTEGRATIONS(this.id)
|
GUILD_INTEGRATIONS(this.id)
|
||||||
)) as GuildIntegrationPayload[]
|
)) as GuildIntegrationPayload[]
|
||||||
return raw.map(e => new GuildIntegration(this.client, e))
|
return raw.map((e) => new GuildIntegration(this.client, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunk the Guild Members, i.e. cache them.
|
||||||
|
* @param options Options regarding the Members Request
|
||||||
|
* @param wait Whether to wait for all Members to come before resolving Promise or not.
|
||||||
|
* @param timeout Configurable timeout to cancel the wait to safely remove listener.
|
||||||
|
*/
|
||||||
|
async chunk(
|
||||||
|
options: RequestMembersOptions,
|
||||||
|
wait: boolean = false,
|
||||||
|
timeout: number = 60000
|
||||||
|
): Promise<Guild> {
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
this.client.gateway?.requestMembers(this.id, options)
|
||||||
|
if (!wait) return resolve(this)
|
||||||
|
else {
|
||||||
|
let chunked = false
|
||||||
|
const listener = (guild: Guild): void => {
|
||||||
|
if (guild.id === this.id) {
|
||||||
|
chunked = true
|
||||||
|
this.client.removeListener('guildMembersChunked', listener)
|
||||||
|
resolve(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.client.on('guildMembersChunked', listener)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!chunked) {
|
||||||
|
this.client.removeListener('guildMembersChunked', listener)
|
||||||
|
}
|
||||||
|
}, timeout)
|
||||||
|
}
|
||||||
|
resolve(this)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +349,7 @@ export class GuildIntegration extends Base {
|
||||||
revoked?: boolean
|
revoked?: boolean
|
||||||
application?: Application
|
application?: Application
|
||||||
|
|
||||||
constructor (client: Client, data: GuildIntegrationPayload) {
|
constructor(client: Client, data: GuildIntegrationPayload) {
|
||||||
super(client, data)
|
super(client, data)
|
||||||
|
|
||||||
this.id = data.id
|
this.id = data.id
|
||||||
|
|
|
@ -1,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,7 +48,15 @@ export class Member extends Base {
|
||||||
else this.permissions = new Permissions(Permissions.DEFAULT)
|
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)
|
super.readFromData(data.user)
|
||||||
this.nick = data.nick ?? this.nick
|
this.nick = data.nick ?? this.nick
|
||||||
this.joinedAt = data.joined_at ?? this.joinedAt
|
this.joinedAt = data.joined_at ?? this.joinedAt
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,14 @@ client.on('typingStart', (user, channel, at, guildData) => {
|
||||||
console.log(`${user.tag} started typing in ${channel.id} at ${at}${guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : ''}`)
|
console.log(`${user.tag} started typing in ${channel.id} at ${at}${guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : ''}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
client.on('voiceStateAdd', (state) => {
|
||||||
|
console.log('VC Join', state)
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on('voiceStateRemove', (state) => {
|
||||||
|
console.log('VC Leave', state)
|
||||||
|
})
|
||||||
|
|
||||||
// client.on('raw', (evt: string) => console.log(`EVENT: ${evt}`))
|
// client.on('raw', (evt: string) => console.log(`EVENT: ${evt}`))
|
||||||
|
|
||||||
const files = Deno.readDirSync('./src/test/cmds')
|
const files = Deno.readDirSync('./src/test/cmds')
|
||||||
|
|
|
@ -104,6 +104,7 @@ export enum GatewayEvents {
|
||||||
Typing_Start = 'TYPING_START',
|
Typing_Start = 'TYPING_START',
|
||||||
User_Update = 'USER_UPDATE',
|
User_Update = 'USER_UPDATE',
|
||||||
Voice_Server_Update = 'VOICE_SERVER_UPDATE',
|
Voice_Server_Update = 'VOICE_SERVER_UPDATE',
|
||||||
|
Voice_State_Update = 'VOICE_STATE_UPDATE',
|
||||||
Webhooks_Update = 'WEBHOOKS_UPDATE'
|
Webhooks_Update = 'WEBHOOKS_UPDATE'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ApplicationPayload } from "./application.ts"
|
import { ApplicationPayload } from './application.ts'
|
||||||
import { ChannelPayload } from './channel.ts'
|
import { ChannelPayload } from './channel.ts'
|
||||||
import { EmojiPayload } from './emoji.ts'
|
import { EmojiPayload } from './emoji.ts'
|
||||||
import { PresenceUpdatePayload } from './gateway.ts'
|
import { PresenceUpdatePayload } from './gateway.ts'
|
||||||
|
@ -66,18 +66,18 @@ export interface MemberPayload {
|
||||||
|
|
||||||
export enum MessageNotification {
|
export enum MessageNotification {
|
||||||
ALL_MESSAGES = 0,
|
ALL_MESSAGES = 0,
|
||||||
ONLY_MENTIONS = 1
|
ONLY_MENTIONS = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ContentFilter {
|
export enum ContentFilter {
|
||||||
DISABLED = 0,
|
DISABLED = 0,
|
||||||
MEMBERS_WITHOUT_ROLES = 1,
|
MEMBERS_WITHOUT_ROLES = 1,
|
||||||
ALL_MEMBERS = 3
|
ALL_MEMBERS = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MFA {
|
export enum MFA {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
ELEVATED = 1
|
ELEVATED = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Verification {
|
export enum Verification {
|
||||||
|
@ -85,19 +85,19 @@ export enum Verification {
|
||||||
LOW = 1,
|
LOW = 1,
|
||||||
MEDIUM = 2,
|
MEDIUM = 2,
|
||||||
HIGH = 3,
|
HIGH = 3,
|
||||||
VERY_HIGH = 4
|
VERY_HIGH = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PremiumTier {
|
export enum PremiumTier {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
TIER_1 = 1,
|
TIER_1 = 1,
|
||||||
TIER_2 = 2,
|
TIER_2 = 2,
|
||||||
TIER_3 = 3
|
TIER_3 = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SystemChannelFlags {
|
export enum SystemChannelFlags {
|
||||||
SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0,
|
SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0,
|
||||||
SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1
|
SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GuildFeatures =
|
export type GuildFeatures =
|
||||||
|
@ -116,7 +116,7 @@ export type GuildFeatures =
|
||||||
|
|
||||||
export enum IntegrationExpireBehavior {
|
export enum IntegrationExpireBehavior {
|
||||||
REMOVE_ROLE = 0,
|
REMOVE_ROLE = 0,
|
||||||
KICK = 1
|
KICK = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IntegrationAccountPayload {
|
export interface IntegrationAccountPayload {
|
||||||
|
@ -141,3 +141,8 @@ export interface GuildIntegrationPayload {
|
||||||
revoked?: boolean
|
revoked?: boolean
|
||||||
application?: ApplicationPayload
|
application?: ApplicationPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GuildBanPayload {
|
||||||
|
reason: string | null
|
||||||
|
user: UserPayload
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue