import { EventEmitter } from 'node:events'; /** * The known data for a user in a Discord voice connection. */ export interface VoiceUserData { /** * The SSRC of the user's audio stream. */ audioSSRC: number; /** * The Discord user id of the user. */ userId: string; /** * The SSRC of the user's video stream (if one exists) * Cannot be 0. If undefined, the user has no video stream. */ videoSSRC?: number; } export interface SSRCMap extends EventEmitter { on(event: 'create', listener: (newData: VoiceUserData) => void): this; on(event: 'update', listener: (oldData: VoiceUserData | undefined, newData: VoiceUserData) => void): this; on(event: 'delete', listener: (deletedData: VoiceUserData) => void): this; } /** * Maps audio SSRCs to data of users in voice connections. */ export class SSRCMap extends EventEmitter { /** * The underlying map. */ private readonly map: Map; public constructor() { super(); this.map = new Map(); } /** * Updates the map with new user data * * @param data - The data to update with */ public update(data: VoiceUserData) { const existing = this.map.get(data.audioSSRC); const newValue = { ...this.map.get(data.audioSSRC), ...data, }; this.map.set(data.audioSSRC, newValue); if (!existing) this.emit('create', newValue); this.emit('update', existing, newValue); } /** * Gets the stored voice data of a user. * * @param target - The target, either their user id or audio SSRC */ public get(target: number | string) { if (typeof target === 'number') { return this.map.get(target); } for (const data of this.map.values()) { if (data.userId === target) { return data; } } return undefined; } /** * Deletes the stored voice data about a user. * * @param target - The target of the delete operation, either their audio SSRC or user id * @returns The data that was deleted, if any */ public delete(target: number | string) { if (typeof target === 'number') { const existing = this.map.get(target); if (existing) { this.map.delete(target); this.emit('delete', existing); } return existing; } for (const [audioSSRC, data] of this.map.entries()) { if (data.userId === target) { this.map.delete(audioSSRC); this.emit('delete', data); return data; } } return undefined; } }