Merge branch 'main' into main
This commit is contained in:
commit
2af8e15452
40 changed files with 813 additions and 1080 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -109,4 +109,7 @@ yarn.lock
|
|||
|
||||
# PRIVACY XDDDD
|
||||
src/test/config.ts
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
# macOS is shit xD
|
||||
**/.DS_Store
|
||||
|
|
33
CONTRIBUTING.md
Normal file
33
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Welcome!
|
||||
|
||||
This document is for people who want to contribute to this repository!
|
||||
|
||||
## Code Style
|
||||
|
||||
We use [standard.js](https://standardjs.org) with [eslint](https://eslint.org) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint).
|
||||
So please don't make as lint errors as possible. There're many rules in standard.js but the highlight things are:
|
||||
|
||||
- Use `camelCase` for function names, variables, etc.
|
||||
- Use `PascalCase` for class names.
|
||||
- Add return types on function. Ex:
|
||||
|
||||
```ts
|
||||
const example = (): void => {}
|
||||
```
|
||||
|
||||
- Do not make unused variables or unused imports.
|
||||
|
||||
These are not on standard.js but we want you to follow.
|
||||
|
||||
- Make names to simple but understandable for someone whose English is not a primary language.
|
||||
|
||||
## File Name Style
|
||||
|
||||
Nothing much, but please make it as simple as possible, and in `camelCase`.
|
||||
|
||||
## Submitting PR
|
||||
|
||||
When submitting PR, please make the title as simple as possible. Ex: `[Feature improvement]: Cache can now be loaded much faster`
|
||||
Also, please make it understandable for someone whose English is not a primary language.
|
||||
|
||||
Thanks!
|
53
README.md
53
README.md
|
@ -1,3 +1,52 @@
|
|||
# discord.deno
|
||||
# discord-deno
|
||||
|
||||
## Feature
|
||||
![banner](images/discord-deno.png)
|
||||
|
||||
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
|
||||
|
||||
Discord Deno API that is easy to use
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Usage](#usage)
|
||||
- [Docs](#docs)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { Client } from 'https://deno.land/x/discord-deno/models/client.ts'
|
||||
import { Message } from 'https://deno.land/x/discord-deno/structures/message.ts'
|
||||
|
||||
const bot = new Client()
|
||||
|
||||
bot.on('messageCreate', (msg: Message): void => {
|
||||
if (msg.content === '!ping') {
|
||||
msg.channel.send(`Pong! ping: ${bot.ping}`)
|
||||
}
|
||||
})
|
||||
|
||||
bot.connect(TOKEN, [GatewayIntents.GUILD_MESSAGES])
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
Not made yet
|
||||
|
||||
## Maintainers
|
||||
|
||||
[@Helloyunho](https://github.com/Helloyunho)
|
||||
|
||||
## Contributing
|
||||
|
||||
See [the contributing file](CONTRIBUTING.md)!
|
||||
|
||||
PRs accepted.
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
MIT © 2020 Helloyunho
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
import getChannelByType from '../../utils/getChannelByType.ts'
|
||||
import { ChannelPayload } from '../../types/channel.ts'
|
||||
|
||||
export const channelCreate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: any
|
||||
d: ChannelPayload
|
||||
) => {
|
||||
const channel = getChannelByType(gateway.client, d)
|
||||
if (channel !== undefined) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
import { Channel } from '../../structures/channel.ts'
|
||||
import { ChannelPayload } from '../../types/channel.ts'
|
||||
|
||||
export const channelDelete: GatewayEventHandler = async(
|
||||
export const channelDelete: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: any
|
||||
d: ChannelPayload
|
||||
) => {
|
||||
const channel: Channel | void = await gateway.client.channels.get(d.id)
|
||||
if (channel !== undefined) {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
import cache from '../../models/cache.ts'
|
||||
import { TextChannel } from '../../structures/textChannel.ts'
|
||||
import { ChannelPayload } from "../../types/channel.ts"
|
||||
import { ChannelPinsUpdatePayload } from '../../types/gateway.ts'
|
||||
|
||||
export const channelPinsUpdate: GatewayEventHandler = async(
|
||||
export const channelPinsUpdate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: any
|
||||
d: ChannelPinsUpdatePayload
|
||||
) => {
|
||||
const after: TextChannel | void = await gateway.client.channels.get<TextChannel>(d.channel_id)
|
||||
if (after !== undefined) {
|
||||
const before = after.refreshFromData({
|
||||
last_pin_timestamp: d.last_pin_timestamp
|
||||
})
|
||||
const raw = await gateway.client.channels._get(d.channel_id) ;
|
||||
await gateway.client.channels.set(after.id, Object.assign(raw, { last_pin_timestamp: d.last_pin_timestamp }))
|
||||
const raw = await gateway.client.channels._get(d.channel_id)
|
||||
await gateway.client.channels.set(
|
||||
after.id,
|
||||
Object.assign(raw, { last_pin_timestamp: d.last_pin_timestamp })
|
||||
)
|
||||
gateway.client.emit('channelPinsUpdate', before, after)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { Channel } from '../../structures/channel.ts'
|
||||
import { Guild } from "../../structures/guild.ts"
|
||||
import { ChannelPayload } from '../../types/channel.ts'
|
||||
import getChannelByType from '../../utils/getChannelByType.ts'
|
||||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
|
||||
export const channelUpdate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: any
|
||||
d: ChannelPayload
|
||||
) => {
|
||||
const oldChannel: Channel | void = await gateway.client.channels.get(d.id)
|
||||
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
import cache from '../../models/cache.ts'
|
||||
import { Guild } from '../../structures/guild.ts'
|
||||
import { User } from '../../structures/user.ts'
|
||||
import { GuildBanAddPayload } from '../../types/gateway.ts'
|
||||
|
||||
export const guildBanAdd: GatewayEventHandler = (gateway: Gateway, d: any) => {
|
||||
const guild: Guild = cache.get('guild', d.guild_id)
|
||||
export const guildBanAdd: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: GuildBanAddPayload
|
||||
) => {
|
||||
const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id)
|
||||
const user: User =
|
||||
cache.get('user', d.user.id) ?? new User(gateway.client, d.user)
|
||||
(await gateway.client.users.get(d.user.id)) ??
|
||||
new User(gateway.client, d.user)
|
||||
|
||||
if (guild !== undefined) {
|
||||
guild.members = guild.members?.filter(member => member.id !== d.user.id)
|
||||
gateway.client.emit('guildBanAdd', guild, user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ import { Gateway, GatewayEventHandler } from '../index.ts'
|
|||
import cache from '../../models/cache.ts'
|
||||
import { Guild } from '../../structures/guild.ts'
|
||||
import { User } from '../../structures/user.ts'
|
||||
import { GuildBanRemovePayload } from '../../types/gateway.ts'
|
||||
|
||||
export const guildBanRemove: GatewayEventHandler = (
|
||||
gateway: Gateway,
|
||||
d: any
|
||||
d: GuildBanRemovePayload
|
||||
) => {
|
||||
const guild: Guild = cache.get('guild', d.guild_id)
|
||||
const user: User =
|
||||
|
|
|
@ -7,7 +7,7 @@ import { RolePayload } from "../../types/role.ts"
|
|||
import { RolesManager } from "../../managers/RolesManager.ts"
|
||||
|
||||
export const guildCreate: GatewayEventHandler = async(gateway: Gateway, d: GuildPayload) => {
|
||||
let guild: Guild | void = await gateway.client.guilds.get(d.id)
|
||||
let guild: Guild | undefined = await gateway.client.guilds.get(d.id)
|
||||
if (guild !== undefined) {
|
||||
// It was just lazy load, so we don't fire the event as its gonna fire for every guild bot is in
|
||||
await gateway.client.guilds.set(d.id, d)
|
||||
|
@ -47,6 +47,7 @@ export const guildCreate: GatewayEventHandler = async(gateway: Gateway, d: Guild
|
|||
guild.roles = roles
|
||||
}
|
||||
await guild.roles.fromPayload(d.roles)
|
||||
guild = new Guild(gateway.client, d)
|
||||
gateway.client.emit('guildCreate', guild)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Guild } from '../../structures/guild.ts'
|
||||
import { GuildPayload } from '../../types/guild.ts'
|
||||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
|
||||
export const guildDelte: GatewayEventHandler = async (gateway: Gateway, d: any) => {
|
||||
const guild: Guild | void = await gateway.client.guilds.get(d.id)
|
||||
export const guildDelte: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: GuildPayload
|
||||
) => {
|
||||
const guild: Guild | undefined = await gateway.client.guilds.get(d.id)
|
||||
|
||||
if (guild !== undefined) {
|
||||
guild.refreshFromData(d)
|
||||
|
|
14
src/gateway/handlers/guildEmojiUpdate.ts
Normal file
14
src/gateway/handlers/guildEmojiUpdate.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import cache from '../../models/cache.ts'
|
||||
import { Guild } from '../../structures/guild.ts'
|
||||
import { GuildEmojiUpdatePayload } from '../../types/gatewayTypes.ts'
|
||||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
|
||||
const guildEmojiUpdate: GatewayEventHandler = (
|
||||
gateway: Gateway,
|
||||
d: GuildEmojiUpdatePayload
|
||||
) => {
|
||||
const guild: Guild = cache.get('guild', d.guild_id)
|
||||
if (guild !== undefined) {
|
||||
const emojis = guild.emojis
|
||||
}
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
import { Guild } from '../../structures/guild.ts'
|
||||
import { GuildPayload } from '../../types/guild.ts'
|
||||
|
||||
export const guildUpdate: GatewayEventHandler = async(gateway: Gateway, d: any) => {
|
||||
const before: Guild | void = await gateway.client.guilds.get(d.id)
|
||||
if(!before) return
|
||||
export const guildUpdate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: GuildPayload
|
||||
) => {
|
||||
const after: Guild | undefined = await gateway.client.guilds.get(d.id)
|
||||
if (after === undefined) return
|
||||
const before = after.refreshFromData(d)
|
||||
await gateway.client.guilds.set(d.id, d)
|
||||
const after: Guild | void = await gateway.client.guilds.get(d.id)
|
||||
gateway.client.emit('guildUpdate', before, after)
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import { Channel } from "../../structures/channel.ts"
|
||||
import { Message } from "../../structures/message.ts"
|
||||
import { MessageMentions } from "../../structures/MessageMentions.ts"
|
||||
import { TextChannel } from "../../structures/textChannel.ts"
|
||||
import { User } from "../../structures/user.ts"
|
||||
import { MessagePayload } from "../../types/channel.ts"
|
||||
import { Channel } from '../../structures/channel.ts'
|
||||
import { Message } from '../../structures/message.ts'
|
||||
import { MessageMentions } from '../../structures/MessageMentions.ts'
|
||||
import { TextChannel } from '../../structures/textChannel.ts'
|
||||
import { User } from '../../structures/user.ts'
|
||||
import { MessagePayload } from '../../types/channel.ts'
|
||||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
|
||||
export const messageCreate: GatewayEventHandler = async(
|
||||
export const messageCreate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: MessagePayload
|
||||
) => {
|
||||
let channel = await gateway.client.channels.get<TextChannel>(d.channel_id)
|
||||
// Fetch the channel if not cached
|
||||
if(!channel) channel = (await gateway.client.channels.fetch(d.channel_id) as any) as TextChannel
|
||||
let user = new User(gateway.client, d.author)
|
||||
if (channel === undefined)
|
||||
channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel
|
||||
const user = new User(gateway.client, d.author)
|
||||
await gateway.client.users.set(d.author.id, d.author)
|
||||
let guild
|
||||
if(d.guild_id) {
|
||||
|
|
|
@ -5,7 +5,11 @@ import {
|
|||
DISCORD_API_VERSION
|
||||
} from '../consts/urlsAndVersions.ts'
|
||||
import { GatewayResponse } from '../types/gatewayResponse.ts'
|
||||
import { GatewayOpcodes, GatewayIntents, GatewayCloseCodes } from '../types/gateway.ts'
|
||||
import {
|
||||
GatewayOpcodes,
|
||||
GatewayIntents,
|
||||
GatewayCloseCodes
|
||||
} from '../types/gateway.ts'
|
||||
import { gatewayHandlers } from './handlers/index.ts'
|
||||
import { GATEWAY_BOT } from '../types/endpoint.ts'
|
||||
import { GatewayBotPayload } from "../types/gatewayBot.ts"
|
||||
|
@ -52,7 +56,7 @@ class Gateway {
|
|||
|
||||
private onopen(): void {
|
||||
this.connected = true
|
||||
this.debug("Connected to Gateway!")
|
||||
this.debug('Connected to Gateway!')
|
||||
}
|
||||
|
||||
private async onmessage(event: MessageEvent): Promise<void> {
|
||||
|
@ -70,12 +74,15 @@ class Gateway {
|
|||
switch (op) {
|
||||
case GatewayOpcodes.HELLO:
|
||||
this.heartbeatInterval = d.heartbeat_interval
|
||||
this.debug(`Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}`)
|
||||
this.debug(
|
||||
`Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}`
|
||||
)
|
||||
this.heartbeatIntervalID = setInterval(() => {
|
||||
if (this.heartbeatServerResponded) {
|
||||
this.heartbeatServerResponded = false
|
||||
} else {
|
||||
clearInterval(this.heartbeatIntervalID)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect()
|
||||
return
|
||||
}
|
||||
|
@ -91,7 +98,8 @@ class Gateway {
|
|||
this.sendIdentify(this.client.forceNewSession)
|
||||
this.initialized = true
|
||||
} else {
|
||||
console.log("Calling Resume")
|
||||
console.log('Calling Resume')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.sendResume()
|
||||
}
|
||||
break
|
||||
|
@ -99,13 +107,16 @@ class Gateway {
|
|||
case GatewayOpcodes.HEARTBEAT_ACK:
|
||||
this.heartbeatServerResponded = true
|
||||
this.client.ping = Date.now() - this.lastPingTimestamp
|
||||
this.debug(`Received Heartbeat Ack. Ping Recognized: ${this.client.ping}ms`)
|
||||
this.debug(
|
||||
`Received Heartbeat Ack. Ping Recognized: ${this.client.ping}ms`
|
||||
)
|
||||
break
|
||||
|
||||
case GatewayOpcodes.INVALID_SESSION:
|
||||
// Because we know this gonna be bool
|
||||
this.debug(`Invalid Session! Identifying with forced new session`)
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||
setTimeout(() => this.sendIdentify(true), 3000)
|
||||
break
|
||||
|
||||
|
@ -113,7 +124,7 @@ class Gateway {
|
|||
this.heartbeatServerResponded = true
|
||||
if (s !== null) {
|
||||
this.sequenceID = s
|
||||
await this.cache.set("seq", s)
|
||||
await this.cache.set('seq', s)
|
||||
}
|
||||
if (t !== null && t !== undefined) {
|
||||
const handler = gatewayHandlers[t]
|
||||
|
@ -128,11 +139,12 @@ class Gateway {
|
|||
// this.token = d.token
|
||||
this.sessionID = d.session_id
|
||||
this.sequenceID = d.seq
|
||||
await this.cache.set("seq", d.seq)
|
||||
await this.cache.set("session_id", this.sessionID)
|
||||
await this.cache.set('seq', d.seq)
|
||||
await this.cache.set('session_id', this.sessionID)
|
||||
break
|
||||
}
|
||||
case GatewayOpcodes.RECONNECT: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect()
|
||||
break
|
||||
}
|
||||
|
@ -141,41 +153,46 @@ class Gateway {
|
|||
}
|
||||
}
|
||||
|
||||
private onclose(event: CloseEvent): void {
|
||||
this.debug("Connection Closed with code: " + event.code)
|
||||
private onclose (event: CloseEvent): void {
|
||||
this.debug(`Connection Closed with code: ${event.code}`)
|
||||
|
||||
if (event.code == GatewayCloseCodes.UNKNOWN_ERROR) {
|
||||
this.debug("API has encountered Unknown Error. Reconnecting...")
|
||||
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
|
||||
this.debug('API has encountered Unknown Error. Reconnecting...')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect()
|
||||
} else if (event.code == GatewayCloseCodes.UNKNOWN_OPCODE) {
|
||||
} else if (event.code === GatewayCloseCodes.UNKNOWN_OPCODE) {
|
||||
throw new Error("Unknown OP Code was sent. This shouldn't happen!")
|
||||
} else if (event.code == GatewayCloseCodes.DECODE_ERROR) {
|
||||
} else if (event.code === GatewayCloseCodes.DECODE_ERROR) {
|
||||
throw new Error("Invalid Payload was sent. This shouldn't happen!")
|
||||
} else if (event.code == GatewayCloseCodes.NOT_AUTHENTICATED) {
|
||||
throw new Error("Not Authorized: Payload was sent before Identifying.")
|
||||
} else if (event.code == GatewayCloseCodes.AUTHENTICATION_FAILED) {
|
||||
throw new Error("Invalid Token provided!")
|
||||
} else if (event.code == GatewayCloseCodes.INVALID_SEQ) {
|
||||
this.debug("Invalid Seq was sent. Reconnecting.")
|
||||
} else if (event.code === GatewayCloseCodes.NOT_AUTHENTICATED) {
|
||||
throw new Error('Not Authorized: Payload was sent before Identifying.')
|
||||
} else if (event.code === GatewayCloseCodes.AUTHENTICATION_FAILED) {
|
||||
throw new Error('Invalid Token provided!')
|
||||
} else if (event.code === GatewayCloseCodes.INVALID_SEQ) {
|
||||
this.debug('Invalid Seq was sent. Reconnecting.')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect()
|
||||
} else if (event.code == GatewayCloseCodes.RATE_LIMITED) {
|
||||
} else if (event.code === GatewayCloseCodes.RATE_LIMITED) {
|
||||
throw new Error("You're ratelimited. Calm down.")
|
||||
} else if (event.code == GatewayCloseCodes.SESSION_TIMED_OUT) {
|
||||
this.debug("Session Timeout. Reconnecting.")
|
||||
} else if (event.code === GatewayCloseCodes.SESSION_TIMED_OUT) {
|
||||
this.debug('Session Timeout. Reconnecting.')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect(true)
|
||||
} else if (event.code == GatewayCloseCodes.INVALID_SHARD) {
|
||||
this.debug("Invalid Shard was sent. Reconnecting.")
|
||||
} else if (event.code === GatewayCloseCodes.INVALID_SHARD) {
|
||||
this.debug('Invalid Shard was sent. Reconnecting.')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect()
|
||||
} else if (event.code == GatewayCloseCodes.SHARDING_REQUIRED) {
|
||||
} else if (event.code === GatewayCloseCodes.SHARDING_REQUIRED) {
|
||||
throw new Error("Couldn't connect. Sharding is requried!")
|
||||
} else if (event.code == GatewayCloseCodes.INVALID_API_VERSION) {
|
||||
} else if (event.code === GatewayCloseCodes.INVALID_API_VERSION) {
|
||||
throw new Error("Invalid API Version was used. This shouldn't happen!")
|
||||
} else if (event.code == GatewayCloseCodes.INVALID_INTENTS) {
|
||||
throw new Error("Invalid Intents")
|
||||
} else if (event.code == GatewayCloseCodes.DISALLOWED_INTENTS) {
|
||||
} else if (event.code === GatewayCloseCodes.INVALID_INTENTS) {
|
||||
throw new Error('Invalid Intents')
|
||||
} else if (event.code === GatewayCloseCodes.DISALLOWED_INTENTS) {
|
||||
throw new Error("Given Intents aren't allowed")
|
||||
} else {
|
||||
this.debug("Unknown Close code, probably connection error. Reconnecting.")
|
||||
this.debug('Unknown Close code, probably connection error. Reconnecting.')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.reconnect()
|
||||
}
|
||||
}
|
||||
|
@ -185,20 +202,25 @@ class Gateway {
|
|||
console.log(eventError)
|
||||
}
|
||||
|
||||
private async sendIdentify(forceNewSession?: boolean) {
|
||||
this.debug("Fetching /gateway/bot...")
|
||||
const info = await this.client.rest.get(GATEWAY_BOT()) as GatewayBotPayload
|
||||
if (info.session_start_limit.remaining == 0) throw new Error("Session Limit Reached. Retry After " + info.session_start_limit.reset_after + "ms")
|
||||
this.debug("Recommended Shards: " + info.shards)
|
||||
this.debug("=== Session Limit Info ===")
|
||||
this.debug(`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`)
|
||||
private async sendIdentify (forceNewSession?: boolean): Promise<void> {
|
||||
this.debug('Fetching /gateway/bot...')
|
||||
const info = await this.client.rest.get(GATEWAY_BOT())
|
||||
if (info.session_start_limit.remaining === 0)
|
||||
throw new Error(
|
||||
`Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms`
|
||||
)
|
||||
this.debug(`Recommended Shards: ${info.shards}`)
|
||||
this.debug('=== Session Limit Info ===')
|
||||
this.debug(
|
||||
`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`
|
||||
)
|
||||
this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`)
|
||||
if (!forceNewSession) {
|
||||
let sessionIDCached = await this.cache.get("session_id")
|
||||
if (sessionIDCached) {
|
||||
this.debug("Found Cached SessionID: " + sessionIDCached)
|
||||
if (forceNewSession === undefined || !forceNewSession) {
|
||||
const sessionIDCached = await this.cache.get('session_id')
|
||||
if (sessionIDCached !== undefined) {
|
||||
this.debug(`Found Cached SessionID: ${sessionIDCached}`)
|
||||
this.sessionID = sessionIDCached
|
||||
return this.sendResume()
|
||||
return await this.sendResume()
|
||||
}
|
||||
}
|
||||
this.send({
|
||||
|
@ -224,27 +246,29 @@ class Gateway {
|
|||
private async sendResume(): Promise<void> {
|
||||
this.debug(`Preparing to resume with Session: ${this.sessionID}`)
|
||||
if (this.sequenceID === undefined) {
|
||||
let cached = await this.cache.get("seq")
|
||||
if (cached) this.sequenceID = typeof cached == "string" ? parseInt(cached) : cached
|
||||
const cached = await this.cache.get('seq')
|
||||
if (cached !== undefined)
|
||||
this.sequenceID = typeof cached === 'string' ? parseInt(cached) : cached
|
||||
}
|
||||
const resumePayload = {
|
||||
op: GatewayOpcodes.RESUME,
|
||||
d: {
|
||||
token: this.token,
|
||||
session_id: this.sessionID,
|
||||
seq: this.sequenceID || null
|
||||
seq: this.sequenceID ?? null
|
||||
}
|
||||
}
|
||||
this.send(resumePayload)
|
||||
}
|
||||
|
||||
debug(msg: string) {
|
||||
this.client.debug("Gateway", msg)
|
||||
debug (msg: string): void {
|
||||
this.client.debug('Gateway', msg)
|
||||
}
|
||||
|
||||
async reconnect(forceNew?: boolean) {
|
||||
async reconnect (forceNew?: boolean): Promise<void> {
|
||||
clearInterval(this.heartbeatIntervalID)
|
||||
if (forceNew) await this.cache.delete("session_id")
|
||||
if (forceNew === undefined || !forceNew)
|
||||
await this.cache.delete('session_id')
|
||||
this.close()
|
||||
this.initWebsocket()
|
||||
}
|
||||
|
|
|
@ -4,29 +4,29 @@ import { Collection } from "../utils/collection.ts";
|
|||
export class BaseManager<T, T2> {
|
||||
client: Client
|
||||
cacheName: string
|
||||
dataType: any
|
||||
DataType: any
|
||||
|
||||
constructor(client: Client, cacheName: string, dataType: any) {
|
||||
constructor (client: Client, cacheName: string, DataType: any) {
|
||||
this.client = client
|
||||
this.cacheName = cacheName
|
||||
this.dataType = dataType
|
||||
this.DataType = DataType
|
||||
}
|
||||
|
||||
_get(key: string): Promise<T> {
|
||||
return this.client.cache.get(this.cacheName, key) as Promise<T>
|
||||
async _get (key: string): Promise<T | undefined> {
|
||||
return this.client.cache.get(this.cacheName, key)
|
||||
}
|
||||
|
||||
async get(key: string): Promise<T2 | void> {
|
||||
async get (key: string): Promise<T2 | undefined> {
|
||||
const raw = await this._get(key)
|
||||
if(!raw) return
|
||||
return new this.dataType(this.client, raw) as any
|
||||
if (raw === undefined) return
|
||||
return new this.DataType(this.client, raw)
|
||||
}
|
||||
|
||||
set(key: string, value: T) {
|
||||
async set (key: string, value: T): Promise<any> {
|
||||
return this.client.cache.set(this.cacheName, key, value)
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
async delete (key: string): Promise<boolean> {
|
||||
return this.client.cache.delete(this.cacheName, key)
|
||||
}
|
||||
|
||||
|
@ -49,4 +49,4 @@ export class BaseManager<T, T2> {
|
|||
flush() {
|
||||
return this.client.cache.deleteCache(this.cacheName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,4 +48,4 @@ export class ChannelsManager extends BaseManager<ChannelPayload, Channel> {
|
|||
}).catch(e => rej(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
import { Client } from "../models/client.ts";
|
||||
import { Emoji } from "../structures/emoji.ts";
|
||||
import { EmojiPayload } from "../types/emoji.ts";
|
||||
import { CHANNEL } from "../types/endpoint.ts";
|
||||
import { BaseManager } from "./BaseManager.ts";
|
||||
import { Client } from '../models/client.ts'
|
||||
import { Emoji } from '../structures/emoji.ts'
|
||||
import { EmojiPayload } from '../types/emoji.ts'
|
||||
import { CHANNEL } from '../types/endpoint.ts'
|
||||
import { BaseManager } from './BaseManager.ts'
|
||||
|
||||
export class EmojisManager extends BaseManager<EmojiPayload, Emoji> {
|
||||
constructor(client: Client) {
|
||||
super(client, "emojis", Emoji)
|
||||
constructor (client: Client) {
|
||||
super(client, 'emojis', Emoji)
|
||||
}
|
||||
|
||||
fetch(id: string) {
|
||||
return new Promise((res, rej) => {
|
||||
this.client.rest.get(CHANNEL(id)).then(data => {
|
||||
this.set(id, data as EmojiPayload)
|
||||
res(new Emoji(this.client, data as EmojiPayload))
|
||||
}).catch(e => rej(e))
|
||||
async fetch (id: string): Promise<Emoji> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this.client.rest
|
||||
.get(CHANNEL(id))
|
||||
.then(data => {
|
||||
this.set(id, data as EmojiPayload)
|
||||
resolve(new Emoji(this.client, data as EmojiPayload))
|
||||
})
|
||||
.catch(e => reject(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
import { Client } from "../models/client.ts";
|
||||
import { Client } from '../models/client.ts'
|
||||
|
||||
export class GatewayCache {
|
||||
client: Client
|
||||
cacheName: string = "discord_gateway_cache"
|
||||
|
||||
constructor(client: Client, cacheName?: string) {
|
||||
this.client = client
|
||||
if(cacheName) this.cacheName = cacheName
|
||||
}
|
||||
client: Client
|
||||
cacheName: string = 'discord_gateway_cache'
|
||||
|
||||
get(key: string) {
|
||||
return this.client.cache.get(this.cacheName, key)
|
||||
}
|
||||
constructor (client: Client, cacheName?: string) {
|
||||
this.client = client
|
||||
if (cacheName !== undefined) this.cacheName = cacheName
|
||||
}
|
||||
|
||||
set(key: string, value: any) {
|
||||
return this.client.cache.set(this.cacheName, key, value)
|
||||
}
|
||||
async get (key: string): Promise<undefined | any> {
|
||||
const result = await this.client.cache.get(this.cacheName, key)
|
||||
return result
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
return this.client.cache.delete(this.cacheName, key)
|
||||
}
|
||||
}
|
||||
async set (key: string, value: any): Promise<any> {
|
||||
const result = await this.client.cache.set(this.cacheName, key, value)
|
||||
return result
|
||||
}
|
||||
|
||||
async delete (key: string): Promise<boolean> {
|
||||
console.log(`[GatewayCache] DEL ${key}`)
|
||||
const result = await this.client.cache.delete(this.cacheName, key)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import { BaseManager } from "./BaseManager.ts";
|
|||
import { MembersManager } from "./MembersManager.ts";
|
||||
|
||||
export class GuildManager extends BaseManager<GuildPayload, Guild> {
|
||||
constructor(client: Client) {
|
||||
super(client, "guilds", Guild)
|
||||
constructor (client: Client) {
|
||||
super(client, 'guilds', Guild)
|
||||
}
|
||||
|
||||
fetch(id: string) {
|
||||
|
@ -25,4 +25,4 @@ export class GuildManager extends BaseManager<GuildPayload, Guild> {
|
|||
}).catch(e => rej(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import { UserPayload } from "../types/user.ts";
|
|||
import { BaseManager } from "./BaseManager.ts";
|
||||
|
||||
export class MessagesManager extends BaseManager<MessagePayload, Message> {
|
||||
constructor(client: Client) {
|
||||
super(client, "messages", Message)
|
||||
constructor (client: Client) {
|
||||
super(client, 'messages', Message)
|
||||
}
|
||||
|
||||
async get(key: string): Promise<Message | void> {
|
||||
|
@ -38,4 +38,4 @@ export class MessagesManager extends BaseManager<MessagePayload, Message> {
|
|||
}).catch(e => rej(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
import { Client } from "../models/client.ts";
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import { Role } from "../structures/role.ts";
|
||||
import { GUILD_ROLE } from "../types/endpoint.ts";
|
||||
import { RolePayload } from "../types/role.ts";
|
||||
import { BaseManager } from "./BaseManager.ts";
|
||||
import { Client } from '../models/client.ts'
|
||||
import { Guild } from '../structures/guild.ts'
|
||||
import { Role } from '../structures/role.ts'
|
||||
import { GUILD_ROLE } from '../types/endpoint.ts'
|
||||
import { RolePayload } from '../types/role.ts'
|
||||
import { BaseManager } from './BaseManager.ts'
|
||||
|
||||
export class RolesManager extends BaseManager<RolePayload, Role> {
|
||||
guild: Guild
|
||||
|
||||
constructor(client: Client, guild: Guild) {
|
||||
super(client, "roles:" + guild.id, Role)
|
||||
constructor (client: Client, guild: Guild) {
|
||||
super(client, `roles:${guild.id}`, Role)
|
||||
this.guild = guild
|
||||
}
|
||||
|
||||
fetch(id: string) {
|
||||
return new Promise((res, rej) => {
|
||||
this.client.rest.get(GUILD_ROLE(this.guild.id, id)).then(data => {
|
||||
this.set(id, data as RolePayload)
|
||||
res(new Role(this.client, data as RolePayload))
|
||||
}).catch(e => rej(e))
|
||||
async fetch (id: string): Promise<Role> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this.client.rest
|
||||
.get(GUILD_ROLE(this.guild.id, id))
|
||||
.then(data => {
|
||||
this.set(id, data as RolePayload)
|
||||
resolve(new Role(this.client, data as RolePayload))
|
||||
})
|
||||
.catch(e => reject(e))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -28,4 +31,4 @@ export class RolesManager extends BaseManager<RolePayload, Role> {
|
|||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
import { Client } from "../models/client.ts";
|
||||
import { User } from "../structures/user.ts";
|
||||
import { USER } from "../types/endpoint.ts";
|
||||
import { UserPayload } from "../types/user.ts";
|
||||
import { BaseManager } from "./BaseManager.ts";
|
||||
import { Client } from '../models/client.ts'
|
||||
import { User } from '../structures/user.ts'
|
||||
import { USER } from '../types/endpoint.ts'
|
||||
import { UserPayload } from '../types/user.ts'
|
||||
import { BaseManager } from './BaseManager.ts'
|
||||
|
||||
export class UserManager extends BaseManager<UserPayload, User> {
|
||||
constructor(client: Client) {
|
||||
super(client, "users", User)
|
||||
constructor (client: Client) {
|
||||
super(client, 'users', User)
|
||||
}
|
||||
|
||||
fetch(id: string) {
|
||||
return new Promise((res, rej) => {
|
||||
this.client.rest.get(USER(id)).then(data => {
|
||||
this.set(id, data as UserPayload)
|
||||
res(new User(this.client, data as UserPayload))
|
||||
}).catch(e => rej(e))
|
||||
async fetch (id: string): Promise<User> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this.client.rest
|
||||
.get(USER(id))
|
||||
.then(data => {
|
||||
this.set(id, data as UserPayload)
|
||||
resolve(new User(this.client, data as UserPayload))
|
||||
})
|
||||
.catch(e => reject(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import { Collection } from "../utils/collection.ts";
|
||||
import { Client } from "./client.ts";
|
||||
import { connect, Redis, RedisConnectOptions } from "https://denopkg.com/keroxp/deno-redis/mod.ts";
|
||||
import { Collection } from '../utils/collection.ts'
|
||||
import { Client } from './client.ts'
|
||||
import {
|
||||
connect,
|
||||
Redis,
|
||||
RedisConnectOptions
|
||||
} from 'https://denopkg.com/keroxp/deno-redis/mod.ts'
|
||||
|
||||
export interface ICacheAdapter {
|
||||
client: Client
|
||||
get: (cacheName: string, key: string) => Promise<any> | any
|
||||
set: (cacheName: string, key: string, value: any) => Promise<any> | any
|
||||
delete: (cacheName: string, key: string) => Promise<boolean> | boolean
|
||||
array: (cacheName: string) => void | any[] | Promise<any[] | void>
|
||||
array: (cacheName: string) => undefined | any[] | Promise<any[] | undefined>
|
||||
deleteCache: (cacheName: string) => any
|
||||
}
|
||||
|
||||
|
@ -17,34 +21,34 @@ export class DefaultCacheAdapter implements ICacheAdapter {
|
|||
[name: string]: Collection<string, any>
|
||||
} = {}
|
||||
|
||||
constructor(client: Client) {
|
||||
constructor (client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async get(cacheName: string, key: string) {
|
||||
async get (cacheName: string, key: string): Promise<undefined | any> {
|
||||
const cache = this.data[cacheName]
|
||||
if (!cache) return;
|
||||
if (cache === undefined) return
|
||||
return cache.get(key)
|
||||
}
|
||||
|
||||
async set(cacheName: string, key: string, value: any) {
|
||||
async set (cacheName: string, key: string, value: any): Promise<any> {
|
||||
let cache = this.data[cacheName]
|
||||
if (!cache) {
|
||||
if (cache === undefined) {
|
||||
this.data[cacheName] = new Collection()
|
||||
cache = this.data[cacheName]
|
||||
}
|
||||
cache.set(key, value)
|
||||
return cache.set(key, value)
|
||||
}
|
||||
|
||||
async delete(cacheName: string, key: string) {
|
||||
async delete (cacheName: string, key: string): Promise<boolean> {
|
||||
const cache = this.data[cacheName]
|
||||
if (!cache) return false
|
||||
if (cache === undefined) return false
|
||||
return cache.delete(key)
|
||||
}
|
||||
|
||||
async array(cacheName: string) {
|
||||
async array (cacheName: string): Promise<any[] | undefined> {
|
||||
const cache = this.data[cacheName]
|
||||
if (!cache) return []
|
||||
if (cache === undefined) return
|
||||
return cache.array()
|
||||
}
|
||||
|
||||
|
@ -59,45 +63,60 @@ export class RedisCacheAdapter implements ICacheAdapter {
|
|||
redis?: Redis
|
||||
ready: boolean = false
|
||||
|
||||
constructor(client: Client, options: RedisConnectOptions) {
|
||||
constructor (client: Client, options: RedisConnectOptions) {
|
||||
this.client = client
|
||||
this._redis = connect(options)
|
||||
this._redis.then(redis => {
|
||||
this.redis = redis
|
||||
this.ready = true
|
||||
})
|
||||
this._redis.then(
|
||||
redis => {
|
||||
this.redis = redis
|
||||
this.ready = true
|
||||
},
|
||||
() => {
|
||||
// TODO: Make error for this
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async _checkReady() {
|
||||
if(!this.ready) return await this._redis;
|
||||
else return;
|
||||
async _checkReady (): Promise<void> {
|
||||
if (!this.ready) await this._redis
|
||||
}
|
||||
|
||||
async get(cacheName: string, key: string) {
|
||||
async get (cacheName: string, key: string): Promise<string | undefined> {
|
||||
await this._checkReady()
|
||||
let cache = await this.redis?.hget(cacheName, key)
|
||||
if(!cache) return
|
||||
const cache = await this.redis?.hget(cacheName, key)
|
||||
if (cache === undefined) return
|
||||
try {
|
||||
return JSON.parse(cache as string)
|
||||
} catch(e) { return cache }
|
||||
return JSON.parse(cache)
|
||||
} catch (e) {
|
||||
return cache
|
||||
}
|
||||
}
|
||||
|
||||
async set(cacheName: string, key: string, value: any) {
|
||||
async set (
|
||||
cacheName: string,
|
||||
key: string,
|
||||
value: any
|
||||
): Promise<number | undefined> {
|
||||
await this._checkReady()
|
||||
return await this.redis?.hset(cacheName, key, typeof value === "object" ? JSON.stringify(value) : value)
|
||||
const result = await this.redis?.hset(
|
||||
cacheName,
|
||||
key,
|
||||
typeof value === 'object' ? JSON.stringify(value) : value
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
async delete(cacheName: string, key: string) {
|
||||
async delete (cacheName: string, key: string): Promise<boolean> {
|
||||
await this._checkReady()
|
||||
let exists = await this.redis?.hexists(cacheName, key)
|
||||
if(!exists) return false
|
||||
const exists = await this.redis?.hexists(cacheName, key)
|
||||
if (exists === 0) return false
|
||||
await this.redis?.hdel(cacheName, key)
|
||||
return true
|
||||
}
|
||||
|
||||
async array(cacheName: string) {
|
||||
async array (cacheName: string): Promise<any[] | undefined> {
|
||||
await this._checkReady()
|
||||
let data = await this.redis?.hvals(cacheName)
|
||||
const data = await this.redis?.hvals(cacheName)
|
||||
return data?.map((e: string) => JSON.parse(e))
|
||||
}
|
||||
|
||||
|
@ -105,4 +124,4 @@ export class RedisCacheAdapter implements ICacheAdapter {
|
|||
await this._checkReady()
|
||||
return await this.redis?.del(cacheName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ export class Client extends EventEmitter {
|
|||
cache: ICacheAdapter = new DefaultCacheAdapter(this)
|
||||
intents?: GatewayIntents[]
|
||||
forceNewSession?: boolean
|
||||
|
||||
users: UserManager = new UserManager(this)
|
||||
guilds: GuildManager = new GuildManager(this)
|
||||
channels: ChannelsManager = new ChannelsManager(this)
|
||||
|
@ -46,11 +45,11 @@ export class Client extends EventEmitter {
|
|||
this.token = options.token
|
||||
this.intents = options.intents
|
||||
this.forceNewSession = options.forceNewSession
|
||||
if(options.cache) this.cache = options.cache
|
||||
if(options.presence) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence)
|
||||
if (options.cache !== undefined) this.cache = options.cache
|
||||
if (options.presence !== undefined) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence)
|
||||
}
|
||||
|
||||
setAdapter(adapter: ICacheAdapter) {
|
||||
setAdapter (adapter: ICacheAdapter): Client {
|
||||
this.cache = adapter
|
||||
return this
|
||||
}
|
||||
|
@ -72,16 +71,15 @@ export class Client extends EventEmitter {
|
|||
* @param intents Gateway intents in array. This is required.
|
||||
*/
|
||||
connect (token?: string, intents?: GatewayIntents[]): void {
|
||||
if(!token && this.token) token = this.token
|
||||
else if(!this.token && token) {
|
||||
if (token === undefined && this.token !== undefined) token = this.token
|
||||
else if (this.token === undefined && token !== undefined) {
|
||||
this.token = token
|
||||
}
|
||||
else throw new Error("No Token Provided")
|
||||
if(!intents && this.intents) intents = this.intents
|
||||
else if(intents && !this.intents) {
|
||||
} else throw new Error('No Token Provided')
|
||||
if (intents === undefined && this.intents !== undefined)
|
||||
intents = this.intents
|
||||
else if (intents !== undefined && this.intents === undefined) {
|
||||
this.intents = intents
|
||||
}
|
||||
else throw new Error("No Gateway Intents were provided")
|
||||
} else throw new Error('No Gateway Intents were provided')
|
||||
this.gateway = new Gateway(this, token, intents)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { delay } from "../utils/index.ts";
|
||||
import * as baseEndpoints from "../consts/urlsAndVersions.ts";
|
||||
import { Client } from "./client.ts";
|
||||
import { delay } from '../utils/index.ts'
|
||||
import * as baseEndpoints from '../consts/urlsAndVersions.ts'
|
||||
import { Client } from './client.ts'
|
||||
|
||||
export enum HttpResponseCode {
|
||||
Ok = 200,
|
||||
|
@ -17,350 +17,376 @@ export enum HttpResponseCode {
|
|||
}
|
||||
|
||||
export type RequestMethods =
|
||||
| "get"
|
||||
| "post"
|
||||
| "put"
|
||||
| "patch"
|
||||
| "head"
|
||||
| "delete";
|
||||
| 'get'
|
||||
| 'post'
|
||||
| 'put'
|
||||
| 'patch'
|
||||
| 'head'
|
||||
| 'delete'
|
||||
|
||||
export interface QueuedRequest {
|
||||
callback: () => Promise<
|
||||
void | {
|
||||
rateLimited: any;
|
||||
beforeFetch: boolean;
|
||||
bucketID?: string | null;
|
||||
}
|
||||
>;
|
||||
bucketID?: string | null;
|
||||
url: string;
|
||||
callback: () => Promise<
|
||||
| {
|
||||
rateLimited: any
|
||||
beforeFetch: boolean
|
||||
bucketID?: string | null
|
||||
}
|
||||
| undefined
|
||||
>
|
||||
bucketID?: string | null
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface RateLimitedPath {
|
||||
url: string;
|
||||
resetTimestamp: number;
|
||||
bucketID: string | null;
|
||||
url: string
|
||||
resetTimestamp: number
|
||||
bucketID: string | null
|
||||
}
|
||||
|
||||
export class RESTManager {
|
||||
client: Client;
|
||||
globallyRateLimited: boolean = false;
|
||||
queueInProcess: boolean = false;
|
||||
pathQueues: { [key: string]: QueuedRequest[] } = {};
|
||||
ratelimitedPaths = new Map<string, RateLimitedPath>();
|
||||
client: Client
|
||||
globallyRateLimited: boolean = false
|
||||
queueInProcess: boolean = false
|
||||
pathQueues: { [key: string]: QueuedRequest[] } = {}
|
||||
ratelimitedPaths = new Map<string, RateLimitedPath>()
|
||||
|
||||
constructor(client: Client) {
|
||||
this.client = client;
|
||||
}
|
||||
constructor (client: Client) {
|
||||
this.client = client
|
||||
setTimeout(this.processRateLimitedPaths, 1000)
|
||||
}
|
||||
|
||||
async processRateLimitedPaths() {
|
||||
const now = Date.now();
|
||||
this.ratelimitedPaths.forEach((value, key) => {
|
||||
if (value.resetTimestamp > now) return;
|
||||
this.ratelimitedPaths.delete(key);
|
||||
if (key === "global") this.globallyRateLimited = false;
|
||||
});
|
||||
async processRateLimitedPaths (): Promise<void> {
|
||||
const now = Date.now()
|
||||
this.ratelimitedPaths.forEach((value, key) => {
|
||||
if (value.resetTimestamp > now) return
|
||||
this.ratelimitedPaths.delete(key)
|
||||
if (key === 'global') this.globallyRateLimited = false
|
||||
})
|
||||
}
|
||||
|
||||
await delay(1000);
|
||||
this.processRateLimitedPaths();
|
||||
}
|
||||
addToQueue (request: QueuedRequest): void {
|
||||
const route = request.url.substring(
|
||||
// eslint seriously?
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
baseEndpoints.DISCORD_API_URL.length + 1
|
||||
)
|
||||
const parts = route.split('/')
|
||||
// Remove the major param
|
||||
parts.shift()
|
||||
const [id] = parts
|
||||
|
||||
addToQueue(request: QueuedRequest) {
|
||||
const route = request.url.substring(baseEndpoints.DISCORD_API_URL.length + 1);
|
||||
const parts = route.split("/");
|
||||
// Remove the major param
|
||||
parts.shift();
|
||||
const [id] = parts;
|
||||
|
||||
if (this.pathQueues[id]) {
|
||||
this.pathQueues[id].push(request);
|
||||
} else {
|
||||
this.pathQueues[id] = [request];
|
||||
}
|
||||
}
|
||||
|
||||
async cleanupQueues() {
|
||||
Object.entries(this.pathQueues).map(([key, value]) => {
|
||||
if (!value.length) {
|
||||
// Remove it entirely
|
||||
delete this.pathQueues[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.pathQueues[id] !== undefined) {
|
||||
this.pathQueues[id].push(request)
|
||||
} else {
|
||||
this.pathQueues[id] = [request]
|
||||
}
|
||||
}
|
||||
|
||||
async processQueue() {
|
||||
if (
|
||||
(Object.keys(this.pathQueues).length) && !this.globallyRateLimited
|
||||
) {
|
||||
await Promise.allSettled(
|
||||
Object.values(this.pathQueues).map(async (pathQueue) => {
|
||||
const request = pathQueue.shift();
|
||||
if (!request) return;
|
||||
|
||||
const rateLimitedURLResetIn = await this.checkRatelimits(request.url);
|
||||
|
||||
if (request.bucketID) {
|
||||
const rateLimitResetIn = await this.checkRatelimits(request.bucketID);
|
||||
if (rateLimitResetIn) {
|
||||
// This request is still rate limited readd to queue
|
||||
this.addToQueue(request);
|
||||
} else if (rateLimitedURLResetIn) {
|
||||
// This URL is rate limited readd to queue
|
||||
this.addToQueue(request);
|
||||
} else {
|
||||
// This request is not rate limited so it should be run
|
||||
const result = await request.callback();
|
||||
if (result && result.rateLimited) {
|
||||
this.addToQueue(
|
||||
{ ...request, bucketID: result.bucketID || request.bucketID },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rateLimitedURLResetIn) {
|
||||
// This URL is rate limited readd to queue
|
||||
this.addToQueue(request);
|
||||
} else {
|
||||
// This request has no bucket id so it should be processed
|
||||
const result = await request.callback();
|
||||
if (request && result && result.rateLimited) {
|
||||
this.addToQueue(
|
||||
{ ...request, bucketID: result.bucketID || request.bucketID },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (Object.keys(this.pathQueues).length) {
|
||||
await delay(1000);
|
||||
this.processQueue();
|
||||
this.cleanupQueues();
|
||||
} else this.queueInProcess = false;
|
||||
}
|
||||
async cleanupQueues (): Promise<void> {
|
||||
Object.entries(this.pathQueues).forEach(([key, value]) => {
|
||||
if (value.length === 0) {
|
||||
// Remove it entirely
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete this.pathQueues[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
createRequestBody(body: any, method: RequestMethods) {
|
||||
const headers: { [key: string]: string } = {
|
||||
Authorization: `Bot ${this.client.token}`,
|
||||
"User-Agent":
|
||||
`DiscordBot (discord.deno)`,
|
||||
};
|
||||
async processQueue (): Promise<void> {
|
||||
if (
|
||||
Object.keys(this.pathQueues).length !== 0 &&
|
||||
!this.globallyRateLimited
|
||||
) {
|
||||
await Promise.allSettled(
|
||||
Object.values(this.pathQueues).map(async pathQueue => {
|
||||
const request = pathQueue.shift()
|
||||
if (request === undefined) return
|
||||
|
||||
if(!this.client.token) delete headers.Authorization;
|
||||
|
||||
if (method === "get") body = undefined;
|
||||
|
||||
if (body?.reason) {
|
||||
headers["X-Audit-Log-Reason"] = encodeURIComponent(body.reason);
|
||||
}
|
||||
|
||||
if (body?.file) {
|
||||
const form = new FormData();
|
||||
form.append("file", body.file.blob, body.file.name);
|
||||
form.append("payload_json", JSON.stringify({ ...body, file: undefined }));
|
||||
body.file = form;
|
||||
} else if (
|
||||
body && !["get", "delete"].includes(method)
|
||||
) {
|
||||
headers["Content-Type"] = "application/json";
|
||||
}
|
||||
|
||||
return {
|
||||
headers,
|
||||
body: body?.file || JSON.stringify(body),
|
||||
method: method.toUpperCase(),
|
||||
};
|
||||
}
|
||||
const rateLimitedURLResetIn = await this.checkRatelimits(request.url)
|
||||
|
||||
async checkRatelimits(url: string) {
|
||||
const ratelimited = this.ratelimitedPaths.get(url);
|
||||
const global = this.ratelimitedPaths.get("global");
|
||||
const now = Date.now();
|
||||
|
||||
if (ratelimited && now < ratelimited.resetTimestamp) {
|
||||
return ratelimited.resetTimestamp - now;
|
||||
}
|
||||
if (global && now < global.resetTimestamp) {
|
||||
return global.resetTimestamp - now;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
if (typeof request.bucketID === 'string') {
|
||||
const rateLimitResetIn = await this.checkRatelimits(
|
||||
request.bucketID
|
||||
)
|
||||
if (rateLimitResetIn !== false) {
|
||||
// This request is still rate limited read to queue
|
||||
this.addToQueue(request)
|
||||
} else {
|
||||
// This request is not rate limited so it should be run
|
||||
const result = await request.callback()
|
||||
if (result?.rateLimited !== undefined) {
|
||||
this.addToQueue({
|
||||
...request,
|
||||
bucketID: result.bucketID ?? request.bucketID
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rateLimitedURLResetIn !== false) {
|
||||
// This URL is rate limited readd to queue
|
||||
this.addToQueue(request)
|
||||
} else {
|
||||
// This request has no bucket id so it should be processed
|
||||
const result = await request.callback()
|
||||
if (result?.rateLimited !== undefined) {
|
||||
this.addToQueue({
|
||||
...request,
|
||||
bucketID: result.bucketID ?? request.bucketID
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async runMethod(
|
||||
method: RequestMethods,
|
||||
url: string,
|
||||
body?: unknown,
|
||||
retryCount = 0,
|
||||
bucketID?: string | null,
|
||||
) {
|
||||
const errorStack = new Error("Location In Your Files:");
|
||||
Error.captureStackTrace(errorStack);
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
const callback = async () => {
|
||||
try {
|
||||
const rateLimitResetIn = await this.checkRatelimits(url);
|
||||
if (rateLimitResetIn) {
|
||||
return { rateLimited: rateLimitResetIn, beforeFetch: true, bucketID };
|
||||
}
|
||||
|
||||
const query = method === "get" && body
|
||||
? Object.entries(body as any).map(([key, value]) =>
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(value as any)}`
|
||||
)
|
||||
.join("&")
|
||||
: "";
|
||||
const urlToUse = method === "get" && query ? `${url}?${query}` : url;
|
||||
if (Object.keys(this.pathQueues).length !== 0) {
|
||||
await delay(1000)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.processQueue()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.cleanupQueues()
|
||||
} else this.queueInProcess = false
|
||||
}
|
||||
|
||||
createRequestBody (
|
||||
body: any,
|
||||
method: RequestMethods
|
||||
): { [key: string]: any } {
|
||||
const headers: { [key: string]: string } = {
|
||||
Authorization: `Bot ${this.client.token}`,
|
||||
'User-Agent': `DiscordBot (discord.deno)`
|
||||
}
|
||||
|
||||
if (this.client.token !== undefined) delete headers.Authorization
|
||||
|
||||
if (method === 'get') body = undefined
|
||||
|
||||
if (body?.reason !== undefined) {
|
||||
headers['X-Audit-Log-Reason'] = encodeURIComponent(body.reason)
|
||||
}
|
||||
|
||||
if (body?.file !== undefined) {
|
||||
const form = new FormData()
|
||||
form.append('file', body.file.blob, body.file.name)
|
||||
form.append('payload_json', JSON.stringify({ ...body, file: undefined }))
|
||||
body.file = form
|
||||
} else if (body !== undefined && !['get', 'delete'].includes(method)) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
|
||||
return {
|
||||
headers,
|
||||
body: body?.file ?? JSON.stringify(body),
|
||||
method: method.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
async checkRatelimits (url: string): Promise<number | false> {
|
||||
const ratelimited = this.ratelimitedPaths.get(url)
|
||||
const global = this.ratelimitedPaths.get('global')
|
||||
const now = Date.now()
|
||||
|
||||
if (ratelimited !== undefined && now < ratelimited.resetTimestamp) {
|
||||
return ratelimited.resetTimestamp - now
|
||||
}
|
||||
if (global !== undefined && now < global.resetTimestamp) {
|
||||
return global.resetTimestamp - now
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
async runMethod (
|
||||
method: RequestMethods,
|
||||
url: string,
|
||||
body?: unknown,
|
||||
retryCount = 0,
|
||||
bucketID?: string | null
|
||||
): Promise<any> {
|
||||
const errorStack = new Error('Location In Your Files:')
|
||||
Error.captureStackTrace(errorStack)
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
const callback = async (): Promise<undefined | any> => {
|
||||
try {
|
||||
const rateLimitResetIn = await this.checkRatelimits(url)
|
||||
if (rateLimitResetIn !== false) {
|
||||
return {
|
||||
rateLimited: rateLimitResetIn,
|
||||
beforeFetch: true,
|
||||
bucketID
|
||||
}
|
||||
}
|
||||
|
||||
const query =
|
||||
method === 'get' && body !== undefined
|
||||
? Object.entries(body as any)
|
||||
.map(
|
||||
([key, value]) =>
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(
|
||||
value as any
|
||||
)}`
|
||||
)
|
||||
.join('&')
|
||||
: ''
|
||||
const urlToUse =
|
||||
method === 'get' && query !== '' ? `${url}?${query}` : url
|
||||
|
||||
const response = await fetch(
|
||||
urlToUse,
|
||||
this.createRequestBody(body, method)
|
||||
)
|
||||
const bucketIDFromHeaders = this.processHeaders(url, response.headers)
|
||||
this.handleStatusCode(response, errorStack)
|
||||
|
||||
const response = await fetch(urlToUse, this.createRequestBody(body, method));
|
||||
const bucketIDFromHeaders = this.processHeaders(url, response.headers);
|
||||
|
||||
// Sometimes Discord returns an empty 204 response that can't be made to JSON.
|
||||
if (response.status === 204) return resolve();
|
||||
if (response.status === 204) return resolve(undefined)
|
||||
|
||||
this.handleStatusCode(response, errorStack);
|
||||
|
||||
const json = await response.json();
|
||||
if (
|
||||
json.retry_after ||
|
||||
json.message === "You are being rate limited."
|
||||
) {
|
||||
if (retryCount > 10) {
|
||||
throw new Error("Max RateLimit Retries hit");
|
||||
}
|
||||
|
||||
return {
|
||||
rateLimited: json.retry_after,
|
||||
beforeFetch: false,
|
||||
bucketID: bucketIDFromHeaders,
|
||||
};
|
||||
}
|
||||
return resolve(json);
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
this.addToQueue({
|
||||
callback,
|
||||
bucketID,
|
||||
url,
|
||||
});
|
||||
if (!this.queueInProcess) {
|
||||
this.queueInProcess = true;
|
||||
this.processQueue();
|
||||
}
|
||||
});
|
||||
}
|
||||
const json = await response.json()
|
||||
if (
|
||||
json.retry_after !== undefined ||
|
||||
json.message === 'You are being rate limited.'
|
||||
) {
|
||||
if (retryCount > 10) {
|
||||
throw new Error('Max RateLimit Retries hit')
|
||||
}
|
||||
|
||||
async logErrors(response: Response, errorStack?: unknown) {
|
||||
try {
|
||||
const error = await response.json();
|
||||
console.error(error);
|
||||
} catch {
|
||||
console.error(response);
|
||||
}
|
||||
}
|
||||
return {
|
||||
rateLimited: json.retry_after,
|
||||
beforeFetch: false,
|
||||
bucketID: bucketIDFromHeaders
|
||||
}
|
||||
}
|
||||
return resolve(json)
|
||||
} catch (error) {
|
||||
return reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
async handleStatusCode(response: Response, errorStack?: unknown) {
|
||||
this.addToQueue({
|
||||
callback,
|
||||
bucketID,
|
||||
url
|
||||
})
|
||||
if (!this.queueInProcess) {
|
||||
this.queueInProcess = true
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.processQueue()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async logErrors (response: Response, errorStack?: unknown): Promise<void> {
|
||||
try {
|
||||
const error = await response.json()
|
||||
console.error(error)
|
||||
} catch {
|
||||
console.error(response)
|
||||
}
|
||||
}
|
||||
|
||||
handleStatusCode (
|
||||
response: Response,
|
||||
errorStack?: unknown
|
||||
): undefined | boolean {
|
||||
const status = response.status
|
||||
|
||||
if (
|
||||
(status >= 200 && status < 400) ||
|
||||
status === HttpResponseCode.TooManyRequests
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.logErrors(response, errorStack);
|
||||
|
||||
switch (status) {
|
||||
case HttpResponseCode.BadRequest:
|
||||
case HttpResponseCode.Unauthorized:
|
||||
case HttpResponseCode.Forbidden:
|
||||
case HttpResponseCode.NotFound:
|
||||
case HttpResponseCode.MethodNotAllowed:
|
||||
throw new Error("Request Client Error. Code: " + status);
|
||||
case HttpResponseCode.GatewayUnavailable:
|
||||
throw new Error("Request Server Error. Code: " + status);
|
||||
}
|
||||
|
||||
// left are all unknown
|
||||
throw new Error("Request Unknown Error");
|
||||
}
|
||||
|
||||
processHeaders(url: string, headers: Headers) {
|
||||
let ratelimited = false;
|
||||
|
||||
// Get all useful headers
|
||||
const remaining = headers.get("x-ratelimit-remaining");
|
||||
const resetTimestamp = headers.get("x-ratelimit-reset");
|
||||
const retryAfter = headers.get("retry-after");
|
||||
const global = headers.get("x-ratelimit-global");
|
||||
const bucketID = headers.get("x-ratelimit-bucket");
|
||||
|
||||
// If there is no remaining rate limit for this endpoint, we save it in cache
|
||||
if (remaining && remaining === "0") {
|
||||
ratelimited = true;
|
||||
|
||||
this.ratelimitedPaths.set(url, {
|
||||
url,
|
||||
resetTimestamp: Number(resetTimestamp) * 1000,
|
||||
bucketID,
|
||||
});
|
||||
|
||||
if (bucketID) {
|
||||
this.ratelimitedPaths.set(bucketID, {
|
||||
url,
|
||||
resetTimestamp: Number(resetTimestamp) * 1000,
|
||||
bucketID,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (global) {
|
||||
const reset = Date.now() + Number(retryAfter);
|
||||
this.globallyRateLimited = true;
|
||||
ratelimited = true;
|
||||
|
||||
this.ratelimitedPaths.set("global", {
|
||||
url: "global",
|
||||
resetTimestamp: reset,
|
||||
bucketID,
|
||||
});
|
||||
|
||||
if (bucketID) {
|
||||
this.ratelimitedPaths.set(bucketID, {
|
||||
url: "global",
|
||||
resetTimestamp: reset,
|
||||
bucketID,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ratelimited ? bucketID : undefined;
|
||||
}
|
||||
if (
|
||||
(status >= 200 && status < 400) ||
|
||||
status === HttpResponseCode.TooManyRequests
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
get(url: string, body?: unknown) {
|
||||
return this.runMethod("get", url, body);
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.logErrors(response, errorStack)
|
||||
|
||||
switch (status) {
|
||||
case HttpResponseCode.BadRequest:
|
||||
case HttpResponseCode.Unauthorized:
|
||||
case HttpResponseCode.Forbidden:
|
||||
case HttpResponseCode.NotFound:
|
||||
case HttpResponseCode.MethodNotAllowed:
|
||||
throw new Error('Request Client Error')
|
||||
case HttpResponseCode.GatewayUnavailable:
|
||||
throw new Error('Request Server Error')
|
||||
}
|
||||
|
||||
// left are all unknown
|
||||
throw new Error('Request Unknown Error')
|
||||
}
|
||||
|
||||
post(url: string, body?: unknown) {
|
||||
return this.runMethod("post", url, body);
|
||||
processHeaders (url: string, headers: Headers): string | null | undefined {
|
||||
let ratelimited = false
|
||||
|
||||
// Get all useful headers
|
||||
const remaining = headers.get('x-ratelimit-remaining')
|
||||
const resetTimestamp = headers.get('x-ratelimit-reset')
|
||||
const retryAfter = headers.get('retry-after')
|
||||
const global = headers.get('x-ratelimit-global')
|
||||
const bucketID = headers.get('x-ratelimit-bucket')
|
||||
|
||||
// If there is no remaining rate limit for this endpoint, we save it in cache
|
||||
if (remaining !== null && remaining === '0') {
|
||||
ratelimited = true
|
||||
|
||||
this.ratelimitedPaths.set(url, {
|
||||
url,
|
||||
resetTimestamp: Number(resetTimestamp) * 1000,
|
||||
bucketID
|
||||
})
|
||||
|
||||
if (bucketID !== null) {
|
||||
this.ratelimitedPaths.set(bucketID, {
|
||||
url,
|
||||
resetTimestamp: Number(resetTimestamp) * 1000,
|
||||
bucketID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no remaining global limit, we save it in cache
|
||||
if (global !== null) {
|
||||
const reset = Date.now() + Number(retryAfter)
|
||||
this.globallyRateLimited = true
|
||||
ratelimited = true
|
||||
|
||||
this.ratelimitedPaths.set('global', {
|
||||
url: 'global',
|
||||
resetTimestamp: reset,
|
||||
bucketID
|
||||
})
|
||||
|
||||
if (bucketID !== null) {
|
||||
this.ratelimitedPaths.set(bucketID, {
|
||||
url: 'global',
|
||||
resetTimestamp: reset,
|
||||
bucketID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ratelimited ? bucketID : undefined
|
||||
}
|
||||
|
||||
delete(url: string, body?: unknown) {
|
||||
return this.runMethod("delete", url, body);
|
||||
async get (url: string, body?: unknown): Promise<any> {
|
||||
return await this.runMethod('get', url, body)
|
||||
}
|
||||
|
||||
patch(url: string, body?: unknown) {
|
||||
return this.runMethod("patch", url, body);
|
||||
async post (url: string, body?: unknown): Promise<any> {
|
||||
return await this.runMethod('post', url, body)
|
||||
}
|
||||
|
||||
put(url: string, body?: unknown) {
|
||||
return this.runMethod("put", url, body);
|
||||
async delete (url: string, body?: unknown): Promise<any> {
|
||||
return await this.runMethod('delete', url, body)
|
||||
}
|
||||
}
|
||||
|
||||
async patch (url: string, body?: unknown): Promise<any> {
|
||||
return await this.runMethod('patch', url, body)
|
||||
}
|
||||
|
||||
async put (url: string, body?: unknown): Promise<any> {
|
||||
return await this.runMethod('put', url, body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { ChannelPayload, ChannelTypes } from '../types/channel.ts'
|
||||
import { Base } from './base.ts'
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { DMChannelPayload } from '../types/channel.ts'
|
||||
import { UserPayload } from '../types/user.ts'
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { GroupDMChannelPayload } from '../types/channel.ts'
|
||||
import { Channel } from './channel.ts'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Client } from '../models/client.ts'
|
||||
import { GuildFeatures, GuildPayload } from '../types/guild.ts'
|
||||
import { PresenceUpdatePayload } from '../types/presence.ts'
|
||||
import { PresenceUpdatePayload } from '../types/gateway.ts'
|
||||
import { Base } from './base.ts'
|
||||
import { Channel } from './channel.ts'
|
||||
import { Emoji } from './emoji.ts'
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { GuildVoiceChannelPayload, Overwrite } from '../types/channel.ts'
|
||||
import { Channel } from './channel.ts'
|
||||
|
|
|
@ -14,7 +14,6 @@ import { User } from './user.ts'
|
|||
import { Member } from './member.ts'
|
||||
import { Embed } from './embed.ts'
|
||||
import { CHANNEL_MESSAGE } from '../types/endpoint.ts'
|
||||
import cache from '../models/cache.ts'
|
||||
import { Channel } from "./channel.ts"
|
||||
import { MessageMentions } from "./MessageMentions.ts"
|
||||
import { TextChannel } from "./textChannel.ts"
|
||||
|
@ -51,7 +50,13 @@ export class Message extends Base {
|
|||
messageReference?: MessageReference
|
||||
flags?: number
|
||||
|
||||
constructor (client: Client, data: MessagePayload, channel: TextChannel, author: User, mentions: MessageMentions) {
|
||||
constructor (
|
||||
client: Client,
|
||||
data: MessagePayload,
|
||||
channel: TextChannel,
|
||||
author: User,
|
||||
mentions: MessageMentions
|
||||
) {
|
||||
super(client)
|
||||
this.data = data
|
||||
this.id = data.id
|
||||
|
@ -120,7 +125,7 @@ export class Message extends Base {
|
|||
}
|
||||
|
||||
edit (text?: string, option?: MessageOption): Promise<Message> {
|
||||
return (this.channel as TextChannel).edit(this.id, text, option)
|
||||
return this.channel.edit(this.id, text, option)
|
||||
}
|
||||
|
||||
reply(text: string, options?: MessageOption) {
|
||||
|
@ -129,7 +134,7 @@ export class Message extends Base {
|
|||
return this.channel.send(`${this.author.mention}, ${text}`, options)
|
||||
}
|
||||
|
||||
delete (): Promise<void> {
|
||||
return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) as any
|
||||
async delete (): Promise<void> {
|
||||
return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { Base } from './base.ts'
|
||||
import { RolePayload } from '../types/role.ts'
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { MessageOption, MessagePayload, TextChannelPayload } from '../types/channel.ts'
|
||||
import { MessageOption, TextChannelPayload } from '../types/channel.ts'
|
||||
import { CHANNEL_MESSAGE, CHANNEL_MESSAGES } from '../types/endpoint.ts'
|
||||
import { Channel } from './channel.ts'
|
||||
import { Message } from './message.ts'
|
||||
import { MessageMentions } from "./MessageMentions.ts"
|
||||
import { User } from "./user.ts"
|
||||
import { MessageMentions } from './MessageMentions.ts'
|
||||
|
||||
export class TextChannel extends Channel {
|
||||
lastMessageID?: string
|
||||
|
@ -37,7 +35,7 @@ export class TextChannel extends Channel {
|
|||
allowed_mentions: option?.allowedMention
|
||||
})
|
||||
|
||||
return new Message(this.client, resp as any, this, this.client.user as User, new MessageMentions())
|
||||
return new Message(this.client, resp as any, this, this.client.user, new MessageMentions())
|
||||
}
|
||||
|
||||
async edit (
|
||||
|
@ -45,21 +43,31 @@ export class TextChannel extends Channel {
|
|||
text?: string,
|
||||
option?: MessageOption
|
||||
): Promise<Message> {
|
||||
if (text !== undefined && option !== undefined) {
|
||||
if (text === undefined && option === undefined) {
|
||||
throw new Error('Either text or option is necessary.')
|
||||
}
|
||||
|
||||
let newMsg = await this.client.rest.patch(CHANNEL_MESSAGE(this.id, typeof message == "string" ? message : message.id), {
|
||||
content: text,
|
||||
embed: option?.embed.toJSON(),
|
||||
file: option?.file,
|
||||
tts: option?.tts,
|
||||
allowed_mentions: option?.allowedMention
|
||||
}) as MessagePayload
|
||||
if (this.client.user === undefined) {
|
||||
throw new Error('Client user has not initialized.')
|
||||
}
|
||||
|
||||
const newMsg = await this.client.rest.patch(
|
||||
CHANNEL_MESSAGE(
|
||||
this.id,
|
||||
typeof message === 'string' ? message : message.id
|
||||
),
|
||||
{
|
||||
content: text,
|
||||
embed: option?.embed.toJSON(),
|
||||
file: option?.file,
|
||||
tts: option?.tts,
|
||||
allowed_mentions: option?.allowedMention
|
||||
}
|
||||
)
|
||||
|
||||
// TODO: Actually construct this object
|
||||
let mentions = new MessageMentions()
|
||||
const mentions = new MessageMentions()
|
||||
|
||||
return new Message(this.client, newMsg, this, this.client.user as User, mentions)
|
||||
return new Message(this.client, newMsg, this, this.client.user, mentions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { UserPayload } from '../types/user.ts'
|
||||
import { Base } from './base.ts'
|
||||
|
@ -18,8 +17,8 @@ export class User extends Base {
|
|||
premiumType?: 0 | 1 | 2
|
||||
publicFlags?: number
|
||||
|
||||
get tag(): string {
|
||||
return `${this.username}#${this.discriminator}`;
|
||||
get tag (): string {
|
||||
return `${this.username}#${this.discriminator}`
|
||||
}
|
||||
|
||||
get nickMention (): string {
|
||||
|
@ -65,7 +64,7 @@ export class User extends Base {
|
|||
this.publicFlags = data.public_flags ?? this.publicFlags
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.mention;
|
||||
toString (): string {
|
||||
return this.mention
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import cache from '../models/cache.ts'
|
||||
import { Client } from '../models/client.ts'
|
||||
import { MemberPayload } from '../types/guild.ts'
|
||||
import { VoiceStatePayload } from '../types/voice.ts'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
|
||||
import { EmojiPayload } from './emoji.ts'
|
||||
import { MemberPayload } from './guild.ts'
|
||||
import { ActivityPayload, PresenceUpdatePayload } from './presence.ts'
|
||||
import { ActivityPayload } from './presence.ts'
|
||||
import { RolePayload } from './role.ts'
|
||||
import { UserPayload } from './user.ts'
|
||||
|
||||
|
@ -100,7 +100,7 @@ enum GatewayEvents {
|
|||
Webhooks_Update = 'WEBHOOKS_UPDATE'
|
||||
}
|
||||
|
||||
interface IdentityPayload {
|
||||
export interface IdentityPayload {
|
||||
token: string
|
||||
properties: IdentityConnection
|
||||
compress?: boolean
|
||||
|
@ -119,19 +119,19 @@ enum UpdateStatus {
|
|||
offline = 'offline'
|
||||
}
|
||||
|
||||
interface IdentityConnection {
|
||||
export interface IdentityConnection {
|
||||
$os: 'darwin' | 'windows' | 'linux' | 'custom os'
|
||||
$browser: 'discord.deno'
|
||||
$device: 'discord.deno'
|
||||
}
|
||||
|
||||
interface Resume {
|
||||
export interface Resume {
|
||||
token: string
|
||||
session_id: string
|
||||
seq: number
|
||||
}
|
||||
|
||||
interface GuildRequestMembers {
|
||||
export interface GuildRequestMembers {
|
||||
guild_id: string | string[]
|
||||
query?: string
|
||||
limit: number
|
||||
|
@ -140,25 +140,25 @@ interface GuildRequestMembers {
|
|||
nonce?: string
|
||||
}
|
||||
|
||||
interface GatewayVoiceStateUpdate {
|
||||
export interface GatewayVoiceStateUpdate {
|
||||
guild_id: string
|
||||
channel_id: string
|
||||
self_mute: boolean
|
||||
self_deaf: boolean
|
||||
}
|
||||
|
||||
interface GatewayStatusUpdate {
|
||||
export interface GatewayStatusUpdate {
|
||||
since: number | undefined
|
||||
activities: ActivityPayload[]
|
||||
status: string
|
||||
afk: boolean
|
||||
}
|
||||
|
||||
interface Hello {
|
||||
export interface Hello {
|
||||
heartbeat_interval: number
|
||||
}
|
||||
|
||||
interface ReadyEvent {
|
||||
export interface ReadyEvent {
|
||||
v: number
|
||||
user: UserPayload
|
||||
privateChannels: []
|
||||
|
@ -167,40 +167,40 @@ interface ReadyEvent {
|
|||
shard?: number[]
|
||||
}
|
||||
|
||||
interface ChannelPinsUpdate {
|
||||
export interface ChannelPinsUpdatePayload {
|
||||
guild_id?: string
|
||||
channel_id: string
|
||||
last_pin_timestamp?: string
|
||||
}
|
||||
|
||||
interface GuildBanAdd {
|
||||
export interface GuildBanAddPayload {
|
||||
guild_id: string
|
||||
user: UserPayload
|
||||
}
|
||||
|
||||
interface GuildBanRemove {
|
||||
export interface GuildBanRemovePayload {
|
||||
guild_id: string
|
||||
user: UserPayload
|
||||
}
|
||||
|
||||
interface GuildEmojiUpdate {
|
||||
export interface GuildEmojiUpdatePayload {
|
||||
guild_id: string
|
||||
emojis: []
|
||||
}
|
||||
|
||||
interface GuildIntegrationsUpdate {
|
||||
export interface GuildIntegrationsUpdatePayload {
|
||||
guild_id: string
|
||||
}
|
||||
|
||||
interface GuildMemberAddExtra {
|
||||
export interface GuildMemberAddPayload {
|
||||
guild_id: string
|
||||
}
|
||||
|
||||
interface GuildMemberRemove {
|
||||
export interface GuildMemberRemovePayload {
|
||||
guild_id: string
|
||||
user: UserPayload
|
||||
}
|
||||
interface GuildMemberUpdate {
|
||||
export interface GuildMemberUpdatePayload {
|
||||
guild_id: string
|
||||
roles: string[]
|
||||
user: UserPayload
|
||||
|
@ -209,7 +209,7 @@ interface GuildMemberUpdate {
|
|||
premium_since?: string | undefined
|
||||
}
|
||||
|
||||
interface GuildMemberChunk {
|
||||
export interface GuildMemberChunkPayload {
|
||||
guild_id: string
|
||||
members: MemberPayload[]
|
||||
chunk_index: number
|
||||
|
@ -219,22 +219,22 @@ interface GuildMemberChunk {
|
|||
nonce?: string
|
||||
}
|
||||
|
||||
interface GuildRoleCreate {
|
||||
export interface GuildRoleCreatePayload {
|
||||
guild_id: string
|
||||
role: RolePayload
|
||||
}
|
||||
|
||||
interface GuildRoleUpdate {
|
||||
export interface GuildRoleUpdatePayload {
|
||||
guild_id: string
|
||||
role: RolePayload
|
||||
}
|
||||
|
||||
interface GuildRoleDelete {
|
||||
export interface GuildRoleDeletePayload {
|
||||
guild_id: string
|
||||
role_id: string
|
||||
}
|
||||
|
||||
interface InviteCreate {
|
||||
export interface InviteCreatePayload {
|
||||
channel_id: string
|
||||
code: string
|
||||
created_at: string
|
||||
|
@ -248,25 +248,25 @@ interface InviteCreate {
|
|||
uses: number
|
||||
}
|
||||
|
||||
interface InviteDelete {
|
||||
export interface InviteDeletePayload {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
code: string
|
||||
}
|
||||
|
||||
interface MessageDelete {
|
||||
export interface MessageDeletePayload {
|
||||
id: string
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
}
|
||||
|
||||
interface MessageDeleteBulk {
|
||||
export interface MessageDeleteBulkPayload {
|
||||
ids: string[]
|
||||
channel_id: string
|
||||
guild_id: string
|
||||
}
|
||||
|
||||
interface MessageReactionAdd {
|
||||
export interface MessageReactionAddPayload {
|
||||
user_id: string
|
||||
channel_id: string
|
||||
message_id: string
|
||||
|
@ -274,7 +274,7 @@ interface MessageReactionAdd {
|
|||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface MessageReactionRemove {
|
||||
export interface MessageReactionRemovePayload {
|
||||
user_id: string
|
||||
channel_id: string
|
||||
message_id: string
|
||||
|
@ -282,21 +282,13 @@ interface MessageReactionRemove {
|
|||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface MessageReactionRemoveAll {
|
||||
export interface MessageReactionRemoveAllPayload {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
message_id: string
|
||||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface MessageReactionRemove {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
message_id: string
|
||||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface PresenceUpdate {
|
||||
export interface PresenceUpdatePayload {
|
||||
user: UserPayload
|
||||
guild_id: string
|
||||
status: string
|
||||
|
@ -304,76 +296,7 @@ interface PresenceUpdate {
|
|||
client_status: UpdateStatus[]
|
||||
}
|
||||
|
||||
interface CilentStatus {
|
||||
desktop?: string
|
||||
moblie?: string
|
||||
web?: string
|
||||
}
|
||||
|
||||
interface Activity {
|
||||
name: string
|
||||
type: number
|
||||
url?: string | undefined
|
||||
created_at: number
|
||||
timestamps?: string
|
||||
application_id: string
|
||||
details?: string | undefined
|
||||
state?: string | undefined
|
||||
emoji?: EmojiPayload | undefined
|
||||
party?: ActivityParty
|
||||
assets?: ActivityAssets
|
||||
secrets?: ActivitySecrets
|
||||
instance?: boolean
|
||||
flags?: number
|
||||
}
|
||||
|
||||
enum ActivityTypes {
|
||||
GAME = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
CUSTOM = 4,
|
||||
COMPETING = 5
|
||||
}
|
||||
|
||||
interface ActivityTimestamps {
|
||||
start?: number
|
||||
end?: number
|
||||
}
|
||||
|
||||
interface ActivityEmoji {
|
||||
name: string
|
||||
id?: string
|
||||
animated?: boolean
|
||||
}
|
||||
|
||||
interface ActivityParty {
|
||||
id?: string
|
||||
size?: number[]
|
||||
}
|
||||
|
||||
interface ActivityAssets {
|
||||
large_image?: string
|
||||
large_text?: string
|
||||
small_image?: string
|
||||
small_text?: string
|
||||
}
|
||||
|
||||
interface ActivitySecrets {
|
||||
join?: string
|
||||
spectate?: string
|
||||
match?: string
|
||||
}
|
||||
|
||||
enum ActivityFlags {
|
||||
INSTANCE = 1 << 0,
|
||||
JOIN = 1 << 1,
|
||||
SPECTATE = 1 << 2,
|
||||
JOIN_REQUEST = 1 << 3,
|
||||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5
|
||||
}
|
||||
|
||||
interface TypeStart {
|
||||
export interface TypeStart {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
user_id: string
|
||||
|
@ -381,16 +304,22 @@ interface TypeStart {
|
|||
member?: MemberPayload
|
||||
}
|
||||
|
||||
interface VoiceServerUpdate {
|
||||
export interface VoiceServerUpdatePayload {
|
||||
token: string
|
||||
guild_id: string
|
||||
endpoint: string
|
||||
}
|
||||
|
||||
interface WebhooksUpdate {
|
||||
export interface WebhooksUpdatePayload {
|
||||
guild_id: string
|
||||
channel_id: string
|
||||
}
|
||||
|
||||
// https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields
|
||||
export { GatewayCloseCodes, GatewayOpcodes, GatewayIntents, GatewayEvents }
|
||||
export {
|
||||
GatewayCloseCodes,
|
||||
GatewayOpcodes,
|
||||
GatewayIntents,
|
||||
GatewayEvents,
|
||||
UpdateStatus
|
||||
}
|
||||
|
|
|
@ -1,396 +0,0 @@
|
|||
// https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway
|
||||
// https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
|
||||
import { EmojiPayload } from './emoji.ts'
|
||||
import { MemberPayload } from './guild.ts'
|
||||
import { ActivityPayload, PresenceUpdatePayload } from './presence.ts'
|
||||
import { RolePayload } from './role.ts'
|
||||
import { UserPayload } from './user.ts'
|
||||
|
||||
/**
|
||||
* Gateway OPcodes from Discord docs.
|
||||
*/
|
||||
enum GatewayOpcodes { // 문서를 확인해본 결과 Opcode 5번은 비어있다. - UnderC -
|
||||
DISPATCH = 0,
|
||||
HEARTBEAT = 1,
|
||||
IDENTIFY = 2,
|
||||
PRESENCE_UPDATE = 3,
|
||||
VOICE_STATE_UPDATE = 4,
|
||||
RESUME = 6,
|
||||
RECONNECT = 7,
|
||||
REQUEST_GUILD_MEMBERS = 8,
|
||||
INVALID_SESSION = 9,
|
||||
HELLO = 10,
|
||||
HEARTBEAT_ACK = 11
|
||||
}
|
||||
|
||||
/**
|
||||
* Gateway Close Codes from Discord docs.
|
||||
*/
|
||||
enum GatewayCloseCodes {
|
||||
UNKNOWN_ERROR = 4000,
|
||||
UNKNOWN_OPCODE = 4001,
|
||||
DECODE_ERROR = 4002,
|
||||
NOT_AUTHENTICATED = 4003,
|
||||
AUTHENTICATION_FAILED = 4004,
|
||||
ALREADY_AUTHENTICATED = 4005,
|
||||
INVALID_SEQ = 4007,
|
||||
RATE_LIMITED = 4008,
|
||||
SESSION_TIMED_OUT = 4009,
|
||||
INVALID_SHARD = 4010,
|
||||
SHARDING_REQUIRED = 4011,
|
||||
INVALID_API_VERSION = 4012,
|
||||
INVALID_INTENTS = 4013,
|
||||
DISALLOWED_INTENTS = 4014
|
||||
}
|
||||
|
||||
enum GatewayIntents {
|
||||
GUILDS = 1 << 0,
|
||||
GUILD_MEMBERS = 1 << 1,
|
||||
GUILD_BANS = 1 << 2,
|
||||
GUILD_EMOJIS = 1 << 3,
|
||||
GUILD_INTEGRATIONS = 1 << 4,
|
||||
GUILD_WEBHOOKS = 1 << 5,
|
||||
GUILD_INVITES = 1 << 6,
|
||||
GUILD_VOICE_STATES = 1 << 7,
|
||||
GUILD_PRESENCES = 1 << 8,
|
||||
GUILD_MESSAGES = 1 << 9,
|
||||
GUILD_MESSAGE_REACTIONS = 1 << 10,
|
||||
GUILD_MESSAGE_TYPING = 1 << 11,
|
||||
DIRECT_MESSAGES = 1 << 12,
|
||||
DIRECT_MESSAGE_REACTIONS = 1 << 13,
|
||||
DIRECT_MESSAGE_TYPING = 1 << 13
|
||||
}
|
||||
|
||||
enum GatewayEvents {
|
||||
Ready = 'READY',
|
||||
Resumed = 'RESUMED',
|
||||
Reconnect = 'RECONNECT',
|
||||
Channel_Create = 'CHANNEL_CREATE',
|
||||
Channel_Update = 'CHANNEL_UPDATE',
|
||||
Channel_Delete = 'CHANNEL_DELETE',
|
||||
Channel_Pins_Update = 'CHANNEL_PINS_UPDATE',
|
||||
Guild_Create = 'GUILD_CREATE',
|
||||
Guild_Update = 'GUILD_UPDATE',
|
||||
Guild_Delete = 'GUILD_DELETE',
|
||||
Guild_Ban_Add = 'GUILD_BAN_ADD',
|
||||
Guild_Ban_Remove = 'GUILD_BAN_REMOVE',
|
||||
Guild_Emojis_Update = 'GUILD_EMOJIS_UPDATE',
|
||||
Guild_Integrations_Update = 'GUILD_INTEGRATIONS_UPDATE',
|
||||
Guild_Member_Add = 'GUILD_MEMBER_ADD',
|
||||
Guild_Member_Remove = 'GUILD_MEMBER_REMOVE',
|
||||
Guild_Member_Update = 'GUILD_MEMBER_UPDATE',
|
||||
Guild_Members_Chunk = 'GUILD_MEMBERS_CHUNK',
|
||||
Guild_Role_Create = 'GUILD_ROLE_CREATE',
|
||||
Guild_Role_Update = 'GUILD_ROLE_UPDATE',
|
||||
Guild_Role_Delete = 'GUILD_ROLE_DELETE',
|
||||
Invite_Create = 'INVITE_CREATE',
|
||||
Invite_Delete = 'INVITE_DELETE',
|
||||
Message_Create = 'MESSAGE_CREATE',
|
||||
Message_Update = 'MESSAGE_UPDATE',
|
||||
Message_Delete = 'MESSAGE_DELETE',
|
||||
Message_Delete_Bulk = 'MESSAGE_DELETE_BULK',
|
||||
Message_Reaction_Add = 'MESSAGE_REACTION_ADD',
|
||||
Message_Reaction_Remove = 'MESSAGE_REACTION_REMOVE',
|
||||
Message_Reaction_Remove_All = 'MESSAGE_REACTION_REMOVE_ALL',
|
||||
Message_Reaction_Remove_Emoji = 'MESSAGE_REACTION_REMOVE_EMOJI',
|
||||
Presence_Update = 'PRESENCE_UPDATE',
|
||||
Typing_Start = 'TYPING_START',
|
||||
User_Update = 'USER_UPDATE',
|
||||
Voice_Server_Update = 'VOICE_SERVER_UPDATE',
|
||||
Webhooks_Update = 'WEBHOOKS_UPDATE'
|
||||
}
|
||||
|
||||
interface IdentityPayload {
|
||||
token: string
|
||||
properties: IdentityConnection
|
||||
compress?: boolean
|
||||
large_threshold?: number
|
||||
shard?: number[]
|
||||
presence?: UpdateStatus
|
||||
guildSubscriptions?: boolean
|
||||
intents: number
|
||||
}
|
||||
|
||||
enum UpdateStatus {
|
||||
online = 'online',
|
||||
dnd = 'dnd',
|
||||
afk = 'idle',
|
||||
invisible = 'invisible',
|
||||
offline = 'offline'
|
||||
}
|
||||
|
||||
interface IdentityConnection {
|
||||
$os: 'darwin' | 'windows' | 'linux' | 'custom os'
|
||||
$browser: 'discord.deno'
|
||||
$device: 'discord.deno'
|
||||
}
|
||||
|
||||
interface Resume {
|
||||
token: string
|
||||
session_id: string
|
||||
seq: number
|
||||
}
|
||||
|
||||
interface GuildRequestMembers {
|
||||
guild_id: string | string[]
|
||||
query?: string
|
||||
limit: number
|
||||
presences?: boolean
|
||||
user_ids?: string | string[]
|
||||
nonce?: string
|
||||
}
|
||||
|
||||
interface GatewayVoiceStateUpdate {
|
||||
guild_id: string
|
||||
channel_id: string
|
||||
self_mute: boolean
|
||||
self_deaf: boolean
|
||||
}
|
||||
|
||||
interface GatewayStatusUpdate {
|
||||
since: number | undefined
|
||||
activities: ActivityPayload[]
|
||||
status: string
|
||||
afk: boolean
|
||||
}
|
||||
|
||||
interface Hello {
|
||||
heartbeat_interval: number
|
||||
}
|
||||
|
||||
interface ReadyEvent {
|
||||
v: number
|
||||
user: UserPayload
|
||||
privateChannels: []
|
||||
guilds: []
|
||||
session_id: string
|
||||
shard?: number[]
|
||||
}
|
||||
|
||||
interface ChannelPinsUpdate {
|
||||
guild_id?: string
|
||||
channel_id: string
|
||||
last_pin_timestamp?: string
|
||||
}
|
||||
|
||||
interface GuildBanAdd {
|
||||
guild_id: string
|
||||
user: UserPayload
|
||||
}
|
||||
|
||||
interface GuildBanRemove {
|
||||
guild_id: string
|
||||
user: UserPayload
|
||||
}
|
||||
|
||||
interface GuildEmojiUpdate {
|
||||
guild_id: string
|
||||
emojis: []
|
||||
}
|
||||
|
||||
interface GuildIntegrationsUpdate {
|
||||
guild_id: string
|
||||
}
|
||||
|
||||
interface GuildMemberAddExtra {
|
||||
guild_id: string
|
||||
}
|
||||
|
||||
interface GuildMemberRemove {
|
||||
guild_id: string
|
||||
user: UserPayload
|
||||
}
|
||||
interface GuildMemberUpdate {
|
||||
guild_id: string
|
||||
roles: string[]
|
||||
user: UserPayload
|
||||
nick?: string | undefined
|
||||
joined_at: string
|
||||
premium_since?: string | undefined
|
||||
}
|
||||
|
||||
interface GuildMemberChunk {
|
||||
guild_id: string
|
||||
members: MemberPayload[]
|
||||
chunk_index: number
|
||||
chunk_count: number
|
||||
not_found?: []
|
||||
presences?: PresenceUpdatePayload[]
|
||||
nonce?: string
|
||||
}
|
||||
|
||||
interface GuildRoleCreate {
|
||||
guild_id: string
|
||||
role: RolePayload
|
||||
}
|
||||
|
||||
interface GuildRoleUpdate {
|
||||
guild_id: string
|
||||
role: RolePayload
|
||||
}
|
||||
|
||||
interface GuildRoleDelete {
|
||||
guild_id: string
|
||||
role_id: string
|
||||
}
|
||||
|
||||
interface InviteCreate {
|
||||
channel_id: string
|
||||
code: string
|
||||
created_at: string
|
||||
guild_id?: string
|
||||
inviter?: UserPayload
|
||||
max_age: number
|
||||
max_uses: number
|
||||
target_user?: UserPayload
|
||||
target_user_type?: number
|
||||
temporary: boolean
|
||||
uses: number
|
||||
}
|
||||
|
||||
interface InviteDelete {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
code: string
|
||||
}
|
||||
|
||||
interface MessageDelete {
|
||||
id: string
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
}
|
||||
|
||||
interface MessageDeleteBulk {
|
||||
ids: string[]
|
||||
channel_id: string
|
||||
guild_id: string
|
||||
}
|
||||
|
||||
interface MessageReactionAdd {
|
||||
user_id: string
|
||||
channel_id: string
|
||||
message_id: string
|
||||
guild_id?: string
|
||||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface MessageReactionRemove {
|
||||
user_id: string
|
||||
channel_id: string
|
||||
message_id: string
|
||||
guild_id?: string
|
||||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface MessageReactionRemoveAll {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
message_id: string
|
||||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface MessageReactionRemove {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
message_id: string
|
||||
emoji: EmojiPayload
|
||||
}
|
||||
|
||||
interface PresenceUpdate {
|
||||
user: UserPayload
|
||||
guild_id: string
|
||||
status: string
|
||||
activities: ActivityPayload[]
|
||||
client_status: UpdateStatus[]
|
||||
}
|
||||
|
||||
interface CilentStatus {
|
||||
desktop?: string
|
||||
moblie?: string
|
||||
web?: string
|
||||
}
|
||||
|
||||
interface Activity {
|
||||
name: string
|
||||
type: number
|
||||
url?: string | undefined
|
||||
created_at: number
|
||||
timestamps?: string
|
||||
application_id: string
|
||||
details?: string | undefined
|
||||
state?: string | undefined
|
||||
emoji?: EmojiPayload | undefined
|
||||
party?: ActivityParty
|
||||
assets?: ActivityAssets
|
||||
secrets?: ActivitySecrets
|
||||
instance?: boolean
|
||||
flags?: number
|
||||
}
|
||||
|
||||
enum ActivityTypes {
|
||||
GAME = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
CUSTOM = 4,
|
||||
COMPETING = 5
|
||||
}
|
||||
|
||||
interface ActivityTimestamps {
|
||||
start?: number
|
||||
end?: number
|
||||
}
|
||||
|
||||
interface ActivityEmoji {
|
||||
name: string
|
||||
id?: string
|
||||
animated?: boolean
|
||||
}
|
||||
|
||||
interface ActivityParty {
|
||||
id?: string
|
||||
size?: number[]
|
||||
}
|
||||
|
||||
interface ActivityAssets {
|
||||
large_image?: string
|
||||
large_text?: string
|
||||
small_image?: string
|
||||
small_text?: string
|
||||
}
|
||||
|
||||
interface ActivitySecrets {
|
||||
join?: string
|
||||
spectate?: string
|
||||
match?: string
|
||||
}
|
||||
|
||||
enum ActivityFlags {
|
||||
INSTANCE = 1 << 0,
|
||||
JOIN = 1 << 1,
|
||||
SPECTATE = 1 << 2,
|
||||
JOIN_REQUEST = 1 << 3,
|
||||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5
|
||||
}
|
||||
|
||||
interface TypeStart {
|
||||
channel_id: string
|
||||
guild_id?: string
|
||||
user_id: string
|
||||
timestamp: number
|
||||
member?: MemberPayload
|
||||
}
|
||||
|
||||
interface VoiceServerUpdate {
|
||||
token: string
|
||||
guild_id: string
|
||||
endpoint: string
|
||||
}
|
||||
|
||||
interface WebhooksUpdate {
|
||||
guild_id: string
|
||||
channel_id: string
|
||||
}
|
||||
|
||||
// https://discord.com/developers/docs/topics/gateway#typing-start-typing-start-event-fields
|
||||
export { GatewayCloseCodes, GatewayOpcodes, GatewayIntents, GatewayEvents }
|
|
@ -39,3 +39,5 @@ enum PermissionFlags {
|
|||
MANAGE_WEBHOOKS = 0x20000000,
|
||||
MANAGE_EMOJIS = 0x40000000
|
||||
}
|
||||
|
||||
export { PermissionFlags }
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
import { UserPayload } from './user.ts'
|
||||
|
||||
export interface PresenceUpdatePayload {
|
||||
user: UserPayload
|
||||
guild_id: string
|
||||
status: string
|
||||
activities: ActivityPayload
|
||||
client_status: ClientStatus
|
||||
}
|
||||
|
||||
interface ClientStatus {
|
||||
export interface ClientStatus {
|
||||
desktop?: string
|
||||
mobile?: string
|
||||
web?: string
|
||||
|
@ -31,30 +21,30 @@ export interface ActivityPayload {
|
|||
flags?: number
|
||||
}
|
||||
|
||||
interface ActivityTimestamps {
|
||||
export interface ActivityTimestamps {
|
||||
start?: number
|
||||
end?: number
|
||||
}
|
||||
|
||||
interface ActivityEmoji {
|
||||
export interface ActivityEmoji {
|
||||
name: string
|
||||
id?: string
|
||||
animated?: boolean
|
||||
}
|
||||
|
||||
interface ActivityParty {
|
||||
export interface ActivityParty {
|
||||
id?: string
|
||||
size?: number[]
|
||||
}
|
||||
|
||||
interface ActivityAssets {
|
||||
export interface ActivityAssets {
|
||||
large_image?: string
|
||||
large_text?: string
|
||||
small_image?: string
|
||||
small_text?: string
|
||||
}
|
||||
|
||||
interface ActivitySecrets {
|
||||
export interface ActivitySecrets {
|
||||
join?: string
|
||||
spectate?: string
|
||||
match?: string
|
||||
|
@ -68,3 +58,5 @@ enum ActivityFlags {
|
|||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5
|
||||
}
|
||||
|
||||
export { ActivityFlags }
|
||||
|
|
Loading…
Reference in a new issue