mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-01 16:30:08 +00:00
@@ -1,31 +1,47 @@
|
||||
import Client from '../module/Client.ts'
|
||||
import Client from "../module/Client.ts";
|
||||
import { RequestMethod } from "../types/fetch";
|
||||
|
||||
type RequestBody = string | Blob | ArrayBufferView | ArrayBuffer | FormData | URLSearchParams | null | undefined;
|
||||
|
||||
class RequestManager {
|
||||
client: Client
|
||||
token: string
|
||||
client: Client;
|
||||
token: string;
|
||||
|
||||
constructor(client: Client, token: string) {
|
||||
this.client = client
|
||||
this.token = token
|
||||
}
|
||||
|
||||
async get(url: string, payload?: unknown) {
|
||||
// THIS IS IMPORTANT. It keeps clean stack errors in the users own files to better help debug errors.
|
||||
// const stackHolder = {};
|
||||
// TODO: Figure out why this doesnt work
|
||||
// Error.captureStackTrace(stackHolder)
|
||||
async get(url: string) {
|
||||
const headers = this.getDiscordHeaders();
|
||||
return fetch(url, { headers }).then(res => res.json())
|
||||
}
|
||||
|
||||
// let attempts = 0
|
||||
const headers = {
|
||||
Authorization: this.token,
|
||||
'User-Agent': `DiscordBot (https://github.com/skillz4killz/discordeno, 0.0.1)`
|
||||
}
|
||||
async post (url: string, body: RequestBody) {
|
||||
const headers = this.getDiscordHeaders();
|
||||
return fetch(url, {
|
||||
method: RequestMethod.Post,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
}
|
||||
|
||||
console.log('payload', payload)
|
||||
async delete (url: string, body: RequestBody) {
|
||||
const headers = this.getDiscordHeaders();
|
||||
return fetch(url, {
|
||||
method: RequestMethod.Delete,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
}
|
||||
|
||||
const data = await fetch(url, { headers }).then(res => res.json())
|
||||
return data
|
||||
}
|
||||
// The Record type here plays nice with Deno's `fetch.headers` expected type.
|
||||
getDiscordHeaders (): Record<string, string> {
|
||||
return {
|
||||
Authorization: this.token,
|
||||
"User-Agent": `DiscordBot (https://github.com/skillz4killz/discordeno, 0.0.1)`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default RequestManager
|
||||
|
||||
24
mod.ts
24
mod.ts
@@ -1,7 +1,21 @@
|
||||
import Client from './module/Client.ts'
|
||||
import { configs } from './configs.ts'
|
||||
import Client from "./module/Client.ts"
|
||||
import { configs } from "./configs.ts"
|
||||
import { StatusType, GatewayOpcode } from "./types/discord.ts";
|
||||
|
||||
const Discordeno = new Client(configs.token)
|
||||
Discordeno.connect()
|
||||
(async function () {
|
||||
const client = new Client({
|
||||
token: configs.token
|
||||
});
|
||||
|
||||
export default Discordeno
|
||||
const { gateway, connection } = await client.bootstrap();
|
||||
|
||||
for await (const message of connection) {
|
||||
if (message.data?.op === GatewayOpcode.Hello) {
|
||||
await message.action;
|
||||
await gateway.updateStatus({
|
||||
afk: false,
|
||||
status: StatusType.DoNotDisturb
|
||||
})
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
133
module/Client.ts
133
module/Client.ts
@@ -1,6 +1,6 @@
|
||||
import { endpoints } from '../constants/discord.ts'
|
||||
import RequestManager from '../managers/RequestManager.ts'
|
||||
import { DiscordBotGateway, DiscordPayload, DiscordHeartbeatPayload } from '../types/discord.ts'
|
||||
import { DiscordBotGatewayData, DiscordPayload, DiscordHeartbeatPayload, GatewayOpcode } from '../types/discord.ts'
|
||||
import ShardingManager from '../managers/ShardingManager.ts'
|
||||
import {
|
||||
connectWebSocket,
|
||||
@@ -9,11 +9,9 @@ import {
|
||||
isWebSocketPongEvent,
|
||||
WebSocket
|
||||
} from 'https://deno.land/std/ws/mod.ts'
|
||||
// import { encode } from "https://deno.land/std/strings/mod.ts"
|
||||
// import { BufReader } from "https://deno.land/std/io/bufio.ts"
|
||||
// import { TextProtoReader } from "https://deno.land/std/textproto/mod.ts"
|
||||
import { keepDiscordWebsocketAlive, updatePreviousSequenceNumber } from './websocket.ts'
|
||||
import { logGreen, logRed, logYellow, logBlue } from '../utils/logger.ts'
|
||||
import Gateway from './gateway.ts'
|
||||
import { ClientOptions, FulfilledClientOptions } from '../types/options.ts'
|
||||
import { CollectedMessageType } from '../types/message-type.ts'
|
||||
|
||||
class Client {
|
||||
/** The bot's token. This should never be used by end users. It is meant to be used internally to make requests to the Discord API. */
|
||||
@@ -23,35 +21,94 @@ class Client {
|
||||
/** Creates and handles all the shards necessary for the bot. */
|
||||
ShardingManager: ShardingManager
|
||||
|
||||
constructor(token: string) {
|
||||
this.token = `Bot ${token}`
|
||||
this.RequestManager = new RequestManager(this, this.token)
|
||||
/** The options (with defaults) passed to the `Client` constructor. */
|
||||
options: FulfilledClientOptions
|
||||
|
||||
protected authorization: string
|
||||
|
||||
constructor(options: ClientOptions) {
|
||||
// Assign some defaults to the options to make them fulfilled / not annoying to use.
|
||||
this.options = Object.assign(
|
||||
{
|
||||
properties: {
|
||||
$os: '...',
|
||||
$browser: '...',
|
||||
$device: '...'
|
||||
},
|
||||
compress: false
|
||||
},
|
||||
options
|
||||
)
|
||||
this.token = options.token
|
||||
this.authorization = `Bot ${this.options.token}`
|
||||
this.RequestManager = new RequestManager(this, this.authorization)
|
||||
this.ShardingManager = new ShardingManager()
|
||||
}
|
||||
|
||||
/** Begins initial handshake, creates the websocket with Discord and spawns all necessary shards. */
|
||||
async connect() {
|
||||
const data = (await this.RequestManager.get(endpoints.GATEWAY_BOT)) as DiscordBotGateway
|
||||
// Open a WS with the url from discord.
|
||||
const sock = await connectWebSocket(data.url)
|
||||
console.log(sock)
|
||||
logGreen("ws connected! (type 'close' to quit)")
|
||||
getGatewayData() {
|
||||
return this.RequestManager.get(endpoints.GATEWAY_BOT) as Promise<DiscordBotGatewayData>
|
||||
}
|
||||
|
||||
for await (const msg of sock.receive()) {
|
||||
if (typeof msg === 'string') {
|
||||
try {
|
||||
const json = JSON.parse(msg)
|
||||
this.handleDiscordPayload(json, sock)
|
||||
} catch {
|
||||
logRed(`Invalid JSON String send by discord: ${msg}`)
|
||||
createWebsocketConnection(data: DiscordBotGatewayData) {
|
||||
console.log({ data })
|
||||
return connectWebSocket(data.url)
|
||||
}
|
||||
|
||||
async bootstrap() {
|
||||
const data = await this.getGatewayData()
|
||||
const socket = await this.createWebsocketConnection(data);
|
||||
const gateway = new Gateway(socket);
|
||||
const messages = this.collectMessages(gateway);
|
||||
await gateway.identify(this.options);
|
||||
return {
|
||||
data,
|
||||
socket,
|
||||
gateway,
|
||||
messages,
|
||||
connection: this.connect(gateway, data)
|
||||
}
|
||||
}
|
||||
|
||||
async *collectMessages(gateway: Gateway) {
|
||||
const { socket } = gateway
|
||||
for await (const message of socket.receive()) {
|
||||
if (typeof message === 'string') {
|
||||
yield {
|
||||
type: CollectedMessageType.Message,
|
||||
data: JSON.parse(message)
|
||||
}
|
||||
logYellow('< ' + msg)
|
||||
} else if (isWebSocketPingEvent(msg)) {
|
||||
logBlue('< ping')
|
||||
} else if (isWebSocketPongEvent(msg)) {
|
||||
logBlue('< pong')
|
||||
} else if (isWebSocketCloseEvent(msg)) {
|
||||
logRed(`closed: code=${msg.code}, reason=${msg.reason}`)
|
||||
} else if (isWebSocketCloseEvent(message)) {
|
||||
yield { type: CollectedMessageType.Close, ...message }
|
||||
return
|
||||
} else if (isWebSocketPingEvent(message)) {
|
||||
yield { type: CollectedMessageType.Ping }
|
||||
} else if (isWebSocketPongEvent(message)) {
|
||||
yield { type: CollectedMessageType.Pong }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Begins initial handshake, creates the websocket with Discord and spawns all necessary shards. */
|
||||
async *connect(gateway: Gateway, data: DiscordBotGatewayData): AsyncGenerator<{ type: CollectedMessageType, data?: DiscordPayload, action?: Promise<void> }> {
|
||||
for await (const message of this.collectMessages(gateway)) {
|
||||
switch (message.type) {
|
||||
case CollectedMessageType.Ping:
|
||||
console.log('Ping!')
|
||||
yield message;
|
||||
break
|
||||
case CollectedMessageType.Pong:
|
||||
console.log('Pong!')
|
||||
yield message;
|
||||
break
|
||||
case CollectedMessageType.Close:
|
||||
console.log('Close :(', message)
|
||||
yield message;
|
||||
break
|
||||
case CollectedMessageType.Message:
|
||||
await this.handleDiscordPayload(message.data, gateway);
|
||||
yield message;
|
||||
console.log({ yay: true, ...message });
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,15 +116,15 @@ class Client {
|
||||
this.spawnShards(data.shards)
|
||||
}
|
||||
|
||||
handleDiscordPayload(data: DiscordPayload, socket: WebSocket) {
|
||||
handleDiscordPayload(data: DiscordPayload, gateway: Gateway) {
|
||||
switch (data.op) {
|
||||
case 10: // Initial Heartbeat
|
||||
keepDiscordWebsocketAlive(socket, (data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s)
|
||||
break
|
||||
case 11:
|
||||
updatePreviousSequenceNumber(data.s)
|
||||
break
|
||||
}
|
||||
case GatewayOpcode.Hello:
|
||||
console.log('heartbeating...');
|
||||
return gateway.sendConstantHeartbeats((data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s);
|
||||
}
|
||||
|
||||
// Make all code paths return a promise for consistency.
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
spawnShards(total: number, id = 1) {
|
||||
|
||||
3
module/Ratelimiter.ts
Normal file
3
module/Ratelimiter.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class Ratelimiter {
|
||||
|
||||
}
|
||||
56
module/gateway.ts
Normal file
56
module/gateway.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
connectWebSocket,
|
||||
isWebSocketCloseEvent,
|
||||
isWebSocketPingEvent,
|
||||
isWebSocketPongEvent,
|
||||
WebSocket
|
||||
} from "https://deno.land/std/ws/mod.ts";
|
||||
import { GatewayOpcode, Status } from "../types/discord.ts";
|
||||
import { FulfilledClientOptions } from "../types/options.ts";
|
||||
import { delay } from 'https://deno.land/std/util/async.ts';
|
||||
|
||||
export default class Gateway {
|
||||
constructor (public socket: WebSocket) {}
|
||||
|
||||
identify (options: FulfilledClientOptions) {
|
||||
return this.sendObject({
|
||||
op: GatewayOpcode.Identify,
|
||||
d: {
|
||||
token: options.token,
|
||||
// TOOD: Let's get compression working, eh?
|
||||
compress: false,
|
||||
properties: options.properties
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sendHeartbeat (previousSequenceNumber: number | null = null) {
|
||||
return this.sendObject({
|
||||
op: GatewayOpcode.Heartbeat,
|
||||
d: previousSequenceNumber
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus (status: Status) {
|
||||
this.sendObject({
|
||||
op: GatewayOpcode.StatusUpdate,
|
||||
d: status
|
||||
});
|
||||
}
|
||||
|
||||
async sendConstantHeartbeats (interval: number, previousSequenceNumber: number | null = null, shouldContinue: () => boolean = () => true): Promise<void> {
|
||||
await delay(interval);
|
||||
|
||||
if (!shouldContinue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: If the initial seq num is null, this will make it forever null until a restart. Is this good?
|
||||
this.sendHeartbeat(previousSequenceNumber === null ? previousSequenceNumber : previousSequenceNumber++);
|
||||
return this.sendConstantHeartbeats(interval, previousSequenceNumber);
|
||||
}
|
||||
|
||||
sendObject (object: object) {
|
||||
return this.socket.send(JSON.stringify(object));
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { WebSocket } from 'https://deno.land/std/ws/mod.ts'
|
||||
|
||||
let previousSequenceNumber: number | null = null
|
||||
|
||||
export const keepDiscordWebsocketAlive = (
|
||||
socket: WebSocket,
|
||||
millesecondsInterval: number,
|
||||
payload: number | null = null
|
||||
) => {
|
||||
previousSequenceNumber = payload
|
||||
|
||||
setInterval(() => {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
op: 1,
|
||||
d: previousSequenceNumber
|
||||
})
|
||||
)
|
||||
}, millesecondsInterval)
|
||||
}
|
||||
|
||||
export const updatePreviousSequenceNumber = (sequence: number | null = null) => {
|
||||
previousSequenceNumber = sequence
|
||||
}
|
||||
20
structures/activity.ts
Normal file
20
structures/activity.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Timestamps } from "../types/discord.ts";
|
||||
|
||||
export interface ActivityPayload {
|
||||
/** The activity's name */
|
||||
name: string;
|
||||
|
||||
/** */
|
||||
type: number;
|
||||
url?: string;
|
||||
created_at: number;
|
||||
timestamps: Timestamps;
|
||||
details?: string;
|
||||
}
|
||||
|
||||
export enum ActivityType {
|
||||
Game,
|
||||
Streaming,
|
||||
Listening,
|
||||
Custom = 4
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
export interface EmojiPayload {
|
||||
name: string;
|
||||
id?: string;
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
export const createEmoji = (data: unknown) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
@@ -8,239 +8,132 @@ import { createMember } from './member'
|
||||
import { createChannel } from './channel'
|
||||
import { createPresence } from './presence'
|
||||
|
||||
interface CreateGuildPayload {
|
||||
/** The guild id */
|
||||
id: string
|
||||
/** The guild name 2-100 characters */
|
||||
name: string
|
||||
/** The guild icon image hash */
|
||||
icon: string | null
|
||||
/** The guild splash image hash */
|
||||
splash: string | null
|
||||
/** The id of the owner */
|
||||
owner_id: string
|
||||
/** The voice region id for the guild */
|
||||
region: string
|
||||
/** The afk channel id */
|
||||
afk_channel_id: string | null
|
||||
/** AFK Timeout in seconds. */
|
||||
afk_timeout: number
|
||||
/** The verification level required for the guild */
|
||||
verification_level: number
|
||||
/** The roles in the guild */
|
||||
roles: Role[]
|
||||
/** The custom guild emojis */
|
||||
emojis: Emoji[]
|
||||
/** Enabled guild features */
|
||||
features: GuildFeatures[]
|
||||
/** Required MFA level for the guild */
|
||||
mfa_level: number
|
||||
/** The id of the channel to which system mesages are sent */
|
||||
system_channel_id: string | null
|
||||
/** When this guild was joined at */
|
||||
joined_at: string
|
||||
/** Whether this is considered a large guild */
|
||||
large: boolean
|
||||
/** Whether this guild is unavailable */
|
||||
unavailable: boolean
|
||||
/** Total number of members in this guild */
|
||||
member_count: number
|
||||
voice_states: VoiceState[]
|
||||
/** Users in the guild */
|
||||
members: Member[]
|
||||
/** Channels in the guild */
|
||||
channels: Channel[]
|
||||
presences: Presence[]
|
||||
/** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */
|
||||
max_presences?: number | null
|
||||
/** The maximum amount of members for the guild */
|
||||
max_members?: number
|
||||
/** The vanity url code for the guild */
|
||||
vanity_url_code: string | null
|
||||
/** The description for the guild */
|
||||
description: string | null
|
||||
/** The banner hash */
|
||||
banner: string | null
|
||||
/** The premium tier */
|
||||
premium_tier: number
|
||||
/** The total number of users currently boosting this server. */
|
||||
premium_subscription_count: number
|
||||
/** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */
|
||||
preferred_locale: string
|
||||
export interface CreateGuildPayload {
|
||||
/** The guild id */
|
||||
id: string
|
||||
/** The guild name 2-100 characters */
|
||||
name: string
|
||||
/** The guild icon image hash */
|
||||
icon: string | null
|
||||
/** The guild splash image hash */
|
||||
splash: string | null
|
||||
/** The id of the owner */
|
||||
owner_id: string
|
||||
/** The voice region id for the guild */
|
||||
region: string
|
||||
/** The afk channel id */
|
||||
afk_channel_id: string | null
|
||||
/** AFK Timeout in seconds. */
|
||||
afk_timeout: number
|
||||
/** The verification level required for the guild */
|
||||
verification_level: number
|
||||
/** The roles in the guild */
|
||||
roles: Role[]
|
||||
/** The custom guild emojis */
|
||||
emojis: Emoji[]
|
||||
/** Enabled guild features */
|
||||
features: GuildFeatures[]
|
||||
/** Required MFA level for the guild */
|
||||
mfa_level: number
|
||||
/** The id of the channel to which system mesages are sent */
|
||||
system_channel_id: string | null
|
||||
/** When this guild was joined at */
|
||||
joined_at: string
|
||||
/** Whether this is considered a large guild */
|
||||
large: boolean
|
||||
/** Whether this guild is unavailable */
|
||||
unavailable: boolean
|
||||
/** Total number of members in this guild */
|
||||
member_count: number
|
||||
voice_states: VoiceState[]
|
||||
/** Users in the guild */
|
||||
members: Member[]
|
||||
/** Channels in the guild */
|
||||
channels: Channel[]
|
||||
presences: Presence[]
|
||||
/** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */
|
||||
max_presences?: number | null
|
||||
/** The maximum amount of members for the guild */
|
||||
max_members?: number
|
||||
/** The vanity url code for the guild */
|
||||
vanity_url_code: string | null
|
||||
/** The description for the guild */
|
||||
description: string | null
|
||||
/** The banner hash */
|
||||
banner: string | null
|
||||
/** The premium tier */
|
||||
premium_tier: number
|
||||
/** The total number of users currently boosting this server. */
|
||||
premium_subscription_count: number
|
||||
/** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */
|
||||
preferred_locale: string
|
||||
}
|
||||
|
||||
interface Guild {
|
||||
/** The guild id */
|
||||
id: string
|
||||
/** The guild name 2-100 characters */
|
||||
name: string
|
||||
/** The guild icon image hash */
|
||||
icon: string | null
|
||||
/** The guild splash image hash */
|
||||
splash: string | null
|
||||
/** The id of the owner */
|
||||
ownerID: string
|
||||
/** The voice region id for the guild */
|
||||
region: string
|
||||
/** The afk channel id */
|
||||
afkChannelID: string | null
|
||||
/** AFK Timeout in seconds. */
|
||||
afkTimeout: number
|
||||
/** The verification level required for the guild */
|
||||
verificationLevel: number
|
||||
/** The roles in the guild */
|
||||
roles: Role[]
|
||||
/** The custom guild emojis */
|
||||
emojis: Emoji[]
|
||||
/** Enabled guild features */
|
||||
features: GuildFeatures[]
|
||||
/** Required MFA level for the guild */
|
||||
mfaLevel: number
|
||||
/** The id of the channel to which system mesages are sent */
|
||||
systemChannelID: string | null
|
||||
/** When this guild was joined at */
|
||||
joinedAt: number
|
||||
/** Whether this is considered a large guild */
|
||||
large: boolean
|
||||
/** Whether this guild is unavailable */
|
||||
unavailable: boolean
|
||||
/** Total number of members in this guild */
|
||||
memberCount: number
|
||||
voiceStates: VoiceState[]
|
||||
/** Users in the guild */
|
||||
members: Member[]
|
||||
/** Channels in the guild */
|
||||
channels: Channel[]
|
||||
presences: Presence[]
|
||||
/** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */
|
||||
maxPresences?: number | null
|
||||
/** The maximum amount of members for the guild */
|
||||
maxMembers?: number
|
||||
/** The vanity url code for the guild */
|
||||
vanityURLCode: string | null
|
||||
/** The description for the guild */
|
||||
description: string | null
|
||||
/** The banner hash */
|
||||
banner: string | null
|
||||
/** The premium tier */
|
||||
premiumTier: number
|
||||
/** The total number of users currently boosting this server. */
|
||||
premiumSubscriptionCount: number
|
||||
/** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */
|
||||
preferredLocale: string
|
||||
/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */
|
||||
iconURL(size?: ImageSize, format?: ImageFormats): string | undefined
|
||||
/** The full URL of the splash from Discords CDN. Undefined if no splash is set. */
|
||||
splashURL(size?: ImageSize, format?: ImageFormats): string | undefined
|
||||
/** The full URL of the banner from Discords CDN. Undefined if no banner is set. */
|
||||
bannerURL(size?: ImageSize, format?: ImageFormats): string | undefined
|
||||
/** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */
|
||||
createChannel(name: string, options: ChannelCreateOptions): Promise<Channel>
|
||||
/** Create an emoji in the server. Emojis and animated emojis have a maximum file size of 256kb. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a JSON status code. */
|
||||
createEmoji(name: string, image: string, options: CreateEmojisOptions): Promise<Emoji>
|
||||
/** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */
|
||||
editEmoji(id: string, options: EditEmojisOptions): Promise<Emoji>
|
||||
/** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */
|
||||
deleteEmoji(id: string, reason?: string): Promise<void>
|
||||
/** Create a new role for the guild. Requires the MANAGE_ROLES permission. */
|
||||
createRole(options: CreateRoleOptions): Promise<Role>
|
||||
/** Edi a guild role. Requires the MANAGE_ROLES permission. */
|
||||
editRole(id: string, options: CreateRoleOptions): Promise<Role>
|
||||
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
|
||||
deleteRole(id: string): Promise<void>
|
||||
/** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */
|
||||
getPruneCount(days: number): Promise<number>
|
||||
/** Begin pruning all members in the given time period */
|
||||
pruneMembers(days: number): Promise<void>
|
||||
getAuditLogs(options: GetAuditLogsOptions): Promise<AuditLog>
|
||||
|
||||
leaveVoiceChannel(): Promise<void>
|
||||
}
|
||||
|
||||
export interface GetAuditLogsOptions {
|
||||
/** Filter the logs for actions made by this user. */
|
||||
user_id?: string
|
||||
/** The type of audit log. */
|
||||
action_type?: AuditLogType
|
||||
/** Filter the logs before a certain log entry. */
|
||||
before?: string
|
||||
/** How many entries are returned. Between 1-100. Default 50. */
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export type AuditLogType =
|
||||
| `GUILD_UPDATE`
|
||||
| `CHANNEL_CREATE`
|
||||
| `CHANNEL_UPDATE`
|
||||
| `CHANNEL_DELETE`
|
||||
| `CHANNEL_OVERWRITE_CREATE`
|
||||
| `CHANNEL_OVERWRITE_UPDATE`
|
||||
| `CHANNEL_OVERWRITE_DELETE`
|
||||
| `MEMBER_KICK`
|
||||
| `MEMBER_PRUNE`
|
||||
| `MEMBER_BAN_ADD`
|
||||
| `MEMBER_BAN_REMOVE`
|
||||
| `MEMBER_UPDATE`
|
||||
| `MEMBER_ROLE_UPDATE`
|
||||
| `MEMBER_MOVE`
|
||||
| `MEMBER_DISCONNECT`
|
||||
| `BOT_ADD`
|
||||
| `ROLE_CREATE`
|
||||
| `ROLE_UPDATE`
|
||||
| `ROLE_DELETE`
|
||||
| `INVITE_CREATE`
|
||||
| `INVITE_UPDATE`
|
||||
| `INVITE_DELETE`
|
||||
| `WEBHOOK_CREATE`
|
||||
| `WEBHOOK_UPDATE`
|
||||
| `WEBHOOK_DELETE`
|
||||
| `EMOJI_CREATE`
|
||||
| `EMOJI_UPDATE`
|
||||
| `EMOJI_DELETE`
|
||||
| `MESSAGE_DELETE`
|
||||
| `MESSAGE_BULK_DELETE`
|
||||
| `MESSAGE_PIN`
|
||||
| `MESSAGE_UNPIN`
|
||||
| `INTEGRATION_CREATE`
|
||||
| `INTEGRATION_UPDATE`
|
||||
| `INTEGRATION_DELETE`
|
||||
|
||||
export enum AuditLogs {
|
||||
GUILD_UPDATE = 1,
|
||||
CHANNEL_CREATE = 10,
|
||||
CHANNEL_UPDATE,
|
||||
CHANNEL_DELETE,
|
||||
CHANNEL_OVERWRITE_CREATE,
|
||||
CHANNEL_OVERWRITE_UPDATE,
|
||||
CHANNEL_OVERWRITE_DELETE,
|
||||
MEMBER_KICK = 20,
|
||||
MEMBER_PRUNE,
|
||||
MEMBER_BAN_ADD,
|
||||
MEMBER_BAN_REMOVE,
|
||||
MEMBER_UPDATE,
|
||||
MEMBER_ROLE_UPDATE,
|
||||
MEMBER_MOVE,
|
||||
MEMBER_DISCONNECT,
|
||||
BOT_ADD,
|
||||
ROLE_CREATE = 30,
|
||||
ROLE_UPDATE,
|
||||
ROLE_DELETE,
|
||||
INVITE_CREATE = 40,
|
||||
INVITE_UPDATE,
|
||||
INVITE_DELETE,
|
||||
WEBHOOK_CREATE = 50,
|
||||
WEBHOOK_UPDATE,
|
||||
WEBHOOK_DELETE,
|
||||
EMOJI_CREATE = 60,
|
||||
EMOJI_UPDATE,
|
||||
EMOJI_DELETE,
|
||||
MESSAGE_DELETE = 72,
|
||||
MESSAGE_BULK_DELETE,
|
||||
MESSAGE_PIN,
|
||||
MESSAGE_UNPIN,
|
||||
INTEGRATION_CREATE = 80,
|
||||
INTEGRATION_UPDATE,
|
||||
INTEGRATION_DELETE
|
||||
export interface Guild {
|
||||
/** The guild id */
|
||||
id: string
|
||||
/** The guild name 2-100 characters */
|
||||
name: string
|
||||
/** The guild icon image hash */
|
||||
icon: string | null
|
||||
/** The guild splash image hash */
|
||||
splash: string | null
|
||||
/** The id of the owner */
|
||||
ownerID: string
|
||||
/** The voice region id for the guild */
|
||||
region: string
|
||||
/** The afk channel id */
|
||||
afkChannelID: string | null
|
||||
/** AFK Timeout in seconds. */
|
||||
afkTimeout: number
|
||||
/** The verification level required for the guild */
|
||||
verificationLevel: number
|
||||
/** The roles in the guild */
|
||||
roles: Role[]
|
||||
/** The custom guild emojis */
|
||||
emojis: Emoji[]
|
||||
/** Enabled guild features */
|
||||
features: GuildFeatures[]
|
||||
/** Required MFA level for the guild */
|
||||
mfaLevel: number
|
||||
/** The id of the channel to which system mesages are sent */
|
||||
systemChannelID: string | null
|
||||
/** When this guild was joined at */
|
||||
joinedAt: number
|
||||
/** Whether this is considered a large guild */
|
||||
large: boolean
|
||||
/** Whether this guild is unavailable */
|
||||
unavailable: boolean
|
||||
/** Total number of members in this guild */
|
||||
memberCount: number
|
||||
voiceStates: VoiceState[]
|
||||
/** Users in the guild */
|
||||
members: Member[]
|
||||
/** Channels in the guild */
|
||||
channels: Channel[]
|
||||
presences: Presence[]
|
||||
/** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */
|
||||
maxPresences?: number | null
|
||||
/** The maximum amount of members for the guild */
|
||||
maxMembers?: number
|
||||
/** The vanity url code for the guild */
|
||||
vanityURLCode: string | null
|
||||
/** The description for the guild */
|
||||
description: string | null
|
||||
/** The banner hash */
|
||||
banner: string | null
|
||||
/** The premium tier */
|
||||
premiumTier: number
|
||||
/** The total number of users currently boosting this server. */
|
||||
premiumSubscriptionCount: number
|
||||
/** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */
|
||||
preferredLocale: string
|
||||
/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */
|
||||
iconURL(): string | undefined
|
||||
/** The full URL of the splash from Discords CDN. Undefined if no splash is set. */
|
||||
splashURL(): string | undefined
|
||||
/** The full URL of the banner from Discords CDN. Undefined if no banner is set. */
|
||||
bannerURL(): string | undefined
|
||||
}
|
||||
|
||||
export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
import { UserPayload } from "./user.ts";
|
||||
import { ActivityPayload } from "./activity";
|
||||
import { StatusType } from "../types/discord";
|
||||
|
||||
export type PresencePayload = Partial<{
|
||||
/** The user presence is being updated for */
|
||||
user: UserPayload;
|
||||
|
||||
/** Roles this user is in */
|
||||
roles: string[];
|
||||
|
||||
/** Null, or the user's current activity */
|
||||
game: ActivityPayload;
|
||||
|
||||
/** Id of the guild */
|
||||
guild_id: string;
|
||||
|
||||
// This is a deviation from the docs, as it pretty much says `: StatusType`.
|
||||
/** The updated status */
|
||||
status: StatusType;
|
||||
|
||||
/** User's current activities */
|
||||
activities: ActivityPayload[];
|
||||
|
||||
/** User's platform-dependent status */
|
||||
client_status: ClientStatusPayload;
|
||||
|
||||
/** When the user used their Nitro boost on the server */
|
||||
premium_since: string;
|
||||
|
||||
/** This users guild nickname (if one is set) */
|
||||
nick: string;
|
||||
}> & { id: string };
|
||||
|
||||
export interface ClientStatusPayload {
|
||||
/** The user's status set for an active desktop (Windows, Linux, Mac) application session */
|
||||
desktop?: StatusType;
|
||||
|
||||
/** The user's status set for an active mobile (iOS, Android) application session */
|
||||
mobile?: StatusType;
|
||||
|
||||
/** The user's status set for an active web (browser, bot account) application session */
|
||||
web?: StatusType;
|
||||
}
|
||||
|
||||
export const createPresence = (data: unknown) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
34
structures/user.ts
Normal file
34
structures/user.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export interface UserPayload {
|
||||
/** The user's id */
|
||||
id: string;
|
||||
|
||||
/** The user's username, not unique across the platform */
|
||||
username: string;
|
||||
|
||||
/** The user's 4-digit discord-tag */
|
||||
discriminator: string;
|
||||
|
||||
/** The user's avatar hash */
|
||||
avatar: string;
|
||||
|
||||
/** Whether the user belongs to an OAuth2 application */
|
||||
bot?: boolean;
|
||||
|
||||
/** Whether the user is an Official Discord System user (part of the urgent message system) */
|
||||
system?: boolean;
|
||||
|
||||
/** Whether the user has two factor enabled on their account */
|
||||
mfa_enabled?: boolean;
|
||||
|
||||
// Types with "email" scope intentionally left out.
|
||||
/** The flags on a user's account */
|
||||
flags?: number;
|
||||
|
||||
/** The type of Nitro subscription on a user's account */
|
||||
premium_type?: PremiumType;
|
||||
}
|
||||
|
||||
export const enum PremiumType {
|
||||
NitroClassic = 1,
|
||||
Nitro
|
||||
}
|
||||
@@ -1,14 +1,66 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"noUnusedLocals": true /* Report errors on unused locals. */,
|
||||
"noUnusedParameters": true /* Report errors on unused parameters. */,
|
||||
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
|
||||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
|
||||
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"lib": ["ES7", "DOM"], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
"noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// Hack to stop VSCode from suggesting imports without ./ or ../ as a prefix.
|
||||
"baseUrl": "../../", /* Base directory to resolve non-absolute module names. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "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. */
|
||||
|
||||
/* Experimental Options */
|
||||
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,20 @@ export interface DiscordPayload {
|
||||
t?: string
|
||||
}
|
||||
|
||||
export interface DiscordBotGateway {
|
||||
/** The WSS URL that can be used for connecting to the gateway. */
|
||||
url: string
|
||||
/** The recommended number of shards to use when connecting. */
|
||||
shards: number
|
||||
/** Info on the current start limit. */
|
||||
session_start_limit: {
|
||||
/** The total number of session starts the current user is allowed. */
|
||||
total: number
|
||||
/** The remaining number of session starts the current user is allowed. */
|
||||
remaining: number
|
||||
/** Milliseconds left until limit is reset. */
|
||||
reset_after: number
|
||||
}
|
||||
export interface DiscordBotGatewayData {
|
||||
/** The WSS URL that can be used for connecting to the gateway. */
|
||||
url: string
|
||||
/** The recommended number of shards to use when connecting. */
|
||||
shards: number
|
||||
/** Info on the current start limit. */
|
||||
session_start_limit: {
|
||||
/** The total number of session starts the current user is allowed. */
|
||||
total: number
|
||||
/** The remaining number of session starts the current user is allowed. */
|
||||
remaining: number
|
||||
/** Milliseconds left until limit is reset. */
|
||||
reset_after: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface DiscordHeartbeatPayload {
|
||||
@@ -30,17 +30,17 @@ export interface DiscordHeartbeatPayload {
|
||||
}
|
||||
|
||||
export enum GatewayOpcode {
|
||||
Dispatch = 0,
|
||||
Heartbeat,
|
||||
Identify,
|
||||
StatusUpdate,
|
||||
VoiceStateUpdate,
|
||||
Resume,
|
||||
Reconnect,
|
||||
RequestGuildMembers,
|
||||
InvalidSession,
|
||||
Hello,
|
||||
HeartbeatACK
|
||||
Dispatch = 0,
|
||||
Heartbeat,
|
||||
Identify,
|
||||
StatusUpdate,
|
||||
VoiceStateUpdate,
|
||||
Resume = 6,
|
||||
Reconnect,
|
||||
RequestGuildMembers,
|
||||
InvalidSession,
|
||||
Hello,
|
||||
HeartbeatACK
|
||||
}
|
||||
|
||||
export enum GatewayCloseEventCode {
|
||||
@@ -154,3 +154,33 @@ export enum JSONErrorCode {
|
||||
ReactionBlocked = 90001,
|
||||
ResourceOverloaded = 130000
|
||||
}
|
||||
|
||||
export interface Properties {
|
||||
$os: string;
|
||||
$browser: string;
|
||||
$device: string;
|
||||
}
|
||||
|
||||
export interface Timestamps {
|
||||
start?: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
export interface Emoji {
|
||||
name: string;
|
||||
id?: string;
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
export enum StatusType {
|
||||
Online = 'online',
|
||||
DoNotDisturb = 'dnd',
|
||||
Idle = 'idle',
|
||||
Invisible = 'invisible',
|
||||
Offline = 'offline'
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
afk: boolean;
|
||||
status: StatusType;
|
||||
}
|
||||
8
types/fetch.ts
Normal file
8
types/fetch.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const enum RequestMethod {
|
||||
Get = 'get',
|
||||
Post = 'post',
|
||||
Put = 'put',
|
||||
Patch = 'patch',
|
||||
Head = 'head',
|
||||
Delete = 'delete'
|
||||
}
|
||||
6
types/message-type.ts
Normal file
6
types/message-type.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum CollectedMessageType {
|
||||
Ping,
|
||||
Pong,
|
||||
Close,
|
||||
Message
|
||||
}
|
||||
13
types/options.ts
Normal file
13
types/options.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Properties } from "./discord.ts";
|
||||
|
||||
export interface FulfilledClientOptions {
|
||||
token: string;
|
||||
properties: Properties;
|
||||
compress: boolean;
|
||||
}
|
||||
|
||||
export interface ClientOptions {
|
||||
token: string;
|
||||
properties?: Properties;
|
||||
compress?: boolean;
|
||||
}
|
||||
38
types/queue.ts
Normal file
38
types/queue.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { DiscordPayload } from "./discord";
|
||||
import Gateway from "../module/gateway.ts";
|
||||
|
||||
export abstract class ActionQueue<Action> {
|
||||
protected actions: Action[] = [];
|
||||
|
||||
push (action: Action) {
|
||||
if (this.shouldDispatchImmediately(action)) {
|
||||
this.dispatch(action);
|
||||
} else {
|
||||
this.actions.push(action);
|
||||
}
|
||||
}
|
||||
|
||||
dispatchAll () {
|
||||
let index = 0;
|
||||
for (const action of this.actions) {
|
||||
this.actions.splice(index, 1);
|
||||
this.dispatch(action);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
abstract dispatch (action: Action): void;
|
||||
abstract shouldDispatchImmediately (action: Action): boolean;
|
||||
}
|
||||
|
||||
export class GatewayActionQueue extends ActionQueue<DiscordPayload> {
|
||||
constructor (protected gateway: Gateway) {
|
||||
super();
|
||||
}
|
||||
|
||||
dispatch (action: DiscordPayload) {
|
||||
this.gateway.sendObject(action);
|
||||
}
|
||||
|
||||
shouldDispatchImmediately ()
|
||||
}
|
||||
Reference in New Issue
Block a user