Greasy Fork is available in English.

Remove emoji comment

youtube live chatの絵文字コメントを消す

// ==UserScript==
// @name         Remove emoji comment
// @version      0.1.1.2
// @description  youtube live chatの絵文字コメントを消す
// @author       nekocell
// @namespace    https://greasyfork.org/ja/users/762895-nekocell
// @match        https://*.youtube.com/live_chat?*
// @match        https://*.youtube.com/live_chat_replay?*
// @icon         https://www.google.com/s2/favicons?domain=youtube.com
// @run-at       document-start
// @grant        none
// ==/UserScript==

!function(){
  'use strict'

  const replaceFunction = function(mainCode, funcName, newFuncCode) {
    let funcCode = null;

    switch(typeof newFuncCode){
      case 'string':
        funcCode = newFuncCode;
        break;
      case 'function':
        funcCode = newFuncCode.toString();
        break;
      default:
        throw new Error("Invalid newFuncCode in replaceFunction()");
    }

    const tmp = funcName + ':';
    const funcStartIndex = mainCode.indexOf(tmp) + tmp.length;
    const searchIndex = mainCode.indexOf('{', funcStartIndex) + 1;
    let funcEndIndex = '';
    let bracesCnt = 1;

    for(let i = searchIndex; i < mainCode.length; ++i) {
      const c = mainCode[i];

      switch(c){
        case '{': ++bracesCnt; break;
        case '}': --bracesCnt; break;
      }

      if(bracesCnt === 0) {
        funcEndIndex = i;
        break;
      }
    }

    const beforeFunc = mainCode.substring(0, funcStartIndex);
    const afterFunc = mainCode.substring(funcEndIndex + 1, mainCode.length - 1);

    return beforeFunc + funcCode + afterFunc;
  }

  const patch = function(node){
    if(node.tagName !== "SCRIPT") {
      return;
    }
    if(!node.src || !node.src.endsWith("live_chat_polymer.js")) {
      return;
    }

    const id = node.src.match(/(?<=desktop\/).+(?=\/jsbin)/)[0];
    const url = `/s/desktop/${id}/jsbin/live_chat_polymer.vflset/live_chat_polymer.js`;

    node.removeAttribute("src");

    fetch(url).then(res => res.text()).then(data => {
      let code = data;

      code = replaceFunction(code, "handleAddChatItemAction_", function(a) {
        const runs = a.item.liveChatTextMessageRenderer.message.runs;

        for(let i = 0; i < runs.length; ++i) {
          const run = runs[i];
          if(run.emoji){
            return;
          }
          if(run.text && /\p{Emoji}/u.test(run.text)){
            return;
          }
        }

        var b = a.item
          , c = Object.keys(b)[0]
          , d = b[c]
          , e = !1;
        this.forEachItem_(function(g, k, l) {
          var m = Object.keys(b)[0];
          !(k = k[m]) || k.id != a.clientId && k.id != d.id || ("visibleItems" == g ? (this.visibleItems[l] = b,
          this.$.items.children[l].set("data", d)) : this.activeItems_[l] = b,
          e = !0)
        });
        if (c = this.get("stickinessParams.dockAtTopDurationMs", a) || 0) {
          var f = Array.prototype.find.call(Q(this.$.items).children, function(g) {
            return g.id == d.id
          });
          f ? this.maybeAddDockableMessage_(f) : this.itemIdToDockDurationMap[d.id] = c
        }
        e || this.activeItems_.push(b)
      });

      node.innerHTML = code;
    });
  }

  const observer = new MutationObserver((mutations) => {
    for(const m of mutations) {
      for(const n of m.addedNodes) {
        patch(n);
      }
    }
  });

  observer.observe(document, {
    childList: true,
    subtree: true
  });
}();