LoginSignup
1
1

More than 5 years have passed since last update.

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

Posted at

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.まとめ

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

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

以 上

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1