// ==UserScript==
// @name Shiki Friend's History
// @author Chortowod (https://openuserjs.org/users/Chortowod)
// @description Добавляет историю просмотренного друзей на страницу "Друзья", а также строку "Друзья" в выпадающий профиль в правом верхнем углу
// @namespace http://shikimori.me/
// @version 1.2.6
// @match *://shikimori.org/*
// @match *://shikimori.one/*
// @match *://shikimori.me/*
// @icon https://www.google.com/s2/favicons?domain=shikimori.me
// @license MIT
// @require https://gist.githubusercontent.com/arantius/3123124/raw/grant-none-shim.js
// @updateURL https://openuserjs.org/meta/Chortowod/Shiki_Friends_History.meta.js
// @downloadURL https://openuserjs.org/install/Chortowod/Shiki_Friends_History.user.js
// @require https://gist.githubusercontent.com/Chortowod/814b010c68fc97e5f900df47bf79059c/raw/chtw_settings.js?v1
// @copyright 2023, Chortowod (https://openuserjs.org/users/Chortowod)
// ==/UserScript==
let settings = new ChtwSettings('chtwFriendsHistory', '<a target="_blank" href="https://openuserjs.org/scripts/Chortowod/Shiki_Friends_History">История друзей</a>');
let friendsCustomIDs;
let style = 'summary { cursor: pointer } table td { padding: 0px 10px 0 0; }';
let redirectToFriends = document.getElementsByClassName('submenu-triangle')[1].getAttribute("href") + "/friends";
let domain = document.location.origin;
function initSettings() {
settings.createOption('isEng', 'Названия на английском', false);
settings.createOption('textColor', 'Цвет текста первых двух столбцов', '#f2f8ff', 'color');
settings.createOption('backColor', 'Цвет задника таблицы', 'black', 'color');
settings.createOption('linkColor', 'Цвет ссылок на аниме', '#34c34d', 'color');
settings.createOption('timeShort', 'Только дата (без времени)', false);
settings.createOption('entLimit', 'Сколько записей будет браться у каждого пользователя', 50, 'number');
settings.createOption('daysLimit', 'За какой период будет браться информация (в днях)', 30, 'number');
settings.createOption('friendsLimit', 'Сколько друзей брать в расчет', 10, 'number');
settings.createOption('friendsCP', 'Вкл./выкл. возможность вписать конкретные профили для отслеживания', false);
settings.createOption('friendsIDs', 'Список конкретных профилей (через запятую без пробелов)', '', 'text', true);
friendsCustomIDs = (settings.getOption('friendsCP') && settings.getOption('friendsIDs')) ? settings.getOption('friendsIDs').split(',') : [];
}
function addFriends() {
if (document.getElementById('ctwFriends')) return;
let mainAppend = document.querySelector('.menu-dropdown.profile > .submenu');
if (!mainAppend) return;
let whatAppend = document.getElementsByClassName('icon-users')[0].cloneNode(true);
whatAppend.href = redirectToFriends;
whatAppend.id = 'ctwFriends';
whatAppend.title = "Друзья";
whatAppend.textContent = "Друзья";
mainAppend.insertBefore(whatAppend, mainAppend.childNodes[5]);
}
function addFriendHistory() {
if (!location.href.includes("/friends")) return;
if (document.getElementById('ctwFriendsHistory')) return;
let allEntries = [];
let friendsID = [];
let timeOptions = settings.getOption('timeShort') ? {year: 'numeric', month: 'long', day: 'numeric'} : {year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'};
let timeSeparate = {hour: 'numeric', minute: 'numeric'};
if (settings.getOption('friendsCP'))
friendsID = friendsCustomIDs;
else
$(".b-user.c-column.named_avatar").each(function() { friendsID.push(this.textContent); } );
let friendsCounter = 0;
let mainAppend = document.querySelector('.l-page > div');
let whatAppend = document.createElement('details');
whatAppend.classList.add('block', 'is-own-profile');
let whatAppendSummary = document.createElement('summary');
whatAppendSummary.innerText = 'История просмотра друзей';
whatAppendSummary.classList.add('subheadline');
GM_addStyle(style);
let whatAppendDiv = document.createElement('div');
whatAppendDiv.classList.add('cc');
whatAppendDiv.innerText = 'Loading...';
whatAppend.append(whatAppendSummary);
whatAppend.append(whatAppendDiv);
whatAppend.id = 'ctwFriendsHistory';
mainAppend.append(whatAppend);
if (friendsID.length > settings.getOption('friendsLimit'))
friendsID = friendsID.slice(0, settings.getOption('friendsLimit'));
let colors = ['#ce00ff20', '#0035ff20', '#ff000020', '#9bff0020', '#ff8f0020', '#00c2ff20'];
friendsID.forEach(function (id, index) {
setTimeout(function(){
$.ajax({
url: domain+'/api/users/'+id+'/history?limit='+settings.getOption('entLimit'),
success: function(data) {
if (data.length === 0) {
console.log('Что-то пошло не так.');
}
else {
let color = colors.pop();
data.forEach(function (item) {
let currentDate = new Date();
let itemDate = new Date(item.created_at)
if (currentDate - itemDate < (settings.getOption('daysLimit') * 60 * 60 * 24 * 1000)) {
item.id = id;
item.color = color;
allEntries.push(item);
}
})
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
whatAppend.getElementsByClassName('cc')[0].innerText = 'Произошла ошибка при загрузке. Обновите страницу или посмотрите причину ошибки в консоли разработчика.';
console.log("Status: " + textStatus + " | Error: " + errorThrown);
},
complete: function() {
friendsCounter++;
}
});
}, (index+1)*250);
});
let countdownPing = 60;
let countdownShower = setInterval(function(){
if (friendsCounter === friendsID.length) {
clearInterval(countdownShower);
allEntries.sort(function (a, b) {
if (a.created_at > b.created_at) return 1;
if (a.created_at < b.created_at) return -1;
return 0;
});
allEntries.reverse();
whatAppend.getElementsByClassName('cc')[0].innerText = '';
let table = document.createElement('table');
allEntries.forEach(function (item) {
if (!item.target)
return;
let titleName = settings.getOption('isEng') ? item.target.name : (item.target.russian || item.target.name);
if (titleName.length > 80)
titleName = titleName.slice(0, 80)+'...';
let animeLink = "<a style='color: "+settings.getOption('linkColor')+";' href='"+domain+item.target.url+"'>"+titleName+"</a>"
let tr = document.createElement('tr');
let td1 = document.createElement('td');
let td2 = document.createElement('td');
let td3 = document.createElement('td');
let td4 = document.createElement('td');
let td5 = document.createElement('td');
tr.style.backgroundColor = item.color;
td1.innerHTML = item.id;
td2.innerHTML = new Date(item.created_at).toLocaleString("ru", timeOptions);
td2.title = new Date(item.created_at).toLocaleString("ru", timeSeparate);
td3.innerHTML = animeLink;
td4.innerHTML = item.description;
if (item.description.includes('Удалено') || item.description.includes('Брошено'))
td4.style.color = '#ff2a00';
else if (item.description.includes('Добавлено'))
td4.style.color = '#04f1ff';
else if (item.description.includes('эпизод') || item.description.includes('Смотрю') || item.description.includes('глав') || item.description.includes('Читаю'))
td4.style.color = '#1cd616';
else if (item.description.includes('цен') || item.description === 'Просмотрено' || item.description === 'Прочитано')
td4.style.color = '#b2ff00';
else if (item.description.includes('Пере'))
td4.style.color = '#ff9200';
td5.innerHTML = item.target.url.includes('/animes') ? 'Аниме' : (item.target.url.includes('/mangas') ? 'Манга' : 'Ранобе');
tr.append(td1);
tr.append(td2);
tr.append(td5);
tr.append(td3);
tr.append(td4);
table.append(tr);
});
table.style.background = settings.getOption('backColor');
table.style.color = settings.getOption('textColor');
table.style.width = '100%';
whatAppend.getElementsByClassName('cc')[0].append(table);
}
else if (countdownPing === 0) {
clearInterval(countdownShower);
whatAppend.getElementsByClassName('cc')[0].innerText = 'Произошла ошибка при загрузке. Обновите страницу или посмотрите причину ошибки в консоли разработчика.';
console.log("Количество друзей в профиле не совпало с полученным ответом по API.");
}
else
countdownPing--;
}, 250);
}
function ready(fn) {
document.addEventListener('page:load', fn);
document.addEventListener('turbolinks:load', fn);
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") fn();
else document.addEventListener('DOMContentLoaded', fn);
}
ready(initSettings);
ready(addFriends);
ready(addFriendHistory);
Donate for the site OpenUserJS
Are you sure you want to go to an external site to donate a monetary value?
WARNING: Some countries laws may supersede the payment processors policy such as the GDPR and PayPal. While it is highly appreciated to donate, please check with your countries privacy and identity laws regarding privacy of information first. Use at your utmost discretion.