Skip to content

Instantly share code, notes, and snippets.

@Sayrix
Created January 24, 2026 13:41
Show Gist options
  • Select an option

  • Save Sayrix/5c8ac9d05051f82c5e8af222f7d1aa39 to your computer and use it in GitHub Desktop.

Select an option

Save Sayrix/5c8ac9d05051f82c5e8af222f7d1aa39 to your computer and use it in GitHub Desktop.
// ==================== CONFIGURATION ====================
const CONFIG = {
DISCORD_TOKEN: "YOUR_DISCORD_TOKEN_HERE",
GUILD_ID: "YOUR_GUILD_ID_HERE", // Discord server ID
CHANNEL_ID: "YOUR_CHANNEL_ID_HERE", // Voice channel ID to join
STREAM_URL:
"https://archive.org/download/kevin-mc-leod-monkeys-spinning-monkeys/Kevin%20McLeod%20-%20Monkeys%20Spinning%20Monkeys.ogg" // Ogg Opus stream
};
// ==================== IMPORTS ====================
const { Client, GatewayIntentBits } = require("@discordjs/core");
const { REST } = require("@discordjs/rest");
const { WebSocketManager } = require("@discordjs/ws");
const {
joinVoiceChannel,
createAudioPlayer,
createAudioResource,
NoSubscriberBehavior,
VoiceConnectionStatus,
AudioPlayerStatus,
StreamType,
entersState
} = require("@discordjs/voice");
const { LRUCache } = require("lru-cache");
const { GatewayDispatchEvents } = require("discord-api-types/v10");
// ==================== VOICE ADAPTER CREATOR ====================
/**
* Creates a voice adapter creator that bridges @discordjs/core with @discordjs/voice
* Simplified version assuming single shard (shard 0)
*/
function createVoiceAdapterCreator(gateway, client) {
const adapters = new Map(); // guildId -> adapter
const sharedState = {
adapters,
trackedBotId: null
};
// Set up event handlers once
client.on(GatewayDispatchEvents.VoiceStateUpdate, (data) => {
const voiceState = data.data;
const userId = voiceState.user_id;
const guildId = voiceState.guild_id;
// Track bot's user ID from the first voice state update
if (sharedState.trackedBotId === null && voiceState.member?.user?.id) {
sharedState.trackedBotId = voiceState.member.user.id;
}
// Only process updates for the bot itself
if (userId !== sharedState.trackedBotId) return;
const adapter = adapters.get(guildId);
if (!adapter) return;
// If bot was disconnected, destroy the adapter
if (voiceState.channel_id === null) {
adapter.destroy();
return;
}
// Call the adapter's onVoiceStateUpdate if it's been set by the voice library
if (typeof adapter.onVoiceStateUpdate === "function") {
adapter.onVoiceStateUpdate(voiceState);
}
});
client.on(GatewayDispatchEvents.VoiceServerUpdate, (data) => {
const voiceServer = data.data;
const guildId = voiceServer.guild_id;
const adapter = adapters.get(guildId);
if (!adapter) return;
// Call the adapter's onVoiceServerUpdate if it's been set by the voice library
if (typeof adapter.onVoiceServerUpdate === "function") {
adapter.onVoiceServerUpdate(voiceServer);
}
});
return (methods) => {
// Extract guild ID from the first method call or use a temporary approach
// The voice library will call these methods, and we'll figure out the guild from the data
const guildId = CONFIG.GUILD_ID; // For single-guild benchmark, use the configured guild
const adapter = {
destroyed: false,
sendPayload(data) {
if (this.destroyed) return false;
const shardId = 0; // Always use shard 0 for single shard
try {
if (typeof gateway.send === "function") {
gateway.send(shardId, data);
return true;
}
} catch (err) {
console.error("[Adapter] Error sending payload:", err);
}
return false;
},
destroy() {
if (this.destroyed) return;
this.destroyed = true;
adapters.delete(guildId);
console.log(`[Adapter] Destroyed for guild ${guildId}`);
}
};
// Store the methods from the voice library
adapter.onVoiceStateUpdate = methods.onVoiceStateUpdate;
adapter.onVoiceServerUpdate = methods.onVoiceServerUpdate;
adapters.set(guildId, adapter);
return adapter;
};
}
// ==================== LRU CACHE WITH CHANNEL INDEX ====================
/**
* Voice state cache with O(1) member counting per channel
* Maintains a secondary index: guildId -> channelId -> Set<userId>
*/
const channelIndex = new Map(); // guildId -> Map<channelId, Set<userId>>
function removeFromIndex(guildId, channelId, userId) {
if (!channelId) return;
const guildChannels = channelIndex.get(guildId);
if (!guildChannels) return;
const channelMembers = guildChannels.get(channelId);
if (!channelMembers) return;
channelMembers.delete(userId);
if (channelMembers.size === 0) {
guildChannels.delete(channelId);
if (guildChannels.size === 0) {
channelIndex.delete(guildId);
}
}
}
function addToIndex(guildId, channelId, userId, isBot) {
if (!channelId || isBot) return;
if (!channelIndex.has(guildId)) {
channelIndex.set(guildId, new Map());
}
const guildChannels = channelIndex.get(guildId);
if (!guildChannels.has(channelId)) {
guildChannels.set(channelId, new Set());
}
guildChannels.get(channelId).add(userId);
}
const voiceStateCache = new LRUCache({
maxSize: 999999999,
sizeCalculation: () => 1,
dispose: (value, key) => {
const parts = key.split(":");
const guildId = parts[0];
const userId = parts.slice(1).join(":");
removeFromIndex(guildId, value.data.channel_id, userId);
}
});
const voiceStateUtils = {
setSnapshot(guildId, userId, data) {
const key = `${guildId}:${userId}`;
const old = voiceStateCache.peek(key);
// Remove from old channel index
if (old?.data.channel_id) {
removeFromIndex(guildId, old.data.channel_id, userId);
}
// Add to new channel index
if (data.channel_id) {
const isBot = data.member?.user?.bot || false;
addToIndex(guildId, data.channel_id, userId, isBot);
}
voiceStateCache.set(key, {
data,
ts: Date.now()
});
},
getSnapshot(guildId, userId) {
const key = `${guildId}:${userId}`;
return voiceStateCache.get(key);
},
peekSnapshot(guildId, userId) {
const key = `${guildId}:${userId}`;
return voiceStateCache.peek(key);
},
deleteSnapshot(guildId, userId) {
const key = `${guildId}:${userId}`;
voiceStateCache.delete(key);
},
countMembersInChannel(guildId, channelId) {
const guildChannels = channelIndex.get(guildId);
if (!guildChannels) return 0;
const channelMembers = guildChannels.get(channelId);
if (!channelMembers) return 0;
return channelMembers.size;
},
getMembersInChannel(guildId, channelId) {
const guildChannels = channelIndex.get(guildId);
if (!guildChannels) return [];
const channelMembers = guildChannels.get(channelId);
if (!channelMembers) return [];
return Array.from(channelMembers)
.map((userId) => {
const snapshot = this.peekSnapshot(guildId, userId);
return snapshot?.data;
})
.filter(Boolean);
},
clear() {
voiceStateCache.clear();
channelIndex.clear();
}
};
// ==================== CONNECTION MANAGER & AUDIO PLAYER ====================
const activeConnections = new Map(); // guildId -> VoiceConnection
// Create shared audio player
const player = createAudioPlayer({
behaviors: {
maxMissedFrames: 9999,
noSubscriber: NoSubscriberBehavior.Stop
}
});
player.on("stateChange", (oldState, newState) => {
console.log(`[Player] ${oldState.status} -> ${newState.status}`);
});
player.on(AudioPlayerStatus.Idle, () => {
if (player.subscribers && player.subscribers.length > 0) {
console.log("[Player] Idle - attempting to restart stream");
playStream();
} else {
console.log("[Player] Idle - no subscribers, not restarting");
}
});
player.on("error", (error) => {
console.error("[Player] Error:", error);
});
function playStream() {
try {
const resource = createAudioResource(CONFIG.STREAM_URL, {
inputType: StreamType.OggOpus
});
player.play(resource);
console.log("[Player] Started playing stream");
} catch (error) {
console.error("[Player] Failed to create audio resource:", error);
}
}
function getConnection(guildId) {
return activeConnections.get(guildId);
}
function destroyConnection(guildId) {
const connection = activeConnections.get(guildId);
if (!connection) return false;
if (connection.state.status !== VoiceConnectionStatus.Destroyed) {
try {
connection.state.subscription?.unsubscribe();
} catch (err) {
console.error("[Connection] Error unsubscribing:", err);
}
try {
connection.destroy();
console.log(`[Connection] Destroyed for guild ${guildId}`);
} catch (err) {
console.error("[Connection] Error destroying:", err);
}
}
activeConnections.delete(guildId);
return true;
}
function subscribeToPlayer(connection) {
if (!connection) return;
// Unsubscribe first if already subscribed
if (connection.state.subscription) {
connection.state.subscription.unsubscribe();
}
connection.subscribe(player);
console.log("[Connection] Subscribed to player");
// Start playing if not already playing
if (player.state.status === AudioPlayerStatus.Idle) {
playStream();
}
}
async function connectToVoiceChannel(adapterCreator, guildId, channelId, shouldSubscribe = false) {
return new Promise((resolve, reject) => {
console.log(`[Connection] Connecting to guild ${guildId}, channel ${channelId}, subscribe: ${shouldSubscribe}`);
const connection = joinVoiceChannel({
guildId,
channelId,
adapterCreator,
selfDeaf: true,
selfMute: false
});
activeConnections.set(guildId, connection);
connection.on(VoiceConnectionStatus.Signalling, () => {
console.log(`[Connection] Signalling (${guildId})`);
});
connection.on(VoiceConnectionStatus.Ready, () => {
console.log(`[Connection] Ready (${guildId})`);
resolve();
});
connection.once(VoiceConnectionStatus.Destroyed, () => {
console.log(`[Connection] Destroyed (${guildId})`);
if (activeConnections.get(guildId) === connection) {
activeConnections.delete(guildId);
}
});
if (shouldSubscribe) {
subscribeToPlayer(connection);
}
connection.on(VoiceConnectionStatus.Disconnected, async () => {
console.log(`[Connection] Disconnected (${guildId})`);
if (connection.state?.status === VoiceConnectionStatus.Destroyed) {
console.log("[Connection] Already destroyed, ignoring disconnect");
return;
}
try {
await Promise.race([
entersState(connection, VoiceConnectionStatus.Signalling, 5000),
entersState(connection, VoiceConnectionStatus.Connecting, 5000)
]);
console.log("[Connection] Reconnecting...");
} catch {
console.log("[Connection] Failed to reconnect, destroying");
try {
connection.destroy();
} catch (err) {
console.error("[Connection] Error destroying:", err);
}
}
});
// Timeout after 10 seconds
setTimeout(() => {
if (connection.state.status !== VoiceConnectionStatus.Ready) {
reject(new Error("Connection timeout"));
}
}, 10000);
});
}
// ==================== EVENT HANDLERS ====================
let client = null;
let adapterCreator = null;
// GuildCreate Handler - Populate voice state cache
function onGuildCreate(event) {
const guild = event.data;
if (guild.unavailable) {
console.log(`[GuildCreate] Guild ${guild.id} unavailable, skipping`);
return;
}
console.log(`[GuildCreate] Guild ${guild.id} loaded, populating cache`);
if (guild.voice_states && guild.voice_states.length > 0) {
for (const voiceState of guild.voice_states) {
const userId = voiceState.user_id;
const guildId = guild.id;
if (userId && guildId) {
const fullVoiceState = {
...voiceState,
member: guild.members?.find((m) => m.user.id === userId),
guild_id: guildId
};
voiceStateUtils.setSnapshot(guildId, userId, fullVoiceState);
}
}
console.log(`[GuildCreate] Populated ${guild.voice_states.length} voice states for guild ${guild.id}`);
}
}
// VoiceStateUpdate Handler - Auto subscribe/unsubscribe
function onVoiceStateUpdate(event) {
const newState = event.data;
const userId = newState.user_id;
const guildId = newState.guild_id;
// Skip if not our configured guild
if (guildId !== CONFIG.GUILD_ID) return;
// Skip all bots
if (newState.member?.user?.bot) return;
const oldState = voiceStateUtils.peekSnapshot(guildId, userId);
voiceStateUtils.setSnapshot(guildId, userId, newState);
// No channel change
if (oldState?.data.channel_id === newState.channel_id) return;
console.log(
`[VoiceState] User ${userId} changed channels: ${oldState?.data.channel_id || "none"} -> ${newState.channel_id || "none"}`
);
// Handle leave
if (oldState?.data.channel_id === CONFIG.CHANNEL_ID) {
handleLeave(guildId, oldState.data.channel_id);
}
// Handle join
if (newState.channel_id === CONFIG.CHANNEL_ID) {
handleJoin(guildId, newState.channel_id);
}
}
function handleLeave(guildId, channelId) {
const memberCount = voiceStateUtils.countMembersInChannel(guildId, channelId);
console.log(`[VoiceState] User left, ${memberCount} members remaining in channel`);
if (memberCount === 0) {
const connection = getConnection(guildId);
if (connection?.state.subscription) {
connection.state.subscription.unsubscribe();
console.log("[VoiceState] Unsubscribed from player - channel empty");
}
}
}
function handleJoin(guildId, channelId) {
const memberCount = voiceStateUtils.countMembersInChannel(guildId, channelId);
console.log(`[VoiceState] User joined, ${memberCount} members in channel`);
const connection = getConnection(guildId);
if (!connection) {
console.log("[VoiceState] No connection exists, ignoring join");
return;
}
if (connection.state.status === VoiceConnectionStatus.Destroyed) {
console.log("[VoiceState] Connection destroyed, ignoring join");
return;
}
if (!connection.state.subscription) {
subscribeToPlayer(connection);
console.log("[VoiceState] Subscribed to player - users present");
}
}
// Ready Handler - Auto join voice channel
function onReady(event) {
console.log(`[Ready] Logged in as ${event.data.user.username}#${event.data.user.discriminator}`);
console.log(`[Ready] Bot ID: ${event.data.user.id}`);
console.log(`[Ready] Guilds: ${event.data.guilds.length}`);
// Wait a bit for guild data to be populated
setTimeout(async () => {
try {
console.log(`[Ready] Joining voice channel ${CONFIG.CHANNEL_ID} in guild ${CONFIG.GUILD_ID}`);
await connectToVoiceChannel(adapterCreator, CONFIG.GUILD_ID, CONFIG.CHANNEL_ID, false);
console.log("[Ready] Successfully joined voice channel (not subscribed)");
} catch (error) {
console.error("[Ready] Failed to join voice channel:", error);
}
}, 2000);
}
// ==================== INITIALIZATION ====================
async function main() {
// Validate configuration
if (CONFIG.DISCORD_TOKEN === "YOUR_DISCORD_TOKEN_HERE") {
console.error("ERROR: Please set DISCORD_TOKEN in the configuration");
process.exit(1);
}
// Initialize REST client
const rest = new REST({ version: "10" }).setToken(CONFIG.DISCORD_TOKEN);
// Initialize WebSocket Gateway (single shard)
const gateway = new WebSocketManager({
token: CONFIG.DISCORD_TOKEN,
intents: GatewayIntentBits.Guilds | GatewayIntentBits.GuildVoiceStates,
shardCount: 1,
shardIds: [0],
rest
});
// Create client
client = new Client({ rest, gateway });
// Create voice adapter
adapterCreator = createVoiceAdapterCreator(gateway, client);
// Register event handlers
client.on(GatewayDispatchEvents.Ready, onReady);
client.on(GatewayDispatchEvents.GuildCreate, onGuildCreate);
client.on(GatewayDispatchEvents.VoiceStateUpdate, onVoiceStateUpdate);
// Handle process termination
process.on("SIGINT", () => {
console.log("\n[Shutdown] Cleaning up...");
for (const [guildId] of activeConnections) {
destroyConnection(guildId);
}
voiceStateUtils.clear();
gateway.destroy();
process.exit(0);
});
// Connect to Discord
console.log("[Init] Connecting to Discord...");
await gateway.connect();
}
// Start the bot
main().catch((error) => {
console.error("[Fatal] Startup error:", error);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment