// ==UserScript==
// @name Togetter User Ban
// @namespace https://greasyfork.org/ja/scripts/387330-togetter-user-ban
// @version 1.0
// @description try to take over the world!
// @author You
// @match https://togetter.com/*
// @grant none
// @downloadURL https://update.greasyfork.icu/scripts/387330/Togetter%20User%20Ban.user.js
// @updateURL https://update.greasyfork.icu/scripts/387330/Togetter%20User%20Ban.meta.js
// ==/UserScript==
/**
* ユーザーを表すデータ型
* @typedef {Object} User
* @property {string} userId
* @property {string} icon
*/
/**
* @param {string} userId
* @param {string} icon
* @returns {User}
*/
const User = (userId, icon) => {
return { userId: userId, icon: icon };
};
/**
* user1がuser2と同じユーザーを示すか判別します。
* @param {User} user1
* @param {User} user2
*/
const isSameUser = (user1, user2) => {
return (user1.userId === user2.userId);
};
/**
* 与えられた時間だけスレッドを停止します。
* @param {number} ms ミリ秒
*/
const sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
/**
* 文字列からHTMLElementを作り返します。
* @param {string} h htmlを表現する文字列
* @returns {Node}
*/
const html = h => {
const div = document.createElement("div");
div.insertAdjacentHTML("afterbegin", h);
return div.firstChild;
}
/**
* userListがuserを持っているか判別します。
* @param {User[]} userList
* @param {User} user
*/
const userContains = (userList, user) => userList.filter(i => isSameUser(i, user)).length > 0;
/**
* objListをJSON CSVに変換して返します。
* @param {{}[]} objList
*/
const objListToJson = objList => objList.map(o => JSON.stringify(o)).join(",");
//-------------------------------------------------------------------------------------------------
const ele = {};
/**
* バンリストの項目を返します。
* @param {User} user
*/
ele.li = user => {
const li = html(
`
@${user.userId}
`);
/** @type {HTMLElement} */
// @ts-ignore
const button = html(``);
button.onclick = () => {
dao.delete(user);
li.parentElement.removeChild(li);
};
li.appendChild(button);
return li;
};
/**
* バンリストを返します。
* @param {User[]} users
*/
ele.div = users => {
const lis = users.map(u => ele.li(u));
/** @type {HTMLElement} */
// @ts-ignore
const div = html(
``);
lis.forEach(l => div.querySelector("div.main_box ul").appendChild(l));
return div;
};
/**
* ユーザーのバンやバン解除を行うボタンを返します。
* @param {User} user
*/
ele.button = user => {
const isBanned = userContains(dao.find(), user);
const text = isBanned ? "バンしている" : "バンする";
const clazz = isBanned ? "btn active" : "btn";
/** @type {HTMLElement} */
// @ts-ignore
const button = html(`${text}`);
const clickEvent = () => {
const isActive = button.classList.contains("active");
if (isActive) {
dao.delete(user);
button.setAttribute("class", "btn");
button.innerText = button.getAttribute("data-title");
console.log(`${user.userId}のバンを解除しました。`);
} else {
dao.add(user);
button.setAttribute("class", "btn active");
button.innerText = button.getAttribute("data-active-title");
console.log(`${user.userId}をバンしました。`);
};
};
const hoverEvent = () => {
const isActive = button.classList.contains("active");
if (!isActive) return false;
button.innerText = button.getAttribute("data-active-hover-title");
};
const outEvent = () => {
const isActive = button.classList.contains("active");
if (!isActive) return false;
button.innerText = button.getAttribute("data-active-title");
};
button.onclick = clickEvent;
button.onmouseover = hoverEvent;
button.onmouseout = outEvent;
return button;
};
//-------------------------------------------------------------------------------------------------
const dao = {};
/**
* ローカルストレージに保存されたユーザーの配列を返します。
* @returns {User[]}
*/
dao.find = () => {
const raw = localStorage.bannedUser;
if (raw === undefined || raw === "[]") return [];
return raw.split(/,(?={)/).map(i => JSON.parse(i));
};
/**
* ローカルストレージにuserを保存します。
* @param {User} user
*/
dao.add = user => {
const bannedUsers = dao.find();
if (!userContains(bannedUsers, user)) bannedUsers.push(user);
const jsonList = objListToJson(bannedUsers);
dao.save(jsonList);
};
/**
* ローカルストレージからuserを消去します。
* @param {User} user
*/
dao.delete = user => {
const bannedUsers = dao.find();
const newUserList = bannedUsers.filter(i => !isSameUser(i, user));
const jsonList = objListToJson(newUserList);
dao.save(jsonList);
};
/**
* ローカルストレージのbannedUserキーに文字列を保存します。
* @param {string} str
*/
dao.save = str => localStorage.setItem("bannedUser", str);
//HTMLの取得と操作---------------------------------------------------------------------------------
/**
* 条件に一致した要素を見えなくします。
* @param {string} selector 消したい要素のセレクター
* @param {string} checkEleSel eleSelを祖先に持ち、かつattrを有する要素のセレクター
* @param {string} attr 要素を消す基準の属性。"text"を与えた場合はinnerTextを探す。
* @param {string[]} bannedList バンリスト
*/
const hideElement = (selector, checkEleSel, attr, bannedList) => {
/** @type {NodeListOf} */
const elementList = document.querySelectorAll(selector);
const getProperty = e => (attr === "text") ? e.innerText : e.getAttribute(attr);
Array.from(elementList)
.filter(e => e.style.display !== "none")
.filter(e => e.querySelector(checkEleSel) !== null)
.forEach(e => {
[e]
.map(e => e.querySelector(checkEleSel))
.map(getProperty)
.filter(p => bannedList.includes(p))
.forEach(p => {
e.style.display = "none";
console.log(`hideElement: 要素を消しました。(${p})`);
});
});
};
/**
* togetterサイトのフォローボタンを格納したボックスを返します。
* バンボタンを設置するのに使います。
*/
const followBox = () => document.querySelector("#follow_box");
/**
* togetterサイトのプロフィールボックスを返します。
*/
const profileBox = () => document.querySelector("div.profile_box").parentElement;
/**
* バンボタンを設置します。
*/
const addBanButton = () => {
const profile = profileBox();
// @ts-ignore
const userId = profile.querySelector("a.status_name").innerText.replace("@", "");
const icon = profile.querySelector("img").getAttribute("src");
const user = User(userId, icon);
followBox().appendChild(ele.button(user));
};
/**
* バンリストを設置します。
*/
const addBannedList = () => {
const bannedUsers = dao.find();
const div = ele.div(bannedUsers);
document.querySelector("#right_wrap_middle .right_wrap").appendChild(div);
};
/**
* Twitterのデフォルトアイコン
*/
const defaultIcon = "https://abs.twimg.com/sticky/default_profile_images/default_profile_normal.png";
/**
* userListからアイコンのリストを作り返します。
* @param {User[]} userList
*/
const iconList = userList => {
return userList
.map(u => u.icon)
.filter(i => i !== defaultIcon);
debugger
};
/**
* userListからIDのリストを作り変えします。
* @param {User[]} userList
*/
const idList = userList => {
return userList
.map(u => u.userId)
.map(i => "@" + i);
};
/**
* 特定のユーザーが作ったまとめを非表示にします。
* @param {User[]} bannedList 非表示にしたいユーザーのリスト
*/
const hideMatome = bannedList => {
const list = iconList(bannedList);
hideElement("li.clearfix", "img.icon_24", "data-lazy-src", list);
};
/**
* 特定のユーザーのコメントを非表示にします。
* @param {User[]} bannedList 非表示にしたいユーザーのリスト
*/
const hideComment = bannedList => {
const list = idList(bannedList);
hideElement("#comment_box .list_box", ".status_name", "text", list);
};
/**
* 最近見たまとめなどのまとめを非表示にします。
* @param {User[]} bannedList 非表示にしたいユーザーのリスト
*/
const hideThumbList = bannedList => {
const list = iconList(bannedList);
hideElement("ul.simple_list.thumb_list li", "img.icon_20", "data-lazy-src", list);
};
/**
* おすすめまとめの要素を非表示にします。
* @param {User[]} bannedList
*/
const hideRecommendList = bannedList => {
const list = iconList(bannedList);
hideElement(".list_recommend .clearfix", "img.icon_24", "data-lazy-src", list);
};
/**
*
* @param {User[]} bannedList
*/
const hideCommentPopular = bannedList => {
const list = iconList(bannedList);
hideElement(".comment_popular .clearfix", "img.icon_24", "data-lazy-src", list);
};
/**
* @param {Function} callback
* @param {number} ms
*/
const thread = async (callback, ms = 300) => {
while (true) {
callback();
await sleep(ms);
};
};
/**
* selectorの要素に何らかの変更が加えられたとき、callbackを呼びます。
* @param {MutationCallback} callback
* @param {string} selector
* @param {MutationObserverInit} options
*/
const observe = (callback, selector, options = { childList: true }) => {
// @ts-ignore
callback();
const observer = new MutationObserver(callback);
const content = document.querySelector(selector);
observer.observe(content, options);
};
(async () => {
'use strict';
const bannedList = dao.find();
hideThumbList(bannedList);
hideRecommendList(bannedList);
hideCommentPopular(bannedList);
addBannedList();
if (location.href.startsWith("https://togetter.com/id/")) {
addBanButton();
return false;
};
if (location.href.startsWith("https://togetter.com/li/")) {
addBanButton();
observe(() => hideComment(bannedList), "#comment_box");
return false;
};
observe(() => hideMatome(bannedList), ".simple_list");
})();