collectors, rest options and all that
This commit is contained in:
parent
e9f461fef4
commit
48976e779b
9 changed files with 381 additions and 39 deletions
4
mod.ts
4
mod.ts
|
@ -4,7 +4,9 @@ export { Gateway } from './src/gateway/index.ts'
|
||||||
export type { ClientEvents } from './src/gateway/handlers/index.ts'
|
export type { ClientEvents } from './src/gateway/handlers/index.ts'
|
||||||
export * from './src/models/client.ts'
|
export * from './src/models/client.ts'
|
||||||
export * from './src/models/slashClient.ts'
|
export * from './src/models/slashClient.ts'
|
||||||
export { RESTManager } from './src/models/rest.ts'
|
export { RESTManager, TokenType, HttpResponseCode } from './src/models/rest.ts'
|
||||||
|
export type { RequestHeaders } from './src/models/rest.ts'
|
||||||
|
export type { RESTOptions } from './src/models/rest.ts'
|
||||||
export * from './src/models/cacheAdapter.ts'
|
export * from './src/models/cacheAdapter.ts'
|
||||||
export {
|
export {
|
||||||
Command,
|
Command,
|
||||||
|
|
|
@ -348,4 +348,11 @@ export interface ClientEvents {
|
||||||
* @param message Debug message
|
* @param message Debug message
|
||||||
*/
|
*/
|
||||||
debug: [message: string]
|
debug: [message: string]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw event which gives you access to raw events DISPATCH'd from Gateway
|
||||||
|
* @param evt Event name string
|
||||||
|
* @param payload Payload JSON of the event
|
||||||
|
*/
|
||||||
|
raw: [evt: string, payload: any]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { User } from '../structures/user.ts'
|
import { User } from '../structures/user.ts'
|
||||||
import { GatewayIntents } from '../types/gateway.ts'
|
import { GatewayIntents } from '../types/gateway.ts'
|
||||||
import { Gateway } from '../gateway/index.ts'
|
import { Gateway } from '../gateway/index.ts'
|
||||||
import { RESTManager } from './rest.ts'
|
import { RESTManager, RESTOptions, TokenType } from './rest.ts'
|
||||||
import { EventEmitter } from '../../deps.ts'
|
import { EventEmitter } from '../../deps.ts'
|
||||||
import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts'
|
import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts'
|
||||||
import { UsersManager } from '../managers/users.ts'
|
import { UsersManager } from '../managers/users.ts'
|
||||||
|
@ -20,6 +20,7 @@ import { Application } from '../structures/application.ts'
|
||||||
import { Invite } from '../structures/invite.ts'
|
import { Invite } from '../structures/invite.ts'
|
||||||
import { INVITE } from '../types/endpoint.ts'
|
import { INVITE } from '../types/endpoint.ts'
|
||||||
import { ClientEvents } from '../gateway/handlers/index.ts'
|
import { ClientEvents } from '../gateway/handlers/index.ts'
|
||||||
|
import type { Collector } from './collectors.ts'
|
||||||
|
|
||||||
/** OS related properties sent with Gateway Identify */
|
/** OS related properties sent with Gateway Identify */
|
||||||
export interface ClientProperties {
|
export interface ClientProperties {
|
||||||
|
@ -54,6 +55,10 @@ export interface ClientOptions {
|
||||||
clientProperties?: ClientProperties
|
clientProperties?: ClientProperties
|
||||||
/** Enable/Disable Slash Commands Integration (enabled by default) */
|
/** Enable/Disable Slash Commands Integration (enabled by default) */
|
||||||
enableSlash?: boolean
|
enableSlash?: boolean
|
||||||
|
/** Disable taking token from env if not provided (token is taken from env if present by default) */
|
||||||
|
disableEnvToken?: boolean
|
||||||
|
/** Override REST Options */
|
||||||
|
restOptions?: RESTOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare interface Client {
|
export declare interface Client {
|
||||||
|
@ -89,7 +94,7 @@ export class Client extends EventEmitter {
|
||||||
/** Gateway object */
|
/** Gateway object */
|
||||||
gateway?: Gateway
|
gateway?: Gateway
|
||||||
/** REST Manager - used to make all requests */
|
/** REST Manager - used to make all requests */
|
||||||
rest: RESTManager = new RESTManager(this)
|
rest: RESTManager
|
||||||
/** User which Client logs in to, undefined until logs in */
|
/** User which Client logs in to, undefined until logs in */
|
||||||
user?: User
|
user?: User
|
||||||
/** WebSocket ping of Client */
|
/** WebSocket ping of Client */
|
||||||
|
@ -118,8 +123,6 @@ export class Client extends EventEmitter {
|
||||||
channels: ChannelsManager = new ChannelsManager(this)
|
channels: ChannelsManager = new ChannelsManager(this)
|
||||||
emojis: EmojisManager = new EmojisManager(this)
|
emojis: EmojisManager = new EmojisManager(this)
|
||||||
|
|
||||||
/** Whether the REST Manager will use Canary API or not */
|
|
||||||
canary: boolean = false
|
|
||||||
/** Client's presence. Startup one if set before connecting */
|
/** Client's presence. Startup one if set before connecting */
|
||||||
presence: ClientPresence = new ClientPresence()
|
presence: ClientPresence = new ClientPresence()
|
||||||
_decoratedEvents?: {
|
_decoratedEvents?: {
|
||||||
|
@ -140,6 +143,7 @@ export class Client extends EventEmitter {
|
||||||
shard: number = 0
|
shard: number = 0
|
||||||
/** Shard Manager of this Client if Sharded */
|
/** Shard Manager of this Client if Sharded */
|
||||||
shardManager?: ShardManager
|
shardManager?: ShardManager
|
||||||
|
collectors: Set<Collector> = new Set()
|
||||||
|
|
||||||
constructor(options: ClientOptions = {}) {
|
constructor(options: ClientOptions = {}) {
|
||||||
super()
|
super()
|
||||||
|
@ -153,7 +157,6 @@ export class Client extends EventEmitter {
|
||||||
options.presence instanceof ClientPresence
|
options.presence instanceof ClientPresence
|
||||||
? options.presence
|
? options.presence
|
||||||
: new ClientPresence(options.presence)
|
: new ClientPresence(options.presence)
|
||||||
if (options.canary === true) this.canary = true
|
|
||||||
if (options.messageCacheLifetime !== undefined)
|
if (options.messageCacheLifetime !== undefined)
|
||||||
this.messageCacheLifetime = options.messageCacheLifetime
|
this.messageCacheLifetime = options.messageCacheLifetime
|
||||||
if (options.reactionCacheLifetime !== undefined)
|
if (options.reactionCacheLifetime !== undefined)
|
||||||
|
@ -185,6 +188,27 @@ export class Client extends EventEmitter {
|
||||||
client: this,
|
client: this,
|
||||||
enabled: options.enableSlash
|
enabled: options.enableSlash
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (this.token === undefined) {
|
||||||
|
try {
|
||||||
|
const token = Deno.env.get('DISCORD_TOKEN')
|
||||||
|
if (token !== undefined) {
|
||||||
|
this.token = token
|
||||||
|
this.debug('Info', 'Found token in ENV')
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const restOptions: RESTOptions = {
|
||||||
|
token: () => this.token,
|
||||||
|
tokenType: TokenType.Bot,
|
||||||
|
canary: options.canary,
|
||||||
|
client: this
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.restOptions !== undefined)
|
||||||
|
Object.assign(restOptions, options.restOptions)
|
||||||
|
this.rest = new RESTManager(restOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -244,8 +268,8 @@ export class Client extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is used for connecting to discord.
|
* This function is used for connecting to discord.
|
||||||
* @param token Your token. This is required.
|
* @param token Your token. This is required if not given in ClientOptions.
|
||||||
* @param intents Gateway intents in array. This is required.
|
* @param intents Gateway intents in array. This is required if not given in ClientOptions.
|
||||||
*/
|
*/
|
||||||
connect(token?: string, intents?: GatewayIntents[]): void {
|
connect(token?: string, intents?: GatewayIntents[]): void {
|
||||||
if (token === undefined && this.token !== undefined) token = this.token
|
if (token === undefined && this.token !== undefined) token = this.token
|
||||||
|
@ -262,9 +286,40 @@ export class Client extends EventEmitter {
|
||||||
} else if (intents !== undefined && this.intents === undefined) {
|
} else if (intents !== undefined && this.intents === undefined) {
|
||||||
this.intents = intents
|
this.intents = intents
|
||||||
} else throw new Error('No Gateway Intents were provided')
|
} else throw new Error('No Gateway Intents were provided')
|
||||||
|
|
||||||
|
this.rest.token = token
|
||||||
this.gateway = new Gateway(this, token, intents)
|
this.gateway = new Gateway(this, token, intents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Add a new Collector */
|
||||||
|
addCollector(collector: Collector): boolean {
|
||||||
|
if (this.collectors.has(collector)) return false
|
||||||
|
else {
|
||||||
|
this.collectors.add(collector)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove a Collector */
|
||||||
|
removeCollector(collector: Collector): boolean {
|
||||||
|
if (!this.collectors.has(collector)) return false
|
||||||
|
else {
|
||||||
|
this.collectors.delete(collector)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(event: keyof ClientEvents, ...args: any[]): boolean {
|
||||||
|
const collectors: Collector[] = []
|
||||||
|
for (const collector of this.collectors.values()) {
|
||||||
|
if (collector.event === event) collectors.push(collector)
|
||||||
|
}
|
||||||
|
if (collectors.length !== 0) {
|
||||||
|
this.collectors.forEach((collector) => collector._fire(...args))
|
||||||
|
}
|
||||||
|
return super.emit(event, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
/** Wait for an Event (optionally satisfying an event) to occur */
|
/** Wait for an Event (optionally satisfying an event) to occur */
|
||||||
async waitFor<K extends keyof ClientEvents>(
|
async waitFor<K extends keyof ClientEvents>(
|
||||||
event: K,
|
event: K,
|
||||||
|
@ -293,10 +348,13 @@ export class Client extends EventEmitter {
|
||||||
|
|
||||||
/** Event decorator to create an Event handler from function */
|
/** Event decorator to create an Event handler from function */
|
||||||
export function event(name?: keyof ClientEvents) {
|
export function event(name?: keyof ClientEvents) {
|
||||||
return function (client: Client | Extension, prop: keyof ClientEvents) {
|
return function (
|
||||||
|
client: Client | Extension,
|
||||||
|
prop: keyof ClientEvents | string
|
||||||
|
) {
|
||||||
const listener = ((client as unknown) as {
|
const listener = ((client as unknown) as {
|
||||||
[name in keyof ClientEvents]: (...args: ClientEvents[name]) => any
|
[name in keyof ClientEvents]: (...args: ClientEvents[name]) => any
|
||||||
})[prop]
|
})[name ?? ((prop as unknown) as keyof ClientEvents)]
|
||||||
if (typeof listener !== 'function')
|
if (typeof listener !== 'function')
|
||||||
throw new Error('@event decorator requires a function')
|
throw new Error('@event decorator requires a function')
|
||||||
if (client._decoratedEvents === undefined) client._decoratedEvents = {}
|
if (client._decoratedEvents === undefined) client._decoratedEvents = {}
|
||||||
|
|
162
src/models/collectors.ts
Normal file
162
src/models/collectors.ts
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
import { Collection } from '../utils/collection.ts'
|
||||||
|
import { EventEmitter } from '../../deps.ts'
|
||||||
|
import type { Client } from './client.ts'
|
||||||
|
|
||||||
|
export type CollectorFilter = (...args: any[]) => boolean | Promise<boolean>
|
||||||
|
|
||||||
|
export interface CollectorOptions {
|
||||||
|
/** Event name to listen for */
|
||||||
|
event: string
|
||||||
|
/** Optionally Client object for deinitOnEnd functionality */
|
||||||
|
client?: Client
|
||||||
|
/** Filter function */
|
||||||
|
filter?: CollectorFilter
|
||||||
|
/** Max entries to collect */
|
||||||
|
max?: number
|
||||||
|
/** Whether or not to de-initialize on end */
|
||||||
|
deinitOnEnd?: boolean
|
||||||
|
/** Timeout to end the Collector if not fulfilled if any filter or max */
|
||||||
|
timeout?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Collector extends EventEmitter {
|
||||||
|
client?: Client
|
||||||
|
private _started: boolean = false
|
||||||
|
event: string
|
||||||
|
filter: CollectorFilter = () => true
|
||||||
|
collected: Collection<string, any[]> = new Collection()
|
||||||
|
max?: number
|
||||||
|
deinitOnEnd: boolean = false
|
||||||
|
timeout?: number
|
||||||
|
private _timer?: number
|
||||||
|
|
||||||
|
get started(): boolean {
|
||||||
|
return this._started
|
||||||
|
}
|
||||||
|
|
||||||
|
set started(d: boolean) {
|
||||||
|
if (d !== this._started) {
|
||||||
|
this._started = d
|
||||||
|
if (d) this.emit('start')
|
||||||
|
else {
|
||||||
|
if (this.deinitOnEnd && this.client !== undefined)
|
||||||
|
this.deinit(this.client)
|
||||||
|
this.emit('end')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options: CollectorOptions | string) {
|
||||||
|
super()
|
||||||
|
if (typeof options === 'string') this.event = options
|
||||||
|
else {
|
||||||
|
this.event = options.event
|
||||||
|
this.client = options.client
|
||||||
|
this.filter = options.filter ?? (() => true)
|
||||||
|
this.max = options.max
|
||||||
|
this.deinitOnEnd = options.deinitOnEnd ?? false
|
||||||
|
this.timeout = options.timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start collecting */
|
||||||
|
collect(): Collector {
|
||||||
|
this.started = true
|
||||||
|
if (this.client !== undefined) this.init(this.client)
|
||||||
|
if (this._timer !== undefined) clearTimeout(this._timer)
|
||||||
|
if (this.timeout !== undefined) {
|
||||||
|
this._timer = setTimeout(() => {
|
||||||
|
this.end()
|
||||||
|
}, this.timeout)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** End collecting */
|
||||||
|
end(): Collector {
|
||||||
|
this.started = false
|
||||||
|
if (this._timer !== undefined) clearTimeout(this._timer)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reset collector and start again */
|
||||||
|
reset(): Collector {
|
||||||
|
this.collected = new Collection()
|
||||||
|
this.collect()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Init the Collector on Client */
|
||||||
|
init(client: Client): Collector {
|
||||||
|
this.client = client
|
||||||
|
client.addCollector(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** De initialize the Collector i.e. remove cleanly */
|
||||||
|
deinit(client: Client): Collector {
|
||||||
|
client.removeCollector(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks we may want to perform on an extended version of Collector */
|
||||||
|
protected check(..._args: any[]): boolean | Promise<boolean> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fire the Collector */
|
||||||
|
async _fire(...args: any[]): Promise<void> {
|
||||||
|
if (!this.started) return
|
||||||
|
const check = await this.check(...args)
|
||||||
|
if (!check) return
|
||||||
|
const filter = await this.filter(...args)
|
||||||
|
if (!filter) return
|
||||||
|
this.collected.set((Number(this.collected.size) + 1).toString(), args)
|
||||||
|
this.emit('collect', ...args)
|
||||||
|
if (
|
||||||
|
this.max !== undefined &&
|
||||||
|
// linter: send help
|
||||||
|
this.max < Number(this.collected.size) + 1
|
||||||
|
) {
|
||||||
|
this.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set filter of the Collector */
|
||||||
|
when(filter: CollectorFilter): Collector {
|
||||||
|
this.filter = filter
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a new listener for 'collect' event */
|
||||||
|
each(handler: CallableFunction): Collector {
|
||||||
|
this.on('collect', () => handler())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a Promise resolved when Collector ends or a timeout occurs */
|
||||||
|
async wait(timeout: number = this.timeout ?? 0): Promise<Collector> {
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||||
|
if (!timeout)
|
||||||
|
throw new Error(
|
||||||
|
'Timeout is required parameter if not given in CollectorOptions'
|
||||||
|
)
|
||||||
|
|
||||||
|
let done = false
|
||||||
|
const onend = (): void => {
|
||||||
|
done = true
|
||||||
|
this.removeListener('end', onend)
|
||||||
|
resolve(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.on('end', onend)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!done) {
|
||||||
|
this.removeListener('end', onend)
|
||||||
|
reject(new Error('Timeout'))
|
||||||
|
}
|
||||||
|
}, timeout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import * as baseEndpoints from '../consts/urlsAndVersions.ts'
|
import * as baseEndpoints from '../consts/urlsAndVersions.ts'
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
|
import { Client } from './client.ts'
|
||||||
|
|
||||||
export type RequestMethods =
|
export type RequestMethods =
|
||||||
| 'get'
|
| 'get'
|
||||||
|
@ -60,15 +61,23 @@ export type MethodFunction = (
|
||||||
) => Promise<any>
|
) => Promise<any>
|
||||||
|
|
||||||
export interface APIMap extends MethodFunction {
|
export interface APIMap extends MethodFunction {
|
||||||
|
/** Make a GET request to current route */
|
||||||
get: APIMap
|
get: APIMap
|
||||||
|
/** Make a POST request to current route */
|
||||||
post: APIMap
|
post: APIMap
|
||||||
|
/** Make a PATCH request to current route */
|
||||||
patch: APIMap
|
patch: APIMap
|
||||||
|
/** Make a PUT request to current route */
|
||||||
put: APIMap
|
put: APIMap
|
||||||
|
/** Make a DELETE request to current route */
|
||||||
delete: APIMap
|
delete: APIMap
|
||||||
|
/** Make a HEAD request to current route */
|
||||||
head: APIMap
|
head: APIMap
|
||||||
|
/** Continue building API Route */
|
||||||
[name: string]: APIMap
|
[name: string]: APIMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** API Route builder function */
|
||||||
export const builder = (rest: RESTManager, acum = '/'): APIMap => {
|
export const builder = (rest: RESTManager, acum = '/'): APIMap => {
|
||||||
const routes = {}
|
const routes = {}
|
||||||
const proxy = new Proxy(routes, {
|
const proxy = new Proxy(routes, {
|
||||||
|
@ -94,25 +103,76 @@ export const builder = (rest: RESTManager, acum = '/'): APIMap => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RESTOptions {
|
export interface RESTOptions {
|
||||||
token?: string
|
/** Token to use for authorization */
|
||||||
|
token?: string | (() => string | undefined)
|
||||||
|
/** Headers to patch with if any */
|
||||||
headers?: { [name: string]: string | undefined }
|
headers?: { [name: string]: string | undefined }
|
||||||
|
/** Whether to use Canary instance of Discord API or not */
|
||||||
canary?: boolean
|
canary?: boolean
|
||||||
|
/** Discord REST API version to use */
|
||||||
version?: 6 | 7 | 8
|
version?: 6 | 7 | 8
|
||||||
|
/** Token Type to use for Authorization */
|
||||||
|
tokenType?: TokenType
|
||||||
|
/** User Agent to use (Header) */
|
||||||
|
userAgent?: string
|
||||||
|
/** Optional Harmony client */
|
||||||
|
client?: Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Token Type for REST API. */
|
||||||
|
export enum TokenType {
|
||||||
|
/** Token type for Bot User */
|
||||||
|
Bot = 'Bot',
|
||||||
|
/** Token Type for OAuth2 */
|
||||||
|
Bearer = 'Bearer',
|
||||||
|
/** No Token Type. Can be used for User accounts. */
|
||||||
|
None = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An easier to use interface for interacting with Discord REST API. */
|
||||||
export class RESTManager {
|
export class RESTManager {
|
||||||
client?: RESTOptions
|
|
||||||
queues: { [key: string]: QueuedItem[] } = {}
|
queues: { [key: string]: QueuedItem[] } = {}
|
||||||
rateLimits = new Collection<string, RateLimit>()
|
rateLimits = new Collection<string, RateLimit>()
|
||||||
|
/** Whether we are globally ratelimited or not */
|
||||||
globalRateLimit: boolean = false
|
globalRateLimit: boolean = false
|
||||||
|
/** Whether requests are being processed or not */
|
||||||
processing: boolean = false
|
processing: boolean = false
|
||||||
|
/** API Version being used by REST Manager */
|
||||||
version: number = 8
|
version: number = 8
|
||||||
|
/**
|
||||||
|
* API Map - easy to use way for interacting with Discord API.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* * ```ts
|
||||||
|
* rest.api.users['123'].get().then(userPayload => doSomething)
|
||||||
|
* ```
|
||||||
|
* * ```ts
|
||||||
|
* rest.api.guilds['123'].channels.post({ name: 'my-channel', type: 0 }).then(channelPayload => {})
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
api: APIMap
|
api: APIMap
|
||||||
|
/** Token being used for Authorization */
|
||||||
|
token?: string | (() => string | undefined)
|
||||||
|
/** Token Type of the Token if any */
|
||||||
|
tokenType: TokenType = TokenType.Bot
|
||||||
|
/** Headers object which patch the current ones */
|
||||||
|
headers: any = {}
|
||||||
|
/** Optional custom User Agent (header) */
|
||||||
|
userAgent?: string
|
||||||
|
/** Whether REST Manager is using Canary API */
|
||||||
|
canary?: boolean
|
||||||
|
/** Optional Harmony Client object */
|
||||||
|
client?: Client
|
||||||
|
|
||||||
constructor(client?: RESTOptions) {
|
constructor(options?: RESTOptions) {
|
||||||
this.client = client
|
|
||||||
this.api = builder(this)
|
this.api = builder(this)
|
||||||
if (client?.version !== undefined) this.version = client.version
|
if (options?.token !== undefined) this.token = options.token
|
||||||
|
if (options?.version !== undefined) this.version = options.version
|
||||||
|
if (options?.headers !== undefined) this.headers = options.headers
|
||||||
|
if (options?.tokenType !== undefined) this.tokenType = options.tokenType
|
||||||
|
if (options?.userAgent !== undefined) this.userAgent = options.userAgent
|
||||||
|
if (options?.canary !== undefined) this.canary = options.canary
|
||||||
|
if (options?.client !== undefined) this.client = options.client
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.handleRateLimits()
|
this.handleRateLimits()
|
||||||
}
|
}
|
||||||
|
@ -193,13 +253,16 @@ export class RESTManager {
|
||||||
|
|
||||||
private prepare(body: any, method: RequestMethods): { [key: string]: any } {
|
private prepare(body: any, method: RequestMethods): { [key: string]: any } {
|
||||||
const headers: RequestHeaders = {
|
const headers: RequestHeaders = {
|
||||||
'User-Agent': `DiscordBot (harmony, https://github.com/harmony-org/harmony)`
|
'User-Agent':
|
||||||
|
this.userAgent ??
|
||||||
|
`DiscordBot (harmony, https://github.com/harmony-org/harmony)`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client !== undefined)
|
if (this.token !== undefined) {
|
||||||
headers.Authorization = `Bot ${this.client.token}`
|
const token = typeof this.token === 'string' ? this.token : this.token()
|
||||||
|
if (token !== undefined)
|
||||||
if (this.client?.token === undefined) delete headers.Authorization
|
headers.Authorization = `${this.tokenType} ${token}`.trim()
|
||||||
|
}
|
||||||
|
|
||||||
if (method === 'get' || method === 'head' || method === 'delete')
|
if (method === 'get' || method === 'head' || method === 'delete')
|
||||||
body = undefined
|
body = undefined
|
||||||
|
@ -220,9 +283,7 @@ export class RESTManager {
|
||||||
headers['Content-Type'] = 'application/json'
|
headers['Content-Type'] = 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client?.headers !== undefined)
|
if (this.headers !== undefined) Object.assign(headers, this.headers)
|
||||||
Object.assign(headers, this.client.headers)
|
|
||||||
|
|
||||||
const data: { [name: string]: any } = {
|
const data: { [name: string]: any } = {
|
||||||
headers,
|
headers,
|
||||||
body: body?.file ?? JSON.stringify(body),
|
body: body?.file ?? JSON.stringify(body),
|
||||||
|
@ -305,13 +366,25 @@ export class RESTManager {
|
||||||
body: any,
|
body: any,
|
||||||
data: { [key: string]: any },
|
data: { [key: string]: any },
|
||||||
reject: CallableFunction
|
reject: CallableFunction
|
||||||
): Promise<undefined> {
|
): Promise<void> {
|
||||||
const status = response.status
|
const status = response.status
|
||||||
|
|
||||||
|
// We have hit ratelimit - this should not happen
|
||||||
|
if (status === HttpResponseCode.TooManyRequests) {
|
||||||
|
if (this.client !== undefined)
|
||||||
|
this.client.emit('rateLimit', {
|
||||||
|
method: data.method,
|
||||||
|
url: response.url,
|
||||||
|
body
|
||||||
|
})
|
||||||
|
reject(new Error('RateLimited'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's a normal status code... just continue
|
||||||
if (
|
if (
|
||||||
(status >= 200 && status < 400) ||
|
(status >= 200 && status < 400) ||
|
||||||
status === HttpResponseCode.NoContent ||
|
status === HttpResponseCode.NoContent
|
||||||
status === HttpResponseCode.TooManyRequests
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -322,9 +395,7 @@ export class RESTManager {
|
||||||
|
|
||||||
if (status === HttpResponseCode.Unauthorized)
|
if (status === HttpResponseCode.Unauthorized)
|
||||||
reject(
|
reject(
|
||||||
new DiscordAPIError(
|
new DiscordAPIError(`Request was Unauthorized. Invalid Token.\n${text}`)
|
||||||
`Request was not successful (Unauthorized). Invalid Token.\n${text}`
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// At this point we know it is error
|
// At this point we know it is error
|
||||||
|
@ -342,7 +413,7 @@ export class RESTManager {
|
||||||
}
|
}
|
||||||
}) ?? {}
|
}) ?? {}
|
||||||
).map((entry) => {
|
).map((entry) => {
|
||||||
return [entry[0], entry[1]._errors]
|
return [entry[0], entry[1]._errors ?? []]
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -379,7 +450,7 @@ export class RESTManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a Request to Discord API
|
* Makes a Request to Discord API.
|
||||||
* @param method HTTP Method to use
|
* @param method HTTP Method to use
|
||||||
* @param url URL of the Request
|
* @param url URL of the Request
|
||||||
* @param body Body to send with Request
|
* @param body Body to send with Request
|
||||||
|
@ -422,7 +493,18 @@ export class RESTManager {
|
||||||
let urlToUse =
|
let urlToUse =
|
||||||
method === 'get' && query !== '' ? `${url}?${query}` : url
|
method === 'get' && query !== '' ? `${url}?${query}` : url
|
||||||
|
|
||||||
if (this.client?.canary === true) {
|
// It doesn't start with HTTP, that means it's an incomplete URL
|
||||||
|
if (!urlToUse.startsWith('http')) {
|
||||||
|
if (!urlToUse.startsWith('/')) urlToUse = `/${urlToUse}`
|
||||||
|
urlToUse =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||||
|
baseEndpoints.DISCORD_API_URL +
|
||||||
|
'/v' +
|
||||||
|
baseEndpoints.DISCORD_API_VERSION +
|
||||||
|
urlToUse
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.canary === true && urlToUse.startsWith('http')) {
|
||||||
const split = urlToUse.split('//')
|
const split = urlToUse.split('//')
|
||||||
urlToUse = split[0] + '//canary.' + split[1]
|
urlToUse = split[0] + '//canary.' + split[1]
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,9 @@ export class Webhook {
|
||||||
rest: RESTManager
|
rest: RESTManager
|
||||||
|
|
||||||
get url(): string {
|
get url(): string {
|
||||||
return `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/webhooks/${this.id}/${this.token}`
|
return `${DISCORD_API_URL}/v${
|
||||||
|
this.rest.version ?? DISCORD_API_VERSION
|
||||||
|
}/webhooks/${this.id}/${this.token}`
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(data: WebhookPayload, client?: Client, rest?: RESTManager) {
|
constructor(data: WebhookPayload, client?: Client, rest?: RESTManager) {
|
||||||
|
@ -170,7 +172,7 @@ export class Webhook {
|
||||||
* @param options Options to edit the Webhook.
|
* @param options Options to edit the Webhook.
|
||||||
*/
|
*/
|
||||||
async edit(options: WebhookEditOptions): Promise<Webhook> {
|
async edit(options: WebhookEditOptions): Promise<Webhook> {
|
||||||
if (options.channelID !== undefined && this.rest.client === undefined)
|
if (options.channelID !== undefined && this.client === undefined)
|
||||||
throw new Error('Authentication is required for editing Webhook Channel')
|
throw new Error('Authentication is required for editing Webhook Channel')
|
||||||
if (
|
if (
|
||||||
options.avatar !== undefined &&
|
options.avatar !== undefined &&
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
ChannelTypes,
|
ChannelTypes,
|
||||||
GuildTextChannel
|
GuildTextChannel
|
||||||
} from '../../mod.ts'
|
} from '../../mod.ts'
|
||||||
|
import { Collector } from '../models/collectors.ts'
|
||||||
import { TOKEN } from './config.ts'
|
import { TOKEN } from './config.ts'
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
|
@ -122,6 +123,26 @@ client.on('messageCreate', async (msg: Message) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
msg.channel.send(`Received: ${receivedMsg?.content}`)
|
msg.channel.send(`Received: ${receivedMsg?.content}`)
|
||||||
|
} else if (msg.content.startsWith('!collect') === true) {
|
||||||
|
let count = parseInt(msg.content.replace(/\D/g, ''))
|
||||||
|
if (isNaN(count)) count = 5
|
||||||
|
await msg.channel.send(`Collecting ${count} messages for 5s`)
|
||||||
|
const coll = new Collector({
|
||||||
|
event: 'messageCreate',
|
||||||
|
filter: (m) => m.author.id === msg.author.id,
|
||||||
|
deinitOnEnd: true,
|
||||||
|
max: count,
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
coll.init(client)
|
||||||
|
coll.collect()
|
||||||
|
coll.on('start', () => msg.channel.send('[COL] Started'))
|
||||||
|
coll.on('end', () =>
|
||||||
|
msg.channel.send(`[COL] Ended. Collected Size: ${coll.collected.size}`)
|
||||||
|
)
|
||||||
|
coll.on('collect', (msg) =>
|
||||||
|
msg.channel.send(`[COL] Collect: ${msg.content}`)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,7 @@ import {
|
||||||
GuildTextChannel
|
GuildTextChannel
|
||||||
} from '../../mod.ts'
|
} from '../../mod.ts'
|
||||||
import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts'
|
import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts'
|
||||||
import {
|
import { Manager, Player } from 'https://deno.land/x/lavadeno/mod.ts'
|
||||||
Manager,
|
|
||||||
Player
|
|
||||||
} from '../../deps.ts'
|
|
||||||
import { Interaction } from '../structures/slash.ts'
|
import { Interaction } from '../structures/slash.ts'
|
||||||
import { slash } from '../models/client.ts'
|
import { slash } from '../models/client.ts'
|
||||||
// import { SlashCommandOptionType } from '../types/slash.ts'
|
// import { SlashCommandOptionType } from '../types/slash.ts'
|
||||||
|
|
|
@ -9,6 +9,11 @@ export class MyClient extends Client {
|
||||||
console.log(`Logged in as ${this.user?.tag}!`)
|
console.log(`Logged in as ${this.user?.tag}!`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@event('debug')
|
||||||
|
debugEvt(txt: string): void {
|
||||||
|
console.log(txt)
|
||||||
|
}
|
||||||
|
|
||||||
@slash()
|
@slash()
|
||||||
send(d: Interaction): void {
|
send(d: Interaction): void {
|
||||||
d.respond({
|
d.respond({
|
||||||
|
@ -92,5 +97,11 @@ export class MyClient extends Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new MyClient()
|
const client = new MyClient({
|
||||||
|
presence: {
|
||||||
|
status: 'dnd',
|
||||||
|
activity: { name: 'Slash Commands', type: 'LISTENING' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
client.connect(TOKEN, Intents.None)
|
client.connect(TOKEN, Intents.None)
|
||||||
|
|
Loading…
Reference in a new issue