AbemaTV Quick NG Comment

AbemaTVのコメントを一時的にNG。連投コメントをブロック

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        AbemaTV Quick NG Comment
// @namespace   https://greasyfork.org/ja/scripts/22040
// @description AbemaTVのコメントを一時的にNG。連投コメントをブロック
// @include     https://abema.tv/now-on-air/*
// @version     3
// @grant       GM_addStyle
// ==/UserScript==

(function() {
  'use strict';

  function id(a) {
    return document.getElementById(a);
  }

  function log(s, t) {
    if (ls.debug) {
      if (t) console[t]('QuickNG', s);
      else console.log('QuickNG', s);
    }
  }

  //デスクトップ通知
  function notify(a, t) {
    var title = 'AbemaTV Quick NG Comment',
      message = a,
      notifi;
    log(['notify', a, t]);
    if (t === 'ng') message += '\nをNGワード登録しました。';
    else if (t === 'duplicate') message += '\nはNGワード登録済みです。';
    else if (t === 'consecutive') message = '連投コメントを' + ((a) ? 'ブロック' : '表示') + 'します。';
    if ('Notification' in window) {
      if (Notification.permission === 'granted') {
        notifi = new Notification(title, { body: message });
      } else if (Notification.permission !== 'denied') {
        Notification.requestPermission(function(permission) {
          if (permission === 'granted') {
            notifi = new Notification(title, { body: message });
          }
        });
      }
      if (notifi) setTimeout(notifi.close.bind(notifi), 3000);
    } else alert('AbemaTV Quick NG Comment\n\n' + message);
  }

  //コメントをクリックしたとき
  function clickComment(a) {
    a.addEventListener('click', function(e) {
      //Ctrl+クリックでNGワード追加
      if (e.ctrlKey && !e.shiftKey) {
        var el = e.target;
        if (a === defaultListComment) {
          if (/^styles__message/.test(el.className)) addNgComment(el.textContent);
        } else {
          if (/comment|movingComment/.test(el.className) && !el.classList.contains('quickng')) addNgComment(el.textContent, el);
        }
      }
      //Shift+クリックで連投コメントブロックのON・OFF
      else if (!e.ctrlKey && e.shiftKey) {
        ls.blockconsecutivecomment = (ls.blockconsecutivecomment) ? false : true;
        localStorage.setItem('QuickNG', JSON.stringify(ls));
        notify(ls.blockconsecutivecomment, 'consecutive');
      }
    });
  }

  //NGワードを登録
  function addNgComment(s, e) {
    if (ss.ngcomment.some(function(val) {
        return (val === s);
      })) {
      notify(s, 'duplicate');
    } else {
      if (e) e.classList.add('quickng');
      ss.ngcomment.unshift(s);
      sessionStorage.setItem('QuickNG', JSON.stringify(ss));
      notify(s, 'ng');
    }
  }

  //新しいコメントを読み込んだときにコメントの内容を調べる
  function checkComment(r) {
    var word = '',
      checkPreWord = function(val, ind, arr) {
        if (val.word === word) {
          val.count++;
          if (ind > 0) {
            arr.splice(ind, 1);
            arr.unshift(val);
          }
          if (val.count === 5) {
            arr.splice(ind, 1);
            ss.concecutivecomment.unshift(word);
            sessionStorage.setItem('QuickNG', JSON.stringify(ss));
          }
          log(['連投', val.count, word], 'info');
          return true;
        }
        return false;
      },
      checkConcecutive = function(val) {
        return (val === word);
      },
      addNgClass = function(elm) {
        if (!elm.classList.contains('quickng')) elm.classList.add('quickng');
      };
    //変更があった要素の回数分ループ
    for (var i1 = 0, l1 = r.length, lNg = ss.ngcomment.length, type; i1 < l1; i1++) {
      if (!r[i1].addedNodes.length) continue;
      switch (r[i1].addedNodes[0].parentNode) {
        case id('floatComments'):
          type = 'list';
          break;
        case id('scrollComments'):
        case id('moveContainer'):
          type = 'scroll';
          break;
        default:
          return;
      }
      //新しいコメントの回数分ループ
      for (var i2 = 0, l2 = r[i1].addedNodes.length, node; i2 < l2; i2++) {
        node = r[i1].addedNodes[i2];
        word = node.textContent;
        log(['check', r.length, r[i1].addedNodes.length, word, type]);
        //連投コメントなら表示しない
        if (ls.blockconsecutivecomment) {
          if (ss.concecutivecomment.some(checkConcecutive)) {
            addNgClass(node);
            log(['連投NGコメント', word], 'info');
            continue;
          }
          if (preWord[type].some(checkPreWord)) {
            addNgClass(node);
            continue;
          }
        }
        if (preWord[type].length >= 50) preWord[type].pop();
        preWord[type].unshift({ word: word, count: 1 });
        //登録したNGワードの回数分ループ
        for (var i3 = 0; i3 < lNg; i3++) {
          //新しいコメントがNGワードを含んでいるなら表示しない
          if (word.indexOf(ss.ngcomment[i3]) !== -1) {
            addNgClass(node);
            log(['NGワード', word], 'info');
            break;
          }
        }
      }
    }
  }

  //最初の処理
  function start() {
    log('start');
    preWord.list = [];
    preWord.scroll = [];
    if (!ls.blockconsecutivecomment) ls.blockconsecutivecomment = true;
    if (!ss.ngcomment) ss.ngcomment = [];
    if (!ss.concecutivecomment) ss.concecutivecomment = [];
    GM_addStyle(style);
    //フッターが表示されるまで待つ
    interval = setInterval(function() {
      var footer = document.querySelector('p[class^="styles__program-highlight"] > span > span');
      if (footer) {
        clearInterval(interval);
        observerP.observe(footer, moConfig2);
      }
    }, 200);
  }

  //フッターが表示されたら実行
  function pre(r) {
    log('pre');
    //左下に番組名が表示されたとき
    if (r[0].addedNodes[0].length && r[0].addedNodes[0].textContent) {
      observerP.disconnect();
      init();
    }
  }

  //視聴者数などのデータが読み込まれたら実行
  function init() {
    log('init');
    defaultListComment = document.querySelector('div[class^="styles__comment-list-wrapper"] > div');
    //拡張機能が動作してるとき
    if (id('moveContainer')) scrollComment = id('moveContainer');
    //スクリプトが動作しているとき
    else if (id('floatComments')) {
      listComment = id('floatComments');
      scrollComment = id('scrollComments');
      observerL.observe(listComment, moConfig);
      clickComment(listComment);
    }
    clickComment(defaultListComment);
    log(['init', listComment, scrollComment]);
    if (scrollComment) {
      clickComment(scrollComment);
      observerS.observe(scrollComment, moConfig);
    }
  }

  var observerP = new MutationObserver(pre),
    observerL = new MutationObserver(checkComment),
    observerS = new MutationObserver(checkComment),
    moConfig = { childList: true },
    moConfig2 = { childList: true, subtree: true },
    ls = JSON.parse(localStorage.getItem('QuickNG')) || {},
    ss = JSON.parse(sessionStorage.getItem('QuickNG')) || {},
    style = '.quickng {display: none !important;}' +
    '#moveContainer {z-index: 11 !important; pointer-events: auto !important;}' +
    '#moveContainer > .movingComment {z-index: 13 !important;}' +
    '#scrollComments > .scroll:hover, #moveContainer > .movingComment:hover {transition-duration: 9999s !important; left: 0 !important; background-color: rgba(200, 0, 0, 0.4); }' +
    '#floatComments > .comment:hover { background-color: rgba(200, 0, 0, 0.4); }' +
    'div[class^="styles__comment-list-wrapper"] p[class^="styles__message"]:hover {background-color: rgba(200, 0, 0, 0.2); }',
    preWord = {},
    defaultListComment, listComment, scrollComment, interval;
  start();
})();