Greasy Fork

Greasy Fork is available in English.

AttachHowOldtoUserinPosts

Show how old a user is in posts

当前为 2025-02-07 提交的版本,查看 最新版本

// ==UserScript==
// @name         AttachHowOldtoUserinPosts
// @namespace    https://jirehlov.com
// @version      1.16
// @description  Show how old a user is in posts
// @author       Jirehlov
// @match        https://bgm.tv/*
// @match        https://chii.in/*
// @match        https://bangumi.tv/*
// @grant        none
// @license      MIT
// ==/UserScript==
(function () {
	const delay = 4000;
	const ageColors = [
		{
			threshold: 2,
			color: "#FFC966"
		},
		{
			threshold: 5,
			color: "#FFA500"
		},
		{
			threshold: 10,
			color: "#F09199"
		},
		{
			threshold: Infinity,
			color: "#FF0000"
		}
	];
	let requestCount = 0;
	let usersToFetch = [];
	const sortedUserIds = Object.keys(localStorage).filter(key => key.startsWith("userAge_")).map(key => key.replace("userAge_", "")).filter(value => Number.isInteger(parseInt(value, 10))).map(value => parseInt(value, 10)).sort((a, b) => a - b);
	function calculateAge(birthDate) {
		const today = new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), new Date().getUTCDate()));
		const dob = new Date(birthDate);
		let age = today.getUTCFullYear() - dob.getUTCFullYear();
		const monthDiff = today.getUTCMonth() - dob.getUTCMonth();
		if (monthDiff < 0 || monthDiff === 0 && today.getUTCDate() < dob.getUTCDate()) {
			age--;
		}
		return age;
	}
	function findClosestMatchingDates(userId) {
		let lower = -1, upper = -1;
		for (let i = 0; i < sortedUserIds.length; i++) {
			if (sortedUserIds[i] < userId) {
				lower = sortedUserIds[i];
			}
			if (sortedUserIds[i] > userId) {
				upper = sortedUserIds[i];
				break;
			}
		}
		if (lower !== -1 && upper !== -1) {
			let lowerDate = localStorage.getItem("userAge_" + lower);
			let upperDate = localStorage.getItem("userAge_" + upper);
			if (lowerDate === upperDate) {
				return lowerDate;
			}
		}
		return null;
	}
	function fetchAndStoreUserAge(userLink, delayTime) {
		setTimeout(() => {
			fetch(userLink, { credentials: "omit" }).then(response => response.text()).then(data => {
				const parser = new DOMParser();
				const doc = parser.parseFromString(data, "text/html");
                let registrationDate = doc.querySelector("ul.network_service li:first-child span.tip")?.textContent?.replace(/加入/g, "").trim();
				if (registrationDate) {
					const userId = userLink.split("/").pop();
					localStorage.setItem("userAge_" + userId, registrationDate);
					displayUserAge(userId, registrationDate);
				}
			}).catch(console.error);
		}, delayTime);
	}
	function displayUserAge(userId, registrationDate) {
		const userAnchor = $("strong a.l[href$='/user/" + userId + "']");
		if (userAnchor.length > 0 && userAnchor.next(".age-badge").length === 0) {
			const userAge = calculateAge(registrationDate);
			if (!isNaN(userAge)) {
				let badgeColor = ageColors.find(color => userAge <= color.threshold).color;
				const badge = `
                    <span class="age-badge" style="
                        background-color: ${ badgeColor };
                        font-size: 11px;
                        padding: 2px 5px;
                        color: #FFF;
                        border-radius: 100px;
                        line-height: 150%;
                        display: inline-block;
                    ">${ userAge }年</span>`;
				userAnchor.after($(badge));
			}
		}
	}
	function importUserAges() {
		const input = document.createElement("input");
		input.type = "file";
		input.accept = "application/json";
		input.onchange = function (event) {
			const file = event.target.files[0];
			if (file) {
				const reader = new FileReader();
				reader.onload = function (e) {
					try {
						const userAges = JSON.parse(e.target.result);
						Object.keys(userAges).forEach(key => localStorage.setItem(key, userAges[key]));
						alert("导入成功");
					} catch (error) {
						alert("无效的JSON文件\uFF1A", error);
					}
				};
				reader.readAsText(file);
			}
		};
		input.click();
	}
	function exportUserAges() {
		const userAges = {};
		Object.keys(localStorage).forEach(key => {
			if (key.startsWith("userAge_")) {
				userAges[key] = localStorage.getItem(key);
			}
		});
		const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
		const entryCount = Object.keys(userAges).length;
		const filename = `userAges_${ timestamp }_${ entryCount }entries.json`;
		const blob = new Blob([JSON.stringify(userAges, null, 2)], { type: "application/json" });
		const url = URL.createObjectURL(blob);
		const a = document.createElement("a");
		a.href = url;
		a.download = filename;
		a.click();
		URL.revokeObjectURL(url);
	}
	const badgeUserPanel = document.querySelector("ul#badgeUserPanel");
	if (badgeUserPanel) {
		const importButton = document.createElement("a");
		importButton.href = "#";
		importButton.textContent = "导入用户生日数据";
		importButton.onclick = event => {
			event.preventDefault();
			importUserAges();
		};
		const exportButton = document.createElement("a");
		exportButton.href = "#";
		exportButton.textContent = "导出用户生日数据";
		exportButton.onclick = event => {
			event.preventDefault();
			exportUserAges();
		};
		const listItem = document.createElement("li");
		listItem.appendChild(importButton);
		badgeUserPanel.appendChild(listItem);
		const exportListItem = document.createElement("li");
		exportListItem.appendChild(exportButton);
		badgeUserPanel.appendChild(exportListItem);
	}
	$("strong a.l:not(.avatar)").each(function () {
		const userLink = $(this).attr("href");
		const userId = userLink.split("/").pop();
        const avatarMatch = $(this).closest("div.inner").prev("a.avatar").find("span.avatarNeue").attr("style")?.match(/\/(\d+)\.jpg/);
		let realUserId = avatarMatch ? avatarMatch[1] : userId;
		let storedDate = localStorage.getItem("userAge_" + userId);
		if (!storedDate) {
			if (!isNaN(userId)) {
				storedDate = findClosestMatchingDates(userId);
				if (storedDate) {
					console.log(`${ userId } ${ storedDate }`);
				}
			} else if (!isNaN(realUserId)) {
				storedDate = findClosestMatchingDates(realUserId);
				if (storedDate) {
					console.log(`${ userId } ${ realUserId } ${ storedDate }`);
				}
			}
		}
		if (storedDate) {
			localStorage.setItem("userAge_" + userId, storedDate);
			displayUserAge(userId, storedDate);
		} else {
			usersToFetch.push(userLink);
		}
	});
	usersToFetch = [...new Set(usersToFetch)];
	console.log("Users to fetch:", usersToFetch);
	usersToFetch.forEach((userLink, index) => {
		fetchAndStoreUserAge(userLink, index * delay);
	});
	const jsonURL = "https://jirehlov.com/userages.json";
	function fetchUserAgesOnline() {
		const lastFetchTime = localStorage.getItem("lastFetchedUserAges");
		const now = Date.now();
		if (!lastFetchTime || now - parseInt(lastFetchTime, 10) > 7 * 24 * 60 * 60 * 1000) {
			fetch(jsonURL).then(response => response.json()).then(data => {
				Object.keys(data).forEach(key => {
					localStorage.setItem(key, data[key]);
				});
				localStorage.setItem("lastFetchedUserAges", now);
			}).catch(error => console.error("Failed to fetch user ages:", error));
		}
	}
	fetchUserAgesOnline();
}());