chore: make slashclient standalone and restmanager accept options
This commit is contained in:
		
							parent
							
								
									4d8eabcc2c
								
							
						
					
					
						commit
						cac3c5c9e3
					
				
					 6 changed files with 177 additions and 84 deletions
				
			
		|  | @ -18,6 +18,7 @@ import { GatewayCache } from '../managers/gatewayCache.ts' | |||
| import { delay } from '../utils/delay.ts' | ||||
| import { VoiceChannel } from '../structures/guildVoiceChannel.ts' | ||||
| import { Guild } from '../structures/guild.ts' | ||||
| import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' | ||||
| 
 | ||||
| export interface RequestMembersOptions { | ||||
|   limit?: number | ||||
|  | @ -34,11 +35,11 @@ export interface VoiceStateOptions { | |||
| export const RECONNECT_REASON = 'harmony-reconnect' | ||||
| 
 | ||||
| /** | ||||
|  * Handles Discord gateway connection. | ||||
|  * Handles Discord Gateway connection. | ||||
|  * | ||||
|  * You should not use this and rather use Client class. | ||||
|  */ | ||||
| class Gateway { | ||||
| export class Gateway extends EventEmitter { | ||||
|   websocket: WebSocket | ||||
|   token: string | ||||
|   intents: GatewayIntents[] | ||||
|  | @ -55,6 +56,7 @@ class Gateway { | |||
|   private timedIdentify: number | null = null | ||||
| 
 | ||||
|   constructor(client: Client, token: string, intents: GatewayIntents[]) { | ||||
|     super() | ||||
|     this.token = token | ||||
|     this.intents = intents | ||||
|     this.client = client | ||||
|  | @ -74,6 +76,7 @@ class Gateway { | |||
|   private onopen(): void { | ||||
|     this.connected = true | ||||
|     this.debug('Connected to Gateway!') | ||||
|     this.emit('connect') | ||||
|   } | ||||
| 
 | ||||
|   private async onmessage(event: MessageEvent): Promise<void> { | ||||
|  | @ -112,6 +115,7 @@ class Gateway { | |||
|       case GatewayOpcodes.HEARTBEAT_ACK: | ||||
|         this.heartbeatServerResponded = true | ||||
|         this.client.ping = Date.now() - this.lastPingTimestamp | ||||
|         this.emit('ping', this.client.ping) | ||||
|         this.debug( | ||||
|           `Received Heartbeat Ack. Ping Recognized: ${this.client.ping}ms` | ||||
|         ) | ||||
|  | @ -142,6 +146,7 @@ class Gateway { | |||
|           await this.cache.set('seq', s) | ||||
|         } | ||||
|         if (t !== null && t !== undefined) { | ||||
|           this.emit(t, d) | ||||
|           this.client.emit('raw', t, d) | ||||
| 
 | ||||
|           const handler = gatewayHandlers[t] | ||||
|  | @ -158,9 +163,11 @@ class Gateway { | |||
|         this.sequenceID = d.seq | ||||
|         await this.cache.set('seq', d.seq) | ||||
|         await this.cache.set('session_id', this.sessionID) | ||||
|         this.emit('resume') | ||||
|         break | ||||
|       } | ||||
|       case GatewayOpcodes.RECONNECT: { | ||||
|         this.emit('reconnectRequired') | ||||
|         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|         this.reconnect() | ||||
|         break | ||||
|  | @ -172,6 +179,7 @@ class Gateway { | |||
| 
 | ||||
|   private async onclose(event: CloseEvent): Promise<void> { | ||||
|     if (event.reason === RECONNECT_REASON) return | ||||
|     this.emit('close', event.code, event.reason) | ||||
|     this.debug(`Connection Closed with code: ${event.code}`) | ||||
| 
 | ||||
|     if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) { | ||||
|  | @ -223,7 +231,7 @@ class Gateway { | |||
| 
 | ||||
|   private onerror(event: Event | ErrorEvent): void { | ||||
|     const eventError = event as ErrorEvent | ||||
|     console.log(eventError) | ||||
|     this.emit('error', eventError) | ||||
|   } | ||||
| 
 | ||||
|   private async sendIdentify(forceNewSession?: boolean): Promise<void> { | ||||
|  | @ -266,6 +274,7 @@ class Gateway { | |||
|     } | ||||
| 
 | ||||
|     this.debug('Sending Identify payload...') | ||||
|     this.emit('sentIdentify') | ||||
|     this.send({ | ||||
|       op: GatewayOpcodes.IDENTIFY, | ||||
|       d: payload | ||||
|  | @ -291,6 +300,7 @@ class Gateway { | |||
|         seq: this.sequenceID ?? null | ||||
|       } | ||||
|     } | ||||
|     this.emit('sentResume') | ||||
|     this.debug('Sending Resume payload...') | ||||
|     this.send(resumePayload) | ||||
|   } | ||||
|  | @ -341,6 +351,7 @@ class Gateway { | |||
|   } | ||||
| 
 | ||||
|   async reconnect(forceNew?: boolean): Promise<void> { | ||||
|     this.emit('reconnecting') | ||||
|     clearInterval(this.heartbeatIntervalID) | ||||
|     if (forceNew === true) { | ||||
|       await this.cache.delete('session_id') | ||||
|  | @ -351,6 +362,7 @@ class Gateway { | |||
|   } | ||||
| 
 | ||||
|   initWebsocket(): void { | ||||
|     this.emit('init') | ||||
|     this.debug('Initializing WebSocket...') | ||||
|     this.websocket = new WebSocket( | ||||
|       // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
 | ||||
|  | @ -414,5 +426,3 @@ class Gateway { | |||
| } | ||||
| 
 | ||||
| export type GatewayEventHandler = (gateway: Gateway, d: any) => void | ||||
| 
 | ||||
| export { Gateway } | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ import { SlashClient } from './slashClient.ts' | |||
| import { Interaction } from '../structures/slash.ts' | ||||
| import { SlashModule } from './slashModule.ts' | ||||
| import type { ShardManager } from './shard.ts' | ||||
| import { Application } from '../structures/application.ts' | ||||
| 
 | ||||
| /** OS related properties sent with Gateway Identify */ | ||||
| export interface ClientProperties { | ||||
|  | @ -26,6 +27,8 @@ export interface ClientProperties { | |||
| 
 | ||||
| /** Some Client Options to modify behaviour */ | ||||
| export interface ClientOptions { | ||||
|   /** ID of the Client/Application to initialize Slash Client REST */ | ||||
|   id?: string | ||||
|   /** Token of the Bot/User */ | ||||
|   token?: string | ||||
|   /** Gateway Intents */ | ||||
|  | @ -100,6 +103,7 @@ export class Client extends EventEmitter { | |||
|   }> | ||||
| 
 | ||||
|   _decoratedSlashModules?: SlashModule[] | ||||
|   _id?: string | ||||
| 
 | ||||
|   private readonly _untypedOn = this.on | ||||
| 
 | ||||
|  | @ -120,6 +124,7 @@ export class Client extends EventEmitter { | |||
| 
 | ||||
|   constructor(options: ClientOptions = {}) { | ||||
|     super() | ||||
|     this._id = options.id | ||||
|     this.token = options.token | ||||
|     this.intents = options.intents | ||||
|     this.forceNewSession = options.forceNewSession | ||||
|  | @ -156,7 +161,9 @@ export class Client extends EventEmitter { | |||
|           } | ||||
|         : options.clientProperties | ||||
| 
 | ||||
|     this.slash = new SlashClient(this, { | ||||
|     this.slash = new SlashClient({ | ||||
|       id: () => this.getEstimatedID(), | ||||
|       client: this, | ||||
|       enabled: options.enableSlash | ||||
|     }) | ||||
|   } | ||||
|  | @ -185,8 +192,24 @@ export class Client extends EventEmitter { | |||
|     this.emit('debug', `[${tag}] ${msg}`) | ||||
|   } | ||||
| 
 | ||||
|   // TODO(DjDeveloperr): Implement this
 | ||||
|   // fetchApplication(): Promise<Application>
 | ||||
|   getEstimatedID(): string { | ||||
|     if (this.user !== undefined) return this.user.id | ||||
|     else if (this.token !== undefined) { | ||||
|       try { | ||||
|         return atob(this.token.split('.')[0]) | ||||
|       } catch (e) { | ||||
|         return this._id ?? 'unknown' | ||||
|       } | ||||
|     } else { | ||||
|       return this._id ?? 'unknown' | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** Fetch Application of the Client */ | ||||
|   async fetchApplication(): Promise<Application> { | ||||
|     const app = await this.rest.api.oauth2.applications['@me'].get() | ||||
|     return new Application(this, app) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * This function is used for connecting to discord. | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import * as baseEndpoints from '../consts/urlsAndVersions.ts' | ||||
| import { Client } from './client.ts' | ||||
| import { Collection } from '../utils/collection.ts' | ||||
| 
 | ||||
| export type RequestMethods = | ||||
|  | @ -94,8 +93,14 @@ export const builder = (rest: RESTManager, acum = '/'): APIMap => { | |||
|   return (proxy as unknown) as APIMap | ||||
| } | ||||
| 
 | ||||
| export interface RESTOptions { | ||||
|   token?: string | ||||
|   headers?: { [name: string]: string | undefined } | ||||
|   canary?: boolean | ||||
| } | ||||
| 
 | ||||
| export class RESTManager { | ||||
|   client?: Client | ||||
|   client?: RESTOptions | ||||
|   queues: { [key: string]: QueuedItem[] } = {} | ||||
|   rateLimits = new Collection<string, RateLimit>() | ||||
|   globalRateLimit: boolean = false | ||||
|  | @ -103,7 +108,7 @@ export class RESTManager { | |||
|   version: number = 8 | ||||
|   api: APIMap | ||||
| 
 | ||||
|   constructor(client?: Client) { | ||||
|   constructor(client?: RESTOptions) { | ||||
|     this.client = client | ||||
|     this.api = builder(this) | ||||
|     // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|  | @ -206,10 +211,16 @@ export class RESTManager { | |||
|       form.append('file', body.file.blob, body.file.name) | ||||
|       form.append('payload_json', JSON.stringify({ ...body, file: undefined })) | ||||
|       body.file = form | ||||
|     } else if (body !== undefined && !['get', 'delete'].includes(method)) { | ||||
|     } else if ( | ||||
|       body !== undefined && | ||||
|       !['get', 'delete'].includes(method.toLowerCase()) | ||||
|     ) { | ||||
|       headers['Content-Type'] = 'application/json' | ||||
|     } | ||||
| 
 | ||||
|     if (this.client?.headers !== undefined) | ||||
|       Object.assign(headers, this.client.headers) | ||||
| 
 | ||||
|     const data: { [name: string]: any } = { | ||||
|       headers, | ||||
|       body: body?.file ?? JSON.stringify(body), | ||||
|  |  | |||
|  | @ -14,10 +14,7 @@ import { | |||
| } from '../types/slash.ts' | ||||
| import { Collection } from '../utils/collection.ts' | ||||
| import { Client } from './client.ts' | ||||
| 
 | ||||
| export interface SlashOptions { | ||||
|   enabled?: boolean | ||||
| } | ||||
| import { RESTManager } from './rest.ts' | ||||
| 
 | ||||
| export class SlashCommand { | ||||
|   slash: SlashCommandsManager | ||||
|  | @ -47,20 +44,22 @@ export class SlashCommand { | |||
| } | ||||
| 
 | ||||
| export class SlashCommandsManager { | ||||
|   client: Client | ||||
|   slash: SlashClient | ||||
| 
 | ||||
|   constructor(client: Client) { | ||||
|     this.client = client | ||||
|     this.slash = client.slash | ||||
|   get rest(): RESTManager { | ||||
|     return this.slash.rest | ||||
|   } | ||||
| 
 | ||||
|   constructor(client: SlashClient) { | ||||
|     this.slash = client | ||||
|   } | ||||
| 
 | ||||
|   /** Get all Global Slash Commands */ | ||||
|   async all(): Promise<Collection<string, SlashCommand>> { | ||||
|     const col = new Collection<string, SlashCommand>() | ||||
| 
 | ||||
|     const res = (await this.client.rest.get( | ||||
|       APPLICATION_COMMANDS(this.client.user?.id as string) | ||||
|     const res = (await this.rest.get( | ||||
|       APPLICATION_COMMANDS(this.slash.getID()) | ||||
|     )) as SlashCommandPayload[] | ||||
|     if (!Array.isArray(res)) return col | ||||
| 
 | ||||
|  | @ -78,9 +77,9 @@ export class SlashCommandsManager { | |||
|   ): Promise<Collection<string, SlashCommand>> { | ||||
|     const col = new Collection<string, SlashCommand>() | ||||
| 
 | ||||
|     const res = (await this.client.rest.get( | ||||
|     const res = (await this.rest.get( | ||||
|       APPLICATION_GUILD_COMMANDS( | ||||
|         this.client.user?.id as string, | ||||
|         this.slash.getID(), | ||||
|         typeof guild === 'string' ? guild : guild.id | ||||
|       ) | ||||
|     )) as SlashCommandPayload[] | ||||
|  | @ -100,11 +99,11 @@ export class SlashCommandsManager { | |||
|     data: SlashCommandPartial, | ||||
|     guild?: Guild | string | ||||
|   ): Promise<SlashCommand> { | ||||
|     const payload = await this.client.rest.post( | ||||
|     const payload = await this.rest.post( | ||||
|       guild === undefined | ||||
|         ? APPLICATION_COMMANDS(this.client.user?.id as string) | ||||
|         ? APPLICATION_COMMANDS(this.slash.getID()) | ||||
|         : APPLICATION_GUILD_COMMANDS( | ||||
|             this.client.user?.id as string, | ||||
|             this.slash.getID(), | ||||
|             typeof guild === 'string' ? guild : guild.id | ||||
|           ), | ||||
|       data | ||||
|  | @ -123,11 +122,11 @@ export class SlashCommandsManager { | |||
|     data: SlashCommandPartial, | ||||
|     guild?: Guild | string | ||||
|   ): Promise<SlashCommandsManager> { | ||||
|     await this.client.rest.patch( | ||||
|     await this.rest.patch( | ||||
|       guild === undefined | ||||
|         ? APPLICATION_COMMAND(this.client.user?.id as string, id) | ||||
|         ? APPLICATION_COMMAND(this.slash.getID(), id) | ||||
|         : APPLICATION_GUILD_COMMAND( | ||||
|             this.client.user?.id as string, | ||||
|             this.slash.getID(), | ||||
|             typeof guild === 'string' ? guild : guild.id, | ||||
|             id | ||||
|           ), | ||||
|  | @ -141,17 +140,31 @@ export class SlashCommandsManager { | |||
|     id: string, | ||||
|     guild?: Guild | string | ||||
|   ): Promise<SlashCommandsManager> { | ||||
|     await this.client.rest.delete( | ||||
|     await this.rest.delete( | ||||
|       guild === undefined | ||||
|         ? APPLICATION_COMMAND(this.client.user?.id as string, id) | ||||
|         ? APPLICATION_COMMAND(this.slash.getID(), id) | ||||
|         : APPLICATION_GUILD_COMMAND( | ||||
|             this.client.user?.id as string, | ||||
|             this.slash.getID(), | ||||
|             typeof guild === 'string' ? guild : guild.id, | ||||
|             id | ||||
|           ) | ||||
|     ) | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   /** Get a Slash Command (global or Guild) */ | ||||
|   async get(id: string, guild?: Guild | string): Promise<SlashCommand> { | ||||
|     const data = await this.rest.get( | ||||
|       guild === undefined | ||||
|         ? APPLICATION_COMMAND(this.slash.getID(), id) | ||||
|         : APPLICATION_GUILD_COMMAND( | ||||
|             this.slash.getID(), | ||||
|             typeof guild === 'string' ? guild : guild.id, | ||||
|             id | ||||
|           ) | ||||
|     ) | ||||
|     return new SlashCommand(this, data) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export type SlashCommandHandlerCallback = (interaction: Interaction) => any | ||||
|  | @ -163,31 +176,61 @@ export interface SlashCommandHandler { | |||
|   handler: SlashCommandHandlerCallback | ||||
| } | ||||
| 
 | ||||
| export interface SlashOptions { | ||||
|   id?: string | (() => string) | ||||
|   client?: Client | ||||
|   enabled?: boolean | ||||
|   token?: string | ||||
|   rest?: RESTManager | ||||
| } | ||||
| 
 | ||||
| export class SlashClient { | ||||
|   client: Client | ||||
|   id: string | (() => string) | ||||
|   client?: Client | ||||
|   token?: string | ||||
|   enabled: boolean = true | ||||
|   commands: SlashCommandsManager | ||||
|   handlers: SlashCommandHandler[] = [] | ||||
|   rest: RESTManager | ||||
| 
 | ||||
|   constructor(client: Client, options?: SlashOptions) { | ||||
|     this.client = client | ||||
|     this.commands = new SlashCommandsManager(client) | ||||
|   constructor(options: SlashOptions) { | ||||
|     let id = options.id | ||||
|     if (options.token !== undefined) id = atob(options.token?.split('.')[0]) | ||||
|     if (id === undefined) | ||||
|       throw new Error('ID could not be found. Pass at least client or token') | ||||
|     this.id = id | ||||
|     this.client = options.client | ||||
|     this.token = options.token | ||||
|     this.commands = new SlashCommandsManager(this) | ||||
| 
 | ||||
|     if (options !== undefined) { | ||||
|       this.enabled = options.enabled ?? true | ||||
|     } | ||||
| 
 | ||||
|     if (this.client._decoratedSlash !== undefined) { | ||||
|     if (this.client?._decoratedSlash !== undefined) { | ||||
|       this.client._decoratedSlash.forEach((e) => { | ||||
|         this.handlers.push(e) | ||||
|       }) | ||||
|     } | ||||
| 
 | ||||
|     this.client.on('interactionCreate', (interaction) => | ||||
|     this.rest = | ||||
|       options.client === undefined | ||||
|         ? options.rest === undefined | ||||
|           ? new RESTManager({ | ||||
|               token: this.token | ||||
|             }) | ||||
|           : options.rest | ||||
|         : options.client.rest | ||||
| 
 | ||||
|     this.client?.on('interactionCreate', (interaction) => | ||||
|       this._process(interaction) | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   getID(): string { | ||||
|     return typeof this.id === 'string' ? this.id : this.id() | ||||
|   } | ||||
| 
 | ||||
|   /** Adds a new Slash Command Handler */ | ||||
|   handle(handler: SlashCommandHandler): SlashClient { | ||||
|     this.handlers.push(handler) | ||||
|  |  | |||
|  | @ -7,8 +7,7 @@ import { | |||
|   groupslash, | ||||
|   CommandContext, | ||||
|   Extension, | ||||
|   Collection, | ||||
|   SlashCommandOptionType | ||||
|   Collection | ||||
| } from '../../mod.ts' | ||||
| import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts' | ||||
| import { | ||||
|  | @ -85,51 +84,52 @@ class MyClient extends CommandClient { | |||
|   ready(): void { | ||||
|     console.log(`Logged in as ${this.user?.tag}!`) | ||||
|     this.manager.init(this.user?.id as string) | ||||
|     this.slash.commands.all().then(console.log) | ||||
| 
 | ||||
|     // this.rest.api.users['422957901716652033'].get().then(console.log)
 | ||||
|     client.slash.commands.create( | ||||
|       { | ||||
|         name: 'cmd', | ||||
|         description: 'Parent command!', | ||||
|         options: [ | ||||
|           { | ||||
|             name: 'sub-cmd-group', | ||||
|             type: SlashCommandOptionType.SUB_COMMAND_GROUP, | ||||
|             description: 'Sub Cmd Group', | ||||
|             options: [ | ||||
|               { | ||||
|                 name: 'sub-cmd', | ||||
|                 type: SlashCommandOptionType.SUB_COMMAND, | ||||
|                 description: 'Sub Cmd' | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           { | ||||
|             name: 'sub-cmd-no-grp', | ||||
|             type: SlashCommandOptionType.SUB_COMMAND, | ||||
|             description: 'Sub Cmd' | ||||
|           }, | ||||
|           { | ||||
|             name: 'sub-cmd-grp-2', | ||||
|             type: SlashCommandOptionType.SUB_COMMAND_GROUP, | ||||
|             description: 'Sub Cmd Group 2', | ||||
|             options: [ | ||||
|               { | ||||
|                 name: 'sub-cmd-1', | ||||
|                 type: SlashCommandOptionType.SUB_COMMAND, | ||||
|                 description: 'Sub Cmd 1' | ||||
|               }, | ||||
|               { | ||||
|                 name: 'sub-cmd-2', | ||||
|                 type: SlashCommandOptionType.SUB_COMMAND, | ||||
|                 description: 'Sub Cmd 2' | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       '783319033205751809' | ||||
|     ) | ||||
|     // client.slash.commands.create(
 | ||||
|     //   {
 | ||||
|     //     name: 'cmd',
 | ||||
|     //     description: 'Parent command!',
 | ||||
|     //     options: [
 | ||||
|     //       {
 | ||||
|     //         name: 'sub-cmd-group',
 | ||||
|     //         type: SlashCommandOptionType.SUB_COMMAND_GROUP,
 | ||||
|     //         description: 'Sub Cmd Group',
 | ||||
|     //         options: [
 | ||||
|     //           {
 | ||||
|     //             name: 'sub-cmd',
 | ||||
|     //             type: SlashCommandOptionType.SUB_COMMAND,
 | ||||
|     //             description: 'Sub Cmd'
 | ||||
|     //           }
 | ||||
|     //         ]
 | ||||
|     //       },
 | ||||
|     //       {
 | ||||
|     //         name: 'sub-cmd-no-grp',
 | ||||
|     //         type: SlashCommandOptionType.SUB_COMMAND,
 | ||||
|     //         description: 'Sub Cmd'
 | ||||
|     //       },
 | ||||
|     //       {
 | ||||
|     //         name: 'sub-cmd-grp-2',
 | ||||
|     //         type: SlashCommandOptionType.SUB_COMMAND_GROUP,
 | ||||
|     //         description: 'Sub Cmd Group 2',
 | ||||
|     //         options: [
 | ||||
|     //           {
 | ||||
|     //             name: 'sub-cmd-1',
 | ||||
|     //             type: SlashCommandOptionType.SUB_COMMAND,
 | ||||
|     //             description: 'Sub Cmd 1'
 | ||||
|     //           },
 | ||||
|     //           {
 | ||||
|     //             name: 'sub-cmd-2',
 | ||||
|     //             type: SlashCommandOptionType.SUB_COMMAND,
 | ||||
|     //             description: 'Sub Cmd 2'
 | ||||
|     //           }
 | ||||
|     //         ]
 | ||||
|     //       }
 | ||||
|     //     ]
 | ||||
|     //   },
 | ||||
|     //   '783319033205751809'
 | ||||
|     // )
 | ||||
|     // client.slash.commands.delete('788719077329207296', '783319033205751809')
 | ||||
|   } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										6
									
								
								src/test/slash-only.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/test/slash-only.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| import { SlashClient } from '../models/slashClient.ts' | ||||
| import { TOKEN } from './config.ts' | ||||
| 
 | ||||
| const slash = new SlashClient({ token: TOKEN }) | ||||
| 
 | ||||
| slash.commands.all().then(console.log) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue