From b4a288154790b4bfd0831435a0f39d54a1dd8d89 Mon Sep 17 00:00:00 2001 From: Daryl Ronningen Date: Sat, 7 Aug 2021 17:15:49 -0700 Subject: [PATCH] refactor: moved types into simpler files --- .gitignore | 2 +- src/api/apiClient.ts | 2 +- src/api/apiHandler.ts | 5 +- src/api/apiHelper.ts | 33 ----- src/api/apiManager.ts | 3 +- src/client/client.ts | 13 +- src/gateway/gatewayClient.ts | 91 -------------- src/index.ts | 1 - src/structures/clientUser.ts | 9 -- src/structures/user.ts | 18 --- src/utils/cacheManager.ts | 51 -------- src/utils/defaults.ts | 15 +-- src/utils/types.ts | 227 ----------------------------------- src/utils/types/api.ts | 21 ++++ src/utils/types/client.ts | 12 ++ src/utils/types/common.ts | 34 ++++++ 16 files changed, 76 insertions(+), 461 deletions(-) delete mode 100644 src/gateway/gatewayClient.ts delete mode 100644 src/structures/clientUser.ts delete mode 100644 src/structures/user.ts delete mode 100644 src/utils/cacheManager.ts delete mode 100644 src/utils/types.ts create mode 100644 src/utils/types/api.ts create mode 100644 src/utils/types/client.ts create mode 100644 src/utils/types/common.ts diff --git a/.gitignore b/.gitignore index 7c1171e..e6ef244 100644 --- a/.gitignore +++ b/.gitignore @@ -674,4 +674,4 @@ DerivedData/ # ---> Project !.yarn/releases -types +/types diff --git a/src/api/apiClient.ts b/src/api/apiClient.ts index 5f3528e..13472d5 100644 --- a/src/api/apiClient.ts +++ b/src/api/apiClient.ts @@ -1,7 +1,7 @@ import { ApiManager } from './apiManager'; import type { Client } from '../client/client'; -import type { IRequestOptions } from '../utils/types'; +import type { IRequestOptions } from '../utils/types/api'; export class ApiClient { public client: Client; diff --git a/src/api/apiHandler.ts b/src/api/apiHandler.ts index 28364c0..ecff3c6 100644 --- a/src/api/apiHandler.ts +++ b/src/api/apiHandler.ts @@ -2,10 +2,11 @@ import { AsyncQueue } from '@sapphire/async-queue'; import { AbortController } from 'abort-controller'; import FormData from 'form-data'; import fetch from 'node-fetch'; -import type { Client } from '../client/client'; import { sleep } from '../utils/sleep'; -import type { IMakeRequestOptions, IRouteIdentifier } from '../utils/types'; + import type { ApiManager } from './apiManager'; +import type { Client } from '../client/client'; +import type { IMakeRequestOptions, IRouteIdentifier } from '../utils/types/api'; function calculateReset(reset: number, resetAfter: number, serverDate: number): number { diff --git a/src/api/apiHelper.ts b/src/api/apiHelper.ts index bb943d7..811a31f 100644 --- a/src/api/apiHelper.ts +++ b/src/api/apiHelper.ts @@ -1,7 +1,6 @@ import { ApiClient } from './apiClient'; import type { Client } from '../client/client'; -import type { IApiCreateMessage, IApiCreateSlashCommand, IApiUser, IFile } from '../utils/types'; export class ApiHelper { public apiClient: ApiClient; @@ -15,36 +14,4 @@ export class ApiHelper { this.apiClient = new ApiClient(this.client, this._token); } - - // TODO: Return message object - public async createMessage(channelId: string, options: IApiCreateMessage, files?: IFile[]): Promise { - await this.apiClient.post({ - path: `/channels/${channelId}/messages`, - requireAuth: true, - body: options, - files: files, - }); - } - - public async getCurrentUser(): Promise { - return await this.apiClient.get({ - path: '/users/@me', - requireAuth: true, - }); - } - - public async createSlashCommand(options: IApiCreateSlashCommand): Promise { - await this.apiClient.post({ - path: `/applications/${this.client.user.id}/commands`, - requireAuth: true, - body: options, - }); - } - - public async getUser(id: string): Promise { - return await this.apiClient.get({ - path: `/users/${id}`, - requireAuth: true, - }); - } } diff --git a/src/api/apiManager.ts b/src/api/apiManager.ts index 4647fe6..f2b03a2 100644 --- a/src/api/apiManager.ts +++ b/src/api/apiManager.ts @@ -2,8 +2,7 @@ import { Snowflake } from '../utils/snowflake'; import { ApiHandler } from './apiHandler'; import type { Client } from '../client/client'; -import type { ApiMethods, IMakeRequestOptions, IRouteIdentifier } from '../utils/types'; - +import type { ApiMethods, IMakeRequestOptions, IRouteIdentifier } from '../utils/types/api'; export class ApiManager { public client: Client; diff --git a/src/client/client.ts b/src/client/client.ts index b525e4e..1e814fc 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,17 +1,14 @@ import _ from 'lodash'; import EventEmitter from 'events'; import { ApiHelper } from '../api/apiHelper'; -import { ClientUser } from '../structures/clientUser'; -import { GatewayClient } from '../gateway/gatewayClient'; import { defaults } from '../utils/defaults'; -import type { DeepRequired, IClientOptions } from '../utils/types'; +import type { IClientOptions } from '../utils/types/client'; +import type { DeepRequired } from '../utils/types/common'; export class Client extends EventEmitter { public readonly api: ApiHelper; public readonly options: DeepRequired; - public readonly user: ClientUser; - public readonly ws: GatewayClient; private _token: string; @@ -22,11 +19,5 @@ export class Client extends EventEmitter { this._token = token; this.api = new ApiHelper(this, this._token); - this.user = new ClientUser(this); - this.ws = new GatewayClient(this, this._token); - } - - public async login(): Promise { - await this.ws.connect(); } } diff --git a/src/gateway/gatewayClient.ts b/src/gateway/gatewayClient.ts deleted file mode 100644 index 70d1f03..0000000 --- a/src/gateway/gatewayClient.ts +++ /dev/null @@ -1,91 +0,0 @@ -import zlib from 'fast-zlib'; -import os from 'os'; -import WebSocket from 'ws'; - -import type { Client } from '../client/client'; -import type { GatewayReceiveMessage, GatewaySendMessage } from '../utils/types'; - -export class GatewayClient { - public client: Client; - public connection: WebSocket | null; - public inflate: zlib.Inflate; - - private _heartbeatInterval: number; - private _heartbeatIntervalTimer: NodeJS.Timer | null; - private _sequence: number; - private _token: string; - - public constructor(client: Client, token: string) { - this.client = client; - - this.connection = null; - this.inflate = new zlib.Inflate(); - - this._heartbeatInterval = 0; - this._heartbeatIntervalTimer= null; - this._sequence = 0; - this._token= token; - } - - public async close(): Promise { - if (!this.connection) throw new Error('You are not connected to the Discord WebSocket Gateway!'); - - this.connection.close(1000); - if (this._heartbeatIntervalTimer) clearTimeout(this._heartbeatIntervalTimer); - } - - public async connect(): Promise { - if (this.connection) throw new Error('You are already connected to the Discord WebSocket Gateway!'); - - this.connection = new WebSocket( - `${this.client.options.ws.url}/?v=${this.client.options.ws.version}${this.client.options.ws.compression ? '&compress=zlib-stream' : ''}&encoding=${this.client.options.ws.encoding}`, - ); - - this.connection.on('message', async (msg: Buffer | string) => { - let parsedMessage: GatewayReceiveMessage; - - if (this.client.options.ws.compression && typeof msg === 'object') parsedMessage = JSON.parse(this.inflate.process(msg).toString('utf8')); - else if (!this.client.options.ws.compression && typeof msg === 'string') parsedMessage = JSON.parse(msg); - else parsedMessage = JSON.parse(msg.toString('utf8')); - - // if (parsedMessage.s) this._sequence = parsedMessage.s; - - this.client.emit('raw', parsedMessage); - - switch (parsedMessage.op) { - case 10: - this._heartbeatInterval = parsedMessage.d.heartbeat_interval; - - this.send({ - op: 2, d: { - compress: this.client.options.ws.compression, - intents: this.client.options.ws.intents, - large_threshold: this.client.options.ws.largeThreshold, - presence: this.client.options.presence, - properties: { - $browser: '@neonjs/library', - $device: '@neonjs/library', - $os: os.platform(), - }, - token: this._token, - }, - }); - - this._heartbeatIntervalTimer = setInterval(() => this._sendHeartbeat(), this._heartbeatInterval * 1000); - break; - default: - break; - } - }); - } - - public send(data: GatewaySendMessage): void { - if (!this.connection) throw new Error('You are not connected to the Discord WebSocket Gateway!'); - - this.connection.send(JSON.stringify(data)); - } - - private _sendHeartbeat(): void { - this.send({ op: 1, d: this._sequence }); - } -} diff --git a/src/index.ts b/src/index.ts index 2c8cbc2..2803000 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,4 +6,3 @@ export * from './client/client'; export * from './utils/defaults'; export * from './utils/sleep'; export * from './utils/snowflake'; -export * from './utils/types'; diff --git a/src/structures/clientUser.ts b/src/structures/clientUser.ts deleted file mode 100644 index 3e5b3f5..0000000 --- a/src/structures/clientUser.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { User } from './user'; - -import type { Client } from '../client/client'; - -export class ClientUser extends User { - public constructor(client: Client) { - super(client); - } -} diff --git a/src/structures/user.ts b/src/structures/user.ts deleted file mode 100644 index bae8950..0000000 --- a/src/structures/user.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { IApiUser } from '..'; -import type { Client } from '../client/client'; - -export class User { - public client: Client; - public id: string; - - private _options: IApiUser; - - public constructor(client: Client, options: IApiUser) { - this.client = client; - this._options = options; - } - - public async fetch(id: string): Promise { - return this.client.api.getUser(id); - } -} diff --git a/src/utils/cacheManager.ts b/src/utils/cacheManager.ts deleted file mode 100644 index 0655e4e..0000000 --- a/src/utils/cacheManager.ts +++ /dev/null @@ -1,51 +0,0 @@ -import cacheManager from 'cache-manager'; -import redisStore from 'cache-manager-ioredis'; - -import type { Client } from '../client/client'; - -export class CacheManager { - public client: Client; - - private readonly _cacheManager: cacheManager.Cache; - - public constructor(client: Client) { - this.client = client; - - if (this.client.options.cache.cache === 'redis') this._cacheManager = cacheManager.caching({ - store: redisStore, - ttl: this.client.options.cache.ttl, - ...this.client.options.cache.options, - }); - else this._cacheManager = cacheManager.caching({ - store: 'memory', - ttl: this.client.options.cache.ttl, - }); - } - - public async del(key: string): Promise { - return new Promise((res, rej) => { - this._cacheManager.del(key, (err) => { - if (err) rej(err); - else res(); - }); - }); - } - - public async get(key: string): Promise { - return new Promise((res, rej) => { - this._cacheManager.get(key, (err, result) => { - if (err) rej(err); - else res(result); - }); - }); - } - - public async set(key: string, value: T): Promise { - return new Promise((res, rej) => { - this._cacheManager.set(key, value, this.client.options.cache.ttl, (err) => { - if (err) rej(err); - else res(value); - }); - }); - } -} diff --git a/src/utils/defaults.ts b/src/utils/defaults.ts index 6301916..b425998 100644 --- a/src/utils/defaults.ts +++ b/src/utils/defaults.ts @@ -1,4 +1,4 @@ -import type { IDefaultOptions } from './types'; +import type { IDefaultOptions } from './types/common'; export const defaults: IDefaultOptions = { clientOptions: { @@ -10,18 +10,5 @@ export const defaults: IDefaultOptions = { url: 'https://discord.com/api', version: 9, }, - cache: { - cache: 'memory', - ttl: 60, - }, - presence: {}, - ws: { - compression: true, - encoding: 'json', - intents: 0, - largeThreshold: 250, - url: 'wss://gateway.discord.gg', - version: 9, - }, }, }; diff --git a/src/utils/types.ts b/src/utils/types.ts deleted file mode 100644 index 7d8fdc4..0000000 --- a/src/utils/types.ts +++ /dev/null @@ -1,227 +0,0 @@ -import type IORedis from 'ioredis'; - -// Interfaces -export interface IApiClientOptions { - offset?: number; - requestTimeout?: number; - retryLimit?: number; - sweepInterval?: number; - url?: string; - version?: number; -} - -export interface ICacheMemoryClientOptions { - cache?: 'memory'; - ttl?: number; -} - -export interface ICacheRedisClientOptions { - cache?: 'redis'; - ttl?: number; - options: IORedis.RedisOptions; -} - -export interface IClientOptions { - api?: IApiClientOptions; - cache?: ICacheMemoryClientOptions | ICacheRedisClientOptions; - presence?: IUpdatePresence; - ws?: IWebSocketClientOptions; -} - -export interface IDefaultOptions { - clientOptions: IClientOptions; -} - -export interface IFile { - file: Buffer; - name: string; -} - -export interface IMakeRequestOptions extends IRequestOptions { - method: ApiMethods; -} - -export interface IMessageEmbed { - author?: { name?: string, url?: string }; - color?: number; - description?: string; - fields?: { inline?: boolean, name: string, value: string }[]; - footer?: { icon_url?: string, text?: string }; - image?: { url?: string }; - provider?: { name?: string, url?: string }; - timestamp?: number; - title?: string; - thumbnail?: { url?: string }; - type?: 'rich'; - url?: string; - video?: { url?: string }; -} - -export interface IRequestOptions { - body?: unknown; - files?: IFile[]; - headers?: Record; - path: string; - reason?: string; - requireAuth?: boolean; -} - -export interface IRouteIdentifier { - majorParameter: string; - route: string; -} - -export interface IUpdatePresence { - activities?: { - name?: string; - type?: EActivityType; - url?: string; - }[]; - afk?: boolean; - since?: number; - status?: EStatus; -} - -export interface IWebSocketClientOptions { - compression?: boolean; - encoding?: 'json'; - intents?: number; - largeThreshold?: number; - url?: string; - version?: number; -} - -// Api Interfaces -// TODO: Add message components -// Dont add the files option. That goes into a separate option when creating a request -export interface IApiCreateMessage { - allowed_mentions?: { - parse?: 'everyone' | 'roles' | 'users'[]; - replied_user?: boolean; - roles?: string[]; - users?: string[]; - }; - content?: string; - embeds?: IMessageEmbed[]; - message_reference?: { - channel_id?: string; - fail_if_not_exists?: boolean; - guild_id?: string; - message_id?: string; - }; - tts?: boolean; -} - -export interface IApiUser { - avatar: string; - bot?: boolean; - discriminator: string; - email?: string; - flags?: number; - id: string; - locale?: string; - mfa_enabled?: boolean; - premium_type?: number; - public_flags?: number; - username: string; - system?: boolean; - verified?: boolean; -} - -export interface IApiCreateSlashCommand { - allowed_mentions?: { - parse?: 'everyone' | 'roles' | 'users'[]; - replied_user?: boolean; - roles?: string[]; - users?: string[]; - }; - content?: string; - embeds?: IMessageEmbed[]; - files?: IFile[]; - message_reference?: { - channel_id?: string; - fail_if_not_exists?: boolean; - guild_id?: string; - message_id?: string; - }; - tts?: boolean; -} - -// WebSocket Gateway Message Interfaces -export interface IGatewayHeartbeatSend { - op: 1; - d: number; -} - -// TODO: Add sharding when i get to it -export interface IGatewayIdentify { - op: 2; - d: { - compress: boolean; - intents: number; - large_threshold: number; - presence?: IUpdatePresence; - properties: { - $browser: string; - $device: string; - $os: string; - }; - token: string; - }; -} - -export interface IGatewayHeartbeat { - op: 10; - d: { - heartbeat_interval: number; - }; -} - -export interface IGatewayHeartbeatAck { - op: 11; -} - -// Enums -export enum EActivityType { - GAME = 0, - STREAMING = 1, - LISTENING = 2, - WATCHING = 3, - COMPETING = 5, -} - -export enum EStatus { - DND = 'dnd', - IDLE = 'idle', - INVISIBLE = 'invisible', - OFFLINE = 'offline', - ONLINE = 'online', -} - -// Type Aliases -export type ApiMethods = 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT' ; -// eslint-disable-next-line @typescript-eslint/ban-types -export type Builtin = Date | Error | Function | Primitives | RegExp; -export type DeepRequired = T extends Builtin - ? NonNullable - : T extends Map - ? Map, DeepRequired> - : T extends ReadonlyMap - ? ReadonlyMap, DeepRequired> - : T extends WeakMap - ? WeakMap, DeepRequired> - : T extends Set - ? Set> - : T extends ReadonlySet - ? ReadonlySet> - : T extends WeakSet - ? WeakSet> - : T extends Promise - ? Promise> - // eslint-disable-next-line @typescript-eslint/ban-types - : T extends {} - ? { [K in keyof T]-?: DeepRequired } - : NonNullable; -export type GatewayReceiveMessage = IGatewayHeartbeat | IGatewayHeartbeatAck; -export type GatewaySendMessage = IGatewayHeartbeatSend | IGatewayIdentify; -export type Primitives = bigint | boolean | null | number | string | symbol | undefined; diff --git a/src/utils/types/api.ts b/src/utils/types/api.ts new file mode 100644 index 0000000..9272fe8 --- /dev/null +++ b/src/utils/types/api.ts @@ -0,0 +1,21 @@ +import type { IFile } from './common'; + +export interface IMakeRequestOptions extends IRequestOptions { + method: ApiMethods; +} + +export interface IRequestOptions { + body?: unknown; + files?: IFile[]; + headers?: Record; + path: string; + reason?: string; + requireAuth?: boolean; +} + +export interface IRouteIdentifier { + majorParameter: string; + route: string; +} + +export type ApiMethods = 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT' ; diff --git a/src/utils/types/client.ts b/src/utils/types/client.ts new file mode 100644 index 0000000..a06f84c --- /dev/null +++ b/src/utils/types/client.ts @@ -0,0 +1,12 @@ +export interface IApiClientOptions { + offset?: number; + requestTimeout?: number; + retryLimit?: number; + sweepInterval?: number; + url?: string; + version?: number; +} + +export interface IClientOptions { + api?: IApiClientOptions; +} diff --git a/src/utils/types/common.ts b/src/utils/types/common.ts new file mode 100644 index 0000000..4813cfb --- /dev/null +++ b/src/utils/types/common.ts @@ -0,0 +1,34 @@ +import type { IClientOptions } from './client'; + +export interface IDefaultOptions { + clientOptions: IClientOptions; +} + +export interface IFile { + file: Buffer; + name: string; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export type Builtin = Date | Error | Function | Primitives | RegExp; +export type DeepRequired = T extends Builtin + ? NonNullable + : T extends Map + ? Map, DeepRequired> + : T extends ReadonlyMap + ? ReadonlyMap, DeepRequired> + : T extends WeakMap + ? WeakMap, DeepRequired> + : T extends Set + ? Set> + : T extends ReadonlySet + ? ReadonlySet> + : T extends WeakSet + ? WeakSet> + : T extends Promise + ? Promise> + // eslint-disable-next-line @typescript-eslint/ban-types + : T extends {} + ? { [K in keyof T]-?: DeepRequired } + : NonNullable; +export type Primitives = bigint | boolean | null | number | string | symbol | undefined;