您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
See people who unfollowed you. Also shows when people change their site name.
- // ==UserScript==
- // @name See unfollows [neocities.org]
- // @namespace http://tampermonkey.net/
- // @version 1.8.3
- // @description See people who unfollowed you. Also shows when people change their site name.
- // @author https://neocities.org/site/dimden
- // @match https://neocities.org/
- // @match https://neocities.org/?page=*
- // @match https://neocities.org/site/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=neocities.org
- // @grant none
- // @run-at document-end
- // @license MIT
- // ==/UserScript==
- (async () => {
- let mySite = document.querySelector(".dropdown-menu > li > a[href^='/site/']").href.split('neocities.org/site/')[1];
- if(location.pathname.startsWith('/site/')) {
- let currentLocation = location.pathname.slice(6).split("?")[0].split("#")[0];
- if(currentLocation !== mySite) return;
- }
- function getDateFromTimeAgo(timeString) {
- const timeAgo = { hours: 0, minutes: 0, seconds: 0, days: 0, weeks: 0, months: 0, years: 0 };
- const secondsAgoMatches = timeString.match(/^(\d+) (seconds? ago)/);
- const minutesAgoMatches = timeString.match(/^(\d+) (minutes? ago)/);
- const hoursAgoMatches = timeString.match(/^(\d+) (hours? ago)/);
- const daysAgoMatches = timeString.match(/^(\d+) (days? ago)/);
- const weeksAgoMatches = timeString.match(/^(\d+) (weeks? ago)/);
- const monthsAgoMatches = timeString.match(/^(\d+) (months? ago)/);
- const yearsAgoMatches = timeString.match(/^(\d+) (years? ago)/);
- if (secondsAgoMatches) {
- timeAgo.seconds = secondsAgoMatches[1];
- }
- if (minutesAgoMatches) {
- timeAgo.minutes = minutesAgoMatches[1];
- }
- if (hoursAgoMatches) {
- timeAgo.hours = hoursAgoMatches[1];
- }
- if (daysAgoMatches) {
- timeAgo.days = daysAgoMatches[1];
- }
- if (weeksAgoMatches) {
- timeAgo.weeks = weeksAgoMatches[1];
- }
- if (monthsAgoMatches) {
- timeAgo.months = monthsAgoMatches[1];
- }
- if (yearsAgoMatches) {
- timeAgo.years = yearsAgoMatches[1];
- }
- let d = Date.now();
- d -= timeAgo.seconds * 1000;
- d -= timeAgo.minutes * 60 * 1000;
- d -= timeAgo.hours * 60 * 60 * 1000;
- d -= timeAgo.days * 24 * 60 * 60 * 1000;
- d -= timeAgo.weeks * 7 * 24 * 60 * 60 * 1000;
- d -= timeAgo.months * 30 * 24 * 60 * 60 * 1000;
- d -= timeAgo.years * 365 * 24 * 60 * 60 * 1000;
- return d;
- }
- function findBetween(date) {
- let news = Array.from(document.getElementsByClassName('news-item')).map(e => ({ el: e, time: getDateFromTimeAgo(e.getElementsByClassName('date')?.[0]?.innerText ?? '1 second ago') }));
- for (let i = 0; i < news.length; i++) {
- if (news[i].time < date && Math.abs(news[i].time-date) < 8.82e+7) {
- return news[i].el;
- }
- }
- }
- const relativeTimePeriods = [
- [31536000, 'year'],
- [2419200, 'month'],
- [604800, 'week'],
- [86400, 'day'],
- [3600, 'hour'],
- [60, 'minute'],
- [1, 'second']
- ];
- function relativeTime(date) {
- if (!(date instanceof Date)) date = new Date(date * 1000);
- const seconds = (new Date() - date) / 1000;
- for (let [secondsPer, name] of relativeTimePeriods) {
- if (seconds >= secondsPer) {
- const amount = Math.floor(seconds / secondsPer);
- return `${amount} ${name}${amount && amount !== 1 ? 's' : ''} ago`;
- }
- }
- return 'just now';
- }
- async function getCurrentFollowers() {
- const followerPage = await fetch(`https://neocities.org/site/${mySite}/followers`).then(res => res.text());
- try {
- const parser = new DOMParser();
- const doc = parser.parseFromString(followerPage, "text/html");
- return Array.from(doc.querySelectorAll('.username > a')).map(u => u.innerText.replace(/\n/g, '').trim()).filter(u => !u.includes('/'));
- } catch(e) {
- console.error('error getting followers', e);
- console.log(followerPage);
- }
- return [];
- }
- let currentFollowers = await getCurrentFollowers();
- if(currentFollowers.length === 0) return;
- let previousFollowers = localStorage.followings ? JSON.parse(localStorage.followings) : [];
- let newUnfollows = previousFollowers.filter(e => !currentFollowers.includes(e));
- let unfollows = localStorage.unfollows ? JSON.parse(localStorage.unfollows) : [];
- let renamed;
- let notifCount = document.getElementsByClassName("notification-value")[0];
- if(newUnfollows.length === 1 && currentFollowers.length === previousFollowers.length && !notifCount) {
- renamed = currentFollowers.find(f => !previousFollowers.includes(f));
- }
- for (let i in newUnfollows) {
- unfollows.push([newUnfollows[i], Date.now(), !(await fetch(`https://neocities.org/site/${newUnfollows[i]}`, {redirect: 'manual'})).ok, renamed]);
- }
- unfollows = unfollows.filter(u => !currentFollowers.includes(u[0]));
- localStorage.unfollows = JSON.stringify(unfollows);
- localStorage.followings = JSON.stringify(currentFollowers);
- unfollows = unfollows.reverse();
- for (let i in unfollows) {
- let [nick, date, disabledProfile, renamed] = unfollows[i];
- let el = findBetween(date);
- if (el) {
- let unfollowElement = document.createElement('div');
- unfollowElement.className = 'news-item unfollow';
- unfollowElement.innerHTML = /*html*/`
- <div class="title">
- <div class="icon"><a href="/site/${nick}" title="${nick}" class="avatar" style="background-image: url(/site_screenshots/21/90/${nick}/index.html.50x50.jpg);"></a></div>
- <div class="text">
- <a href="/site/${nick}" class="user">${nick}</a> ${renamed ? `renamed to <a href="/site/${renamed}" class="user">${renamed}</a>` : disabledProfile ? 'disabled profile.' : 'unfollowed you!'}
- </div>
- <a class="date" style="color:#aaa" href="https://greasyfork.org/en/scripts/450226-see-unfollows-neocities-org" target="_blank">detected ${relativeTime(new Date(date))}</a>
- </div>`;
- el.before(unfollowElement);
- }
- }
- })();