SemenChecker

Checks posts on unique ips.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         SemenChecker
// @namespace    http://tampermonkey.net/
// @version      1.3.1
// @description  Checks posts on unique ips.
// @author       You
// @match        https://2ch.hk/*/*/*.html
// @icon         https://www.google.com/s2/favicons?sz=64&domain=2ch.hk
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @license MIT
// ==/UserScript==

const MAX_ALLOWED_STORE_TIME_IN_DAYS = 2; // Через сколько дней (с последнего посещения определенного треда на момент активации скрипта) удалится информация о треде.
const HIGHLIGHT_COLOR = 'lightgreen'; // Цвет подсветки нового поста.

const url = document.baseURI.replace('.html', '.json');
const current_thread = document.baseURI.split('#')[0].split('/').slice(3).join('/');

let last_modified;
let initial_poster_count

let unhighlighted_ids = [];
let highlighted_ids = [];

(async () => {
    const initial_response = await fetch(url);
    const initial_json = await initial_response.json();
    last_modified = initial_response.headers.get('Last-Modified');
    initial_poster_count = +initial_json.unique_posters;
    main();
})();


async function parse_json(url) {
    const response = await fetch(url, {
        headers: {
            'If-Modified-Since': last_modified
        }
    });
    if (!response.ok) {
        return false;
    }
    last_modified = response.headers.get('Last-Modified')
    return await response.json();
}

function get_json(url) {
    parse_json(url).then(json => {
        if (!json) {return;}
        const last_post = +json.posts_count;
        unique_checker(
            {
                related_poster_count: +json.unique_posters,
                last_post_id: +json.threads[0].posts[last_post].num
            }
        );
    });
}

function get_int_daytime() {
    return new Date().getTime() / 1000 / 3600 / 24;
}

function unique_checker(poster) {
    if (poster.related_poster_count > initial_poster_count) {
        initial_poster_count = poster.related_poster_count;
        const last_element_id = 'post-' + poster.last_post_id;
        unhighlighted_ids.push(last_element_id);
    }
    //console.log('initial poster count = ' + initial_poster_count);
    //console.log('unhighlighted ids = ' + unhighlighted_ids.length);
    //console.log('GM:');
    //console.log(GM_getValue(current_thread));
}

function inner_highlighter() {
    const uids = [...unhighlighted_ids];
    if (!uids.length) {return;}
    for (let i = 0; i < uids.length; i++) {
        const unhighlighted_post = document.getElementById(unhighlighted_ids[0]);
        if (unhighlighted_post) {
            highlighted_ids.push(unhighlighted_ids.shift());
            highlight_element(unhighlighted_post);
        }
    }
    GM_setValue(current_thread, {
        ids: highlighted_ids,
        date: get_int_daytime()
    });
}

function outer_highlighter(mutationsList) {
    for (let mutation of mutationsList) {
        [...mutation.target.children].slice(-1).forEach((post) => {
            if (highlighted_ids.includes(post.id.replace('-body', ''))) {
                highlight_element(post);
            }
        });
    }
}

function highlight_element(post) {
    let identification_element = document.createElement('span');
    identification_element.style.color = HIGHLIGHT_COLOR;
    identification_element.innerHTML = '[NEW IP]';
    identification_element.className = "new_ip";
    let insert_area = post.getElementsByClassName("post__details")[0];
    if (insert_area.getElementsByClassName(identification_element.className)[0] === undefined) {
        insert_area.appendChild(identification_element);
    }
}

function on_load() {
    const stored_data = GM_getValue(current_thread);
    if (!stored_data) {return;}
    stored_data.ids.forEach((item) => {
        let element = document.getElementById(item);
        if (element) {
            highlight_element(element);
            highlighted_ids.push(item);
        }
    });
    GM_setValue(current_thread, {
        ids: highlighted_ids,
        date: get_int_daytime()
    });
}

function main() {
    'use strict';

    on_load();
    //console.log(GM_listValues());
    GM_listValues().forEach((item) => {
        const stored_data = GM_getValue(item);
        //console.log(stored_data);
        if (Math.abs(get_int_daytime() - stored_data.date) >= MAX_ALLOWED_STORE_TIME_IN_DAYS) {
            GM_deleteValue(item);
        }
    });

    const inner_post_area = document.getElementsByClassName('thread')[0];
    const inner_observer = new MutationObserver(inner_highlighter);
    inner_observer.observe(inner_post_area, {childList: true});

    const outer_post_area = inner_post_area.parentElement;
    const outer_observer = new MutationObserver(outer_highlighter);
    outer_observer.observe(outer_post_area, {childList: true});

    setInterval(() => {get_json(url)}, 500);
    document.getElementsByClassName('de-thr-updater')[0].addEventListener("click", inner_highlighter)
}