AbemaTV Quick NG Comment

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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();
})();