yukimu 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.cache/replit/env/latest +72 -72
  2. package/.cache/replit/env/latest.json +1 -1
  3. package/.cache/replit/toolchain.json +1 -1
  4. package/dist/ConnectionPool.d.ts +1 -0
  5. package/dist/ConnectionPool.d.ts.map +1 -1
  6. package/dist/ConnectionPool.js +11 -24
  7. package/dist/ConnectionPool.js.map +1 -1
  8. package/dist/Constants.d.ts +3 -49
  9. package/dist/Constants.d.ts.map +1 -1
  10. package/dist/Constants.js +6 -61
  11. package/dist/Constants.js.map +1 -1
  12. package/dist/Node.d.ts +1 -1
  13. package/dist/Node.d.ts.map +1 -1
  14. package/dist/Node.js +17 -17
  15. package/dist/Node.js.map +1 -1
  16. package/dist/Player.d.ts +9 -5
  17. package/dist/Player.d.ts.map +1 -1
  18. package/dist/Player.js +73 -54
  19. package/dist/Player.js.map +1 -1
  20. package/dist/Plugin.d.ts +0 -14
  21. package/dist/Plugin.d.ts.map +1 -1
  22. package/dist/Plugin.js +0 -12
  23. package/dist/Plugin.js.map +1 -1
  24. package/dist/Queue.d.ts +0 -1
  25. package/dist/Queue.d.ts.map +1 -1
  26. package/dist/Queue.js +7 -22
  27. package/dist/Queue.js.map +1 -1
  28. package/dist/Resolver.d.ts +1 -1
  29. package/dist/Resolver.d.ts.map +1 -1
  30. package/dist/Resolver.js +24 -10
  31. package/dist/Resolver.js.map +1 -1
  32. package/dist/Rest.d.ts +3 -4
  33. package/dist/Rest.d.ts.map +1 -1
  34. package/dist/Rest.js +26 -16
  35. package/dist/Rest.js.map +1 -1
  36. package/dist/TrackCache.d.ts +0 -5
  37. package/dist/TrackCache.d.ts.map +1 -1
  38. package/dist/TrackCache.js +6 -23
  39. package/dist/TrackCache.js.map +1 -1
  40. package/dist/WsQueue.d.ts +2 -11
  41. package/dist/WsQueue.d.ts.map +1 -1
  42. package/dist/WsQueue.js +5 -21
  43. package/dist/WsQueue.js.map +1 -1
  44. package/dist/Yukimu.d.ts +3 -12
  45. package/dist/Yukimu.d.ts.map +1 -1
  46. package/dist/Yukimu.js +2 -8
  47. package/dist/Yukimu.js.map +1 -1
  48. package/dist/connector/DiscordJS.d.ts +1 -18
  49. package/dist/connector/DiscordJS.d.ts.map +1 -1
  50. package/dist/connector/DiscordJS.js.map +1 -1
  51. package/dist/connector/Eris.d.ts +1 -16
  52. package/dist/connector/Eris.d.ts.map +1 -1
  53. package/dist/connector/Eris.js +2 -4
  54. package/dist/connector/Eris.js.map +1 -1
  55. package/dist/connector/Oceanic.d.ts +1 -13
  56. package/dist/connector/Oceanic.d.ts.map +1 -1
  57. package/dist/connector/Oceanic.js +2 -4
  58. package/dist/connector/Oceanic.js.map +1 -1
  59. package/dist/errors/YukimuError.d.ts.map +1 -1
  60. package/dist/errors/YukimuError.js +0 -1
  61. package/dist/errors/YukimuError.js.map +1 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +0 -5
  64. package/dist/index.js.map +1 -1
  65. package/dist/plugins/AutoResume.d.ts +0 -13
  66. package/dist/plugins/AutoResume.d.ts.map +1 -1
  67. package/dist/plugins/AutoResume.js +10 -18
  68. package/dist/plugins/AutoResume.js.map +1 -1
  69. package/dist/plugins/PlayerMoved.js.map +1 -1
  70. package/dist/types.d.ts +25 -34
  71. package/dist/types.d.ts.map +1 -1
  72. package/package.json +2 -2
  73. package/src/ConnectionPool.ts +14 -31
  74. package/src/Constants.ts +6 -62
  75. package/src/Node.ts +44 -51
  76. package/src/Player.ts +86 -91
  77. package/src/Plugin.ts +0 -16
  78. package/src/Queue.ts +7 -32
  79. package/src/Resolver.ts +25 -14
  80. package/src/Rest.ts +34 -28
  81. package/src/TrackCache.ts +7 -30
  82. package/src/WsQueue.ts +9 -29
  83. package/src/Yukimu.ts +10 -45
  84. package/src/connector/DiscordJS.ts +5 -12
  85. package/src/connector/Eris.ts +6 -17
  86. package/src/connector/Oceanic.ts +5 -15
  87. package/src/errors/YukimuError.ts +0 -2
  88. package/src/index.ts +4 -26
  89. package/src/plugins/AutoResume.ts +13 -37
  90. package/src/plugins/PlayerMoved.ts +4 -4
  91. package/src/types.ts +26 -26
  92. package/tsconfig.json +3 -1
package/src/Queue.ts CHANGED
@@ -5,21 +5,16 @@ export class Queue {
5
5
  public previous: Track[] = [];
6
6
  private tracks: Track[] = [];
7
7
 
8
- // ─── Add / Remove ─────────────────────────────────────────────────
9
-
10
8
  public add(tracks: Track | Track[], position?: number): void {
11
9
  const arr = Array.isArray(tracks) ? tracks : [tracks];
12
- if (position !== undefined) {
13
- this.tracks.splice(position, 0, ...arr);
14
- } else {
15
- this.tracks.push(...arr);
16
- }
10
+ if (position !== undefined) this.tracks.splice(position, 0, ...arr);
11
+ else this.tracks.push(...arr);
17
12
  }
18
13
 
19
14
  public next(): Track | null {
20
15
  if (this.current) {
21
16
  this.previous.unshift(this.current);
22
- if (this.previous.length > 10) this.previous.pop(); // keep last 10
17
+ if (this.previous.length > 10) this.previous.pop();
23
18
  }
24
19
  this.current = this.tracks.shift() ?? null;
25
20
  return this.current;
@@ -41,8 +36,6 @@ export class Queue {
41
36
  this.previous = [];
42
37
  }
43
38
 
44
- // ─── Reorder ──────────────────────────────────────────────────────
45
-
46
39
  public shuffle(): void {
47
40
  for (let i = this.tracks.length - 1; i > 0; i--) {
48
41
  const j = Math.floor(Math.random() * (i + 1));
@@ -56,36 +49,18 @@ export class Queue {
56
49
  this.tracks.splice(to, 0, track);
57
50
  }
58
51
 
59
- /** Skip to a specific position in queue */
60
52
  public skipto(index: number): Track | null {
61
53
  if (index < 0 || index >= this.tracks.length) return null;
62
54
  this.tracks.splice(0, index);
63
55
  return this.next();
64
56
  }
65
57
 
66
- // ─── Peek / Find ──────────────────────────────────────────────────
67
-
68
- public peek(count: number = 10): Track[] {
69
- return this.tracks.slice(0, count);
70
- }
71
-
72
- public find(predicate: (track: Track) => boolean): Track | undefined {
73
- return this.tracks.find(predicate);
74
- }
75
-
76
- public filter(predicate: (track: Track) => boolean): Track[] {
77
- return this.tracks.filter(predicate);
78
- }
79
-
80
- // ─── Getters ──────────────────────────────────────────────────────
58
+ public peek(count: number = 10): Track[] { return this.tracks.slice(0, count); }
59
+ public find(predicate: (track: Track) => boolean): Track | undefined { return this.tracks.find(predicate); }
60
+ public filter(predicate: (track: Track) => boolean): Track[] { return this.tracks.filter(predicate); }
81
61
 
82
62
  get size(): number { return this.tracks.length; }
83
-
84
63
  get isEmpty(): boolean { return this.tracks.length === 0; }
85
-
86
- get totalDuration(): number {
87
- return this.tracks.reduce((acc, t) => acc + (t.info?.length ?? 0), 0);
88
- }
89
-
64
+ get totalDuration(): number { return this.tracks.reduce((acc, t) => acc + (t.info?.length ?? 0), 0); }
90
65
  get list(): ReadonlyArray<Track> { return this.tracks; }
91
66
  }
package/src/Resolver.ts CHANGED
@@ -7,19 +7,28 @@ export class Resolver {
7
7
  private spotifyToken: string | null = null;
8
8
  private spotifyExpiry: number = 0;
9
9
 
10
- constructor(manager: Yukimu) {
11
- this.manager = manager;
12
- }
10
+ constructor(manager: Yukimu) { this.manager = manager; }
13
11
 
14
- public async resolve(query: string, source: SearchSource, requester?: unknown): Promise<SearchResult> {
12
+ public async resolve(query: string, source: SearchSource, requester?: any): Promise<SearchResult> {
15
13
  const node = this.manager.getBestNode();
16
14
  const isUrl = /^https?:\/\//.test(query);
17
15
  const identifier = isUrl ? query : `${SOURCE_PREFIXES[source] ?? "ytsearch"}:${query}`;
18
16
 
19
- const result = await node.loadTracks(identifier);
17
+ let result: SearchResult;
18
+ try {
19
+ result = await node.loadTracks(identifier);
20
+ } catch (err: any) {
21
+ console.error("[Resolver] loadTracks error:", err.message);
22
+ return { loadType: "error", tracks: [] };
23
+ }
24
+
25
+ if (!result) return { loadType: "empty", tracks: [] };
26
+ if (!result.tracks) result.tracks = [];
20
27
 
21
- // Attach requester
22
- if (requester) result.tracks.forEach(t => { (t as Track & { requester: unknown }).requester = requester; });
28
+ // Attach requester to all tracks
29
+ if (requester && result.tracks.length > 0) {
30
+ result.tracks.forEach((t: any) => { t.requester = requester; });
31
+ }
23
32
 
24
33
  // Spotify fallback — if LavaSrc not installed
25
34
  if ((result.loadType === "error" || result.loadType === "empty") && isUrl) {
@@ -30,9 +39,11 @@ export class Resolver {
30
39
  }
31
40
  }
32
41
 
33
- // Cache all returned tracks
34
- for (const track of result.tracks) {
35
- if (track.encoded) this.manager.trackCache.set(track.encoded, track);
42
+ // Cache tracks
43
+ if (result.tracks.length > 0) {
44
+ for (const track of result.tracks) {
45
+ if (track?.encoded) this.manager.trackCache.set(track.encoded, track);
46
+ }
36
47
  }
37
48
 
38
49
  return result;
@@ -50,7 +61,7 @@ export class Resolver {
50
61
  if (!opts) return null;
51
62
  if (this.spotifyToken && Date.now() < this.spotifyExpiry) return this.spotifyToken;
52
63
  try {
53
- const res = await fetch("https://accounts.spotify.com/api/token", {
64
+ const res = await (globalThis as any).fetch("https://accounts.spotify.com/api/token", {
54
65
  method: "POST",
55
66
  headers: {
56
67
  "Content-Type": "application/x-www-form-urlencoded",
@@ -58,7 +69,7 @@ export class Resolver {
58
69
  },
59
70
  body: "grant_type=client_credentials",
60
71
  });
61
- const data = await res.json() as { access_token: string; expires_in: number };
72
+ const data = await res.json();
62
73
  this.spotifyToken = data.access_token;
63
74
  this.spotifyExpiry = Date.now() + data.expires_in * 1000 - 5000;
64
75
  return this.spotifyToken;
@@ -69,10 +80,10 @@ export class Resolver {
69
80
  const token = await this.getSpotifyToken();
70
81
  if (!token) return null;
71
82
  try {
72
- const res = await fetch(`https://api.spotify.com/v1/tracks/${trackId}`, {
83
+ const res = await (globalThis as any).fetch(`https://api.spotify.com/v1/tracks/${trackId}`, {
73
84
  headers: { Authorization: `Bearer ${token}` },
74
85
  });
75
- const data = await res.json() as { name: string; artists: { name: string }[] };
86
+ const data = await res.json();
76
87
  return { title: data.name, artist: data.artists[0]?.name ?? "Unknown" };
77
88
  } catch { return null; }
78
89
  }
package/src/Rest.ts CHANGED
@@ -2,35 +2,31 @@ import { RestError } from "./errors/YukimuError";
2
2
  import type { Node } from "./Node";
3
3
  import type { SearchResult, Track, NodeStats, NodeInfo } from "./types";
4
4
 
5
- const TIMEOUT_MS = 15000;
5
+ const TIMEOUT_MS = 30000;
6
6
 
7
7
  export class Rest {
8
8
  private node: Node;
9
9
 
10
- constructor(node: Node) {
11
- this.node = node;
12
- }
10
+ constructor(node: Node) { this.node = node; }
13
11
 
14
12
  get baseUrl(): string {
15
13
  const protocol = this.node.options.secure ? "https" : "http";
16
14
  return `${protocol}://${this.node.options.host}:${this.node.options.port}`;
17
15
  }
18
16
 
19
- get prefix(): string {
20
- return this.node.version === 4 ? "/v4" : "";
21
- }
17
+ get prefix(): string { return this.node.version === 4 ? "/v4" : ""; }
22
18
 
23
19
  get headers(): Record<string, string> {
24
20
  return {
25
21
  Authorization: this.node.options.password,
26
22
  "User-Id": this.node.manager.options.clientId,
27
- "Client-Name": "Yukimu/1.2.0",
23
+ "Client-Name": "Yukimu/1.3.0",
28
24
  "Content-Type": "application/json",
29
25
  ...(this.node.version === 3 ? { "Num-Shards": "1" } : {}),
30
26
  };
31
27
  }
32
28
 
33
- public async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {
29
+ public async request<T = any>(method: string, path: string, body?: any): Promise<T> {
34
30
  const url = `${this.baseUrl}${this.prefix}${path}`;
35
31
  const controller = new AbortController();
36
32
  const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
@@ -51,7 +47,17 @@ export class Rest {
51
47
  }
52
48
 
53
49
  if (res.status === 204) return undefined as T;
54
- return res.json() as Promise<T>;
50
+
51
+ const text = await res.text();
52
+ let parsed: any;
53
+ try { parsed = JSON.parse(text); } catch { return null as T; }
54
+
55
+ // Normalize Lavalink v4.2+ response — uses "data" instead of "tracks"
56
+ if (parsed && parsed.data !== undefined && !parsed.tracks) {
57
+ parsed.tracks = Array.isArray(parsed.data) ? parsed.data : [parsed.data];
58
+ }
59
+
60
+ return parsed as T;
55
61
  } catch (err: any) {
56
62
  clearTimeout(timeout);
57
63
  if (err.name === "AbortError") throw new RestError(`REST timeout on ${method} ${path}`, 408, path);
@@ -60,8 +66,9 @@ export class Rest {
60
66
  }
61
67
 
62
68
  public async loadTracks(identifier: string): Promise<SearchResult> {
63
- const raw = await this.request<Record<string, unknown>>("GET", `/loadtracks?identifier=${encodeURIComponent(identifier)}`);
64
- return this.node.version === 3 ? this.normalizeV3(raw) : raw as unknown as SearchResult;
69
+ const raw = await this.request<any>("GET", `/loadtracks?identifier=${encodeURIComponent(identifier)}`);
70
+ if (!raw) return { loadType: "empty", tracks: [] };
71
+ return this.node.version === 3 ? this.normalizeV3(raw) : raw;
65
72
  }
66
73
 
67
74
  public async decodeTrack(encoded: string): Promise<Track> {
@@ -80,12 +87,7 @@ export class Rest {
80
87
  return this.request("GET", "/stats");
81
88
  }
82
89
 
83
- public async getVersion(): Promise<string> {
84
- const res = await (globalThis as any).fetch(`${this.baseUrl}/version`, { headers: this.headers });
85
- return res.text();
86
- }
87
-
88
- public async updatePlayer(guildId: string, body: unknown, noReplace = false): Promise<unknown> {
90
+ public async updatePlayer(guildId: string, body: any, noReplace = false): Promise<any> {
89
91
  const path = this.node.version === 4
90
92
  ? `/sessions/${this.node.sessionId}/players/${guildId}?noReplace=${noReplace}`
91
93
  : `/players/${guildId}`;
@@ -96,25 +98,29 @@ export class Rest {
96
98
  const path = this.node.version === 4
97
99
  ? `/sessions/${this.node.sessionId}/players/${guildId}`
98
100
  : `/players/${guildId}`;
99
- await this.request("DELETE", path);
101
+ await this.request("DELETE", path).catch(() => {});
100
102
  }
101
103
 
102
- public async updateSession(body: { resuming?: boolean; timeout?: number }): Promise<unknown> {
104
+ public async updateSession(body: { resuming?: boolean; timeout?: number }): Promise<any> {
103
105
  if (this.node.version !== 4) return;
104
106
  return this.request("PATCH", `/sessions/${this.node.sessionId}`, body);
105
107
  }
106
108
 
107
- private normalizeV3(raw: Record<string, unknown>): SearchResult {
108
- const loadType = ((raw.loadType as string) ?? "").toLowerCase();
109
- const norm = (tracks: unknown[]): Track[] =>
110
- (tracks ?? []).map((t: any) => ({ encoded: t.track ?? t.encoded, info: t.info, pluginInfo: t.pluginInfo ?? {} }));
109
+ private normalizeV3(raw: any): SearchResult {
110
+ const loadType = (raw?.loadType ?? "").toLowerCase();
111
+ const norm = (tracks: any[]): Track[] =>
112
+ (tracks ?? []).map((t: any) => ({
113
+ encoded: t.track ?? t.encoded,
114
+ info: t.info,
115
+ pluginInfo: t.pluginInfo ?? {},
116
+ }));
111
117
 
112
118
  switch (loadType) {
113
- case "track_loaded": return { loadType: "track", tracks: norm(raw.tracks as unknown[]) };
114
- case "playlist_loaded": return { loadType: "playlist", tracks: norm(raw.tracks as unknown[]), playlistInfo: raw.playlistInfo as any };
115
- case "search_result": return { loadType: "search", tracks: norm(raw.tracks as unknown[]) };
119
+ case "track_loaded": return { loadType: "track", tracks: norm(raw.tracks) };
120
+ case "playlist_loaded": return { loadType: "playlist", tracks: norm(raw.tracks), playlistInfo: raw.playlistInfo };
121
+ case "search_result": return { loadType: "search", tracks: norm(raw.tracks) };
116
122
  case "no_matches": return { loadType: "empty", tracks: [] };
117
- case "load_failed": return { loadType: "error", tracks: [], exception: raw.exception as any };
123
+ case "load_failed": return { loadType: "error", tracks: [], exception: raw.exception };
118
124
  default: return { loadType: "empty", tracks: [] };
119
125
  }
120
126
  }
package/src/TrackCache.ts CHANGED
@@ -5,51 +5,30 @@ interface CacheEntry {
5
5
  expiresAt: number;
6
6
  }
7
7
 
8
- /**
9
- * Track Decode Cache
10
- * Caches decoded tracks to avoid repeated Lavalink decode calls.
11
- * TTL-based expiry — default 1 hour.
12
- */
13
8
  export class TrackCache {
14
9
  private cache: Map<string, CacheEntry> = new Map();
15
10
  private readonly ttl: number;
16
11
  private sweepInterval: ReturnType<typeof setInterval>;
17
12
 
18
- constructor(ttlMs: number = 3600000 /* 1 hour */) {
13
+ constructor(ttlMs: number = 3600000) {
19
14
  this.ttl = ttlMs;
20
-
21
- // Sweep expired entries every 10 minutes
22
15
  this.sweepInterval = setInterval(() => this.sweep(), 600000);
23
16
  }
24
17
 
25
18
  public set(encoded: string, track: Track): void {
26
- this.cache.set(encoded, {
27
- track,
28
- expiresAt: Date.now() + this.ttl,
29
- });
19
+ this.cache.set(encoded, { track, expiresAt: Date.now() + this.ttl });
30
20
  }
31
21
 
32
22
  public get(encoded: string): Track | null {
33
23
  const entry = this.cache.get(encoded);
34
24
  if (!entry) return null;
35
- if (Date.now() > entry.expiresAt) {
36
- this.cache.delete(encoded);
37
- return null;
38
- }
25
+ if (Date.now() > entry.expiresAt) { this.cache.delete(encoded); return null; }
39
26
  return entry.track;
40
27
  }
41
28
 
42
- public has(encoded: string): boolean {
43
- return this.get(encoded) !== null;
44
- }
45
-
46
- public delete(encoded: string): void {
47
- this.cache.delete(encoded);
48
- }
49
-
50
- public clear(): void {
51
- this.cache.clear();
52
- }
29
+ public has(encoded: string): boolean { return this.get(encoded) !== null; }
30
+ public delete(encoded: string): void { this.cache.delete(encoded); }
31
+ public clear(): void { this.cache.clear(); }
53
32
 
54
33
  private sweep(): void {
55
34
  const now = Date.now();
@@ -63,7 +42,5 @@ export class TrackCache {
63
42
  this.cache.clear();
64
43
  }
65
44
 
66
- get size(): number {
67
- return this.cache.size;
68
- }
45
+ get size(): number { return this.cache.size; }
69
46
  }
package/src/WsQueue.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type WebSocket from "ws";
1
+ import WebSocket from "ws";
2
2
 
3
3
  interface QueuedMessage {
4
4
  data: string;
@@ -6,14 +6,8 @@ interface QueuedMessage {
6
6
  reject: (err: Error) => void;
7
7
  }
8
8
 
9
- /**
10
- * WebSocket Message Queue
11
- * Queues outgoing WS messages and flushes them on reconnect.
12
- * Prevents message loss when node briefly disconnects.
13
- */
14
9
  export class WsQueue {
15
10
  private queue: QueuedMessage[] = [];
16
- private flushing = false;
17
11
  private ws: WebSocket | null = null;
18
12
 
19
13
  public setSocket(ws: WebSocket | null): void {
@@ -23,56 +17,42 @@ export class WsQueue {
23
17
  }
24
18
  }
25
19
 
26
- /** Send immediately if connected, else queue */
27
- public send(data: Record<string, unknown>): Promise<void> {
20
+ public send(data: Record<string, any>): Promise<void> {
28
21
  return new Promise((resolve, reject) => {
29
22
  const serialized = JSON.stringify(data);
30
-
31
- if (this.ws?.readyState === 1 /* OPEN */) {
32
- this.ws.send(serialized, (err) => {
23
+ if (this.ws?.readyState === 1) {
24
+ this.ws.send(serialized, (err: any) => {
33
25
  if (err) reject(err);
34
26
  else resolve();
35
27
  });
36
28
  } else {
37
- // Queue for later when reconnected
38
29
  this.queue.push({ data: serialized, resolve, reject });
39
30
  }
40
31
  });
41
32
  }
42
33
 
43
- /** Flush all queued messages after reconnect */
44
34
  public flush(): void {
45
- if (this.flushing || !this.ws) return;
46
- this.flushing = true;
47
-
35
+ if (!this.ws) return;
48
36
  const toSend = [...this.queue];
49
37
  this.queue = [];
50
-
51
38
  for (const msg of toSend) {
52
- if (this.ws.readyState !== 1 /* OPEN */) {
53
- // Re-queue if disconnected again
39
+ if (this.ws.readyState !== 1) {
54
40
  this.queue.unshift(msg);
55
- this.flushing = false;
56
41
  return;
57
42
  }
58
- this.ws.send(msg.data, (err) => {
43
+ this.ws.send(msg.data, (err: any) => {
59
44
  if (err) msg.reject(err);
60
45
  else msg.resolve();
61
46
  });
62
47
  }
63
-
64
- this.flushing = false;
65
48
  }
66
49
 
67
- /** Drop all queued messages (on intentional destroy) */
68
50
  public clear(): void {
69
51
  for (const msg of this.queue) {
70
- msg.reject(new Error("WsQueue cleared — node destroyed"));
52
+ msg.reject(new Error("WsQueue cleared"));
71
53
  }
72
54
  this.queue = [];
73
55
  }
74
56
 
75
- get size(): number {
76
- return this.queue.length;
77
- }
57
+ get size(): number { return this.queue.length; }
78
58
  }
package/src/Yukimu.ts CHANGED
@@ -8,10 +8,7 @@ import { URL_PATTERNS } from "./Constants";
8
8
  import { YukimuError } from "./errors/YukimuError";
9
9
  import type { Plugin } from "./Plugin";
10
10
  import type { Connector } from "./connector/Connector";
11
- import type {
12
- YukimuOptions, NodeOptions, PlayerOptions,
13
- YukimuEvents, SearchSource, SearchResult,
14
- } from "./types";
11
+ import type { YukimuOptions, NodeOptions, PlayerOptions, YukimuEvents, SearchSource, SearchResult } from "./types";
15
12
 
16
13
  export class Yukimu extends EventEmitter {
17
14
  public readonly options: YukimuOptions;
@@ -21,40 +18,27 @@ export class Yukimu extends EventEmitter {
21
18
  public readonly resolver: Resolver;
22
19
  public readonly pool: ConnectionPool;
23
20
  public readonly trackCache: TrackCache;
24
-
25
21
  private readonly plugins: Map<string, Plugin> = new Map();
26
-
27
22
  public nodeResolver?: (nodes: Map<string, Node>) => Node | undefined;
28
23
 
29
- constructor(
30
- options: YukimuOptions,
31
- connector: Connector,
32
- nodes: NodeOptions[],
33
- plugins: Plugin[] = []
34
- ) {
24
+ constructor(options: YukimuOptions, connector: Connector, nodes: NodeOptions[], plugins: Plugin[] = []) {
35
25
  super();
36
- this.options = {
37
- moveOnDisconnect: true,
38
- voiceConnectionTimeout: 15000,
39
- ...options,
40
- };
26
+ this.options = { moveOnDisconnect: true, voiceConnectionTimeout: 15000, ...options };
41
27
  this.connector = connector.set(this);
42
28
  this.resolver = new Resolver(this);
43
29
  this.pool = new ConnectionPool(this);
44
30
  this.trackCache = new TrackCache();
45
-
46
31
  for (const plugin of plugins) this.use(plugin);
47
32
  this.connector.listen();
48
33
  for (const node of nodes) this.addNode(node);
49
34
  }
50
35
 
51
36
  // ─── Plugin System ────────────────────────────────────────────────
52
-
53
37
  public use(plugin: Plugin): this {
54
38
  if (this.plugins.has(plugin.name)) throw new YukimuError(`Plugin "${plugin.name}" already loaded`);
55
39
  plugin.load(this);
56
40
  this.plugins.set(plugin.name, plugin);
57
- console.log(`[Yukimu] 🧩 Plugin "${plugin.name}" loaded`);
41
+ console.log(`[Yukimu] Plugin "${plugin.name}" loaded`);
58
42
  return this;
59
43
  }
60
44
 
@@ -71,7 +55,6 @@ export class Yukimu extends EventEmitter {
71
55
  }
72
56
 
73
57
  // ─── Node Management ──────────────────────────────────────────────
74
-
75
58
  public addNode(options: NodeOptions): Node {
76
59
  if (this.nodes.has(options.name)) throw new YukimuError(`Node "${options.name}" already exists`);
77
60
  const node = new Node(this, options);
@@ -107,7 +90,6 @@ export class Yukimu extends EventEmitter {
107
90
  }
108
91
 
109
92
  // ─── Player Management ────────────────────────────────────────────
110
-
111
93
  public createPlayer(options: PlayerOptions): Player {
112
94
  const existing = this.players.get(options.guildId);
113
95
  if (existing) return existing;
@@ -133,8 +115,7 @@ export class Yukimu extends EventEmitter {
133
115
  }
134
116
 
135
117
  // ─── Search ───────────────────────────────────────────────────────
136
-
137
- public async search(query: string, options?: { source?: SearchSource; requester?: unknown }): Promise<SearchResult> {
118
+ public async search(query: string, options?: { source?: SearchSource; requester?: any }): Promise<SearchResult> {
138
119
  return this.resolver.resolve(
139
120
  query,
140
121
  options?.source ?? this.options.defaultSource ?? "youtube",
@@ -143,13 +124,7 @@ export class Yukimu extends EventEmitter {
143
124
  }
144
125
 
145
126
  // ─── Voice Gateway ────────────────────────────────────────────────
146
-
147
- public handleVoiceStateUpdate(data: {
148
- guild_id?: string;
149
- user_id: string;
150
- session_id: string;
151
- channel_id?: string | null;
152
- }): void {
127
+ public handleVoiceStateUpdate(data: any): void {
153
128
  if (data.user_id !== this.options.clientId) return;
154
129
  if (!data.guild_id) return;
155
130
 
@@ -184,11 +159,7 @@ export class Yukimu extends EventEmitter {
184
159
  }
185
160
  }
186
161
 
187
- public handleVoiceServerUpdate(data: {
188
- guild_id: string;
189
- token: string;
190
- endpoint?: string | null;
191
- }): void {
162
+ public handleVoiceServerUpdate(data: any): void {
192
163
  if (!data.endpoint) return;
193
164
  this.pool.updateServer(data.guild_id, data.token, data.endpoint);
194
165
  const player = this.players.get(data.guild_id);
@@ -202,7 +173,6 @@ export class Yukimu extends EventEmitter {
202
173
  }
203
174
 
204
175
  // ─── Utility ─────────────────────────────────────────────────────
205
-
206
176
  public detectSource(url: string): SearchSource | null {
207
177
  for (const [source, patterns] of Object.entries(URL_PATTERNS)) {
208
178
  if (patterns.some(p => p.test(url))) return source as SearchSource;
@@ -223,25 +193,20 @@ export class Yukimu extends EventEmitter {
223
193
 
224
194
  public destroy(): void {
225
195
  for (const node of this.nodes.values()) node.destroy();
226
- for (const guildId of this.players.keys()) this.destroyPlayer(guildId).catch(() => {});
227
196
  this.trackCache.destroy();
228
197
  this.nodes.clear();
229
198
  }
230
199
 
231
200
  // ─── Typed EventEmitter ───────────────────────────────────────────
232
-
233
201
  public on<K extends keyof YukimuEvents>(event: K, listener: (...args: YukimuEvents[K]) => void): this {
234
- return super.on(event, listener as (...args: unknown[]) => void);
202
+ return super.on(event, listener as any);
235
203
  }
236
-
237
204
  public once<K extends keyof YukimuEvents>(event: K, listener: (...args: YukimuEvents[K]) => void): this {
238
- return super.once(event, listener as (...args: unknown[]) => void);
205
+ return super.once(event, listener as any);
239
206
  }
240
-
241
207
  public off<K extends keyof YukimuEvents>(event: K, listener: (...args: YukimuEvents[K]) => void): this {
242
- return super.off(event, listener as (...args: unknown[]) => void);
208
+ return super.off(event, listener as any);
243
209
  }
244
-
245
210
  public emit<K extends keyof YukimuEvents>(event: K, ...args: YukimuEvents[K]): boolean {
246
211
  return super.emit(event, ...args);
247
212
  }
@@ -1,27 +1,20 @@
1
1
  import { Connector } from "./Connector";
2
- import type { Yukimu } from "../Yukimu";
3
-
4
- interface DiscordJSClient {
5
- user: { id: string } | null;
6
- guilds: { cache: Map<string, { shard: { send(payload: unknown): void } }> };
7
- on(event: "raw", listener: (packet: { t: string; d: unknown }) => void): this;
8
- }
9
2
 
10
3
  export class DiscordJS extends Connector {
11
- private client: DiscordJSClient;
4
+ private client: any;
12
5
 
13
- constructor(client: DiscordJSClient) {
6
+ constructor(client: any) {
14
7
  super();
15
8
  this.client = client;
16
9
  }
17
10
 
18
11
  public listen(): void {
19
- this.client.on("raw", (packet: { t: string; d: unknown }) => {
12
+ this.client.on("raw", (packet: any) => {
20
13
  if (packet.t === "VOICE_STATE_UPDATE") {
21
- this.manager.handleVoiceStateUpdate(packet.d as Parameters<Yukimu["handleVoiceStateUpdate"]>[0]);
14
+ this.manager.handleVoiceStateUpdate(packet.d);
22
15
  }
23
16
  if (packet.t === "VOICE_SERVER_UPDATE") {
24
- this.manager.handleVoiceServerUpdate(packet.d as Parameters<Yukimu["handleVoiceServerUpdate"]>[0]);
17
+ this.manager.handleVoiceServerUpdate(packet.d);
25
18
  }
26
19
  });
27
20
  }
@@ -1,28 +1,17 @@
1
1
  import { Connector } from "./Connector";
2
- import type { Yukimu } from "../Yukimu";
3
-
4
- interface ErisClient {
5
- guilds: Map<string, { shard: { id: number } }>;
6
- shards: Map<number, { sendWS(op: number, data: unknown): void }>;
7
- on(event: "rawWS", listener: (packet: { t: string; d: unknown }) => void): this;
8
- }
9
2
 
10
3
  export class Eris extends Connector {
11
- private client: ErisClient;
4
+ private client: any;
12
5
 
13
- constructor(client: ErisClient) {
6
+ constructor(client: any) {
14
7
  super();
15
8
  this.client = client;
16
9
  }
17
10
 
18
11
  public listen(): void {
19
- this.client.on("rawWS", (packet: { t: string; d: unknown }) => {
20
- if (packet.t === "VOICE_STATE_UPDATE") {
21
- this.manager.handleVoiceStateUpdate(packet.d as Parameters<Yukimu["handleVoiceStateUpdate"]>[0]);
22
- }
23
- if (packet.t === "VOICE_SERVER_UPDATE") {
24
- this.manager.handleVoiceServerUpdate(packet.d as Parameters<Yukimu["handleVoiceServerUpdate"]>[0]);
25
- }
12
+ this.client.on("rawWS", (packet: any) => {
13
+ if (packet.t === "VOICE_STATE_UPDATE") this.manager.handleVoiceStateUpdate(packet.d);
14
+ if (packet.t === "VOICE_SERVER_UPDATE") this.manager.handleVoiceServerUpdate(packet.d);
26
15
  });
27
16
  }
28
17
 
@@ -30,6 +19,6 @@ export class Eris extends Connector {
30
19
  const guild = this.client.guilds.get(guildId);
31
20
  if (!guild) return;
32
21
  const shard = this.client.shards.get(guild.shard.id);
33
- if (shard) shard.sendWS(4, (payload as { d: unknown }).d);
22
+ if (shard) shard.sendWS(4, (payload as any).d);
34
23
  }
35
24
  }