148 lines
5.9 KiB
JavaScript
148 lines
5.9 KiB
JavaScript
// makes a list of members last OC time, outputs to activity_visualization.md
|
|
|
|
|
|
const torn = require('../torn.js');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
(async () => {
|
|
try {
|
|
// Load stats
|
|
const statsPath = './data/ocStats.json';
|
|
let stats = {};
|
|
if (fs.existsSync(statsPath)) {
|
|
stats = JSON.parse(fs.readFileSync(statsPath, 'utf8'));
|
|
} else {
|
|
console.log("No ocStats.json found.");
|
|
}
|
|
|
|
// Fetch members
|
|
console.log("Fetching members...");
|
|
const members = await torn.faction.members();
|
|
|
|
// Fetch current crimes
|
|
console.log("Fetching current crimes...");
|
|
const activeCrimes = new Map(); // userId -> { crimeId, category, time }
|
|
const categories = ['recruiting', 'planned', 'active'];
|
|
|
|
const promises = categories.map(cat => torn.faction.crimes({ category: cat, limit: 100 }));
|
|
const results = await Promise.all(promises);
|
|
|
|
results.forEach((crimes, index) => {
|
|
const cat = categories[index];
|
|
if (crimes && Array.isArray(crimes)) {
|
|
crimes.forEach(c => {
|
|
const completed = ['Successful', 'Failure', 'Canceled', 'Expired', 'Timeout'];
|
|
// We catch everything but flag status for visualization
|
|
// But if we want to mimic inactive.js strict active check:
|
|
// Only treat as 'current' if NOT completed.
|
|
|
|
// Actually, for visualization, let's keep the record but mark it differently?
|
|
// The user wants to see "Stage".
|
|
// If it is completed, it should be treated as "Historic" essentially, logic-wise for "Active" label.
|
|
|
|
if (c.slots && Array.isArray(c.slots)) {
|
|
c.slots.forEach(s => {
|
|
if (s.user && s.user.id) {
|
|
const newStatus = c.status;
|
|
const newIsCompleted = completed.includes(newStatus);
|
|
|
|
const existing = activeCrimes.get(s.user.id);
|
|
if (existing && !existing.isCompleted && newIsCompleted) {
|
|
// Existing is active, new is completed. Do NOT overwrite.
|
|
return;
|
|
}
|
|
|
|
activeCrimes.set(s.user.id, {
|
|
crimeId: c.id,
|
|
category: cat,
|
|
status: newStatus,
|
|
started: c.time_started || c.initiated_at || c.created_at,
|
|
isCompleted: newIsCompleted
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
let output = "# Activity Visualization\n\n";
|
|
output += "| Name | Stage | Last Time | Details |\n";
|
|
output += "|---|---|---|---|\n";
|
|
|
|
// Calculate latestTime for everyone first to allow sorting
|
|
const memberData = members.map(m => {
|
|
const stat = stats[m.id];
|
|
const current = activeCrimes.get(m.id);
|
|
|
|
const currentStart = current ? current.started * 1000 : 0;
|
|
const lastSeen = stat ? stat.lastSeen : 0;
|
|
const latestTime = Math.max(currentStart, lastSeen);
|
|
|
|
return { m, stat, current, latestTime };
|
|
});
|
|
|
|
// Sort: Longest time ago (smallest timestamp) first
|
|
memberData.sort((a, b) => {
|
|
if (a.latestTime === 0 && b.latestTime === 0) return 0;
|
|
if (a.latestTime === 0) return -1; // Keep members with no activity at the top (or bottom, depending on desired order)
|
|
if (b.latestTime === 0) return 1;
|
|
return a.latestTime - b.latestTime;
|
|
});
|
|
|
|
memberData.forEach(({ m, stat, current, latestTime }) => {
|
|
|
|
let stage = "Unknown";
|
|
let timeStr = "Never";
|
|
let details;
|
|
|
|
const isActuallyActive = current && !current.isCompleted;
|
|
|
|
// Helper to linkify ID
|
|
const linkify = (id) => `[${id}](https://www.torn.com/factions.php?step=your&type=1#/tab=crimes&crimeId=${id})`;
|
|
|
|
// Determine Stage and Details string
|
|
if (isActuallyActive) {
|
|
stage = `**${current.status}**`;
|
|
details = `In: ${linkify(current.crimeId)}`;
|
|
} else if (current && current.isCompleted) {
|
|
// It was found in API but is completed
|
|
stage = `${current.status}`;
|
|
details = `Done: ${linkify(current.crimeId)}`;
|
|
} else if (stat) {
|
|
// Historic
|
|
stage = "Historic";
|
|
const diff = Date.now() - stat.lastSeen;
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
if (days < 3) stage = "Recent";
|
|
else if (days > 7) stage = "Inactive";
|
|
|
|
details = `Last: ${stat.lastCrimeId ? linkify(stat.lastCrimeId) : '?'}`;
|
|
} else {
|
|
stage = "No Data";
|
|
}
|
|
|
|
if (latestTime > 0) {
|
|
const date = new Date(latestTime);
|
|
timeStr = date.toLocaleString();
|
|
|
|
// Add relative time
|
|
const diff = Date.now() - latestTime;
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
if (days === 0) timeStr += " (Today)";
|
|
else if (days === 1) timeStr += " (Yesterday)";
|
|
else timeStr += ` (${days} days ago)`;
|
|
}
|
|
|
|
output += `| ${m.name} | ${stage} | ${timeStr} | ${details} |\n`;
|
|
});
|
|
|
|
fs.writeFileSync('activity_visualization.md', output, 'utf8');
|
|
console.log("Written output to activity_visualization.md");
|
|
|
|
} catch (e) {
|
|
console.error("Error:", e);
|
|
}
|
|
})();
|