Skip to content

Commit

Permalink
feat: rework events & fix intellisense
Browse files Browse the repository at this point in the history
  • Loading branch information
0t4u committed Aug 13, 2024
1 parent 541610c commit 749444e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 49 deletions.
37 changes: 16 additions & 21 deletions src/Shoukaku.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { EventEmitter } from 'events';
import { ShoukakuDefaults, VoiceState } from './Constants';
import { Node } from './node/Node';
import { Node, NodeEvents } from './node/Node';
import { Connector } from './connectors/Connector';
import { Constructor, mergeDefault } from './Utils';
import { Constructor, mergeDefault, TypedEventEmitter } from './Utils';
import { Player } from './guild/Player';
import { Rest } from './node/Rest';
import { Connection } from './guild/Connection';
Expand Down Expand Up @@ -96,7 +95,10 @@ export interface VoiceChannelOptions {
mute?: boolean;
}

export interface ShoukakuEvents {
// Interfaces are not final, but types are, and therefore has an index signature
// https://stackoverflow.com/a/64970740
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type ShoukakuEvents = {
/**
* Emitted when reconnect tries are occurring and how many tries are left
* @eventProperty
Expand Down Expand Up @@ -132,19 +134,12 @@ export interface ShoukakuEvents {
* @eventProperty
*/
'raw': [name: string, json: unknown];
}

export declare interface IShoukaku {
on<K extends keyof ShoukakuEvents>(event: K, listener: (...args: ShoukakuEvents[K]) => void): this;
once<K extends keyof ShoukakuEvents>(event: K, listener: (...args: ShoukakuEvents[K]) => void): this;
off<K extends keyof ShoukakuEvents>(event: K, listener: (...args: ShoukakuEvents[K]) => void): this;
emit(event: string | symbol, ...args: unknown[]): boolean;
}
};

/**
* Main Shoukaku class
*/
export class Shoukaku extends EventEmitter implements IShoukaku {
export class Shoukaku extends TypedEventEmitter<ShoukakuEvents> {
/**
* Discord library connector
*/
Expand Down Expand Up @@ -214,13 +209,13 @@ export class Shoukaku extends EventEmitter implements IShoukaku {
*/
public addNode(options: NodeOption): void {
const node = new Node(this, options);
node.on('debug', (...args: unknown[]) => this.emit('debug', node.name, ...args));
node.on('reconnecting', (...args: unknown[]) => this.emit('reconnecting', node.name, ...args));
node.on('error', (...args: unknown[]) => this.emit('error', node.name, ...args));
node.on('close', (...args: unknown[]) => this.emit('close', node.name, ...args));
node.on('ready', (...args: unknown[]) => this.emit('ready', node.name, ...args));
node.on('raw', (...args: unknown[]) => this.emit('raw', node.name, ...args));
node.once('disconnect', (...args: unknown[]) => this.clean(node, ...args));
node.on('debug', (...args) => this.emit('debug', node.name, ...args));
node.on('reconnecting', (...args) => this.emit('reconnecting', node.name, ...args));
node.on('error', (...args) => this.emit('error', node.name, ...args));
node.on('close', (...args) => this.emit('close', node.name, ...args));
node.on('ready', (...args) => this.emit('ready', node.name, ...args));
node.on('raw', (...args) => this.emit('raw', node.name, ...args));
node.once('disconnect', (...args) => this.clean(node, ...args));
node.connect();
this.nodes.set(node.name, node);
}
Expand Down Expand Up @@ -304,7 +299,7 @@ export class Shoukaku extends EventEmitter implements IShoukaku {
* @returns A Lavalink node or undefined
* @internal
*/
private clean(node: Node, ...args: unknown[]): void {
private clean(node: Node, ...args: NodeEvents['disconnect']): void {
node.removeAllListeners();
this.nodes.delete(node.name);
this.emit('disconnect', node.name, ...args);
Expand Down
25 changes: 25 additions & 0 deletions src/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
import { EventEmitter } from 'events';

// https://stackoverflow.com/a/67244127
export abstract class TypedEventEmitter<T extends Record<string, unknown[]>> extends EventEmitter {
protected constructor() {
super();
}

on<K extends Extract<keyof T, string> | symbol>(eventName: K, listener: (...args: T[Extract<K, string>]) => void): this {
return super.on(eventName, listener);
}

once<K extends Extract<keyof T, string> | symbol>(eventName: K, listener: (...args: T[Extract<K, string>]) => void): this {
return super.once(eventName, listener);
}

off<K extends Extract<keyof T, string> | symbol>(eventName: K, listener: (...args: T[Extract<K, string>]) => void): this {
return super.off(eventName, listener);
}

emit<K extends Extract<keyof T, string> | symbol>(eventName: K, ...args: T[Extract<K, string>]): boolean {
return super.emit(eventName, ...args);
}
}

export type Constructor<T> = new (...args: unknown[]) => T;

/**
Expand Down
24 changes: 8 additions & 16 deletions src/guild/Player.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { EventEmitter } from 'events';
import { Node } from '../node/Node';
import { Connection } from './Connection';
import { OpCodes, State } from '../Constants';
import { Exception, Track, UpdatePlayerInfo, UpdatePlayerOptions } from '../node/Rest';
import { TypedEventEmitter } from '../Utils';

export type TrackEndReason = 'finished' | 'loadFailed' | 'stopped' | 'replaced' | 'cleanup';
export type PlayOptions = Omit<UpdatePlayerOptions, 'filters' | 'voice'>;
Expand Down Expand Up @@ -123,7 +123,10 @@ export interface FilterOptions {
lowPass?: LowPassSettings | null;
}

export interface PlayerEvents {
// Interfaces are not final, but types are, and therefore has an index signature
// https://stackoverflow.com/a/64970740
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type PlayerEvents = {
/**
* Emitted when the current playing track ends
* @eventProperty
Expand Down Expand Up @@ -159,19 +162,12 @@ export interface PlayerEvents {
* @eventProperty
*/
'update': [data: PlayerUpdate];
}

export declare interface IPlayer {
on<K extends keyof PlayerEvents>(event: K, listener: (...args: PlayerEvents[K]) => void): this;
once<K extends keyof PlayerEvents>(event: K, listener: (...args: PlayerEvents[K]) => void): this;
off<K extends keyof PlayerEvents>(event: K, listener: (...args: PlayerEvents[K]) => void): this;
emit(event: string | symbol, ...args: unknown[]): boolean;
}
};

/**
* Wrapper object around Lavalink
*/
export class Player extends EventEmitter implements IPlayer {
export class Player extends TypedEventEmitter<PlayerEvents> {
/**
* GuildId of this player
*/
Expand Down Expand Up @@ -543,11 +539,7 @@ export class Player extends EventEmitter implements IPlayer {
this.emit('closed', json);
break;
default:
this.node.emit(
'debug',
this.node.name,
`[Player] -> [Node] : Unknown Player Event Type, Data => ${JSON.stringify(json)}`
);
this.node.emit('debug', `[Player] -> [Node] : Unknown Player Event Type, Data => ${JSON.stringify(json)}`);
}
}
}
26 changes: 14 additions & 12 deletions src/node/Node.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { EventEmitter } from 'events';
import { IncomingMessage } from 'http';
import { NodeOption, Shoukaku } from '../Shoukaku';
import { NodeOption, Shoukaku, ShoukakuEvents } from '../Shoukaku';
import { OpCodes, ShoukakuClientInfo, State, Versions } from '../Constants';
import { wait } from '../Utils';
import { TypedEventEmitter, wait } from '../Utils';
import { Rest } from './Rest';
import { PlayerUpdate, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, WebSocketClosedEvent } from '../guild/Player';
import Websocket from 'ws';
Expand Down Expand Up @@ -78,10 +77,14 @@ export interface ResumableHeaders {

export type NonResumableHeaders = Omit<ResumableHeaders, 'Session-Id'>;

export type NodeEvents = {
[K in keyof ShoukakuEvents]: ShoukakuEvents[K] extends [unknown, ...infer R] ? R : never;
};

/**
* Represents a Lavalink node
*/
export class Node extends EventEmitter {
export class Node extends TypedEventEmitter<NodeEvents> {
/**
* Shoukaku class
*/
Expand Down Expand Up @@ -223,7 +226,7 @@ export class Node extends EventEmitter {
this.ws.once('upgrade', response => this.open(response));
this.ws.once('close', (...args) => this.close(...args));
this.ws.on('error', error => this.error(error));
this.ws.on('message', data => void this.message(data).catch(error => this.error(error)));
this.ws.on('message', data => void this.message(data).catch(error => this.error(error as Error)));
}

/**
Expand Down Expand Up @@ -278,13 +281,13 @@ export class Node extends EventEmitter {
try {
await this.resumePlayers();
} catch (error) {
this.error(error);
this.error(error as Error);
}
}

this.state = State.CONNECTED;
this.emit('debug', `[Socket] -> [${this.name}] : Lavalink is ready! | Lavalink resume: ${json.resumed} | Lib resume: ${!!resumeByLibrary}`);
this.emit('ready', json.resumed || resumeByLibrary);
this.emit('ready', json.resumed || Boolean(resumeByLibrary));

if (this.manager.options.resume) {
await this.rest.updateSession(this.manager.options.resume, this.manager.options.resumeTimeout);
Expand Down Expand Up @@ -312,9 +315,9 @@ export class Node extends EventEmitter {
* @param code Status close
* @param reason Reason for connection close
*/
private close(code: number, reason: unknown): void {
private close(code: number, reason: Buffer): void {
this.emit('debug', `[Socket] <-/-> [${this.name}] : Connection Closed, Code: ${code || 'Unknown Code'}`);
this.emit('close', code, reason);
this.emit('close', code, String(reason));
if (this.shouldClean)
void this.clean();
else
Expand All @@ -325,8 +328,7 @@ export class Node extends EventEmitter {
* To emit error events easily
* @param error error message
*/
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
public error(error: Error | unknown): void {
public error(error: Error): void {
this.emit('error', error);
}

Expand Down Expand Up @@ -371,7 +373,7 @@ export class Node extends EventEmitter {
try {
count = await this.movePlayers();
} catch (error) {
this.error(error);
this.error(error as Error);
} finally {
this.destroy(count);
}
Expand Down

0 comments on commit 749444e

Please sign in to comment.