kazagumo-bun 3.4.0-b

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 (73) hide show
  1. package/.gitattributes +2 -0
  2. package/.prettierrc +5 -0
  3. package/LICENSE +21 -0
  4. package/LICENSE.md +22 -0
  5. package/README.md +267 -0
  6. package/SECURITY.md +21 -0
  7. package/appveyor.yml +24 -0
  8. package/docs/.nojekyll +1 -0
  9. package/docs/assets/hierarchy.js +1 -0
  10. package/docs/assets/highlight.css +99 -0
  11. package/docs/assets/icons.js +18 -0
  12. package/docs/assets/icons.svg +1 -0
  13. package/docs/assets/main.js +60 -0
  14. package/docs/assets/navigation.js +1 -0
  15. package/docs/assets/search.js +1 -0
  16. package/docs/assets/style.css +1633 -0
  17. package/docs/classes/Kazagumo.Kazagumo.html +307 -0
  18. package/docs/classes/Managers_KazagumoPlayer.KazagumoPlayer.html +81 -0
  19. package/docs/classes/Managers_Supports_KazagumoQueue.KazagumoQueue.html +222 -0
  20. package/docs/classes/Managers_Supports_KazagumoTrack.KazagumoTrack.html +43 -0
  21. package/docs/classes/Modules_Interfaces.KazagumoError.html +33 -0
  22. package/docs/classes/Modules_Interfaces.KazagumoPlugin.html +4 -0
  23. package/docs/classes/Modules_Utils.KazagumoUtils.html +3 -0
  24. package/docs/classes/Plugins_PlayerMoved.KazagumoPlugin.html +13 -0
  25. package/docs/enums/Modules_Interfaces.PlayerState.html +7 -0
  26. package/docs/functions/Modules_Interfaces.escapeRegExp.html +1 -0
  27. package/docs/hierarchy.html +1 -0
  28. package/docs/index.html +208 -0
  29. package/docs/interfaces/Kazagumo.KazagumoEvents.html +40 -0
  30. package/docs/interfaces/Modules_Interfaces.CreatePlayerOptions.html +21 -0
  31. package/docs/interfaces/Modules_Interfaces.KazagumoOptions.html +17 -0
  32. package/docs/interfaces/Modules_Interfaces.KazagumoPlayerOptions.html +9 -0
  33. package/docs/interfaces/Modules_Interfaces.KazagumoSearchOptions.html +5 -0
  34. package/docs/interfaces/Modules_Interfaces.KazagumoSearchResult.html +4 -0
  35. package/docs/interfaces/Modules_Interfaces.Payload.html +4 -0
  36. package/docs/interfaces/Modules_Interfaces.PlayOptions.html +6 -0
  37. package/docs/interfaces/Modules_Interfaces.PlayerMovedChannels.html +3 -0
  38. package/docs/interfaces/Modules_Interfaces.RawTrack.html +4 -0
  39. package/docs/interfaces/Modules_Interfaces.ResolveOptions.html +4 -0
  40. package/docs/modules/Index.html +1 -0
  41. package/docs/modules/Kazagumo.html +1 -0
  42. package/docs/modules/Managers_KazagumoPlayer.html +1 -0
  43. package/docs/modules/Managers_Supports_KazagumoQueue.html +1 -0
  44. package/docs/modules/Managers_Supports_KazagumoTrack.html +1 -0
  45. package/docs/modules/Modules_Interfaces.html +1 -0
  46. package/docs/modules/Modules_Plugins.html +1 -0
  47. package/docs/modules/Modules_Utils.html +1 -0
  48. package/docs/modules/Plugins_PlayerMoved.html +1 -0
  49. package/docs/modules.html +1 -0
  50. package/docs/types/Modules_Interfaces.PlayerMovedState.html +1 -0
  51. package/docs/types/Modules_Interfaces.SearchEngines.html +1 -0
  52. package/docs/types/Modules_Interfaces.SearchResultTypes.html +1 -0
  53. package/docs/types/Modules_Interfaces.YoutubeThumbnail.html +1 -0
  54. package/docs/types/Modules_Utils.Constructor.html +1 -0
  55. package/docs/variables/Index.version.html +1 -0
  56. package/docs/variables/Modules_Interfaces.Events.html +1 -0
  57. package/docs/variables/Modules_Interfaces.SourceIDs.html +1 -0
  58. package/docs/variables/Modules_Interfaces.SupportedSources.html +1 -0
  59. package/docs/variables/Modules_Plugins.default.html +1 -0
  60. package/eslint.config.mjs +15 -0
  61. package/package.json +26 -0
  62. package/src/Index.ts +14 -0
  63. package/src/Kazagumo.ts +396 -0
  64. package/src/Managers/KazagumoPlayer.ts +470 -0
  65. package/src/Managers/Supports/KazagumoQueue.ts +89 -0
  66. package/src/Managers/Supports/KazagumoTrack.ts +211 -0
  67. package/src/Modules/EventEmitter.ts +53 -0
  68. package/src/Modules/Interfaces.ts +204 -0
  69. package/src/Modules/Plugins.ts +5 -0
  70. package/src/Modules/Utils.ts +26 -0
  71. package/src/Plugins/PlayerMoved.ts +54 -0
  72. package/tsconfig.json +23 -0
  73. package/tslint.json +3 -0
@@ -0,0 +1,211 @@
1
+ import { Kazagumo } from '../../Kazagumo';
2
+ import {
3
+ escapeRegExp,
4
+ Events,
5
+ KazagumoError,
6
+ RawTrack,
7
+ ResolveOptions,
8
+ SourceIDs,
9
+ SupportedSources,
10
+ } from '../../Modules/Interfaces';
11
+ import { Track } from 'shoukaku';
12
+ import { KazagumoPlayer } from '../KazagumoPlayer';
13
+
14
+ export class KazagumoTrack {
15
+ /**
16
+ * Kazagumo Instance
17
+ */
18
+ public kazagumo: Kazagumo | undefined;
19
+ /**
20
+ * Track Requester
21
+ */
22
+ public requester: unknown | undefined;
23
+
24
+ /** Track's Base64 */
25
+ public track: string;
26
+ /** Track's source */
27
+ public sourceName: string;
28
+ /** Track's title */
29
+ public title: string;
30
+ /** Track's URI */
31
+ public uri?: string;
32
+ /** Track's identifier */
33
+ public identifier: string;
34
+ /** Whether the track is seekable */
35
+ public isSeekable: boolean;
36
+ /** Whether the track is a stream */
37
+ public isStream: boolean;
38
+ /** Track's author */
39
+ public author: string | undefined;
40
+ /** Track's length */
41
+ public length: number | undefined;
42
+ /** Track's position (I don't know this) */
43
+ public position: number | undefined;
44
+ /** Track's thumbnail, if available */
45
+ public thumbnail: string | undefined;
46
+ /** The YouTube/soundcloud URI for spotify and other unsupported source */
47
+ public realUri?: string;
48
+
49
+ public resolvedBySource: boolean = false;
50
+
51
+ constructor(
52
+ private readonly raw: Track,
53
+ requester: unknown,
54
+ ) {
55
+ this.kazagumo = undefined;
56
+
57
+ this.track = raw.encoded;
58
+ this.sourceName = raw.info.sourceName;
59
+ this.title = raw.info.title;
60
+ this.uri = raw.info.uri;
61
+ this.identifier = raw.info.identifier;
62
+ this.isSeekable = raw.info.isSeekable;
63
+ this.isStream = raw.info.isStream;
64
+ this.author = raw.info.author;
65
+ this.length = raw.info.length;
66
+ this.thumbnail = raw.info.artworkUrl;
67
+ this.realUri = SupportedSources.includes(this.sourceName) ? this.uri : undefined;
68
+
69
+ this.requester = requester;
70
+
71
+ if (this.sourceName === 'youtube' && this.identifier)
72
+ this.thumbnail = `https://img.youtube.com/vi/${this.identifier}/hqdefault.jpg`;
73
+ }
74
+
75
+ /**
76
+ * Get json of this track
77
+ * @returns {RawTrack}
78
+ */
79
+ public getRaw(): RawTrack {
80
+ return {
81
+ track: this.track,
82
+ info: {
83
+ title: this.title,
84
+ uri: this.uri,
85
+ identifier: this.identifier,
86
+ author: this.author,
87
+ sourceName: this.sourceName,
88
+ isSeekable: this.isSeekable,
89
+ isStream: this.isStream,
90
+ length: this.length,
91
+ position: this.position,
92
+ artworkUrl: this.thumbnail,
93
+ },
94
+ _raw: this.raw,
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Set kazagumo instance
100
+ * @param kazagumo Kazagumo instance
101
+ * @returns KazagumoTrack
102
+ */
103
+ setKazagumo(kazagumo: Kazagumo): KazagumoTrack {
104
+ this.kazagumo = kazagumo;
105
+ if (this.sourceName === 'youtube' && this.identifier)
106
+ this.thumbnail = `https://img.youtube.com/vi/${this.identifier}/${
107
+ kazagumo.KazagumoOptions.defaultYoutubeThumbnail ?? 'hqdefault'
108
+ }.jpg`;
109
+
110
+ return this;
111
+ }
112
+
113
+ /**
114
+ * Whether the track is ready to play or need to be solved
115
+ */
116
+ get readyToPlay(): boolean {
117
+ return (
118
+ this.kazagumo !== undefined &&
119
+ !!this.track &&
120
+ !!this.sourceName &&
121
+ !!this.identifier &&
122
+ !!this.author &&
123
+ !!this.length &&
124
+ !!this.title &&
125
+ !!this.uri &&
126
+ !!this.realUri
127
+ );
128
+ }
129
+
130
+ /**
131
+ * Resolve the track
132
+ * @param options Resolve options
133
+ * @returns Promise<KazagumoTrack>
134
+ */
135
+ public async resolve(options?: ResolveOptions): Promise<KazagumoTrack> {
136
+ if (!this.kazagumo) throw new KazagumoError(1, 'Kazagumo is not set');
137
+ if (
138
+ this.kazagumo.KazagumoOptions.trackResolver &&
139
+ typeof this.kazagumo.KazagumoOptions.trackResolver === 'function' &&
140
+ (await this.kazagumo.KazagumoOptions.trackResolver.bind(this)(options))
141
+ )
142
+ return this;
143
+ const resolveSource = this.kazagumo.KazagumoOptions?.sourceForceResolve?.includes(this.sourceName);
144
+ const { forceResolve, overwrite } = options ? options : { forceResolve: false, overwrite: false };
145
+
146
+ if (!forceResolve && this.readyToPlay) return this;
147
+ if (resolveSource && this.resolvedBySource) return this;
148
+ if (resolveSource) {
149
+ this.resolvedBySource = true;
150
+ return this;
151
+ }
152
+
153
+ this.kazagumo.emit(Events.Debug, `Resolving ${this.sourceName} track ${this.title}; Source: ${this.sourceName}`);
154
+
155
+ const result = await this.getTrack(options?.player ?? null);
156
+ if (!result) throw new KazagumoError(2, 'No results found');
157
+
158
+ this.track = result.encoded;
159
+ this.realUri = result.info.uri;
160
+ this.length = result.info.length;
161
+
162
+ if (overwrite || resolveSource) {
163
+ this.title = result.info.title;
164
+ this.identifier = result.info.identifier;
165
+ this.isSeekable = result.info.isSeekable;
166
+ this.author = result.info.author;
167
+ this.length = result.info.length;
168
+ this.isStream = result.info.isStream;
169
+ this.uri = result.info.uri;
170
+ }
171
+ return this;
172
+ }
173
+
174
+ private async getTrack(player: KazagumoPlayer | null): Promise<Track> {
175
+ if (!this.kazagumo) throw new Error('Kazagumo is not set');
176
+
177
+ const defaultSearchEngine = this.kazagumo.KazagumoOptions.defaultSearchEngine;
178
+ const source = ((SourceIDs as any)[defaultSearchEngine || 'youtube'] || 'yt') + 'search:';
179
+ const query = [this.author, this.title].filter((x) => !!x).join(' - ');
180
+ const node = await this.kazagumo.getLeastUsedNode();
181
+
182
+ if (!node) throw new KazagumoError(1, 'No nodes available');
183
+
184
+ const result = player
185
+ ? await player.search(query, { source, requester: this.requester })
186
+ : await this.kazagumo.search(query, { engine: defaultSearchEngine, requester: this.requester });
187
+ if (!result || !result.tracks.length) throw new KazagumoError(2, 'No results found');
188
+
189
+ const rawTracks = result.tracks.map((x) => x.getRaw()._raw);
190
+
191
+ if (this.author) {
192
+ const author = [this.author, `${this.author} - Topic`];
193
+ const officialTrack = rawTracks.find(
194
+ (track) =>
195
+ author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, 'i').test(track.info.author)) ||
196
+ new RegExp(`^${escapeRegExp(this.title)}$`, 'i').test(track.info.title),
197
+ );
198
+ if (officialTrack) return officialTrack;
199
+ }
200
+ if (this.length) {
201
+ const sameDuration = rawTracks.find(
202
+ (track) =>
203
+ track.info.length >= (this.length ? this.length : 0) - 2000 &&
204
+ track.info.length <= (this.length ? this.length : 0) + 2000,
205
+ );
206
+ if (sameDuration) return sameDuration;
207
+ }
208
+
209
+ return rawTracks[0];
210
+ }
211
+ }
@@ -0,0 +1,53 @@
1
+ type Listener = (...args: any[]) => void;
2
+
3
+ export class EventEmitter {
4
+ private events: Map<string | symbol, Listener[]>;
5
+
6
+ constructor() {
7
+ this.events = new Map();
8
+ }
9
+
10
+ public on(event: string | symbol, listener: Listener): this {
11
+ if (!this.events.has(event)) {
12
+ this.events.set(event, []);
13
+ }
14
+ this.events.get(event)!.push(listener);
15
+ return this;
16
+ }
17
+
18
+ public once(event: string | symbol, listener: Listener): this {
19
+ const onceWrapper = (...args: any[]) => {
20
+ this.off(event, onceWrapper);
21
+ listener.apply(this, args);
22
+ };
23
+ return this.on(event, onceWrapper);
24
+ }
25
+
26
+ public off(event: string | symbol, listener: Listener): this {
27
+ const listeners = this.events.get(event);
28
+ if (!listeners) return this;
29
+ const index = listeners.indexOf(listener);
30
+ if (index !== -1) {
31
+ listeners.splice(index, 1);
32
+ }
33
+ return this;
34
+ }
35
+
36
+ public emit(event: string | symbol, ...args: any[]): boolean {
37
+ const listeners = this.events.get(event);
38
+ if (!listeners || listeners.length === 0) return false;
39
+
40
+ // Copy to avoid issues if listeners are removed during execution
41
+ [...listeners].forEach(listener => listener.apply(this, args));
42
+ return true;
43
+ }
44
+
45
+ public removeAllListeners(event?: string | symbol): this {
46
+ if (event) {
47
+ this.events.delete(event);
48
+ } else {
49
+ this.events.clear();
50
+ }
51
+ return this;
52
+ }
53
+ }
@@ -0,0 +1,204 @@
1
+ import { Kazagumo } from '../Kazagumo';
2
+ import { KazagumoPlayer, KazagumoQueue } from '../Index';
3
+ import { KazagumoTrack } from '../Managers/Supports/KazagumoTrack';
4
+ import { Constructor } from './Utils';
5
+ import { Track } from 'shoukaku';
6
+
7
+ export interface KazagumoOptions {
8
+ /** Default search engine if no engine was provided. Default to youtube. If defaultSource is provided, this will be ignored */
9
+ defaultSearchEngine: SearchEngines;
10
+ /** Default source if no source was provided. Default to defaultSearchEngine */
11
+ defaultSource?: string;
12
+ /** Kazagumo plugins */
13
+ plugins?: KazagumoPlugin[];
14
+ /** Source that will be forced to resolve when playing it */
15
+ sourceForceResolve?: string[];
16
+ /** The track resolver. Make sure you set <KazagumoTrack>.track for it to work. (I'm not responsible for any error during playback if you don't set it right) */
17
+ trackResolver?: (this: KazagumoTrack, options?: ResolveOptions) => Promise<boolean>;
18
+ /** The default youtube thumbnail's size */
19
+ defaultYoutubeThumbnail?: YoutubeThumbnail;
20
+ /** Extend some of the Structures */
21
+ extends?: {
22
+ player?: Constructor<KazagumoPlayer>;
23
+ };
24
+ /** Send to guild's shard */
25
+ send: (guildId: string, payload: Payload) => void;
26
+ }
27
+
28
+ export type SearchEngines = 'youtube' | 'soundcloud' | 'youtube_music' | string;
29
+ export type YoutubeThumbnail = 'default' | 'hqdefault' | 'mqdefault' | 'sddefault' | 'maxresdefault';
30
+
31
+ export interface Payload {
32
+ /** The OP code */
33
+ op: number;
34
+ d: {
35
+ guild_id: string;
36
+ channel_id: string | null;
37
+ self_mute: boolean;
38
+ self_deaf: boolean;
39
+ };
40
+ }
41
+
42
+ export const escapeRegExp = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
43
+
44
+ export const SourceIDs = {
45
+ youtube: 'yt',
46
+ youtube_music: 'ytm',
47
+ soundcloud: 'sc',
48
+ };
49
+
50
+ export interface KazagumoPlayerOptions {
51
+ guildId: string;
52
+ voiceId: string;
53
+ textId?: string;
54
+ deaf: boolean;
55
+ volume: number;
56
+ /** Whether the node for searching track should be the same as the node for playing track. Default: true */
57
+ searchWithSameNode?: boolean;
58
+ extends?: {
59
+ queue?: Constructor<KazagumoQueue>;
60
+ };
61
+ }
62
+
63
+ export interface ResolveOptions {
64
+ overwrite?: boolean;
65
+ forceResolve?: boolean;
66
+ player?: KazagumoPlayer;
67
+ }
68
+
69
+ export interface CreatePlayerOptions {
70
+ /** The player's guild ID */
71
+ guildId: string;
72
+ /** The player's voice ID */
73
+ voiceId: string;
74
+ /** The player's text ID */
75
+ textId?: string;
76
+ /** Whether the bot should deafen */
77
+ deaf?: boolean;
78
+ /** Whether the bot should mute */
79
+ mute?: boolean;
80
+ /** The player's guild's shardId */
81
+ shardId?: number;
82
+ /** Balance the node? */
83
+ loadBalancer?: boolean;
84
+ /** The player's volume */
85
+ volume?: number;
86
+ /** Use specific node */
87
+ nodeName?: string;
88
+ /** The player's data, usable when you extends it */
89
+ data?: unknown;
90
+ }
91
+
92
+ export interface RawTrack {
93
+ track: string;
94
+ info: {
95
+ title: string;
96
+ uri?: string;
97
+ identifier: string;
98
+ sourceName: string;
99
+ isSeekable: boolean;
100
+ isStream: boolean;
101
+ author?: string;
102
+ length?: number;
103
+ position?: number;
104
+ artworkUrl?: string;
105
+ };
106
+ _raw: Track;
107
+ }
108
+
109
+ export const Events = {
110
+ // Player events
111
+ PlayerDestroy: 'playerDestroy',
112
+ PlayerCreate: 'playerCreate',
113
+ PlayerStart: 'playerStart',
114
+ PlayerEnd: 'playerEnd',
115
+ PlayerEmpty: 'playerEmpty',
116
+ PlayerClosed: 'playerClosed',
117
+ PlayerUpdate: 'playerUpdate',
118
+ PlayerException: 'playerException',
119
+ PlayerError: 'playerError',
120
+ PlayerResumed: 'playerResumed',
121
+ PlayerStuck: 'playerStuck',
122
+ PlayerResolveError: 'playerResolveError',
123
+ PlayerMoved: 'playerMoved',
124
+ QueueUpdate: 'queueUpdate',
125
+
126
+ // Kazagumo events
127
+ Debug: 'debug',
128
+ };
129
+
130
+ export interface PlayerMovedChannels {
131
+ oldChannelId?: string | null;
132
+ newChannelId?: string | null;
133
+ }
134
+
135
+ export type PlayerMovedState = 'UNKNOWN' | 'JOINED' | 'LEFT' | 'MOVED';
136
+
137
+ export interface KazagumoSearchOptions {
138
+ requester: unknown;
139
+ source?: string;
140
+ engine?: SearchEngines;
141
+ nodeName?: string;
142
+ }
143
+
144
+ export interface KazagumoSearchResult {
145
+ type: SearchResultTypes;
146
+ playlistName?: string;
147
+ tracks: KazagumoTrack[];
148
+ }
149
+
150
+ export type SearchResultTypes = 'PLAYLIST' | 'TRACK' | 'SEARCH';
151
+
152
+ export const SupportedSources = [
153
+ 'bandcamp',
154
+ 'beam',
155
+ 'getyarn',
156
+ 'http',
157
+ 'local',
158
+ 'nico',
159
+ 'soundcloud',
160
+ 'stream',
161
+ 'twitch',
162
+ 'vimeo',
163
+ 'youtube',
164
+ 'spotify',
165
+ ];
166
+
167
+ export interface PlayOptions {
168
+ position?: number;
169
+ endTime?: number;
170
+ volume?: number;
171
+ paused?: boolean;
172
+ replaceCurrent?: boolean;
173
+ }
174
+
175
+ export enum PlayerState {
176
+ CONNECTING,
177
+ CONNECTED,
178
+ DISCONNECTING,
179
+ DISCONNECTED,
180
+ DESTROYING,
181
+ DESTROYED,
182
+ }
183
+
184
+ export class KazagumoPlugin {
185
+ public load(kazagumo: Kazagumo): void {
186
+ throw new KazagumoError(1, 'Plugin must implement load()');
187
+ }
188
+
189
+ public unload(kazagumo: Kazagumo): void {
190
+ throw new KazagumoError(1, 'Plugin must implement unload()');
191
+ }
192
+ }
193
+
194
+
195
+ export class KazagumoError extends Error {
196
+ public code: number;
197
+ public message: string;
198
+
199
+ public constructor(code: number, message: string) {
200
+ super(message);
201
+ this.code = code;
202
+ this.message = message;
203
+ }
204
+ }
@@ -0,0 +1,5 @@
1
+ import { KazagumoPlugin as PlayerMoved } from '../Plugins/PlayerMoved';
2
+
3
+ export default {
4
+ PlayerMoved,
5
+ };
@@ -0,0 +1,26 @@
1
+ import { Track } from 'shoukaku';
2
+ import { KazagumoTrack } from '../Managers/Supports/KazagumoTrack';
3
+
4
+ export class KazagumoUtils {
5
+ static convertKazagumoTrackToTrack(track: KazagumoTrack | Track): Track {
6
+ if ((track as Track).info) return track as Track;
7
+ track = track as KazagumoTrack;
8
+ return {
9
+ encoded: track.track,
10
+ info: {
11
+ isSeekable: track.isSeekable,
12
+ isStream: track.isStream,
13
+ title: track.title,
14
+ uri: track.uri,
15
+ identifier: track.identifier,
16
+ sourceName: track.sourceName,
17
+ author: track.author ?? '',
18
+ length: track.length ?? 0,
19
+ position: track.position ?? 0,
20
+ },
21
+ pluginInfo: {},
22
+ };
23
+ }
24
+ }
25
+ // Credit: Deivu (developer of Shoukaku) https://github.com/shipgirlproject/Shoukaku/blob/e7d94081cabbda7327dc04e467a45fcda49c24f2/src/Utils.ts#L1C1-L2C1
26
+ export type Constructor<T> = new (...args: any[]) => T;
@@ -0,0 +1,54 @@
1
+ import { Kazagumo, Events } from '../Index';
2
+ import { KazagumoPlugin as Plugin } from '../Modules/Interfaces';
3
+
4
+ export class KazagumoPlugin extends Plugin {
5
+ /**
6
+ * Kazagumo instance.
7
+ */
8
+ public kazagumo: Kazagumo | null = null;
9
+
10
+ /**
11
+ * Initialize the plugin.
12
+ * @param client Discord.Client
13
+ */
14
+ constructor(public client: any) {
15
+ super();
16
+ }
17
+
18
+ /**
19
+ * Load the plugin.
20
+ * @param kazagumo Kazagumo
21
+ */
22
+ public load(kazagumo: Kazagumo): void {
23
+ this.kazagumo = kazagumo;
24
+ this.client.on('voiceStateUpdate', this.onVoiceStateUpdate.bind(this));
25
+ }
26
+
27
+ /**
28
+ * Unload the plugin.
29
+ */
30
+ public unload(): void {
31
+ this.client.removeListener('voiceStateUpdate', this.onVoiceStateUpdate.bind(this));
32
+ this.kazagumo = null;
33
+ }
34
+
35
+ private onVoiceStateUpdate(oldState: any, newState: any): void {
36
+ if (!this.kazagumo || oldState.id !== this.client.user.id) return;
37
+
38
+ const newChannelId = newState.channelID || newState.channelId;
39
+ const oldChannelId = oldState.channelID || oldState.channelId;
40
+ const guildId = newState.guild.id;
41
+
42
+ const player = this.kazagumo.players.get(guildId);
43
+ if (!player) return;
44
+
45
+ let state = 'UNKNOWN';
46
+ if (!oldChannelId && newChannelId) state = 'JOINED';
47
+ else if (oldChannelId && !newChannelId) state = 'LEFT';
48
+ else if (oldChannelId && newChannelId && oldChannelId !== newChannelId) state = 'MOVED';
49
+
50
+ if (state === 'UNKNOWN') return;
51
+
52
+ this.kazagumo.emit(Events.PlayerMoved, player, state, { oldChannelId, newChannelId });
53
+ }
54
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
+ "declaration": true,
7
+ "esModuleInterop": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "allowSyntheticDefaultImports": true,
12
+ "types": ["bun-types"]
13
+ },
14
+ "include": [
15
+ "src"
16
+ ],
17
+ "exclude": [
18
+ "node_modules",
19
+ "**/__tests__/*",
20
+ "test",
21
+ "dist"
22
+ ]
23
+ }
package/tslint.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": ["tslint:recommended", "tslint-config-prettier"]
3
+ }