improve calcpayouts
This commit is contained in:
@@ -4,33 +4,131 @@ const torn = require('../../torn.js');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('calcpayout')
|
.setName('calcpayout')
|
||||||
.setDescription('[WIP] Calculate war payout based on participation')
|
.setDescription('Calculate war payout based on participation')
|
||||||
.addIntegerOption(option =>
|
.addIntegerOption(option =>
|
||||||
option.setName('total')
|
option.setName('total')
|
||||||
.setDescription('Full war earnings total before cuts')),
|
.setDescription('Full war earnings total before cuts')
|
||||||
|
.setRequired(true))
|
||||||
|
.addIntegerOption(option =>
|
||||||
|
option.setName('percentage')
|
||||||
|
.setDescription('Percentage of leader cut (default 10)'))
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('method')
|
||||||
|
.setDescription('Calculation method')
|
||||||
|
.addChoices(
|
||||||
|
{ name: 'Participation Based', value: 'flat' },
|
||||||
|
{ name: 'Score Based', value: 'weighted' },
|
||||||
|
{ name: 'Attack Based', value: 'attacks' },
|
||||||
|
)),
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const total = interaction.options.getInteger('total');
|
const total = interaction.options.getInteger('total');
|
||||||
|
const percentage = interaction.options.getInteger('percentage') ?? 10;
|
||||||
|
const method = interaction.options.getString('method') ?? 'flat';
|
||||||
|
|
||||||
|
// Calculate cuts
|
||||||
|
const leaderCut = Math.ceil(total * (percentage / 100));
|
||||||
|
const pool = total - leaderCut;
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const myFaction = await torn.faction.basic();
|
||||||
const lastWarRaw = await torn.faction.rankedWars({ offset: 0, limit: 1, sort: 'DESC' });
|
const lastWarRaw = await torn.faction.rankedWars({ offset: 0, limit: 1, sort: 'DESC' });
|
||||||
const lastWarID = lastWarRaw[0].id
|
const lastWarID = lastWarRaw[0].id
|
||||||
const lastWar = await torn.faction.rankedWarReport(lastWarID);
|
const lastWar = await torn.faction.rankedWarReport(lastWarID);
|
||||||
const ourMembers = lastWar.factions.find(faction => faction.id === 53026).members; // TODO: dont hardcore faction ID
|
const ourFactionId = myFaction.ID || myFaction.id; // API v1 vs v2 fallback checks
|
||||||
let totalParticipants = 0;
|
const ourFaction = lastWar.factions.find(faction => faction.id === ourFactionId);
|
||||||
let message = `# War Payout Calculation for War against ${lastWar.factions.find(faction => faction.id !== 53026).name} with total earnings of $${total.toLocaleString()}:\n`;
|
const enemyFaction = lastWar.factions.find(faction => faction.id !== ourFactionId);
|
||||||
ourMembers.forEach(member => {
|
|
||||||
if (member.id == 2993713) {
|
if (!ourFaction) {
|
||||||
console.log(`User ${member.name} is calculated separately.`);
|
return interaction.editReply('Could not find our faction in the last war report.');
|
||||||
} else if (member.attacks > 0) {
|
|
||||||
console.log(`${member.name} participated with ${member.attacks} attacks.`);
|
|
||||||
totalParticipants++;
|
|
||||||
message += `- ${member.name}: Participated with a score of ${member.score} from ${member.attacks} attacks.\n`;
|
|
||||||
} else {
|
|
||||||
console.log(`${member.name} did not participate.`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const members = ourFaction.members;
|
||||||
|
const participants = [];
|
||||||
|
const nonParticipants = [];
|
||||||
|
let totalScore = 0;
|
||||||
|
let totalAttacks = 0;
|
||||||
|
|
||||||
|
// Filter members
|
||||||
|
for (const memberId in members) {
|
||||||
|
const member = members[memberId];
|
||||||
|
|
||||||
|
if (member.id == myFaction.leader_id) {
|
||||||
|
console.log(`User ${member.name} skipped (Leader exclusion).`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member.attacks > 0) {
|
||||||
|
participants.push(member);
|
||||||
|
totalScore += member.score;
|
||||||
|
totalAttacks += member.attacks;
|
||||||
|
} else {
|
||||||
|
nonParticipants.push(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort logic
|
||||||
|
if (method === 'attacks') {
|
||||||
|
participants.sort((a, b) => b.attacks - a.attacks);
|
||||||
|
} else {
|
||||||
|
participants.sort((a, b) => b.score - a.score);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let message = `# War Payout: ${ourFaction.name} vs ${enemyFaction.name}\n`;
|
||||||
|
message += `**Total Earnings:** $${total.toLocaleString()}\n`;
|
||||||
|
message += `**Leader Cut (${percentage}%):** $${leaderCut.toLocaleString()} (Yameii)\n`;
|
||||||
|
message += `**Distributable Pool:** $${pool.toLocaleString()}\n`;
|
||||||
|
|
||||||
|
let methodText = 'Participation Based';
|
||||||
|
if (method === 'weighted') methodText = 'Score Based';
|
||||||
|
if (method === 'attacks') methodText = 'Attack Based';
|
||||||
|
|
||||||
|
message += `**Calculation Method:** ${methodText}\n`;
|
||||||
|
message += `**Participants:** ${participants.length}\n\n`;
|
||||||
|
|
||||||
|
message += `## Payouts\n`;
|
||||||
|
|
||||||
|
if (method === 'weighted') {
|
||||||
|
participants.forEach(member => {
|
||||||
|
const share = (member.score / totalScore);
|
||||||
|
const payout = Math.floor(pool * share);
|
||||||
|
message += `- **${member.name}**: $${payout.toLocaleString()} (${(share * 100).toFixed(2)}% of pool | Score: ${member.score})\n`;
|
||||||
});
|
});
|
||||||
message += `## OseanWorld. earned $${total.toLocaleString()} with Yameii earning 10% off the top for a total of $${Math.ceil(total * 0.1).toLocaleString()}, leaving ${Math.floor(total * 0.9).toLocaleString()} for ${totalParticipants} participants.\n`;
|
} else if (method === 'attacks') {
|
||||||
message += `## Dividing that out gives each participant approximately $${Math.floor((total * 0.9) / totalParticipants).toLocaleString()} each.`;
|
participants.forEach(member => {
|
||||||
console.log(`there were ${totalParticipants} participants`);
|
const share = (member.attacks / totalAttacks);
|
||||||
console.log(message)
|
const payout = Math.floor(pool * share);
|
||||||
interaction.reply(message);
|
message += `- **${member.name}**: $${payout.toLocaleString()} (${(share * 100).toFixed(2)}% of pool | Attacks: ${member.attacks})\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const payout = Math.floor(pool / participants.length);
|
||||||
|
participants.forEach(member => {
|
||||||
|
message += `- **${member.name}**: $${payout.toLocaleString()}\n`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonParticipants.length > 0) {
|
||||||
|
message += `\n## Non-Participants\n`;
|
||||||
|
message += nonParticipants.map(m => m.name).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discord message limit is 2000 chars. If we have many members, it might split.
|
||||||
|
// For now, assuming it fits or valid first chunk.
|
||||||
|
if (message.length > 2000) {
|
||||||
|
const chunks = message.match(/[\s\S]{1,1900}/g) || [];
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
if (chunk === chunks[0]) await interaction.editReply(chunk);
|
||||||
|
else await interaction.followUp(chunk);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await interaction.editReply(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.editReply('An error occurred while calculating payouts.');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user