// Example of another module using scorekeeper export const init = async (client, config) => { // Set up message listener that adds input points when users chat client.on('messageCreate', async (message) => { if (message.author.bot) return; // Skip if not in a guild if (!message.guild) return; // Add input points based on message length const points = Math.min(Math.ceil(message.content.length / 10), 5); try { await client.scorekeeper.addInput(message.guild.id, message.author.id, points); } catch (error) { client.logger.error(`Error adding input points: ${error.message}`); } }); // Initialize voice tracking state client.voiceTracker = { joinTimes: new Map(), // Tracks when users joined voice activeUsers: new Map() // Tracks users currently earning points }; // Set up a voice state listener that adds input for voice activity client.on('voiceStateUpdate', async (oldState, newState) => { // Skip if not in a guild if (!oldState.guild && !newState.guild) return; const guild = oldState.guild || newState.guild; const member = oldState.member || newState.member; // User joined a voice channel if (!oldState.channelId && newState.channelId) { // Check if the channel has other non-bot users const channel = newState.channel; const otherUsers = channel.members.filter(m => m.id !== member.id && !m.user.bot ); // Store join time if there's at least one other non-bot user if (otherUsers.size > 0) { client.voiceTracker.joinTimes.set(member.id, Date.now()); client.voiceTracker.activeUsers.set(member.id, newState.channelId); client.logger.debug(`${member.user.tag} joined voice with others - tracking time`); } else { client.logger.debug(`${member.user.tag} joined voice alone or with bots - not tracking time`); } } // User left a voice channel else if (oldState.channelId && !newState.channelId) { processVoiceLeave(client, guild, member, oldState.channelId); } // User switched voice channels else if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) { // Process leaving the old channel processVoiceLeave(client, guild, member, oldState.channelId); // Check if the new channel has other non-bot users const channel = newState.channel; const otherUsers = channel.members.filter(m => m.id !== member.id && !m.user.bot ); // Start tracking in the new channel if there are other non-bot users if (otherUsers.size > 0) { client.voiceTracker.joinTimes.set(member.id, Date.now()); client.voiceTracker.activeUsers.set(member.id, newState.channelId); } } // If someone joined or left a channel, update tracking for everyone in that channel updateChannelUserTracking(client, oldState, newState); }); }; /** * Process when a user leaves a voice channel */ function processVoiceLeave(client, guild, member, channelId) { if (client.voiceTracker.activeUsers.get(member.id) === channelId) { const joinTime = client.voiceTracker.joinTimes.get(member.id); if (joinTime) { const duration = (Date.now() - joinTime) / 1000 / 60; // Duration in minutes // Award 1 point per minute, up to 30 per session const points = Math.min(Math.floor(duration), 30); if (points > 0) { try { client.scorekeeper.addInput(guild.id, member.id, points) .then(() => { client.logger.debug(`Added ${points} voice activity points for ${member.user.tag}`); }) .catch(error => { client.logger.error(`Error adding voice points: ${error.message}`); }); } catch (error) { client.logger.error(`Error adding voice points: ${error.message}`); } } } client.voiceTracker.joinTimes.delete(member.id); client.voiceTracker.activeUsers.delete(member.id); } } /** * Updates tracking for all users in affected channels */ function updateChannelUserTracking(client, oldState, newState) { // Get the affected channels const affectedChannels = new Set(); if (oldState.channelId) affectedChannels.add(oldState.channelId); if (newState.channelId) affectedChannels.add(newState.channelId); for (const channelId of affectedChannels) { const channel = oldState.guild.channels.cache.get(channelId); if (!channel) continue; // Check if the channel has at least 2 non-bot users const nonBotMembers = channel.members.filter(m => !m.user.bot); const hasMultipleUsers = nonBotMembers.size >= 2; // For each user in the channel channel.members.forEach(channelMember => { if (channelMember.user.bot) return; // Skip bots const userId = channelMember.id; const isActive = client.voiceTracker.activeUsers.get(userId) === channelId; // Should be active but isn't yet if (hasMultipleUsers && !isActive) { client.voiceTracker.joinTimes.set(userId, Date.now()); client.voiceTracker.activeUsers.set(userId, channelId); client.logger.debug(`Starting tracking for ${channelMember.user.tag} in ${channel.name}`); } // Should not be active but is else if (!hasMultipleUsers && isActive) { processVoiceLeave(client, oldState.guild, channelMember, channelId); client.logger.debug(`Stopping tracking for ${channelMember.user.tag} - not enough users`); } }); } }