collectors, rest options and all that
This commit is contained in:
		
							parent
							
								
									e9f461fef4
								
							
						
					
					
						commit
						48976e779b
					
				
					 9 changed files with 381 additions and 39 deletions
				
			
		|  | @ -348,4 +348,11 @@ export interface ClientEvents { | |||
|    * @param message Debug message | ||||
|    */ | ||||
|   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 { GatewayIntents } from '../types/gateway.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 { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.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 '../types/endpoint.ts' | ||||
| import { ClientEvents } from '../gateway/handlers/index.ts' | ||||
| import type { Collector } from './collectors.ts' | ||||
| 
 | ||||
| /** OS related properties sent with Gateway Identify */ | ||||
| export interface ClientProperties { | ||||
|  | @ -54,6 +55,10 @@ export interface ClientOptions { | |||
|   clientProperties?: ClientProperties | ||||
|   /** Enable/Disable Slash Commands Integration (enabled by default) */ | ||||
|   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 { | ||||
|  | @ -89,7 +94,7 @@ export class Client extends EventEmitter { | |||
|   /** Gateway object */ | ||||
|   gateway?: Gateway | ||||
|   /** 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?: User | ||||
|   /** WebSocket ping of Client */ | ||||
|  | @ -118,8 +123,6 @@ export class Client extends EventEmitter { | |||
|   channels: ChannelsManager = new ChannelsManager(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 */ | ||||
|   presence: ClientPresence = new ClientPresence() | ||||
|   _decoratedEvents?: { | ||||
|  | @ -140,6 +143,7 @@ export class Client extends EventEmitter { | |||
|   shard: number = 0 | ||||
|   /** Shard Manager of this Client if Sharded */ | ||||
|   shardManager?: ShardManager | ||||
|   collectors: Set<Collector> = new Set() | ||||
| 
 | ||||
|   constructor(options: ClientOptions = {}) { | ||||
|     super() | ||||
|  | @ -153,7 +157,6 @@ export class Client extends EventEmitter { | |||
|         options.presence instanceof ClientPresence | ||||
|           ? options.presence | ||||
|           : new ClientPresence(options.presence) | ||||
|     if (options.canary === true) this.canary = true | ||||
|     if (options.messageCacheLifetime !== undefined) | ||||
|       this.messageCacheLifetime = options.messageCacheLifetime | ||||
|     if (options.reactionCacheLifetime !== undefined) | ||||
|  | @ -185,6 +188,27 @@ export class Client extends EventEmitter { | |||
|       client: this, | ||||
|       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. | ||||
|    * @param token Your token. This is required. | ||||
|    * @param intents Gateway intents in array. This is required. | ||||
|    * @param token Your token. This is required if not given in ClientOptions. | ||||
|    * @param intents Gateway intents in array. This is required if not given in ClientOptions. | ||||
|    */ | ||||
|   connect(token?: string, intents?: GatewayIntents[]): void { | ||||
|     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) { | ||||
|       this.intents = intents | ||||
|     } else throw new Error('No Gateway Intents were provided') | ||||
| 
 | ||||
|     this.rest.token = token | ||||
|     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 */ | ||||
|   async waitFor<K extends keyof ClientEvents>( | ||||
|     event: K, | ||||
|  | @ -293,10 +348,13 @@ export class Client extends EventEmitter { | |||
| 
 | ||||
| /** Event decorator to create an Event handler from function */ | ||||
| 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 { | ||||
|       [name in keyof ClientEvents]: (...args: ClientEvents[name]) => any | ||||
|     })[prop] | ||||
|     })[name ?? ((prop as unknown) as keyof ClientEvents)] | ||||
|     if (typeof listener !== 'function') | ||||
|       throw new Error('@event decorator requires a function') | ||||
|     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 { Collection } from '../utils/collection.ts' | ||||
| import { Client } from './client.ts' | ||||
| 
 | ||||
| export type RequestMethods = | ||||
|   | 'get' | ||||
|  | @ -60,15 +61,23 @@ export type MethodFunction = ( | |||
| ) => Promise<any> | ||||
| 
 | ||||
| export interface APIMap extends MethodFunction { | ||||
|   /** Make a GET request to current route */ | ||||
|   get: APIMap | ||||
|   /** Make a POST request to current route */ | ||||
|   post: APIMap | ||||
|   /** Make a PATCH request to current route */ | ||||
|   patch: APIMap | ||||
|   /** Make a PUT request to current route */ | ||||
|   put: APIMap | ||||
|   /** Make a DELETE request to current route */ | ||||
|   delete: APIMap | ||||
|   /** Make a HEAD request to current route */ | ||||
|   head: APIMap | ||||
|   /** Continue building API Route */ | ||||
|   [name: string]: APIMap | ||||
| } | ||||
| 
 | ||||
| /** API Route builder function */ | ||||
| export const builder = (rest: RESTManager, acum = '/'): APIMap => { | ||||
|   const routes = {} | ||||
|   const proxy = new Proxy(routes, { | ||||
|  | @ -94,25 +103,76 @@ export const builder = (rest: RESTManager, acum = '/'): APIMap => { | |||
| } | ||||
| 
 | ||||
| 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 } | ||||
|   /** Whether to use Canary instance of Discord API or not */ | ||||
|   canary?: boolean | ||||
|   /** Discord REST API version to use */ | ||||
|   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 { | ||||
|   client?: RESTOptions | ||||
|   queues: { [key: string]: QueuedItem[] } = {} | ||||
|   rateLimits = new Collection<string, RateLimit>() | ||||
|   /** Whether we are globally ratelimited or not */ | ||||
|   globalRateLimit: boolean = false | ||||
|   /** Whether requests are being processed or not */ | ||||
|   processing: boolean = false | ||||
|   /** API Version being used by REST Manager */ | ||||
|   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 | ||||
|   /** 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) { | ||||
|     this.client = client | ||||
|   constructor(options?: RESTOptions) { | ||||
|     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
 | ||||
|     this.handleRateLimits() | ||||
|   } | ||||
|  | @ -193,13 +253,16 @@ export class RESTManager { | |||
| 
 | ||||
|   private prepare(body: any, method: RequestMethods): { [key: string]: any } { | ||||
|     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) | ||||
|       headers.Authorization = `Bot ${this.client.token}` | ||||
| 
 | ||||
|     if (this.client?.token === undefined) delete headers.Authorization | ||||
|     if (this.token !== undefined) { | ||||
|       const token = typeof this.token === 'string' ? this.token : this.token() | ||||
|       if (token !== undefined) | ||||
|         headers.Authorization = `${this.tokenType} ${token}`.trim() | ||||
|     } | ||||
| 
 | ||||
|     if (method === 'get' || method === 'head' || method === 'delete') | ||||
|       body = undefined | ||||
|  | @ -220,9 +283,7 @@ export class RESTManager { | |||
|       headers['Content-Type'] = 'application/json' | ||||
|     } | ||||
| 
 | ||||
|     if (this.client?.headers !== undefined) | ||||
|       Object.assign(headers, this.client.headers) | ||||
| 
 | ||||
|     if (this.headers !== undefined) Object.assign(headers, this.headers) | ||||
|     const data: { [name: string]: any } = { | ||||
|       headers, | ||||
|       body: body?.file ?? JSON.stringify(body), | ||||
|  | @ -305,13 +366,25 @@ export class RESTManager { | |||
|     body: any, | ||||
|     data: { [key: string]: any }, | ||||
|     reject: CallableFunction | ||||
|   ): Promise<undefined> { | ||||
|   ): Promise<void> { | ||||
|     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 ( | ||||
|       (status >= 200 && status < 400) || | ||||
|       status === HttpResponseCode.NoContent || | ||||
|       status === HttpResponseCode.TooManyRequests | ||||
|       status === HttpResponseCode.NoContent | ||||
|     ) | ||||
|       return | ||||
| 
 | ||||
|  | @ -322,9 +395,7 @@ export class RESTManager { | |||
| 
 | ||||
|     if (status === HttpResponseCode.Unauthorized) | ||||
|       reject( | ||||
|         new DiscordAPIError( | ||||
|           `Request was not successful (Unauthorized). Invalid Token.\n${text}` | ||||
|         ) | ||||
|         new DiscordAPIError(`Request was Unauthorized. Invalid Token.\n${text}`) | ||||
|       ) | ||||
| 
 | ||||
|     // At this point we know it is error
 | ||||
|  | @ -342,7 +413,7 @@ export class RESTManager { | |||
|             } | ||||
|           }) ?? {} | ||||
|         ).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 url URL of the Request | ||||
|    * @param body Body to send with Request | ||||
|  | @ -422,7 +493,18 @@ export class RESTManager { | |||
|           let urlToUse = | ||||
|             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('//') | ||||
|             urlToUse = split[0] + '//canary.' + split[1] | ||||
|           } | ||||
|  |  | |||
|  | @ -47,7 +47,9 @@ export class Webhook { | |||
|   rest: RESTManager | ||||
| 
 | ||||
|   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) { | ||||
|  | @ -170,7 +172,7 @@ export class Webhook { | |||
|    * @param options Options to edit the 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') | ||||
|     if ( | ||||
|       options.avatar !== undefined && | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import { | |||
|   ChannelTypes, | ||||
|   GuildTextChannel | ||||
| } from '../../mod.ts' | ||||
| import { Collector } from '../models/collectors.ts' | ||||
| import { TOKEN } from './config.ts' | ||||
| 
 | ||||
| const client = new Client({ | ||||
|  | @ -122,6 +123,26 @@ client.on('messageCreate', async (msg: Message) => { | |||
|     ) | ||||
| 
 | ||||
|     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 | ||||
| } from '../../mod.ts' | ||||
| import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts' | ||||
| import { | ||||
|   Manager, | ||||
|   Player | ||||
| } from '../../deps.ts' | ||||
| import { Manager, Player } from 'https://deno.land/x/lavadeno/mod.ts' | ||||
| import { Interaction } from '../structures/slash.ts' | ||||
| import { slash } from '../models/client.ts' | ||||
| // import { SlashCommandOptionType } from '../types/slash.ts'
 | ||||
|  |  | |||
|  | @ -9,6 +9,11 @@ export class MyClient extends Client { | |||
|     console.log(`Logged in as ${this.user?.tag}!`) | ||||
|   } | ||||
| 
 | ||||
|   @event('debug') | ||||
|   debugEvt(txt: string): void { | ||||
|     console.log(txt) | ||||
|   } | ||||
| 
 | ||||
|   @slash() | ||||
|   send(d: Interaction): void { | ||||
|     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) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue