「フォローパネル」からリンク登録 

「ver. 0.3」でリンクコードの採取機能を導入しましたが、ツイッターページ上で頻繁に表示される「フォローパネル」から採取が出来ずに、少し悩みました。

 

「フォローパネル」は、リンクの中でも「ツイッターのユーザー」のリンクに対して表示されるもので、ポインターをリンク上に載せると自動的に表示されます。 リンクの種類は幾つかある様ですが、ツイッターの主要機能の「ハッシュタグ」もリンクです。 しかしこれは「ユーザー」ではないので「フォローパネル」は表示されません。

 

 

実例と問題点 

下は、「アメブロの公式アカウント」のツイッターページです。

 

 

下は、このツイッター画面のひとつのツイート例です。 ツイートの先頭部分に、投稿主が表示される定型ですが、この部分にマウスポインターを載せると、「フォローパネル」が表示されます。

 

 

 

下は、このパネルの周辺のリンク(a要素)を全て赤枠で示したものです。

 

 

 

この「フォローパネル」上には 5個の「リンク」があり、アイコンとユーザー表示の部分は「/ameblo_staff」という「ツイッターユーザー」のリンクコードがリンク設定されています。 最後の2個は違って「/ameblo_staff/following」等のリンクで、これは余り必要のないリンクでしょう。

 

てっとり早く「ツイッターのユーザー」のリンクが表示されるパネル上で、リンクコードが採取できないのは、「TwAvoid」としては片手落ちです。

 

 

 

取得出来ない理由 

「ver. 0.3」で「フォローパネル」上のリンクが採取出来ない原因を調べると、このパネルが、ツイッターのシステムで動的にページ上にインジェクトされる要素だと判りました。 マウスを載せるまでページ上に無い要素なので、「TwAvoid」のスクリプトがパネル自体を取得出来ない様です。

 

そこで、パネルの生成を監視して、生成された時点でパネルを取得し、その上のリンクコードを取得するコードを作りました。

 

下は、「ver. 0.4」に使ったコードの概略です。

 

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()

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);
        }}}

 

要点は「#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ツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。

 

● 最新バージョンへのリンクは、以下のページのリンクリストから探せます。