Last active
February 10, 2024 11:54
-
-
Save tac-yacht/0b656af7a9793bc7c08dd8a8d5e2e9d2 to your computer and use it in GitHub Desktop.
now not work.... use by https://addons.mozilla.org/ja/firefox/addon/greasemonkey/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name twitter/x spam blocking helper | |
// @version 1 | |
// @grant none | |
// @include https://twitter.com/* | |
// @include https://x.com/* | |
// ==/UserScript== | |
//configuration | |
const EXCLUDE_PATH = [ | |
'/home', | |
'/explore', | |
'/notifications', | |
'/messages', | |
'/settings', | |
] | |
/** | |
* 以下(<=)だとスパムと見なす | |
* 単位はms | |
*/ | |
const SPAM_USER_JOIN_DURATION = 35 //日間 | |
* 24 //日換算 | |
* 60 //時換算 | |
* 60 //分換算 | |
* 1000 //秒換算 | |
; | |
const SPAM_SCREEN_NAME_REGEX = /^[a-zA-Z_]+\d+/ | |
//ユーティリティ類 | |
// https://himenon.github.io/docs/javascript/wait/ | |
const wait = async (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
/** | |
* 現在年月を取得 | |
* @retrun {Date} 現在年月 | |
*/ | |
function getNowYearMonth() { | |
let now = new Date(); | |
return new Date( | |
now.getFullYear(), | |
now.getMonth() | |
); | |
} | |
// -------------------------- | |
// @see https://qiita.com/nanasi-1/items/dbaee5d609c2199e861b | |
let oldUrl = ''; | |
const observer = new MutationObserver(() => { | |
if (oldUrl !== location.href) { | |
window.dispatchEvent(new CustomEvent('urlChange')); | |
oldUrl = location.href; | |
} | |
}); | |
window.addEventListener('DOMContentLoaded', () => { | |
observer.observe(document.body, { | |
subtree: true, | |
childList: true, | |
attributes: true, | |
characterData: true | |
}); | |
}); | |
// -------------------------- | |
// 画面内の情報拾う系 | |
/** | |
* 利用開始日 | |
* 関数としての入力パラメーターはないが、ユーザーのホームでないと動かない | |
* @retrun {Date} 年/月まで | |
*/ | |
const USER_JOIN_DATE_PARSE_REGEX = /(?<year>\d{4}(?=年)).*(?<month>\d{1,2}(?=月))/ | |
function getUserJoinDate() { | |
let rawText = document.querySelector('[data-testid="UserJoinDate"]').textContent; | |
let result = rawText.match(USER_JOIN_DATE_PARSE_REGEX); | |
return new Date( | |
result.groups.year, | |
result.groups.month - 1 /* monthIndexのため表示と1ずれる */ | |
) | |
} | |
/** | |
* 利用開始日 | |
* 関数としての入力パラメーターはないが、ユーザーのホームでないと動かない | |
* @retrun {string} スクリーンネーム | |
*/ | |
function getScreenName() { | |
return new URL(location.href).pathname.substring(1); | |
} | |
//------------------------------------------------- | |
// ここからメイン処理 | |
/** | |
* スパムか? | |
* 関数としての入力パラメーターはないが、ユーザーのホームでないと動かない | |
* @return {boolean} true:スパム(ぽい) false:非スパム | |
*/ | |
//ふつうとは逆にセーフ判定を早期リターン | |
function isSpamSuspicion() { | |
if (Math.abs(getUserJoinDate().valueOf() - getNowYearMonth().valueOf()) > SPAM_USER_JOIN_DURATION) { | |
return false; | |
} | |
if (!getScreenName().match(SPAM_SCREEN_NAME_REGEX)) { | |
return false; | |
} | |
return true; | |
} | |
//twitterはSPAなので、includeで制御できない | |
/** | |
* 実際に有効にしたいURLであるか? | |
*/ | |
function isIncludeURL() { | |
let pathname = new URL(location.href).pathname); | |
if (EXCLUDE_PATH.includes(pathname) { | |
return false; | |
} | |
//2階層以上パスあるからたぶん違う | |
if (pathname.match(/(\/.*){2,}/)) { | |
return false; | |
} | |
return true; | |
} | |
async function main() { | |
// 若干ラグがあるので、雑に待ち | |
await wait(1500); | |
try { | |
if (!isSpamSuspicion()) { | |
return; | |
} | |
} catch (e) { | |
console.warn('ページ解析に失敗', e); | |
return; | |
} | |
//ここからスパム判定でたときの処理 | |
//報告画面オープン | |
document.querySelector('[data-testid="userActions"').click(); | |
await wait(100); | |
document.evaluate('//*[@data-testid="Dropdown"]//*[contains(text(),"報告")]', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE) | |
.singleNodeValue.click(); | |
await wait(500); | |
//スパムの選択肢クリック | |
document.evaluate('//*[@role="dialog"]//*[contains(text(),"スパム")]', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE) | |
.singleNodeValue.click(); | |
//確定 | |
document.querySelector('data-testid="ChoiceSelectionNextButton"').click(); | |
await wait(500); | |
//ついでにブロック | |
document.evaluate('//*[@role="dialog"]//*[@role="button"]//*[contains(text(),"ブロック")]', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE).singleNodeValue.click() | |
} | |
window.addEventListener('urlChange', () => { | |
if (!isIncludeURL()) { | |
return; | |
} | |
main(); | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment