H::H -spam Fork

    @@ -6,10 +6,20 @@ * @javascript_url */ - // nitpicking - // via + // nitpicking via // http://let.hatelabo.jp/austinburk/let/hLHU6PPgg9Ya + // TODO + // user.js on Gist + + // non i18n similar services/software by id:tukihatu c.f. + // Hatena Haiku a la mode (3rd party web services) + // http://hh-alamode.fanweb.jp/ + // http://h.hatena.ne.jp/target?word=%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%8F%E3%82%A4%E3%82%AF%E3%82%A2%E3%83%A9%E3%83%A2%E3%83%BC%E3%83%89 + // Hatena Haiku Soldier (Chrome/ium Extensions) + // https://chrome.google.com/webstore/detail/hhgejafgjjjfaocaopajkhgmdgaeimin + // http://h.hatena.ne.jp/target?word=%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%8F%E3%82%A4%E3%82%AF%E3%82%BD%E3%83%AB%E3%82%B8%E3%83%A3%E3%83%BC + // e.g. // http://h.hatena.ne.jp (() => {
    @@ -20,7 +30,8 @@ // TODO // - async use w/ await // - Promise.all/.race for timeout - // - + // TBD + // - refresh filter when infinite scrolling let blacklist; const syncBlockingFetch = () => { const xhr = new XMLHttpRequest();
    @@ -34,25 +45,16 @@ throw Error(evt.type, xhr.status, xhr.readyState, evt); }; - xhr.addEventListener('load', (evt) => { + xhr.addEventListener('load', evt => { if (xhr.status !== 200) { cancel(evt); } const THRESHOLD = 5; // TBD omit JSON.parse - /*/ - blacklist = Array.prototype.concat(... - Object.entries(JSON.parse(xhr.response)) - .filter(([, score]) => score >= THRESHOLD) - ); - // .includes('HATENA_ID') - /*/ blacklist = new Map(Object.entries(JSON.parse(xhr.response)) .filter(([, score]) => score >= THRESHOLD) ); - // .has('HATENA_ID') - //*/ }); [ 'abort',
    @@ -67,7 +69,7 @@ // Access-Control-Allow-Origin: http://h.hatena.ne.jp const FILTER_URL = 'https://haikuantispam.lightni.ng/api/recent_scores.json'; // XXX synchronize - xhr.open('GET',FILTER_URL, false); + xhr.open('GET', FILTER_URL, false); xhr.send(); }; syncBlockingFetch();
    @@ -83,13 +85,16 @@ const id = poster.title.slice('id:'.length); if (blacklist.has(id)) { - //*/ - entry.style.backgroundColor = 'red'; - //entry.innerHTML = ` - //<p><a href="https://haikuantispam.lightni.ng/id/${id}" title="spam-detail"> - //https://haikuantispam.lightni.ng/id/${id}</a></p>`; /*/ - //encty.display = 'none'; + //entry.style.backgroundColor = 'red'; + entry.innerHTML = ` + <p><a href="https://haikuantispam.lightni.ng/id/${id}" + title="spam-detail" + target="_blank"> + &lt;censored&gt; + </a></p>`; + /*/ + entry.style.display = 'none'; //*/ } });
  • /*
     * @title H::H lightni.ng filter
     * @description Hatena Haiku spam filtering w/ lightni.ng
     * @include *://h.hatena.ne.jp/*
     * @license  The MIT License https://opensource.org/licenses/MIT
     * @javascript_url
     */
    
    // nitpicking via
    // http://let.hatelabo.jp/austinburk/let/hLHU6PPgg9Ya
    
    // TODO
    // user.js on Gist
    
    // non i18n similar services/software by id:tukihatu c.f.
    // Hatena Haiku a la mode (3rd party web services)
    //  http://hh-alamode.fanweb.jp/
    //   http://h.hatena.ne.jp/target?word=%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%8F%E3%82%A4%E3%82%AF%E3%82%A2%E3%83%A9%E3%83%A2%E3%83%BC%E3%83%89
    // Hatena Haiku Soldier (Chrome/ium Extensions)
    //  https://chrome.google.com/webstore/detail/hhgejafgjjjfaocaopajkhgmdgaeimin
    //   http://h.hatena.ne.jp/target?word=%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%8F%E3%82%A4%E3%82%AF%E3%82%BD%E3%83%AB%E3%82%B8%E3%83%A3%E3%83%BC
    
    // e.g.
    // http://h.hatena.ne.jp
    (() => {
        'use strict';
    
        if (window.self !== window.top) return; // @noframes in Vanilla
    
        // TODO
        // - async use w/ await
        //  - Promise.all/.race for timeout
        // TBD
        // - refresh filter when infinite scrolling
        let blacklist;
        const syncBlockingFetch = () => {
            const xhr = new XMLHttpRequest();
            /* XXX asynchronous only
            xhr.timeout = 3000;
            xhr.responseType = 'json';
            */
    
            // TODO unreachable?
            const cancel = (evt) => {
                throw Error(evt.type, xhr.status, xhr.readyState, evt);
            };
    
            xhr.addEventListener('load', evt => {
                if (xhr.status !== 200) {
                    cancel(evt);
                }
    
                const THRESHOLD = 5;
                // TBD omit JSON.parse
                blacklist = new Map(Object.entries(JSON.parse(xhr.response))
                    .filter(([, score]) => score >= THRESHOLD)
                );
            });
            [
                'abort',
                'timeout',
                'error',
            ].forEach(type => {
                xhr.addEventListener(type, evt => {
                    cancel(evt);
                });
            });
    
            // Access-Control-Allow-Origin: http://h.hatena.ne.jp
            const FILTER_URL = 'https://haikuantispam.lightni.ng/api/recent_scores.json';
            // XXX synchronize
            xhr.open('GET', FILTER_URL, false);
            xhr.send();
        };
        syncBlockingFetch();
    
        const wipeout = (ctx) => {
            if (!ctx.querySelectorAll) return;
    
            ctx.querySelectorAll([
                '.entry.tl-entry'
            ]).forEach(entry => {
                const poster = entry.querySelector('.username a[href][title]');
                if (!poster) return;
    
                const id = poster.title.slice('id:'.length);
                if (blacklist.has(id)) {
                    /*/
                    //entry.style.backgroundColor = 'red';
                    entry.innerHTML = `
                    <p><a href="https://haikuantispam.lightni.ng/id/${id}"
                    title="spam-detail"
                    target="_blank">
                    &lt;censored&gt;
                    </a></p>`;
                    /*/
                    entry.style.display = 'none';
                    //*/
                }
            });
        };
        const timeline = document.body.querySelector('.entries');
        if (!timeline) return;
    
        wipeout(timeline);
    
        new MutationObserver(records => {
            records.forEach(record => {
                wipeout(record.target);
            });
        }).observe(timeline, { childList: true, subtree: true, });
    })();
    
    
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。