「フォローパネル」からリンク登録
「ver. 0.3」でリンクコードの採取機能を導入しましたが、ツイッターページ上で頻繁に表示される「フォローパネル」から採取が出来ずに、少し悩みました。
「フォローパネル」は、リンクの中でも「ツイッターのユーザー」のリンクに対して表示されるもので、ポインターをリンク上に載せると自動的に表示されます。 リンクの種類は幾つかある様ですが、ツイッターの主要機能の「ハッシュタグ」もリンクです。 しかしこれは「ユーザー」ではないので「フォローパネル」は表示されません。
実例と問題点
下は、「アメブロの公式アカウント」のツイッターページです。
下は、このツイッター画面のひとつのツイート例です。 ツイートの先頭部分に、投稿主が表示される定型ですが、この部分にマウスポインターを載せると、「フォローパネル」が表示されます。
下は、このパネルの周辺のリンク(a要素)を全て赤枠で示したものです。
この「フォローパネル」上には 5個の「リンク」があり、アイコンとユーザー表示の部分は「/ameblo_staff」という「ツイッターユーザー」のリンクコードがリンク設定されています。 最後の2個は違って「/ameblo_staff/following」等のリンクで、これは余り必要のないリンクでしょう。
てっとり早く「ツイッターのユーザー」のリンクが表示されるパネル上で、リンクコードが採取できないのは、「TwAvoid」としては片手落ちです。
取得出来ない理由
「ver. 0.3」で「フォローパネル」上のリンクが採取出来ない原因を調べると、このパネルが、ツイッターのシステムで動的にページ上にインジェクトされる要素だと判りました。 マウスを載せるまでページ上に無い要素なので、「TwAvoid」のスクリプトがパネル自体を取得出来ない様です。
そこで、パネルの生成を監視して、生成された時点でパネルを取得し、その上のリンクコードを取得するコードを作りました。
下は、「ver. 0.4」に使ったコードの概略です。
要点は「#layers」とういう要素を監視対象にしている所です。 この要素は「タイムライン」等とは全く別の所に置かれていて、「#layers」の中に「パネル」の実体「[data-testid="hoverCardParent"]」を生成して、パネルを表示する仕組みなので、「#layers」を監視すれば、パネル表示のタイミングが判ります。
最初の「MutationObserver」は、パネルの生成を監視し、パネルが表示される度に、真ん中の関数「card_ck()」を実行します。
「card_ck()」はパネルを取得して、その上での「Alt+左Click」操作で「リンクコード」を取得する「link_pointer()」を実行します。
これで、突然生成される「フォローパネル」上でも、リンクコードが採取できる様になりました。
「リンクコード」の採取方法について
「リンクコード」の採取するには、リンク上で「Alt+右Click」の操作をします。 採取した後の登録や削除について、詳細は前ページに纏めています。
開いたアカウントユーザーのツイートを非表示にしない
前ページで少し書きましたが、フィルター指定したユーザーのツイッターページを開いた場合に、そのタイムラインの殆どはフィルターされる結果になります。
例えば下は「アメーバブログの公式アカウント」のツイッターページです。
このタイムラインの殆どのツイートは、アメーバブログのユーザーリンクを含んでいるので、もしそのリンクコードをフィルターに設定していると、ちょっとまずい事になります。
フィルター動作で、殆どのツイートが非表示になるので、タイムラインのシステムの仕様で、次々と過去のツイートを呼込んで、延々とスクロールが繰り返されます。 この仕様をなんとか出来ないか調べましたが、非表示になったツイートの後方は必ず上に詰めて表示する仕組みで、これを止められません。
その結果、殆どの場合にツイッター側の過剰通信を抑止する機能が働き、下の様なエラー表示になります。
この場合、「 やりなおす」を押しても、ページをリロードしても復帰できません。 それは、このページの通信自体が一時的に停止されているからで、その復帰は数分間待つしかありません。 これを繰り返すと、更に強い処置も考えられます。
ただし、別のページのツイッター画面には、問題は波及しない様ですが、この問題は、フィルターツールの落とし穴と言えます。
「TwAvoid」はセキュリティが主目的ではない
「TwAvoid」のフィルター機能は、ツイッター自体のブロックやフィルター機能とは、使用の目的が異なります。
上の例で、何かの間違いでなければ、アメーバのタイムラインでアメーバ自体(ページのアカウント主)にフィルターを設定する事はないでしょう。 しかし、例えばユーザー自身のタイムラインでアメーバのツイートが邪魔に感じてフィルター設定をして、それを忘れて気付かずにアメーバの公式ページを開く事は、あり得るわけです。
ツイッターのデフォルトのフィルター機能は、SNS上のユーザーセキュリティを主目的としているのに対し、「TwAvoid」はユーザーの自身のタイムラインだけでなく、他のユーザーアカウントのタイムライン上で、選択的にツイートを閲覧する便利さを目的にしています。
この使用目的から、あるタイムラインではフィルターをしても、そのフィルター対象のタイムラインを閲覧する場合が有り得るわけです。
タイムラインの主(アカウントユーザー)を判定する
最初に書いたエラーに陥らないために、安全弁が必要です。 そこで、フィルターに設定したリンクのアカウントページを開いた場合には、そのリンクに対してはフィルターを働かさない工夫をしました。
これは難しい話ではなく、ページを開いた時に URL(上図の❶)から、アカウントのリンクコードを読みとり、それに該当するリンクを除外して、フィルターを機能させる様に改善しました。
これにより、開いたタイムラインの殆どが空白になる様な事態は、避けられます。 もしツイッター操作上で、絶対にブロック対象を見たくない様な場合は、デフォルトのブロックやフィルター機能を使ってください。
「TwAvoid」を利用するには
このツールは Chrome / Edge / Firefox版の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。
❶「Tampermonkey」を導入します
◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。
既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。
拡張機能の導入については、以下のページに簡単な説明があるので参照ください。
❷「Tampermonkey」にスクリプトを登録します
◎「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 右サイドバーの マークのボタンを1度押してください。
コード枠内の右クリック ➔ コード全体の選択 ➔ コピー操作 が可能になります。
◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
〔 TwAvoid 〕 ver. 0.4
// ==UserScript== // @name TwAvoid // @namespace http://tampermonkey.net/ // @version 0.4 // @description Twitter Filter // @author Everyone // @match https://twitter.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com // @grant none // ==/UserScript== // 🔴 特定の文字列を含むツイートを非表示 複数は縦棒で分割 let avoid_regexp=/@RakutenJP|楽天カード|プロモーション/; let mode; // フィルターの追加「1」削除「0」 let avoid=[]; // フィルターの配列 let home_ua; // 開いているタイムラインのユーザーアカウント let read_json=localStorage.getItem('TwAvoid_Link'); // ローカルストレージ保存名 avoid=JSON.parse(read_json); if(avoid==null){ avoid=['temporary-link']; } 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() function main(){ let cell=document.querySelectorAll('[data-testid="cellInnerDiv"]'); for(let k=0; k<cell.length; k++){ if(cell[k].style.opacity!='2'){ // ❶ の検索処理 🟦⬜ let all_link=cell[k].querySelectorAll('a'); let url=[]; for(let i=0; i<all_link.length; i++){ url.push(select_url(all_link[i])); } if(check_url(url)){ cell[k].style.display='none'; } // ❷ の検索処理 🟦⬜ let tweet_text=cell[k].textContent; if(avoid_regexp.test(tweet_text)){ cell[k].style.display='none'; } cell[k].style.opacity='2' }} 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; }} } // main() function link_pointer(){ 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; set_link(link_url); }}} setTimeout(()=>{ let layers=document.querySelector('#layers'); let monitor2=new MutationObserver(card_ck); monitor2.observe(layers, { childList: true }); }, 200); function card_ck(){ let card=document.querySelector('[data-testid="hoverCardParent"]'); if(card){ card.addEventListener('contextmenu', function(event){ if(event.altKey){ link_pointer(); }}); } } // card_ck() document.addEventListener('contextmenu', function(event){ if(event.altKey){ link_pointer(); }}); function disp_panel(){ let panel= '<div id="twa_panel">'+ '<div class="swich_box">'+ '<div class="swich">'+ '<input class="add_b" type="button" value="フィルター追加">'+ '<input class="rem_b" type="button" value="フィルター削除">'+ '</div>'+ '<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: 340px; '+ 'font: 16px Meiryo; color: #000; background: #f4fbff; padding: 15px; '+ 'border: 1px solid #aaa; 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 { display: flex; justify-content: space-between; width: 260px; } '+ '.set_box { font: 16px Meiryo; padding: 4px 10px 3px; margin: 10px 0; '+ 'width: calc(100% - 23px); } '+ '.set_box::placeholder { font-size: 14px; } '+ '.avoid_list { margin: 10px 0 0; padding: 4px 0; 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); } } // disp_panel() function set_link(url){ let set_box=document.querySelector('.set_box'); if(set_box){ set_box.value=url; } set_new_link(); if(mode==1){ add_link(); } else{ rem_link(); } let twa_panel=document.querySelector('#twa_panel'); 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.setAttribute('placeholder', 'ページ上のリンクを「Alt+右Click」して採取'); let avoid_ul=document.querySelector('.avoid_ul'); avoid_ul.classList.remove('rem'); add_b.onclick=()=>{ let link_url=set_box.value; if(link_url!=''){ if(space_check(link_url)){ alert("⛔ リンク表記に空白文字が含まれています"); } else if(avoid.includes(link_url)){ alert("⛔ 既に登録済のリンクです"); } else{ set_box.value=''; avoid.push(link_url); 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」して指定'); 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=()=>{ set_box.value=avoid_li[k].textContent; }} rem_b.onclick=()=>{ if(set_box.value!=''){ if(!avoid.includes(set_box.value)){ alert("⛔ フィルターリストに該当するリンクがありません"); } else{ avoid=avoid.filter(function(item){ return item!=set_box.value; }); set_box.value='' set_new_link(); rem_link(); }}} add_b.onclick=()=>{ mode=1; set_link(''); } } // rem_link() function set_new_link(){ let set=new Set(avoid); avoid=[...set]; // 追加分をチェックして配列を更新 let write_json=JSON.stringify(avoid); localStorage.setItem('TwAvoid_Link', write_json); // ストレージ保存 let avoid_ul=document.querySelector('.avoid_ul'); if(avoid_ul){ avoid_ul.innerHTML=''; // リスト表示をクリア 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); }} } // set_new_link()
「TwAvoid」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。
● 最新バージョンへのリンクは、以下のページのリンクリストから探せます。