「TwAvoid」の更新
●「Tampermonkey」で 「TwAvoid」ver. 0.8 の登録をクリックして、コードの編集画面を開きます。
以下のコードで「ver. 0.8」のコードを上書きして、編集画面を「保存」します。
「TwAvoid」 ver. 0.9
// ==UserScript==
// @name TwAvoid
// @namespace http://tampermonkey.net/
// @version 0.9
// @description Twitter Filter
// @author Everyone
// @match https://twitter.com/*
// @match https://x.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @grant none
// ==/UserScript==
let mode; // フィルターの追加「1」削除「0」
let mode_lt; // Link「0」・Text「1」 の処理選択
let avoid=[]; // リンクフィルターの配列
let avoid_tx=[]; // テキストフィルターの配列
let avoid_regexp; // 正規表現オブジェクト
let home_ua; // 開いているタイムラインのユーザーアカウント
let read_json=localStorage.getItem('TwAvoid_Link'); // ローカルストレージ保存名
avoid=JSON.parse(read_json);
if(avoid==null){
avoid=['temporary-link']; }
read_json=localStorage.getItem('TwAvoid_Text'); // ローカルストレージ保存名
avoid_tx=JSON.parse(read_json);
if(avoid_tx==null){
avoid_tx=[]; }
new_reg();
function new_reg(){
if(avoid_tx.length==0){
avoid_regexp=0; } // 配列の子要素が0の場合は特別処理
if(avoid_tx.length>0){
let tmp_ex=avoid_tx.join('|');
avoid_regexp=new RegExp(tmp_ex); }}
setTimeout(()=>{
page_ck();
let target=document.querySelector('head title');
let monitor0=new MutationObserver(page_ck);
monitor0.observe(target, { childList: true });
}, 200);
function page_ck(){
home_ua='/'+ location.pathname.split('/')[1];
let retry=0;
let interval=setInterval(wait_target, 100);
function wait_target(){
retry++;
if(retry>50){ // リトライ制限 5secまで
clearInterval(interval); }
let time_line=get_line(); // 監視 target
if(time_line){
clearInterval(interval);
main();
let monitor1=new MutationObserver(main);
monitor1.observe(time_line, { childList: true }); }}
function get_line(){
let cell=document.querySelector('[data-testid="cellInnerDiv"]');
if(cell){
let container=cell.parentNode;
return container; }}
} // page_ck()
let count=0; // 非表示にした件数
let un_count=0; // 非表示にしなかった件数
let fuse=0; // 0 : フィルター処理 ON 1 : フィルター処理 OFF
s_meter();
function s_meter(){
count=0;
un_count=0;
setTimeout(()=>{
if(fuse_value(count, un_count)>6){ // 70%以上が非表示の場合 🟦
fuse=1;
let ok=confirm(
'💢 OVERLOAD 💢\n'+
'不適当なフィルターの設定で、通信過剰に陥っています\n'+
'以下のどちらかの方法でフィルター設定を変更してください\n\n'+
'❶ 「OK」➔ フィルター設定を変更する\n'+
'❷ 「キャンセル」➔ タイムラインの表示に戻る');
if(ok){
disp_panel();
mode=0;
mode_lt=0;
set_link('');
s_meter(); }
else{
s_meter(); }}
else{
fuse=0;
s_meter(); }
}, 3000); } // 3secごとの集計 🟦
function fuse_value(c, un_c){
if(c+un_c>50){ // 3secの処理件数 上限 🟦
let av=Math.round(10*c/(c+un_c));
return av; }
else{
return 0; }}
function main(){
if(fuse==0){
let cell=document.querySelectorAll('[data-testid="cellInnerDiv"]');
for(let k=0; k<cell.length; k++){
if(cell[k].style.opacity!='2'){
engine(cell[k]);
cell[k].style.opacity='2' }}
function engine(tweet){
let hav=0;
let all_link=tweet.querySelectorAll('a');
let url=[];
for(let i=0; i<all_link.length; i++){
url.push(select_url(all_link[i])); }
if(check_url(url)){ // check1
hav=1; }
else{
if(avoid_regexp!=0){ // 0の時は処理しない
let tweet_text=tweet.textContent;
if(avoid_regexp.test(tweet_text)){ // check2
hav=1; }}}
if(hav==1){
count+=1;
tweet.style.display='none'; }
else{
un_count+=1; }
} // engine()
function select_url(get_url){
let get=get_url.getAttribute('href');
if(get!=home_ua){
return get; }}
function check_url(url){
let result=url.filter(data=>avoid.includes(data));
if(result.length>0){
return true; }}
}
setTimeout(()=>{
let layers=document.querySelector('#layers');
let monitor2=new MutationObserver(card_ck);
monitor2.observe(layers, { childList: true });
}, 200);
} // main()
function link_pointer(event){
let elem=document.elementFromPoint(event.clientX, event.clientY);
if(elem){
let link_a=elem.closest('a');
if(link_a){
event.preventDefault();
event.stopImmediatePropagation();
let link_url=link_a.getAttribute('href');
disp_panel();
mode=1;
mode_lt=0;
set_link(link_url);
}}}
document.addEventListener('contextmenu', function(event){
if(event.altKey){
link_pointer(event); }});
document.addEventListener('click', function(event){
if(event.altKey){
event.preventDefault();
link_pointer(event); }});
function card_ck(){
let card=document.querySelector('[data-testid="hoverCardParent"]');
if(card){
card.addEventListener('contextmenu', function(event){
if(event.altKey){
link_pointer(event); }});
card.addEventListener('click', function(event){
if(event.altKey){
event.preventDefault();
link_pointer(event); }});
}} // card_ck()
function disp_panel(){
let panel=
'<div id="twa_panel">'+
'<div class="swich_box">'+
'<div class="swich">'+
'<input class="li_tx" type="button" value="Link">'+
'<input class="add_b" type="button" value="フィルター追加">'+
'<input class="rem_b" type="button" value="フィルター削除">'+
'</div>'+
'<div class="twa_file">'+
'<input class="memo_w" type="button" value="ファイルに保存する">'+
'<input class="memo_r" type="button" value="ファイルから読込む">'+
'<input class="twa_file_input" type="file">'+
'</div>'+
'<input class="twa_memo" type="button" value="M">'+
'<input class="twa_close" type="button" value="✖">'+
'</div>'+
'<div>'+
'<input class="set_box" type="text">'+
'</div>'+
'<div class="avoid_list">'+
'<ul class="avoid_ul"></ul>'+
'</div>'+
'<style>#twa_panel { position: fixed; top: 60px; right: 40px; width: 360px; '+
'font: 16px Meiryo; color: #000; background: #f4fbff; padding: 15px; overflow: hidden; '+
'border: 1px solid #aaa; border-radius: 6px; box-shadow: 10px 20px 30px 0 #ccc; } '+
'.swich_box { display: flex; height: 30px; align-items: center; '+
'justify-content: space-between; } '+
'.swich_box input { padding: 3px 7px 2px; border: 1px solid #aaa; '+
'border-radius: 4px; height: 27px; } '+
'.swich_box input.li_tx { font: normal 16px/23px Meiryo; } '+
'.swich { display: flex; justify-content: space-between; width: 280px; } '+
'.set_box { font: 16px Meiryo; padding: 4px 10px 3px; margin: 10px 0; '+
'width: calc(100% - 23px); } '+
'.twa_file { position: absolute; top: 17px; left: 15px; z-index: 1; '+
'background: #fff; box-shadow: 0 0 0 100vh #fff; display: none; } '+
'.memo_w { margin-right: 10px; } '+
'.twa_file_input { display: none; } '+
'.twa_memo, .twa_close { z-index: 1; } '+
'.set_box::placeholder { font-size: 14px; } '+
'.avoid_list { margin: 10px 0 0; padding: 4px 0; min-height: 30px; '+
'max-height: 240px; border: 1px solid #aaa; overflow-y: scroll; } '+
'.avoid_ul { list-style: none; padding: 0; margin: 0; } '+
'.avoid_ul.rem { background: #fff; } '+
'.avoid_ul.rem .avoid_li { cursor: pointer; } '+
'.avoid_li { padding: 3px 10px 0; height: 26px; border-bottom: 1px solid #ddd; '+
'white-space: nowrap; overflow-x: scroll; scrollbar-width: none; } '+
'.avoid_li:first-child { border-top: 1px solid #ddd; } '+
'.avoid_li::-webkit-scrollbar { display: none; } '+
'</style></div>';
if(!document.querySelector('#twa_panel')){
document.body.insertAdjacentHTML('beforeend', panel); }
file_menu();
} // disp_panel()
function set_link(str){
let twa_panel=document.querySelector('#twa_panel');
let set_box=document.querySelector('.set_box');
if(set_box){
set_box.value=str; }
set_new_link();
if(mode==1){
add_link(); }
else{
rem_link(); }
let li_tx=document.querySelector('.li_tx');
if(li_tx && twa_panel){
if(mode_lt==0){
li_tx.value='Link';
twa_panel.style.background='#f4fbff'; }
else{
li_tx.value='Text';
twa_panel.style.background='#fff7e4'; }
li_tx.onclick=()=>{
if(mode_lt==0){
mode_lt=1;
li_tx.value='Text';
twa_panel.style.background='#fff7e4';
set_link(''); }
else{
mode_lt=0;
li_tx.value='Link';
twa_panel.style.background='#f4fbff';
set_link(''); }}}
let twa_close=document.querySelector('.twa_close');
if(twa_panel && twa_close){
twa_close.onclick=()=>{
twa_panel.remove(); }}
} // set_link()
function add_link(){
let add_b=document.querySelector('.add_b');
add_b.style.outline='2px solid #2196f3';
let rem_b=document.querySelector('.rem_b');
rem_b.style.outline='';
let set_box=document.querySelector('.set_box');
set_box.disabled=false;
set_box.style.borderColor='';
set_box.style.background='';
if(mode_lt==0){
set_box.setAttribute('placeholder', 'ページ上のリンクを「Alt+右Click」して採取'); }
else{
set_box.setAttribute('placeholder', 'テキストを記入するかコピーして貼付けます'); }
let avoid_ul=document.querySelector('.avoid_ul');
avoid_ul.classList.remove('rem');
add_b.onclick=()=>{
let link_str=set_box.value;
if(link_str!=''){
if(space_check(link_str)){
alert("⛔ 空白文字が含まれています"); }
else if(mode_lt==0 && avoid.includes(link_str)){
alert("⛔ 既に登録済のリンクコードです"); }
else if(mode_lt==1 && avoid_tx.includes(link_str)){
alert("⛔ 既に登録済のテキストです"); }
else{
set_box.value='';
if(mode_lt==0 ){
avoid.push(link_str); }
else{
avoid_tx.push(link_str); }
set_new_link(); }}
function space_check(str){
let check=/\s+/g;
if(check.test(str)){
return true; }}}
rem_b.onclick=()=>{
mode=0;
set_link(''); }
} // add_link()
function rem_link(){
let add_b=document.querySelector('.add_b');
add_b.style.outline='';
let rem_b=document.querySelector('.rem_b');
rem_b.style.outline='2px solid #2196f3';
let set_box=document.querySelector('.set_box');
set_box.setAttribute('placeholder', '下のフィルターリストを「左Click」して指定');
set_box.disabled=true;
set_box.style.borderColor='transparent';
set_box.style.background='transparent';
let avoid_ul=document.querySelector('.avoid_ul');
avoid_ul.classList.add('rem');
let avoid_li=document.querySelectorAll('.avoid_li');
for(let k=0; k<avoid_li.length; k++){
avoid_li[k].onclick=()=>{
clear();
rem_b.style.outline='2px solid red';
avoid_li[k].style.background='#cfd8dc'; }}
function clear(){
let avoid_li=document.querySelectorAll('.avoid_li');
for(let k=0; k<avoid_li.length; k++){
avoid_li[k].style.background=''; }}
rem_b.onclick=()=>{
let avoid_li=document.querySelectorAll('.avoid_li');
for(let k=0; k<avoid_li.length; k++){
if(avoid_li[k].style.background){
let select_text=avoid_li[k].textContent;
if(mode_lt==0){
avoid=avoid.filter(function(item){
return item!=select_text; }); }
else{
avoid_tx=avoid_tx.filter(function(item){
return item!=select_text; }); }
set_new_link();
rem_link(); }}}
add_b.onclick=()=>{
mode=1;
set_link(''); }
} // rem_link()
function set_new_link(){
if(mode_lt==0){
let set=new Set(avoid);
avoid=[...set]; // 追加分をチェックして配列を更新
let write_json=JSON.stringify(avoid);
localStorage.setItem('TwAvoid_Link', write_json); } // ストレージ保存
else{
let set_tx=new Set(avoid_tx);
avoid_tx=[...set_tx]; // 追加分をチェックして配列を更新
let write_json=JSON.stringify(avoid_tx);
localStorage.setItem('TwAvoid_Text', write_json); } // ストレージ保存
new_reg(); // テキスト検索設定を更新
let avoid_ul=document.querySelector('.avoid_ul');
if(avoid_ul){
avoid_ul.innerHTML=''; // リスト表示をクリア
if(mode_lt==0){
for(let k=avoid.length-1; k>=0; k--){
let link_li='<li class="avoid_li">'+ avoid[k] +'</li>';
avoid_ul.insertAdjacentHTML('beforeend', link_li); }}
else{
for(let k=avoid_tx.length-1; k>=0; k--){
let link_li='<li class="avoid_li">'+ avoid_tx[k] +'</li>';
avoid_ul.insertAdjacentHTML('beforeend', link_li); }}}
} // set_new_link()
function file_menu(){
let act=0; // メニュー非表示
let twa_memo=document.querySelector('.twa_memo');
let twa_file=document.querySelector('.twa_file');
if(twa_memo && twa_file){
if(act==0){
twa_file.style.display='none' }
else{
act=1;
twa_file.style.display='block';
backup(); }
twa_memo.onclick=()=>{
if(act==0){
act=1;
twa_file.style.display='block';
backup(); }
else{
act=0;
twa_file.style.display='none'; }}}
function file_menu_close(n){
twa_file.style.boxShadow='0 0 0 100vh #b0bec5';
setTimeout(()=>{
act=0;
twa_file.style.display='none';
twa_file.style.boxShadow='0 0 0 100vh #fff';
if(n==1){
let twa_panel=document.querySelector('#twa_panel');
if(twa_panel){
twa_panel.remove(); }}
}, 500); }
function backup(){
let memo_w=document.querySelector('.memo_w');
let memo_r=document.querySelector('.memo_r');
let twa_file_input=document.querySelector('.twa_file_input');
memo_w.onclick=function(){
let write_json=JSON.stringify(avoid); // 配列 avoid を取得
let blob=new Blob([write_json], {type: 'application/json'});
let a_elem=document.createElement('a');
a_elem.href=URL.createObjectURL(blob);
a_elem.download='TwAvoid_L.json'; // 保存ファイル名
a_elem.click();
URL.revokeObjectURL(a_elem.href);
write_json=JSON.stringify(avoid_tx); // 配列 avoid_tx を取得
blob=new Blob([write_json], {type: 'application/json'});
let b_elem=document.createElement('a');
b_elem.href=URL.createObjectURL(blob);
b_elem.download='TwAvoid_T.json'; // 保存ファイル名
b_elem.click();
URL.revokeObjectURL(b_elem.href);
file_menu_close(0); }
memo_r.onclick=function(){
twa_file_input.click(); }
twa_file_input.addEventListener("change", function(){
if(!(twa_file_input.value)) return; // ファイルが選択されない場合
let file_list=twa_file_input.files;
if(!file_list) return; // ファイルリストが選択されない場合
let file=file_list[0];
if(!file){
return; } // ファイルが無い場合
else if(file.name.includes('TwAvoid_L')){ // リンクの配列
let file_reader=new FileReader();
file_reader.readAsText(file);
file_reader.onload=function(){
let data_in=JSON.parse(file_reader.result);
let new_arr=Array.from(new Set([...avoid, ...data_in])); // 差分を追加
avoid=new_arr;
let write_json=JSON.stringify(avoid);
localStorage.setItem('TwAvoid_Link', write_json); } // ストレージ保存
file_menu_close(1); }
else if(file.name.includes('TwAvoid_T')){ // テキストの配列
let file_reader=new FileReader();
file_reader.readAsText(file);
file_reader.onload=function(){
let data_in=JSON.parse(file_reader.result);
let new_arr=Array.from(new Set([...avoid_tx, ...data_in])); // 差分を追加
avoid_tx=new_arr;
let write_json=JSON.stringify(avoid_tx);
localStorage.setItem('TwAvoid_Text', write_json); // ストレージ保存
new_reg(); }
file_menu_close(1); }
});
} // backup()
} // file_menu()
データの移植方法
◎「twitter.com」のドメインで開いた任意の画面で、「TwAvoid」で「ファイル保存」を行います。
◎「x.com」のドメインで任意のツイッター画面を開き、「TwAvoid」の「ファイルの読込み」の機能で、保存したファイルを指定してデータを読込みます。
◎ 読込みは、「TwAvoid_L.json」「TwAvoid_T.json」の2種のファイルを、別々に読込む操作が必要です。
実際のファイル操作 ❶
ツイッター画面で、ページ上の任意のリンクを「Alt+右Click」すると、「TwAvoid」の操作パネルが開きます。
このパネルの閉じる「✖」ボタンの左に「M」(メモ)のボタンを配置しました。
●「M」ボタンを押すと、ファイルメニューが表示されます。
●「ファイルに保存する」を押すと、「TwAvoid_L.json」「TwAvoid_T.json」の 2個のファイルが、ダウンロードフォルダに保存されます。
▪ 複数回のファイル保存を行った場合は、自動的に連番が付きます。
(例)「TwAvoid_L.json」➔「TwAvoid_L(1).json」➔「TwAvoid_L(2).json」
実際のファイル操作 ❷
「x.com」のドメインで、ツイッター画面を開き直します。
先と同様に「M」ボタンを押してファイルメニューを表示します。
●「ファイルから読込む」を押すと、読込むファイルを選択する画面が開きます。
▪ 最初に「TwAvoid_L.json」を指定して開くと、パネルが暗転して閉じます。
▪ もういちどパネルを開き、「TwAvoid_T.json」を指定して開くと、再びパネルが暗転して閉じます。
▪ ファイルからの登録データの読込みは、差分の読込みです。 重複したデータは自動的に整理されます。 既に「x.com」で開いて「TwAvoid」で同録したデータがあっても、上書きで削除される事はありません。


