Skip to content

Commit

Permalink
chore: save progress
Browse files Browse the repository at this point in the history
  • Loading branch information
KagChi committed Dec 5, 2023
1 parent fe76596 commit d176c60
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 106 deletions.
4 changes: 3 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@
"@discordjs/collection": "^2.0.0",
"@kirishima/fetch": "workspace:^",
"@kirishima/rest": "workspace:^",
"@kirishima/types": "workspace:^",
"@kirishima/utilities": "workspace:^",
"@kirishima/ws": "workspace:^",
"@sapphire/async-queue": "^1.5.1",
"@sapphire/utilities": "^3.14.0",
"discord-api-types": "^0.37.65",
"lavalink-api-types": "^2.0.0",
"lavalink-api-types": "^2.0.1",
"undici": "^5.28.2",
"ws": "^8.14.2"
}
Expand Down
40 changes: 22 additions & 18 deletions packages/core/src/Structures/BasePlayer.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import { GatewayVoiceServerUpdateDispatch, GatewayVoiceStateUpdateDispatch } from "discord-api-types/gateway/v9";
import { Snowflake } from "discord-api-types/globals";
import { WebsocketOpEnum } from "lavalink-api-types";
import { KirishimaPlayerOptions, KirishimaNode, createVoiceChannelJoinPayload, Kirishima } from "../index.js";
import { shardPayload } from "@kirishima/utilities";
import { KirishimaNode, Kirishima } from "../index.js";
import { PlayerOptions } from "@kirishima/types";
import { Routes } from "lavalink-api-types/v4";
import { GatewayVoiceServerUpdateDispatchData, GatewayVoiceState } from "discord-api-types/v10";

export class BasePlayer {
public get voiceState() {
public get voiceState(): GatewayVoiceState | undefined {
return this.node.voiceStates.get(this.options.guildId);

Check failure on line 11 in packages/core/src/Structures/BasePlayer.ts

View workflow job for this annotation

GitHub Actions / test / lint

Unsafe argument of type `any` assigned to a parameter of type `string`
}

public get voiceServer() {
public get voiceServer(): GatewayVoiceServerUpdateDispatchData | undefined {
return this.node.voiceServers.get(this.options.guildId);

Check failure on line 15 in packages/core/src/Structures/BasePlayer.ts

View workflow job for this annotation

GitHub Actions / test / lint

Unsafe argument of type `any` assigned to a parameter of type `string`
}

public constructor(public options: KirishimaPlayerOptions, public kirishima: Kirishima, public node: KirishimaNode) { }
public constructor(public options: PlayerOptions, public kirishima: Kirishima, public node: KirishimaNode) { }

public async connect() {
await this.kirishima.options.send(this.options, createVoiceChannelJoinPayload(this.options));
public async connect(): Promise<this> {
await this.kirishima.options.send(this.options, shardPayload(this.options));
return this;
}

public async disconnect() {
await this.kirishima.options.send(this.options, createVoiceChannelJoinPayload(this.options, true));
public async disconnect(): Promise<this> {
await this.kirishima.options.send(this.options, shardPayload(this.options, true));
return this;
}

public async setServerUpdate(packet: GatewayVoiceServerUpdateDispatch) {
public async setServerUpdate(packet: GatewayVoiceServerUpdateDispatch): Promise<void> {
this.node.voiceServers.set(packet.d.guild_id, packet.d);
return this.sendVoiceUpdate(packet.d.guild_id);
}

public async setStateUpdate(packet: GatewayVoiceStateUpdateDispatch) {
public async setStateUpdate(packet: GatewayVoiceStateUpdateDispatch): Promise<void> {
if (packet.d.user_id !== this.kirishima.options.clientId) return;

if (packet.d.channel_id && packet.d.guild_id) {
Expand All @@ -44,16 +47,17 @@ export class BasePlayer {
}
}

public async sendVoiceUpdate(guildId: Snowflake) {
public async sendVoiceUpdate(guildId: Snowflake): Promise<void> {
const voiceState = this.node.voiceStates.get(guildId);
const event = this.node.voiceServers.get(guildId);

if (event && voiceState) {
await this.node.ws.send({
op: WebsocketOpEnum.VOICE_UPDATE,
guildId,
sessionId: voiceState.session_id,
event
if (event && voiceState && this.node.sessionId) {
await this.node.rest.patch(Routes.player(this.node.sessionId, guildId), {
body: JSON.stringify({
token: event.token,
endpoint: event.endpoint,
sessionId: voiceState.session_id
})
});
}
}
Expand Down
71 changes: 25 additions & 46 deletions packages/core/src/Structures/Node.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import { WebSocket } from "ws";
import { Gateway } from "@kirishima/ws";
import { REST } from "@kirishima/rest";
import { KirishimaNodeOptions } from "../typings/index.js";
import { Kirishima } from "./Kirishima.js";
import { GatewayVoiceServerUpdateDispatch, GatewayVoiceStateUpdateDispatch } from "discord-api-types/gateway/v9";
import { LavalinkStatsPayload, WebsocketOpEnum } from "lavalink-api-types";
import { BasePlayer } from "./BasePlayer.js";
import { Collection } from "@discordjs/collection";
import { Snowflake } from "discord-api-types/globals";
import { StatsPayload, WebSocketOp } from "lavalink-api-types/v4";
import { BasePlayer } from "./BasePlayer.js";

export class KirishimaNode {
public ws!: Gateway;
public rest!: REST;
public stats: LavalinkStatsPayload | undefined;
public stats: StatsPayload | undefined;
public sessionId: string | null = null;
public reconnect: { attempts: number; timeout?: NodeJS.Timeout } = { attempts: 0 };
public voiceServers = new Collection<Snowflake, GatewayVoiceServerUpdateDispatch["d"]>();
public voiceStates = new Collection<Snowflake, GatewayVoiceStateUpdateDispatch["d"]>();
public constructor(public options: KirishimaNodeOptions, public kirishima: Kirishima) { }
public constructor(public options: KirishimaNodeOptions, public kirishima: Kirishima) {
this.rest = new REST(`${this.options.url.endsWith("443") || this.options.secure ? "https" : "http"}://${this.options.url}`, {
Authorization: this.options.password ??= "youshallnotpass"
});
}

public get connected() {
if (!this.ws) return false;
public get connected(): boolean {
return this.ws.connection?.readyState === WebSocket.OPEN;
}

public async connect(): Promise<KirishimaNode> {
this.rest ??= new REST(`${this.options.url.endsWith("443") || this.options.secure ? "https" : "http"}://${this.options.url}`, {
Authorization: this.options.password ??= "youshallnotpass"
});
if (this.connected) return this;
const headers = {
Authorization: this.options.password ??= "youshallnotpass",
"User-Id": this.kirishima.options.clientId!,
"Client-Name": this.kirishima.options.clientName ??= "Kirishima NodeJS Lavalink Client (https://github.com/kirishima-ship/core)"
};

// @ts-expect-error If you know how to fix this, please open a PR.
if (this.kirishima.options.node?.resumeKey) headers["Resume-Key"] = this.kirishima.options.node.resumeKey;
this.ws = new Gateway(`${this.options.url.endsWith("443") || this.options.secure ? "wss" : "ws"}://${this.options.url}`, headers);
await this.ws.connect();
this.ws.on("open", () => this.open.bind(this));
Expand All @@ -45,24 +45,17 @@ export class KirishimaNode {
return this;
}

public disconnect() {
public disconnect(): void {
this.ws.connection?.close(1000, "Disconnected by user");
if (this.reconnect.timeout) clearTimeout(this.reconnect.timeout);
}

public open(gateway: Gateway) {
public open(gateway: Gateway): void {
this.reconnect.attempts = 0;
if (this.kirishima.options.node?.resumeKey && this.kirishima.options.node.resumeTimeout) {
void this.ws.send({
op: WebsocketOpEnum.CONFIGURE_RESUMING,
key: this.kirishima.options.node.resumeKey,
timeout: this.kirishima.options.node.resumeTimeout
});
}
this.kirishima.emit("nodeConnect", this, gateway);
}

public close(gateway: Gateway, close: number) {
public close(gateway: Gateway, close: number): void {
this.kirishima.emit("nodeDisconnect", this, gateway, close);
if (this.kirishima.options.node && this.kirishima.options.node.reconnectOnDisconnect) {
if (this.reconnect.attempts < (this.kirishima.options.node.reconnectAttempts ?? 3)) {
Expand All @@ -77,41 +70,27 @@ export class KirishimaNode {
}
}

public error(gateway: Gateway, error: Error) {
public error(gateway: Gateway, error: Error): void {
this.kirishima.emit("nodeError", this, gateway, error);
}

public message(gateway: Gateway, raw: string) {
public message(gateway: Gateway, raw: Record<string, unknown>): void {
try {
const message = JSON.parse(raw);
this.kirishima.emit("nodeRaw", this, gateway, message);
if (message.op === WebsocketOpEnum.STATS) this.stats = message;
if (raw.op === WebSocketOp.Ready) {
this.sessionId = raw.sessionId as string;
}
} catch (e) {
this.kirishima.emit("nodeError", this, gateway, e);
}
}

public toJSON() {
return {
identifier: this.options.identifier,
url: this.options.url,
secure: this.options.secure,
password: this.options.password,
group: this.options.group
};
public async handleVoiceServerUpdate(packet: GatewayVoiceServerUpdateDispatch): Promise<void> {
const player = await this.kirishima.options.fetchPlayer!(packet.d.guild_id) as unknown as BasePlayer | undefined;
if (player) await player.setServerUpdate(packet);
}

public async handleVoiceServerUpdate(packet: GatewayVoiceServerUpdateDispatch) {
const player = (await this.kirishima.options.fetchPlayer!(packet.d.guild_id)) as BasePlayer;
if (player) {
await player.setServerUpdate(packet);
}
}

public async handleVoiceStateUpdate(packet: GatewayVoiceStateUpdateDispatch) {
const player = (await this.kirishima.options.fetchPlayer!(packet.d.guild_id!)) as BasePlayer;
if (player) {
await player.setStateUpdate(packet);
}
public async handleVoiceStateUpdate(packet: GatewayVoiceStateUpdateDispatch): Promise<void> {
const player = await this.kirishima.options.fetchPlayer!(packet.d.guild_id!) as unknown as BasePlayer | undefined;
if (player) await player.setStateUpdate(packet);
}
}
5 changes: 0 additions & 5 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,9 @@ export * from "./Structures/Node.js";
export * from "./Structures/Structure.js";
export * from "./Structures/Player.js";
export * from "./Structures/Kirishima.js";
export * from "./Structures/PartialTrack.js";
export * from "./Structures/Track.js";
export * from "./Structures/Filter.js";
export * from "./Structures/Plugin.js";
export * from "./Structures/BasePlayer.js";

export * from "./typings/index.js";

export * from "./Util/createVoiceChannelJoinPayload.js";
export * from "./Util/isPartialTrack.js";
export * from "./Util/isTrack.js";
32 changes: 5 additions & 27 deletions packages/core/src/typings/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Awaitable } from "@sapphire/utilities";
import { GatewayOpcodes } from "discord-api-types/gateway/v9";
import { KirishimaNode } from "../Structures/Node.js";
import { KirishimaPlayer } from "../Structures/Player.js";
import { KirishimaPartialTrack } from "../Structures/PartialTrack.js";
import { KirishimaFilter } from "../Structures/Filter.js";
import { KirishimaPlugin } from "../Structures/Plugin.js";
import { Exception, LoadTypeEnum } from "lavalink-api-types";
import { Track } from "lavalink-api-types/v4"
import { PlayerOptions, ShardPayload } from "@kirishima/types";
import { BasePlayer } from "src/index.js";

export interface KirishimaOptions {
clientId?: string;
Expand All @@ -19,35 +17,15 @@ export interface KirishimaOptions {
reconnectInterval?: number;
reconnectAttempts?: number;
};
send: (options: KirishimaPlayerOptions, payload: payload) => Awaitable<unknown>;
/** @description customize-able spawn-player handler, allow you to set it to collection or even redis. */
send: (options: PlayerOptions, payload: ShardPayload) => Awaitable<unknown>;
spawnPlayer?: SpawnPlayerOptionHook;
/** @description Used for getting global player, most likely used when `VOICE_SERVER_UPDATE` and `VOICE_SERVER_UPDATE` emits. note: you must provide same major method when customizing player handler. */
fetchPlayer?: PlayerOptionHook;
plugins?: KirishimaPlugin[];
}

export type SpawnPlayerOptionHook = (guildId: string, options: KirishimaPlayerOptions, node: KirishimaNode) => Awaitable<unknown>;
export type SpawnPlayerOptionHook = (guildId: string, options: PlayerOptions, node: KirishimaNode) => Awaitable<unknown>;

export type PlayerOptionHook = (guildId: string) => Awaitable<unknown | undefined>;
export interface payload {
op: GatewayOpcodes;
d: {
guild_id: string;
channel_id: string | null;
self_deaf: boolean;
self_mute: boolean;
};
}

export interface KirishimaPlayerOptions {
guildId: string;
shardId?: number;
channelId: string;
textChannelId?: string;
selfDeaf?: boolean;
selfMute?: boolean;
}
export type PlayerOptionHook = (guildId: string) => Awaitable<BasePlayer>;

export interface KirishimaNodeOptions {
identifier?: string;
Expand Down
4 changes: 3 additions & 1 deletion packages/types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
"plugin:@typescript-eslint/recommended",
"@hazmi35/eslint-config/typescript"
],
"rules": {}
"rules": {
"@typescript-eslint/naming-convention": "off"
}
}
}
10 changes: 10 additions & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ export interface PlayerOptions {
selfDeaf?: boolean;
selfMute?: boolean;
}

export interface ShardPayload {
op: number;
d: {
guild_id: string;
channel_id: string | null;
self_deaf: boolean;
self_mute: boolean;
};
}
4 changes: 3 additions & 1 deletion packages/utilities/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"plugin:@typescript-eslint/recommended",
"@hazmi35/eslint-config/typescript"
],
"rules": {}
"rules": {
"@typescript-eslint/naming-convention": "off"
}
},
"dependencies": {
"@kirishima/types": "workspace:^",
Expand Down
6 changes: 3 additions & 3 deletions packages/utilities/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GatewayOpcodes } from "discord-api-types/v10";
import { PlayerOptions } from "@kirishima/types";
import { PlayerOptions, ShardPayload } from "@kirishima/types";

export function createVoiceChannelJoinPayload(options: PlayerOptions, leave?: boolean) {
export function shardPayload(options: PlayerOptions, leave?: boolean): ShardPayload {
return {
op: GatewayOpcodes.VoiceStateUpdate,
d: {
Expand All @@ -11,4 +11,4 @@ export function createVoiceChannelJoinPayload(options: PlayerOptions, leave?: bo
self_mute: options.selfMute ??= false
}
};
}
}
14 changes: 10 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d176c60

Please sign in to comment.