ドシロウトがHTMLファイルひとつで複数のマストドンからストリーミングしてみた

ドシロウトがHTMLファイルひとつで複数のマストドンからストリーミングしてみた:


0.初めに

私はエンジニアではないただのドシロウトです。

自分が登録していないマストドンのインスタンスの直近のローカルタイムライン(LTL)を眺めようとして以下の記事を見つけました。

タイムラインを“のぞき見” アカウント作成不要のマストドン用Webサービス登場 「Mastodon Timeline Peeping Tool」
http://www.itmedia.co.jp/news/articles/1704/20/news037.html
Mastodon Timeline Peeping Toolを利用すれば、確かにアカウントなしで他のインスタンスのローカル、連合タイムライン(FTL)をのぞき見できます。

私は複数インスタンスの直近の情報が見たかったのでこのツールの公開されているソースを調べました。

yukimochi/Mastodon-Timeline-Peeping-Tool
https://github.com/yukimochi/Mastodon-Timeline-Peeping-Tool
多分マストドン基礎知識なんでしょうがストリーミングAPIを利用して直近の情報を得ているとはじめて知りました。

tootsuite/documentation
https://github.com/tootsuite/documentation/blob/master/Using-the-API/Streaming-API.md
そこでMastodon Timeline Peeping Toolを参考にしてHTMLファイル一つで複数インスタンスのLTL、FTLをまとめて取得、簡易表示するツールを作ってみました。


1.作ったもの

以下の画像の通りです。



mastqiita.jpg


ボタンを押すと事前に指定済みの複数インスタンスのLTLまたはFTLに新着投稿があるとリアルタイムに表示します。

HTML表示は可能な限り簡素化しました。


2.コード

短いですが以下です。HTMLファイル一つでストリーミングできてます。

test.html
<!DOCTYPE html> 
<html> 
<head> 
  <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 
</head> 
<body> 
  <h1>マストドンストリーミングAPI</h1> 
  <button class="start-ws-ltl">ローカルタイムライン</button> 
  <button class="start-ws-ftl">連合タイムライン</button> 
  <br><br> 
  <div class="activity-stream"></div> 
<script> 
var ws_connection = null; /* webソケット接続用 */ 
// インスタンスをカンマ区切りで指定 
var instance = '(インスタンスを指定)'; 
var instance2 = instance.split(','); 
var federate = null; /* ローカル、連合タイムライン判定用 */ 
 
window.onload = function () { 
  event.preventDefault(); 
  var ws_ltl = document.getElementsByClassName("start-ws-ltl")[0]; 
  var ws_ftl = document.getElementsByClassName("start-ws-ftl")[0]; 
  // ローカルタイムラインボタン押下時 
  ws_ltl.addEventListener('click', function (event) { 
    event.preventDefault(); 
    federate = false; 
    // インスタンス分だけWebSocket接続を開く 
    for (var i=0;i < instance2.length;i++) { 
      open_ws(instance2[i], federate); 
      // メッセージが受信時のddEventListener 
      ws_connection.addEventListener('message', function (event) { 
        insert_toot(JSON.parse(event.data).payload); 
      }); 
    }; 
  }); 
 
  // 連合タイムラインボタン押下時 
  ws_ftl.addEventListener('click', function (event) { 
    event.preventDefault(); 
    federate = true; 
 
    for (var i=0;i < instance2.length;i++) { 
      open_ws(instance2[i], federate); 
      ws_connection.addEventListener('message', function (event) { 
        insert_toot(JSON.parse(event.data).payload); 
      }); 
    }; 
  }); 
} 
 
// WebSocket接続関数 
function open_ws(instance2, federate) { 
  url = 'wss://' + instance2 + '/api/v1/streaming'; 
  if (federate) { 
      url += '?stream=public'; 
  } else { 
      url += '?stream=public:local'; 
  } 
  ws_connection = new WebSocket(url); 
} 
 
// トゥート→HTML書出し関数 
function insert_toot(text) { 
  entrys = processing_entrys(text); /* トゥート取得 */ 
  if (entrys !== null) { 
    entrys.forEach(entry => { 
      parent = document.getElementsByClassName("activity-stream")[0]; 
      parent.innerHTML = entry+'<hr>'+ parent.innerHTML; 
    }); 
  } 
} 
 
// トゥート編集関数 
function processing_entrys(data) { 
  statuses = [JSON.parse(data)]; 
  if (statuses.length > 0) { 
    entrys = []; 
    for (let idx = 0; idx < statuses.length; idx++) { 
      const status = statuses[idx]; 
      try { 
        account = status['account']; 
        media_attachments = status['media_attachments']; 
        status__header = html_status__header(status['url'], status['created_at'], account['url'], account['avatar'], account['display_name'], account['acct']); 
        status__content = html_status__content(status['content']); 
 
        md_att = []; 
        for (let idx_md_att = 0; idx_md_att < media_attachments.length; idx_md_att++) { 
            const media_attachment = media_attachments[idx_md_att]; 
            md_att.push(media_attachment['url']); 
        } 
        MediaGallery = null; 
        if (media_attachments.length > 0) { 
            MediaGallery = html_MediaGallery(md_att); 
        } 
        entrys.unshift(html_entry(status__header, status__content, MediaGallery)); 
      } 
      catch (e) { 
 
      } 
    }; 
    return entrys; 
  } else { 
    return null; 
  } 
} 
 
// トゥート結合関数 
function html_entry(status__header, status__content, MediaGallery) { 
  var entry; 
  entry = status__header + '<BR>'; 
  entry += status__content + '<BR>'; 
  if (MediaGallery !== null) { 
    entry += MediaGallery + '<BR>'; 
  } 
  return entry; 
} 
 
// トゥートヘッダー編集関数 
function html_status__header(status_url, status_time, author_url, author_avatar_url, author_name, author_id) { 
  var status__header; 
  status__header = author_name + ' ' + new Date(status_time).toLocaleString(); 
 
  var wkaa = author_url.replace('https://',''); 
  wkaa = wkaa.replace(('/@'+author_id),''); 
  status__header += ' @' + author_id +'(' + wkaa +')'; 
 
  return status__header; 
} 
 
// トゥートテキスト部編集関数…特に何もしていない 
function html_status__content(text) { 
  var status__content; 
  status__content = text; 
  return status__content; 
} 
 
// トゥート画像部編集関数 
function html_MediaGallery(img_urls) { 
  var media_gallery; 
  img_urls.forEach(img_url => { 
    media_gallery = '<img width=\"100\" src=\"'; 
    media_gallery += img_url; 
    media_gallery += '\"><BR>'; 
  }); 
  return media_gallery; 
} 
 
</script> 
</body> 
</html> 


3.気づいた事

試していて気づいた事は以下です。

  • 流量の少ないインスタンスをまとめて眺めるのに便利
  • 多分、ずっと動かすと重くなる(HTMLに書き出しつづけるので)
  • インスタンスによってはストリーミングに対応していない(Qiita丼など)
  • 複数タブでこのHTMLを実行するとエラーになる
  • ローカルでも動く


4.まとめ

元のツールが素晴らしいのでたったこれだけでストリーミングが取得できました。

なかなか便利かなと思います。

以 上

コメント

このブログの人気の投稿

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-04-30 23:37:32 RSSフィード2021-04-30 23:00 分まとめ(42件)

投稿時間:2023-02-05 02:09:04 RSSフィード2023-02-05 02:00 分まとめ(9件)