From 55a32406bd92683a957c9cf343d84d5b640cd739 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:41:12 -0500 Subject: [PATCH 1/7] Update parsemembers.js --- commands/parsemembers.js | 42 ++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/commands/parsemembers.js b/commands/parsemembers.js index 01769e59..c0d099d0 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -104,6 +104,7 @@ module.exports = { parseStatusEmbed.data.fields[1].value = 'Processing Data'; await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }); let raiders = imgPlayers.map(imgPlayer => imgPlayer.toLowerCase()); + /** @type {Discord.Collection} */ let voiceUsers = [] let alts = [] let crashers = [] @@ -117,7 +118,8 @@ module.exports = { voiceUsers = raidVc.members; console.log(raidMembers); for (let player of raiders) { - let member = message.guild.findMember(player); + const member = message.guild.findMember(player); + if (member == null) { crashers.push(player); kickList = kickList.concat(` ${player}`) @@ -130,12 +132,12 @@ module.exports = { findA.push(player) } } - for (let i in voiceUsers) { - if (voiceUsers[i].roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) continue; - if (!voiceUsers[i].nickname) continue - let nick = voiceUsers[i].nickname.toLowerCase().replace(/[^a-z|]/gi, '') + for (let [i,member] of voiceUsers) { + if (member.roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) continue; + if (!member.nickname) continue + let nick = member.nickname.toLowerCase().replace(/[^a-z|]/gi, '') if (!raiders.includes(nick)) { - alts.push(`<@!${voiceUsers[i].id}>`); + alts.push(`<@!${member.id}>`); } } @@ -148,17 +150,11 @@ module.exports = { crashers = filterNames(crashers, matchedCrashers); alts = filterNames(alts, matchedCrashers); - let crashersS = ' ', - altsS = ' ', - movedS = ' ', - find = `;find ` - for (let i in crashers) { crashersS = crashersS.concat(crashers[i]) + ', ' } - for (let i in alts) { altsS = altsS.concat(alts[i]) + ', ' } - for (let i in otherChannel) { movedS = movedS.concat(otherChannel[i]) + '\n' } - for (let i in findA) { find = find.concat(findA[i]) + ' ' } - if (crashersS == ' ') { crashersS = 'None' } - if (altsS == ' ') { altsS = 'None' } - if (movedS == ' ') { movedS = 'None' } + let crashersS = crashers.join(', ') || 'None', + altsS = alts.join(', ') || 'None', + movedS = otherChannel.join('\n') || 'None', + find = `;find ${findA.join(' ')}` + let embed = new Discord.EmbedBuilder() .setTitle(`Parse for ${raid.afkTitle()}`) .setColor('#00ff00') @@ -182,16 +178,16 @@ module.exports = { let crashers = [] let findA = [] let kickList = '/kick' + const members = Object.values(raid.reactables).flat() for (let player of raiders) { let member = message.guild.findMember(player); if (member == null) { crashers.push(player); kickList = kickList.concat(` ${player}`) - } else if (!raid.members.includes(member.id)) { - if (member.roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) - continue; - else crashers.unshift(`<@!${member.id}>`); - + } else if (!members.includes(member.id)) { + if (member.roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) continue; + + crashers.unshift(`<@!${member.id}>`); kickList = kickList.concat(` ${player}`) findA.push(player) } @@ -465,7 +461,7 @@ function reassembleAndCheckNames(splitNames, inRaidNames) { function filterNames(namesArray, matchedMap) { return namesArray.filter(name => { - for (let [matchedName, originalNames] of matchedMap.entries()) { + for (let [, originalNames] of matchedMap.entries()) { if (originalNames.includes(name)) { return false; // Exclude this name as it's part of a matched set } From bc1f3325795167000e79fd0ed22ef0cead11902d Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:38:16 -0500 Subject: [PATCH 2/7] Update parsemembers.js --- commands/parsemembers.js | 266 +++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 136 deletions(-) diff --git a/commands/parsemembers.js b/commands/parsemembers.js index c0d099d0..f609b200 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -22,6 +22,14 @@ module.exports = { return 'Image can either be a link, or an embeded image' }, role: 'eventrl', + /** + * + * @param {Discord.Message} message + * @param {string[]} args + * @param {Discord.Client} bot + * @param {import('mysql').Connection} db + * @returns + */ async execute(message, args, bot, db) { // determine raid to parse let raidID @@ -30,8 +38,7 @@ module.exports = { if (args.length && /^\d+$/.test(args[0])) //add ability to parse from a different channel with ;pm channelid memberVoiceChannel = await bot.channels.fetch(args.shift()); - - const raidIDs = afkCheck.returnActiveRaidIDs(bot).filter(r => bot.afkModules[r].guild.id == message.guild.id); + const raidIDs = Object.keys(bot.afkModules).filter(r => bot.afkModules[r].guild?.id == message.guild.id) if (raidIDs.length == 0) return message.channel.send('Could not find an active run. Please try again.') @@ -95,131 +102,112 @@ module.exports = { return; } - async function vcCrasherParse() { - if (raid.channel === null) { - return message.reply("Channel not found, please join a vc or specify channel id"); - } - let raidVc = await bot.channels.fetch(raid.channel.id); - - parseStatusEmbed.data.fields[1].value = 'Processing Data'; - await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }); - let raiders = imgPlayers.map(imgPlayer => imgPlayer.toLowerCase()); - /** @type {Discord.Collection} */ - let voiceUsers = [] - let alts = [] - let crashers = [] - let otherChannel = [] - let findA = [] - let allowedCrashers = [] - let kickList = '/kick' - let raidMembers = raid.members; - - raid.earlySlotMembers.forEach(m => raidMembers.push(m)); - voiceUsers = raidVc.members; - console.log(raidMembers); - for (let player of raiders) { - const member = message.guild.findMember(player); - - if (member == null) { - crashers.push(player); - kickList = kickList.concat(` ${player}`) - } else if (!member.id in voiceUsers) { - if (member.roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) continue; - if (raidMembers.includes(member.id)) allowedCrashers.push(member) - if (member.voice.channel) otherChannel.push(`${member}: ${member.voice.channel}`); - else crashers.unshift(`<@!${member.id}>`); - kickList = kickList.concat(` ${player}`) - findA.push(player) + const vcless = raid.vcOptions == afkTemplate.TemplateVCOptions.NO_VC + + async function runParse() { + if (!vcless && !raid.channel) return message.reply("Channel not found, please join a vc or specify channel id"); + parseStatusEmbed.data.fields[1].value = 'Processing Data' + await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) + + const minimumStaffRolePosition = message.guild.roles.cache.get(settings.roles.almostrl).position + + const raiders = imgPlayers.map(player => player.toLowerCase()) + const raidMembers = !vcless && raid.members.concat(...raid.earlySlotMembers) + const members = vcless ? Object.values(raid.reactables).map(r => r.members).flat() : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) + + /** @type {{ id: string, nicknames: string[] }[]} */ + const alts = [] + /** @type {string[]} */ + const crashers = [] + const kick = [] + const find = [] + const otherChannel = [] + const allowedCrashers = [] + + for (const player of raiders) { + const member = message.guild.findMember(player) + if (!member) { + crashers.push(player) + kick.push(player) + } else if (!members.includes(member.id)) { + if (member.roles.highest.position >= minimumStaffRolePosition) continue + + if (!vcless) { + if (raidMembers.includes(member.id)) allowedCrashers.push(member.id) + if (member.voice.channel) otherChannel.push(`${member}: ${member.voice.channel}`) + else crashers.unshift(`${member}`) + } else crashers.unshift(`${member}`) + + kick.push(player) + find.push(player) } } - for (let [i,member] of voiceUsers) { - if (member.roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) continue; + + for (const memberId of members) { + const member = message.guild.members.cache.get(memberId) + if (member.roles.highest.position > minimumStaffRolePosition) continue if (!member.nickname) continue - let nick = member.nickname.toLowerCase().replace(/[^a-z|]/gi, '') - if (!raiders.includes(nick)) { - alts.push(`<@!${member.id}>`); - } + const nicknames = member.nickname.toLowerCase().replace(/[^a-z|]/gi, '').split('|') + if (!raiders.some(raider => nicknames.includes(raider)) && !alts.some(alt => alt.id == member.id)) alts.push({ id: member.id, nicknames }) } - // Check the names more thoroughly - let normalizedNames = crashers.map(normalizeName); - let normalizedAlts = alts.map(normalizeName); - let matchedCrashers = reassembleAndCheckNames(normalizedNames, normalizedAlts); - - // Remove the names that were matched - crashers = filterNames(crashers, matchedCrashers); - alts = filterNames(alts, matchedCrashers); - - let crashersS = crashers.join(', ') || 'None', - altsS = alts.join(', ') || 'None', - movedS = otherChannel.join('\n') || 'None', - find = `;find ${findA.join(' ')}` - - let embed = new Discord.EmbedBuilder() - .setTitle(`Parse for ${raid.afkTitle()}`) - .setColor('#00ff00') - .setDescription(`There are ${crashers.length} crashers, ${alts.length} potential alts, and ${otherChannel.length} people in other channels`) - .addFields({ name: 'Potential Alts', value: altsS }, { name: 'Other Channels', value: movedS }, { name: 'Crashers', value: crashersS }, { name: 'Find Command', value: `\`\`\`${find}\`\`\`` }, { name: 'Kick List', value: `\`\`\`${kickList}\`\`\`` }) - if (raid) embed.addFields([{name: `Were in VC`, value: `The following can use the \`reconnect\` button:\n${allowedCrashers.map(u => `${u} `)}`}]) - await message.channel.send({ embeds: [embed] }); - parseStatusEmbed.data.fields[1].value = `Crasher Parse Completed. See Below. Beginning Character Parse` - await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) + const normalizedCrashers = crashers.map(normalizeName) + const normalizedAlts = alts.map(({id, nicknames}) => ({ id, nicknames: nicknames.map(normalizeName) })) + const results = reassembleAndCheckNames(normalizedCrashers, normalizedAlts) + const [matchKeys, matchValues] = [Array.from(results.keys()), Array.from(results.values())] + + const actualCrashers = [] + const possibleAlts = [] + const actualKicks = [] + const actualFind = [] + for (const crasher_idx in crashers) { + if (!matchValues.some(m => m.parts.includes(normalizedCrashers[crasher_idx]))) + actualCrashers.push(crashers[crasher_idx]) + } - //post in crasher-list - let key = null - if (raid.reactables.Key && raid.reactables.Key.members[0]) key = raid.reactables.Key.members[0] - if (settings.commands.crasherlist) - postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, key) - } - async function noVcCrasherParse() { - parseStatusEmbed.data.fields[1].value = 'Processing Data' - await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) - let raiders = imgPlayers.map(imgPlayer => imgPlayer.toLowerCase()); - let crashers = [] - let findA = [] - let kickList = '/kick' - const members = Object.values(raid.reactables).flat() - for (let player of raiders) { - let member = message.guild.findMember(player); - if (member == null) { - crashers.push(player); - kickList = kickList.concat(` ${player}`) - } else if (!members.includes(member.id)) { - if (member.roles.highest.position >= message.guild.roles.cache.get(settings.roles.almostrl).position) continue; - - crashers.unshift(`<@!${member.id}>`); - kickList = kickList.concat(` ${player}`) - findA.push(player) - } + for (const alt of normalizedAlts) { + if (!matchKeys.some(full => alt.nicknames.some(name => full == name))) + possibleAlts.push(`<@${alt.id}>`) } - // Check the names more thoroughly - let normalizedNames = crashers.map(normalizeName); - let matchedCrashers = reassembleAndCheckNames(normalizedNames, raid.members); + for (const raider of kick) { + if (!matchValues.some(m => m.parts.includes(normalizeName(raider)))) + actualKicks.push(raider) + } - // Remove the names that were matched - crashers = filterNames(crashers, matchedCrashers); + for (const raider of find) { + if (!matchValues.some(m => m.includes(normalizeName(raider)))) + actualFind.push(raider) + } + - let crashersS = ' '; - let find = `;find `; - crashersS += crashers.join(', '); - find += findA.join(' '); - if (crashersS == ' ') { crashersS = 'None' } - let embed = new Discord.EmbedBuilder() + const embed = new Discord.EmbedBuilder() .setTitle(`Parse for ${raid.afkTitle()}`) .setColor('#00ff00') - .setDescription(`There are ${crashers.length} crashers`) - .addFields({ name: 'Crashers', value: crashersS }, { name: 'Find Command', value: `\`\`\`${find}\`\`\`` }, { name: 'Kick List', value: `\`\`\`${kickList}\`\`\`` }) + .setDescription(`There are ${actualCrashers.length} crashers, ${possibleAlts.length} potential alts` + (vcless ? '' : `, and ${otherChannel.length} people in other channels`)) + .addFields({ name: 'Potential Alts', value: possibleAlts.join(', ') || 'None' }) + + if (!vcless) embed.addFields({ name: 'Other Channels', value: otherChannel.join('\n') || 'None' }) + + embed.addFields( + { name: 'Crashers', value: actualCrashers.join(', ') || 'None' }, + { name: 'Find Command', value: `\`\`\`;find ${actualFind.join(' ')}\`\`\`` }, + { name: 'Kick List', value: actualKicks.length ? `\`\`\`${actualKicks.join(' ')}\`\`\`` : 'None' } + ) + + if (!vcless) embed.addFields({ + name: `Were in VC`, + value: `The following can use the \`reconnect\` button:\n${allowedCrashers.map(u => `<@${u}>`).join(' ')}` + }) + await message.channel.send({ embeds: [embed] }); parseStatusEmbed.data.fields[1].value = `Crasher Parse Completed. See Below. Beginning Character Parse` await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) - //post in crasher-list - let key = null - if (raid.reactables.Key && raid.reactables.Key.members[0]) key = raid.reactables.Key.members[0] if (settings.commands.crasherlist) - postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, key) + postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, raid.reactables.Key?.members[0]) } + async function characterParse() { let unreachable = [] let characterParseEmbed = new Discord.EmbedBuilder() @@ -365,11 +353,7 @@ module.exports = { await message.channel.send({ embeds: [unreachableEmbed] }) } - let parsePromises = [] - if (raid.vcOptions == afkTemplate.TemplateVCOptions.NO_VC) - parsePromises.push(noVcCrasherParse()); - else - parsePromises.push(vcCrasherParse()); + let parsePromises = [runParse()] if (settings.backend.characterparse) parsePromises.push(characterParse()); await Promise.all(parsePromises) @@ -428,44 +412,54 @@ async function postInCrasherList(embed, channel, parser, key) { function normalizeName(name) { return name.toLowerCase().replace(/\s/g, '').replace(/i/g, 'l'); } - +//TODO: inRaidNames is now [{ id, nicknames }] +//return [{ currentName, { components, id }}] // Function to reassemble and check split names -function reassembleAndCheckNames(splitNames, inRaidNames) { +/** + * + * @param {string[]} crasherNames + * @param {[{id: string, nicknames: string[]}]} raidMembers + * @returns {Map { for (let [, originalNames] of matchedMap.entries()) { - if (originalNames.includes(name)) { + if (originalNames.components.includes(name)) { return false; // Exclude this name as it's part of a matched set } } return true; // Include this name as it's not part of any matched set }); -} \ No newline at end of file +} + From ba38f0122bf715c17e8c153b36801a1b5cdd05c3 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:53:02 -0500 Subject: [PATCH 3/7] Update parsemembers.js --- commands/parsemembers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/parsemembers.js b/commands/parsemembers.js index f609b200..ba93f9f1 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -205,7 +205,7 @@ module.exports = { await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) if (settings.commands.crasherlist) - postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, raid.reactables.Key?.members[0]) + postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, raid.reactables?.Key?.members[0]) } async function characterParse() { From 1b3ef54c2c2d75d77916ebc6f3a716e72d003853 Mon Sep 17 00:00:00 2001 From: tro2 <62850247+tro2@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:03:32 -0800 Subject: [PATCH 4/7] Update parsemembers.js Removed deprecated references to raid.reactables --- commands/parsemembers.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/commands/parsemembers.js b/commands/parsemembers.js index ba93f9f1..36539635 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -112,8 +112,8 @@ module.exports = { const minimumStaffRolePosition = message.guild.roles.cache.get(settings.roles.almostrl).position const raiders = imgPlayers.map(player => player.toLowerCase()) - const raidMembers = !vcless && raid.members.concat(...raid.earlySlotMembers) - const members = vcless ? Object.values(raid.reactables).map(r => r.members).flat() : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) + const raidMembers = raid.members + const members = vcless ? raidMembers : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) /** @type {{ id: string, nicknames: string[] }[]} */ const alts = [] @@ -204,8 +204,11 @@ module.exports = { parseStatusEmbed.data.fields[1].value = `Crasher Parse Completed. See Below. Beginning Character Parse` await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) - if (settings.commands.crasherlist) - postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, raid.reactables?.Key?.members[0]) + if (settings.commands.crasherlist) { + const keyMemberId = (raid.buttons["Key"]?.members ?? [])[0]; + postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, keyMemberId) + } + } async function characterParse() { From 8eaab72cf97aecfff7c6ae85465ff74508f5ed91 Mon Sep 17 00:00:00 2001 From: tro2 <62850247+tro2@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:05:21 -0800 Subject: [PATCH 5/7] Update parsemembers.js Made code shorter by one line to make Sauron feel a little tingle in his left nut --- commands/parsemembers.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/commands/parsemembers.js b/commands/parsemembers.js index 36539635..e1bb50eb 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -205,8 +205,7 @@ module.exports = { await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) if (settings.commands.crasherlist) { - const keyMemberId = (raid.buttons["Key"]?.members ?? [])[0]; - postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, keyMemberId) + postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, (raid.buttons["Key"]?.members ?? [])[0]) } } From 390bf0ed060324c30f4e6c7f9e2970681cdedf27 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Thu, 1 Feb 2024 20:53:02 -0500 Subject: [PATCH 6/7] Review changes - Added `isVcless` to `AfkCheck` - When deciding which raid to parse, replaced the many references to bot.afkModules with a filtered list - removed `raidMembers` in favor of using `raid.members` directly - Replaced reassembleAndCheckNames with @Huntifer-RotMG 's suggestion. This has not been rigorously tested but I don't see why it shouldn't work - Removed unused `filterNames` function --- commands/afkCheck.js | 2 + commands/parsemembers.js | 101 +++++++++++++-------------------------- 2 files changed, 36 insertions(+), 67 deletions(-) diff --git a/commands/afkCheck.js b/commands/afkCheck.js index 39a2c49d..60c237b3 100644 --- a/commands/afkCheck.js +++ b/commands/afkCheck.js @@ -214,6 +214,8 @@ class afkCheck { get vcOptions() { return this.#afkTemplate.vcOptions } + get isVcless() { return this.vcOptions == AfkTemplate.TemplateVCOptions.NO_VC } + get channel() { return this.#channel } // needed for parsemembers diff --git a/commands/parsemembers.js b/commands/parsemembers.js index e1bb50eb..d8e39bf2 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -18,7 +18,7 @@ module.exports = { description: 'Parse', alias: ['pm'], args: '', - getNotes(guild, member, bot) { + getNotes() { return 'Image can either be a link, or an embeded image' }, role: 'eventrl', @@ -31,45 +31,36 @@ module.exports = { * @returns */ async execute(message, args, bot, db) { - // determine raid to parse - let raidID let settings = bot.settings[message.guild.id] let memberVoiceChannel = message.member.voice.channel if (args.length && /^\d+$/.test(args[0])) //add ability to parse from a different channel with ;pm channelid memberVoiceChannel = await bot.channels.fetch(args.shift()); - const raidIDs = Object.keys(bot.afkModules).filter(r => bot.afkModules[r].guild?.id == message.guild.id) - if (raidIDs.length == 0) - return message.channel.send('Could not find an active run. Please try again.') - else if (raidIDs.length == 1) { - raidID = raidIDs[0]; - } - else if (raidIDs.some(r => bot.afkModules[r].channel != null && bot.afkModules[r].channel.id == memberVoiceChannel)) // prioritize vc - raidID = raidIDs.find(r => bot.afkModules[r].channel != null && bot.afkModules[r].channel.id == memberVoiceChannel); - else if (raidIDs.filter(r => bot.afkModules[r].members.includes(message.member.id)).length == 1) { // prioritize the raids they've joined - raidID = raidIDs.find(r => bot.afkModules[r].members.includes(message.member.id)); - } - else { + const raids = Object.values(bot.afkModules).filter(afk => afk.guild?.id == message.guild.id) + if (raids.length == 0) return message.channel.send('Could not find an active run. Please try again.') + + let raid + if (raids.length == 1) raid = raids[0] + if (!raid && raids.filter(afk => afk.channel?.id == memberVoiceChannel).length == 1) + raid = raids.find(afk => afk.channel?.id == memberVoiceChannel) + if (!raid && raids.filter(afk => afk.members.includes(message.member.id)).length == 1) //if there's exactly 1 raid they're a part of + raid = raids.find(afk => afk.members.includes(message.member.id)) + if (!raid) { const raidMenu = new Discord.StringSelectMenuBuilder() .setPlaceholder(`Active Runs`) .setMinValues(1) .setMaxValues(1) let text = 'Which active run would you like to parse for?' - let index = 0 - for (let id of raidIDs) { - const label = `${bot.afkModules[id].afkTitle()}` // BUG this shows up undefined upon restart sometimes - text += `\n\`\`${index+1}.\`\` ${label}` - raidMenu.addOptions({ label: `${index+1}. ${bot.afkModules[id].afkTitle()}`, value: id }) - index++ + for (let index = 0; index < raids.length; index++) { + text += `\n\`\`${index+1}.\`\` ${raids[index].afkTitle()}` + raidMenu.addOptions({ label: `${index+1}. ${raids[index].afkTitle()}`, value: String(index) }) } - const { value: id } = await message.selectPanel(text, null, raidMenu, 30000, false, true) - if (!id) return await message.reply('You must specify the raid to parse, or join the raid\'s voice channel.') - raidID = id + const { value} = await message.selectPanel(text, null, raidMenu, 30000, false, true) + if (!value) return await message.reply('You must specify the raid to parse, or join the raid\'s voice channel.') + raid = raids[value] } - const raid = bot.afkModules[raidID]; - // start parse building let parseStatusEmbed = new Discord.EmbedBuilder() .setColor(`#00ff00`) @@ -102,18 +93,15 @@ module.exports = { return; } - const vcless = raid.vcOptions == afkTemplate.TemplateVCOptions.NO_VC - async function runParse() { - if (!vcless && !raid.channel) return message.reply("Channel not found, please join a vc or specify channel id"); + if (!raid.isVcless && !raid.channel) return message.reply("Channel not found, please join a vc or specify channel id"); parseStatusEmbed.data.fields[1].value = 'Processing Data' await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) const minimumStaffRolePosition = message.guild.roles.cache.get(settings.roles.almostrl).position const raiders = imgPlayers.map(player => player.toLowerCase()) - const raidMembers = raid.members - const members = vcless ? raidMembers : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) + const members = raid.isVcless ? raid.members : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) /** @type {{ id: string, nicknames: string[] }[]} */ const alts = [] @@ -132,8 +120,8 @@ module.exports = { } else if (!members.includes(member.id)) { if (member.roles.highest.position >= minimumStaffRolePosition) continue - if (!vcless) { - if (raidMembers.includes(member.id)) allowedCrashers.push(member.id) + if (!raid.isVcless) { + if (raid.members.includes(member.id)) allowedCrashers.push(member.id) if (member.voice.channel) otherChannel.push(`${member}: ${member.voice.channel}`) else crashers.unshift(`${member}`) } else crashers.unshift(`${member}`) @@ -184,10 +172,10 @@ module.exports = { const embed = new Discord.EmbedBuilder() .setTitle(`Parse for ${raid.afkTitle()}`) .setColor('#00ff00') - .setDescription(`There are ${actualCrashers.length} crashers, ${possibleAlts.length} potential alts` + (vcless ? '' : `, and ${otherChannel.length} people in other channels`)) + .setDescription(`There are ${actualCrashers.length} crashers, ${possibleAlts.length} potential alts` + (raid.isVcless ? '' : `, and ${otherChannel.length} people in other channels`)) .addFields({ name: 'Potential Alts', value: possibleAlts.join(', ') || 'None' }) - if (!vcless) embed.addFields({ name: 'Other Channels', value: otherChannel.join('\n') || 'None' }) + if (!raid.isVcless) embed.addFields({ name: 'Other Channels', value: otherChannel.join('\n') || 'None' }) embed.addFields( { name: 'Crashers', value: actualCrashers.join(', ') || 'None' }, @@ -195,7 +183,7 @@ module.exports = { { name: 'Kick List', value: actualKicks.length ? `\`\`\`${actualKicks.join(' ')}\`\`\`` : 'None' } ) - if (!vcless) embed.addFields({ + if (!raid.isVcless) embed.addFields({ name: `Were in VC`, value: `The following can use the \`reconnect\` button:\n${allowedCrashers.map(u => `<@${u}>`).join(' ')}` }) @@ -423,45 +411,24 @@ function normalizeName(name) { * @param {[{id: string, nicknames: string[]}]} raidMembers * @returns {Map 0) { + let currentName = namesToCheck.shift(); - for (const { id, nicknames } of raidMembers) { - if (nicknames.includes(currentName)) { - matchedNamesMap.set(currentName, { parts: originalComponents, id }) - i = j; // Skip the next names as they are part of the current one - break combine - } + for (let i = 0; i < namesToCheck.length; i++) { + const { id: matchingId } = raidMembers.find(({nicknames}) => nicknames.includes(currentName + namesToCheck.slice(0, i).join(''))); + if (matchingId) { + const matchedComponents = namesToCheck.shift(i); + matchedNamesMap.set(currentName + matchedComponents.join(''), { parts: [currentName, ...matchedComponents], id: matchingId }) } } } return matchedNamesMap } - -/** - * - * @param {*} namesArray - * @param {*} matchedMap - * @returns {Map { - for (let [, originalNames] of matchedMap.entries()) { - if (originalNames.components.includes(name)) { - return false; // Exclude this name as it's part of a matched set - } - } - return true; // Include this name as it's not part of any matched set - }); -} - + \ No newline at end of file From 808fda8870ae795a19beb3759ad1cb4b70d00619 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Sat, 3 Feb 2024 00:28:40 -0500 Subject: [PATCH 7/7] Review edits - Moved selecting raid into its own function - Changed isVcless to a function instead of a getter - Utilizing Array.filter for finalized crasher/alt/etc lists - Removed unnecessary null-coalesce & --- commands/afkCheck.js | 3 +- commands/parsemembers.js | 78 +++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 47 deletions(-) diff --git a/commands/afkCheck.js b/commands/afkCheck.js index 60c237b3..8d9df5b9 100644 --- a/commands/afkCheck.js +++ b/commands/afkCheck.js @@ -214,13 +214,14 @@ class afkCheck { get vcOptions() { return this.#afkTemplate.vcOptions } - get isVcless() { return this.vcOptions == AfkTemplate.TemplateVCOptions.NO_VC } get channel() { return this.#channel } // needed for parsemembers get afkTemplateName() { return this.#afkTemplate.templateName } + isVcless() { return this.vcOptions == AfkTemplate.TemplateVCOptions.NO_VC } + raidLeaderDisplayName() { return this.#leader.displayName.replace(/[^a-z|]/gi, '').split('|')[0] } diff --git a/commands/parsemembers.js b/commands/parsemembers.js index d8e39bf2..c2c045c3 100644 --- a/commands/parsemembers.js +++ b/commands/parsemembers.js @@ -37,16 +37,19 @@ module.exports = { if (args.length && /^\d+$/.test(args[0])) //add ability to parse from a different channel with ;pm channelid memberVoiceChannel = await bot.channels.fetch(args.shift()); - const raids = Object.values(bot.afkModules).filter(afk => afk.guild?.id == message.guild.id) - if (raids.length == 0) return message.channel.send('Could not find an active run. Please try again.') - - let raid - if (raids.length == 1) raid = raids[0] - if (!raid && raids.filter(afk => afk.channel?.id == memberVoiceChannel).length == 1) - raid = raids.find(afk => afk.channel?.id == memberVoiceChannel) - if (!raid && raids.filter(afk => afk.members.includes(message.member.id)).length == 1) //if there's exactly 1 raid they're a part of - raid = raids.find(afk => afk.members.includes(message.member.id)) - if (!raid) { + async function getRaid() { + const raids = Object.values(bot.afkModules).filter(afk => afk.guild?.id == message.guild.id) + if (raids.length == 0) { + message.channel.send('Could not find an active run. Please try again.') + return + } + + if (raids.length == 1) return raids[0] + if (raids.filter(afk => afk.channel?.id == memberVoiceChannel).length == 1) + return raids.find(afk => afk.channel?.id == memberVoiceChannel) + if (raids.filter(afk => afk.members.includes(message.member.id)).length == 1) + return raids.find(afk => afk.members.includes(message.member.id)) + const raidMenu = new Discord.StringSelectMenuBuilder() .setPlaceholder(`Active Runs`) .setMinValues(1) @@ -56,10 +59,13 @@ module.exports = { text += `\n\`\`${index+1}.\`\` ${raids[index].afkTitle()}` raidMenu.addOptions({ label: `${index+1}. ${raids[index].afkTitle()}`, value: String(index) }) } - const { value} = await message.selectPanel(text, null, raidMenu, 30000, false, true) - if (!value) return await message.reply('You must specify the raid to parse, or join the raid\'s voice channel.') - raid = raids[value] + const { value } = await message.selectPanel(text, null, raidMenu, 30000, false, true) + if (value) return raids[value] + await message.reply('You must specify the raid to parse, or join the raid\'s voice channel.') } + + const raid = await getRaid() + if (!raid) return // start parse building let parseStatusEmbed = new Discord.EmbedBuilder() @@ -94,14 +100,14 @@ module.exports = { } async function runParse() { - if (!raid.isVcless && !raid.channel) return message.reply("Channel not found, please join a vc or specify channel id"); + if (!raid.isVcless() && !raid.channel) return message.reply("Channel not found, please join a vc or specify channel id"); parseStatusEmbed.data.fields[1].value = 'Processing Data' await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) const minimumStaffRolePosition = message.guild.roles.cache.get(settings.roles.almostrl).position const raiders = imgPlayers.map(player => player.toLowerCase()) - const members = raid.isVcless ? raid.members : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) + const members = raid.isVcless() ? raid.members : bot.channels.cache.get(raid.channel.id).members.map(m => m.id) /** @type {{ id: string, nicknames: string[] }[]} */ const alts = [] @@ -120,7 +126,7 @@ module.exports = { } else if (!members.includes(member.id)) { if (member.roles.highest.position >= minimumStaffRolePosition) continue - if (!raid.isVcless) { + if (!raid.isVcless()) { if (raid.members.includes(member.id)) allowedCrashers.push(member.id) if (member.voice.channel) otherChannel.push(`${member}: ${member.voice.channel}`) else crashers.unshift(`${member}`) @@ -144,38 +150,19 @@ module.exports = { const results = reassembleAndCheckNames(normalizedCrashers, normalizedAlts) const [matchKeys, matchValues] = [Array.from(results.keys()), Array.from(results.values())] - const actualCrashers = [] - const possibleAlts = [] - const actualKicks = [] - const actualFind = [] - for (const crasher_idx in crashers) { - if (!matchValues.some(m => m.parts.includes(normalizedCrashers[crasher_idx]))) - actualCrashers.push(crashers[crasher_idx]) - } - - for (const alt of normalizedAlts) { - if (!matchKeys.some(full => alt.nicknames.some(name => full == name))) - possibleAlts.push(`<@${alt.id}>`) - } - - for (const raider of kick) { - if (!matchValues.some(m => m.parts.includes(normalizeName(raider)))) - actualKicks.push(raider) - } - - for (const raider of find) { - if (!matchValues.some(m => m.includes(normalizeName(raider)))) - actualFind.push(raider) - } + const actualCrashers = crashers.filter((_, idx) => !matchValues.some(m => m.parts.includes(normalizedCrashers[idx]))) + const possibleAlts = normalizedAlts.filter(alt => !matchKeys.some(full => alt.nicknames.some(name => full == name))).map(alt => `<@${alt.id}>`) + const actualKicks = kick.filter(raider => !matchValues.some(m => m.parts.includes(normalizeName(raider)))) + const actualFind = find.filter(raider => !matchValues.some(m => m.parts.includes(normalizeName(raider)))) const embed = new Discord.EmbedBuilder() .setTitle(`Parse for ${raid.afkTitle()}`) .setColor('#00ff00') - .setDescription(`There are ${actualCrashers.length} crashers, ${possibleAlts.length} potential alts` + (raid.isVcless ? '' : `, and ${otherChannel.length} people in other channels`)) + .setDescription(`There are ${actualCrashers.length} crashers, ${possibleAlts.length} potential alts` + (raid.isVcless() ? '' : `, and ${otherChannel.length} people in other channels`)) .addFields({ name: 'Potential Alts', value: possibleAlts.join(', ') || 'None' }) - if (!raid.isVcless) embed.addFields({ name: 'Other Channels', value: otherChannel.join('\n') || 'None' }) + if (!raid.isVcless()) embed.addFields({ name: 'Other Channels', value: otherChannel.join('\n') || 'None' }) embed.addFields( { name: 'Crashers', value: actualCrashers.join(', ') || 'None' }, @@ -183,7 +170,7 @@ module.exports = { { name: 'Kick List', value: actualKicks.length ? `\`\`\`${actualKicks.join(' ')}\`\`\`` : 'None' } ) - if (!raid.isVcless) embed.addFields({ + if (!raid.isVcless()) embed.addFields({ name: `Were in VC`, value: `The following can use the \`reconnect\` button:\n${allowedCrashers.map(u => `<@${u}>`).join(' ')}` }) @@ -193,7 +180,7 @@ module.exports = { await parseStatusMessage.edit({ embeds: [parseStatusEmbed] }) if (settings.commands.crasherlist) { - postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, (raid.buttons["Key"]?.members ?? [])[0]) + postInCrasherList(embed, message.guild.channels.cache.get(settings.channels.parsechannel), message.member, raid.buttons["Key"]?.members[0]) } } @@ -402,14 +389,13 @@ async function postInCrasherList(embed, channel, parser, key) { function normalizeName(name) { return name.toLowerCase().replace(/\s/g, '').replace(/i/g, 'l'); } -//TODO: inRaidNames is now [{ id, nicknames }] -//return [{ currentName, { components, id }}] + // Function to reassemble and check split names /** * * @param {string[]} crasherNames * @param {[{id: string, nicknames: string[]}]} raidMembers - * @returns {Map