Log points daily, export only when value changes, show entries in small popup window with minimize/expand toggle
当前为
// ==UserScript==
// @name Makerworld Points Auto Logger + Viewer (Smart Panel + Toggle)
// @namespace http://tampermonkey.net/
// @version 3.1
// @description Log points daily, export only when value changes, show entries in small popup window with minimize/expand toggle
// @match https://makerworld.com/*
// @grant GM_download
// @license MIT
// ==/UserScript==
(function() {
'use strict';
function logPoints() {
let el = document.querySelector('.mw-css-yyek0l');
if (!el) {
console.log("Element not found");
return;
}
let points = el.textContent.trim().replace(/,/g, "");
let now = new Date();
let date = now.toISOString().split('T')[0];
let time = now.toTimeString().split(' ')[0];
let logs = JSON.parse(localStorage.getItem("pointsLog") || "[]");
let existingIndex = logs.findIndex(entry => entry.date === date);
let shouldExport = true;
if (existingIndex !== -1) {
if (logs[existingIndex].points === points) {
shouldExport = false;
}
logs[existingIndex] = {date: date, time: time, points: points};
} else {
logs.push({date: date, time: time, points: points});
}
localStorage.setItem("pointsLog", JSON.stringify(logs));
console.log("Points logged:", {date, time, points});
if (shouldExport) {
exportFullLog(date, time, logs);
}
renderPointsLog(logs, date, time);
}
function exportFullLog(date, time, logs) {
if (logs.length === 0) return;
let header = "DATE,TIME,POINTS\n";
let lines = logs.map(entry => `${entry.date},${entry.time},${entry.points}`).join("\n");
let csvContent = header + lines + "\n";
let filename = `MWPOINT-${date}-${time}.csv`;
let blob = new Blob([csvContent], {type: "text/csv"});
let url = URL.createObjectURL(blob);
GM_download({ url: url, name: filename });
}
function renderPointsLog(logs, date, time) {
let oldPanel = document.getElementById("pointsLogPanel");
if (oldPanel) oldPanel.remove();
let container = document.createElement("div");
container.id = "pointsLogPanel";
container.style.position = "fixed";
container.style.bottom = "10px";
container.style.right = "10px";
container.style.background = "darkblue";
container.style.color = "yellow";
container.style.border = "2px solid yellow";
container.style.padding = "10px";
container.style.zIndex = 9999;
container.style.fontSize = "12px";
container.style.fontFamily = "monospace";
// Header with minimize toggle
let header = document.createElement("div");
header.style.display = "flex";
header.style.justifyContent = "space-between";
header.style.alignItems = "center";
header.style.marginBottom = "5px";
let title = document.createElement("span");
title.textContent = "Points Log";
title.style.fontWeight = "bold";
header.appendChild(title);
let toggleBtn = document.createElement("button");
toggleBtn.textContent = "Minimize";
toggleBtn.style.background = "yellow";
toggleBtn.style.color = "darkblue";
toggleBtn.style.fontWeight = "bold";
toggleBtn.style.marginLeft = "10px";
toggleBtn.onclick = function() {
let tableWrapper = document.getElementById("pointsLogTableWrapper");
let stats = document.getElementById("pointsStats");
let controls = document.getElementById("pointsControls");
if (tableWrapper.style.display === "none") {
tableWrapper.style.display = "block";
if (stats) stats.style.display = "block";
if (controls) controls.style.display = "block";
toggleBtn.textContent = "Minimize";
} else {
tableWrapper.style.display = "none";
if (stats) stats.style.display = "none";
if (controls) controls.style.display = "none";
toggleBtn.textContent = "Expand";
}
};
header.appendChild(toggleBtn);
container.appendChild(header);
// Control buttons
let controls = document.createElement("div");
controls.id = "pointsControls";
controls.style.marginBottom = "5px";
let clearBtn = document.createElement("button");
clearBtn.textContent = "Clear Log";
clearBtn.style.background = "yellow";
clearBtn.style.color = "darkblue";
clearBtn.style.fontWeight = "bold";
clearBtn.style.marginRight = "5px";
clearBtn.onclick = function() {
if (confirm("Are you sure you want to clear the points log?")) {
localStorage.removeItem("pointsLog");
renderPointsLog([], date, time);
console.log("Points log cleared.");
}
};
controls.appendChild(clearBtn);
let exportBtn = document.createElement("button");
exportBtn.textContent = "Export Log";
exportBtn.style.background = "yellow";
exportBtn.style.color = "darkblue";
exportBtn.style.fontWeight = "bold";
exportBtn.onclick = function() {
let logs = JSON.parse(localStorage.getItem("pointsLog") || "[]");
exportFullLog(date, time, logs);
};
controls.appendChild(exportBtn);
container.appendChild(controls);
// Table wrapper (scrollable)
let tableWrapper = document.createElement("div");
tableWrapper.id = "pointsLogTableWrapper";
tableWrapper.style.maxHeight = "120px"; // enough for ~5 rows
tableWrapper.style.overflowY = "auto";
tableWrapper.style.border = "1px solid yellow";
let table = document.createElement("table");
table.id = "pointsLogTable";
table.style.borderCollapse = "collapse";
table.style.width = "100%";
let headerRow = document.createElement("tr");
["DATE", "TIME", "POINTS", "GAIN"].forEach(h => {
let th = document.createElement("th");
th.textContent = h;
th.style.border = "1px solid yellow";
th.style.padding = "2px 5px";
th.style.background = "navy";
th.style.color = "yellow";
headerRow.appendChild(th);
});
table.appendChild(headerRow);
let lastLogs = logs.slice().reverse();
lastLogs.forEach((entry, idx) => {
let row = document.createElement("tr");
// Calculate daily gain (difference from previous entry)
let gain = "";
if (idx < lastLogs.length - 1) {
let prevPoints = parseFloat(lastLogs[idx + 1].points);
let currPoints = parseFloat(entry.points);
gain = (currPoints - prevPoints >= 0) ? (currPoints - prevPoints) : "";
}
[entry.date, entry.time, entry.points, gain].forEach(val => {
let td = document.createElement("td");
td.textContent = val;
td.style.border = "1px solid yellow";
td.style.padding = "2px 5px";
td.style.color = "yellow";
row.appendChild(td);
});
table.appendChild(row);
});
tableWrapper.appendChild(table);
container.appendChild(tableWrapper);
// Daily average + monthly projection
if (logs.length > 1) {
let diffs = [];
for (let i = 1; i < logs.length; i++) {
let gain = parseFloat(logs[i].points) - parseFloat(logs[i-1].points);
if (gain >= 0) diffs.push(gain);
}
let avg = diffs.length ? (diffs.reduce((a,b)=>a+b,0) / diffs.length) : 0;
let monthly = (avg * 365) / 12;
let stats = document.createElement("div");
stats.id = "pointsStats";
stats.style.marginTop = "5px";
stats.style.fontWeight = "bold";
stats.textContent = `Daily Avg: ${avg.toFixed(2)} | Monthly Projection: ${monthly.toFixed(2)}`;
container.appendChild(stats);
}
document.body.appendChild(container);
}
window.addEventListener('load', logPoints);
})();