情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

Node.jsでMastodonのタイムラインを取得する

そもそもMastodonとは何?

Mastodonとは何かについて知りたい方は下の記事を参照してください。

準備

Mastodonのタイムラインを取得するにはタイムラインの種類に応じて適切なMastodonのAPIを呼び出さなければなりません。

MastodonのAPIを呼び出すにはアクセストークンが必要になりますので、事前に取得しておく必要があります。

Mastodonのアクセストークンを手動で取得する方法については下の記事を参照してください。

Mastodonのアクセストークンを自動で取得する方法については後日記事を書く予定です。

タイムラインの取得

ユーザーのタイムライン(ユーザーのトゥートから成るタイムライン)を取得するにはユーザーのインスタンスの/api/v1/accounts/:id/statusesパスに対してGETHTTPリクエストを行います。ただし、パスの:idの部分にはユーザーの番号を指定します。たとえば、番号が12345であるユーザーのタイムラインを取得するにはユーザーのインスタンスの/api/v1/accounts/12345/statusesパスに対してGETHTTPリクエストを行います。

このHTTPリクエストには下のようなクエリパラメータを指定することができます。

  • only_media・・・添付メディアを有するトゥートのみを取得する場合に指定します。
  • exclude_replies・・・返信であるトゥートを取得しない場合に指定します。

現在のユーザー(APIを呼び出しているユーザー)のホームタイムライン(現在のユーザーがフォローしているユーザーのトゥートから成るタイムライン)を取得するにはユーザーのインスタンスの/api/v1/timelines/homeパスに対してGETHTTPリクエストを行います。

現在のユーザーのお気に入りタイムライン(現在のユーザーがお気に入りしたトゥートから成るタイムライン)を取得するにはユーザーのインスタンスの/api/v1/favouritesパスに対してGETHTTPリクエストを行います。

インスタンスのローカルタイムラインや連合タイムラインを取得するにはインスタンスの/api/v1/timelines/publicパスに対してGETHTTPリクエストを行います。

このHTTPリクエストには下のようなクエリパラメータを指定することができます。

  • local・・・ローカルタイムラインを取得する場合に指定します。逆に、このクエリパラメータを指定しなかった場合には連合タイムラインを取得します。

インスタンスの特定のハッシュタグのローカルタイムラインや連合タイムラインを取得するにはインスタンスの/api/v1/timelines/tag/:hashtagパスに対してGETHTTPリクエストを行います。ただし、パスの:hashtagの部分にはハッシュタグの名称を指定します。たとえば、fxハッシュタグのタイムラインを取得するにはインスタンスの/api/v1/timelines/tag/fxパスに対してGETHTTPリクエストを行います。

このHTTPリクエストには下のようなクエリパラメータを指定することができます。

  • local・・・ローカルタイムラインを取得する場合に指定します。逆に、このクエリパラメータを指定しなかった場合には連合タイムラインを取得します。

また、上の5つのHTTPリクエストには下のようなクエリパラメータを指定することもできます。

  • max_id・・・タイムラインの特定の範囲のトゥートを取得する場合に指定します。範囲の上端をトゥートの番号として指定します。このクエリパラメータを指定した場合には指定した番号以下で、指定した番号に最も近いタイムラインの0個以上のトゥートが取得されます。
  • since_id・・・タイムラインの特定の範囲のトゥートを取得する場合に指定します。範囲の下端をトゥートの番号として指定します。このクエリパラメータを指定した場合には指定した番号より大きく、指定した番号に最も近いタイムラインの0個以上のトゥートが取得されます。
  • limit・・・取得するトゥートの数の最大値を指定します。

また、上の5つのHTTPリクエストに対するHTTPレスポンスのボディはタイムラインのトゥートを表すオブジェクトの配列から成るJSONとなります。配列の前の方にあるトゥート程新しいトゥートとなります。

トゥートを表すオブジェクトは下のようなプロパティを有します。

  • id・・・トゥートの番号です。
  • uri・・・トゥートの識別子です。
  • url・・・トゥートのウェブページのURLです。
  • account・・・トゥートを投稿したユーザーを表すオブジェクトです。
  • in_reply_to_id・・・トゥートが返信である場合には返信先のトゥートの番号です。返信でない場合にはnullです。
  • in_reply_to_account_id・・・トゥートが返信である場合には返信先のトゥートを投稿したユーザーの番号です。返信でない場合にはnullです。
  • reblog・・・トゥートがブーストである場合にはブースト元のトゥートを表すオブジェクトです。
  • content・・・トゥートの内容です。HTML形式です。
  • created_at・・・トゥートが作成された日時です。
  • reblogs_count・・・トゥートがブーストされた回数です。
  • favourites_count・・・トゥートがお気に入りされた回数です。
  • reblogged・・・トゥートがブーストされたかどうかです。
  • favourited・・・トゥートがお気に入りされたかどうかです。
  • sensitive・・・トゥートの添付メディアがデフォルトで隠されているべきかどうかです。
  • spoiler_text・・・トゥートとして最初に表示されるべきテキストです。最初に表示されるべきテキストがある場合にはトゥートの内容はデフォルトでは隠されているべきです。
  • visibility・・・トゥートの種類です。publicunlistedprivatedirectです。publicはパブリックトゥートを表します。unlistedはローカルタイムラインや連合タイムラインや検索結果に出現しないトゥートを表します。privateはプライベートトゥートを表します。directはダイレクトトゥートを表します。
  • media_attachments・・・トゥートの添付メディアを表すオブジェクトの配列です。
  • mentions・・・トゥートに含まれるユーザーへの参照を表すオブジェクトの配列です。
  • tags・・・トゥートに含まれるハッシュタグを表すオブジェクトの配列です。
  • application・・・トゥートを投稿したアプリケーションを表すオブジェクトです。

ユーザーを表すオブジェクトは下のようなプロパティを有します。

  • id・・・ユーザーの番号です。
  • username・・・ユーザーの識別子です。
  • acct・・・ユーザーの完全な識別子です。ユーザーが異なるインスタンスに所属している場合にはインスタンスの名称が含まれます。
  • display_name・・・ユーザーの名称です。
  • locked・・・ユーザーをフォローする場合にユーザーからの承認が必要かどうかです。
  • created_at・・・ユーザーが作成された日時です。
  • followers_count・・・ユーザーがフォローされているユーザー(ユーザーのフォロワー)の数です。
  • following_count・・・ユーザーがフォローしているユーザー(ユーザーのフォロイー)の数です。
  • statuses_count・・・ユーザーのトゥートの数です。
  • note・・・ユーザーのプロフィールです。
  • url・・・ユーザーのウェブページのURLです。
  • avatar・・・ユーザーの画像のURLです。
  • header・・・ユーザーのヘッダ画像のURLです。

添付メディアを表すオブジェクトは下のようなプロパティを有します。

  • id・・・添付メディアの番号です。
  • type・・・添付メディアの種類です。imagevideogifvです。imageは画像を表します。videoは動画を表します。gifvはGIFVを表します。
  • url・・・添付メディアのURLです。
  • remote_url・・・添付メディアの元々のURLです。
  • preview_url・・・添付メディアのプレビューのURLです。
  • text_url・・・添付メディアの短縮URLです。

ユーザーへの参照を表すオブジェクトは下のようなプロパティを有します。

  • url・・・ユーザーのウェブページのURLです。
  • username・・・ユーザーの識別子です。
  • acct・・・ユーザーの完全な識別子です。ユーザーが異なるインスタンスに所属している場合にはインスタンスの名称が含まれます。
  • id・・・ユーザーの番号です。

ハッシュタグを表すオブジェクトは下のようなプロパティを有します。

  • name・・・ハッシュタグの名称です。
  • url・・・ハッシュタグのウェブページのURLです。

アプリケーションを表すオブジェクトは下のようなプロパティを有します。

  • name・・・アプリケーションの名称です。
  • website・・・アプリケーションのウェブサイトのURLです。

また、上の5つのHTTPリクエストに対するHTTPレスポンスのLinkヘッダフィールドにはボディに含まれているタイムラインのトゥートの前のトゥートを取得するためのリンクを表すprevリンクと後のトゥートを取得するためのリンクを表すnextリンクが設定されています。

なお、GETHTTPリクエストを行う方法に関しては下の記事を参照してください。

ただし、HTTPリクエストを行う際にはAuthorizationヘッダフィールドの値にアクセストークンを設定しなければなりません。ただし、アクセストークンの冒頭にはBearerと1文字の空白を付加しなければなりませんので注意してください。

サンプルコード1

mstdn.jpインスタンスの連合タイムラインの最新のトゥートを5件取得するGETHTTPリクエストを行い、HTTPレスポンスのボディをJSONとして標準出力に出力します。また、HTTPレスポンスのLinkヘッダフィールドの値も標準出力に出力します。

mastodon-timeline.js

var http = require('http');
var https = require('https');
var concatStream = require('concat-stream');
var mimelib = require('mimelib');
var jschardet = require('jschardet');
var iconvLite = require('iconv-lite');
var pump = require('pump');

var u = {
    protocol: 'https:', 
    hostname: 'mstdn.jp', 
    path: '/api/v1/timelines/public?limit=5', 
    headers: {
        'Authorization': 'Bearer 1a5429d614323f78d6254d4c1a2446c53f6a027807ff73a2a237963fecc9ff44'
    }
};

var client = (u.protocol === 'https:' ? https : http).get(u, function (res) {
    if (res.statusCode === 200) {
        var writable = concatStream(function (data) {
            var encoding = undefined;
            if (res.headers['content-type'] !== undefined) {
                encoding = mimelib.parseHeaderLine(res.headers['content-type']).charset;
            }
            if (encoding === undefined) {
                encoding = jschardet.detect(data).encoding;
            }
            if (encoding !== undefined && iconvLite.encodingExists(encoding)) {
                console.log(JSON.parse(iconvLite.decode(data, encoding)));
                console.log(res.headers['link']);
            }
            else {
                console.error('can\'t detect encoding.');
                process.exit(1);
            }
        });
        pump(res, writable, function (err) {
            if (err) {
                console.error(err);
                process.exit(1);
            }
        });
    }
    else {
        console.error(http.STATUS_CODES[res.statusCode]);
        process.exit(1);
    }
});
client.setTimeout(10000, function () {
    console.error('timeouted.');
    client.abort();
});
client.on('error', function (err) {
    console.error(err);
    process.exit(1);
});

使用パッケージ

実行結果

C:\work\node>node mastodon-timeline.js
[ { id: 5030242,
    created_at: '2017-04-28T04:21:34.000Z',
    in_reply_to_id: null,
    in_reply_to_account_id: null,
    sensitive: false,
    spoiler_text: '',
    visibility: 'public',
    application: null,
    account:
     { id: 25719,
       username: 'takataka00001',
       acct: 'takataka00001@mastodon.blue',
       display_name: 'takataka00001✅',
       locked: false,
       created_at: '2017-04-15T05:40:52.007Z',
       followers_count: 17,
       following_count: 10,
       statuses_count: 487,
       note: 'Canon IXY 190 でフォト活動中。\nGoogle+ , Tumblr , Twitter をやってます。',
       url: 'https://mastodon.blue/@takataka00001',
       avatar: 'https://media.mstdn.jp/images/accounts/avatars/000/025/719/original/37ed696666be5be2.png',
       avatar_static: 'https://media.mstdn.jp/images/accounts/avatars/000/025/719/original/37ed696666be5be2.png',
       header: 'https://media.mstdn.jp/images/accounts/headers/000/025/719/original/2efa08ae336ca683.JPG',
       header_static: 'https://media.mstdn.jp/images/accounts/headers/000/025/719/original/2efa08ae336ca683.JPG' },
    media_attachments: [],
    mentions: [],
    tags: [],
    uri: 'tag:mastodon.blue,2017-04-28:objectId=515321:objectType=Status',
    content: '<p>ツイッターだと<br>20連続リツイートとか<br>普通にやっちゃうけど<br>ここだと迷惑がかかるっぽい(^_^;)</p>',
    url: 'https://mastodon.blue/users/takataka00001/updates/11260',
    reblogs_count: 0,
    favourites_count: 0,
    reblog: null,
    favourited: null,
    reblogged: null },
  { id: 5030237,
    created_at: '2017-04-28T04:21:25.560Z',
    in_reply_to_id: null,
    in_reply_to_account_id: null,
    sensitive: false,
    spoiler_text: '',
    visibility: 'public',
    application: { name: 'Web', website: null },
    account:
     { id: 134767,
       username: 'cudon',
       acct: 'cudon',
       display_name: 'うどん',
       locked: false,
       created_at: '2017-04-21T01:18:14.096Z',
       followers_count: 80,
       following_count: 38,
       statuses_count: 271,
       note: '<p>うどんは仮の名です。インスタンス立てて実験的に管理運営してます。<br /><a href="https://mbstdn.tokyo/" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">mbstdn.tokyo/</span><span class="invisible"></span></a></p>',
       url: 'https://mstdn.jp/@cudon',
       avatar: 'https://media.mstdn.jp/images/accounts/avatars/000/134/767/original/f326942a464c87a1.png',
       avatar_static: 'https://media.mstdn.jp/images/accounts/avatars/000/134/767/original/f326942a464c87a1.png',
       header: '/headers/original/missing.png',
       header_static: '/headers/original/missing.png' },
    media_attachments: [],
    mentions: [],
    tags: [],
    uri: 'tag:mstdn.jp,2017-04-28:objectId=5030237:objectType=Status',
    content: '<p>やばい。トイレがお友達状態</p>',
    url: 'https://mstdn.jp/@cudon/5030237',
    reblogs_count: 0,
    favourites_count: 0,
    reblog: null,
    favourited: null,
    reblogged: null },
  { id: 5030233,
    created_at: '2017-04-28T04:21:24.000Z',
    in_reply_to_id: null,
    in_reply_to_account_id: null,
    sensitive: false,
    spoiler_text: '',
    visibility: 'public',
    application: null,
    account:
     { id: 85267,
       username: 'zirconia',
       acct: 'zirconia@mstdn-workers.com',
       display_name: '',
       locked: false,
       created_at: '2017-04-17T10:15:18.910Z',
       followers_count: 3,
       following_count: 0,
       statuses_count: 722,
       note: '',
       url: 'https://mstdn-workers.com/@zirconia',
       avatar: 'https://media.mstdn.jp/images/accounts/avatars/000/085/267/original/ca6eeba816ed1898.PNG',
       avatar_static: 'https://media.mstdn.jp/images/accounts/avatars/000/085/267/original/ca6eeba816ed1898.PNG',
       header: '/headers/original/missing.png',
       header_static: '/headers/original/missing.png' },
    media_attachments: [],
    mentions: [],
    tags: [],
    uri: 'tag:mstdn-workers.com,2017-04-28:objectId=868503:objectType=Status',
    content: '<p>なんか電子書籍は作者にお金行かないイメージ</p>',
    url: 'https://mstdn-workers.com/users/zirconia/updates/189053',
    reblogs_count: 0,
    favourites_count: 0,
    reblog: null,
    favourited: null,
    reblogged: null },
  { id: 5030230,
    created_at: '2017-04-28T04:21:22.000Z',
    in_reply_to_id: null,
    in_reply_to_account_id: null,
    sensitive: false,
    spoiler_text: '',
    visibility: 'public',
    application: null,
    account:
     { id: 108344,
       username: 'EzoeRyou',
       acct: 'EzoeRyou@friends.nico',
       display_name: '$\\frac{江添亮}{マストドン主}$',
       locked: false,
       created_at: '2017-04-19T10:08:41.832Z',
       followers_count: 627,
       following_count: 93,
       statuses_count: 5850,
       note: 'https://github.com/EzoeRyou\nhttps://cpplover.blogspot.jp/\nhttps://twitter.com/EzoeRyou\nboostcpp@gmail.com',
       url: 'https://friends.nico/@EzoeRyou',
       avatar: 'https://media.mstdn.jp/images/accounts/avatars/000/108/344/original/67694eb67efdf83e.jpg',
       avatar_static: 'https://media.mstdn.jp/images/accounts/avatars/000/108/344/original/67694eb67efdf83e.jpg',
       header: '/headers/original/missing.png',
       header_static: '/headers/original/missing.png' },
    media_attachments: [],
    mentions: [],
    tags: [],
    uri: 'tag:friends.nico,2017-04-28:objectId=3125410:objectType=Status',
    content: '<p>あと、コメントにアイコンも流れるが、コレについてさらに辛い問題が考えられる。</p>',
    url: 'https://friends.nico/users/EzoeRyou/updates/355345',
    reblogs_count: 0,
    favourites_count: 0,
    reblog: null,
    favourited: null,
    reblogged: null },
  { id: 5030227,
    created_at: '2017-04-28T04:21:22.198Z',
    in_reply_to_id: null,
    in_reply_to_account_id: null,
    sensitive: false,
    spoiler_text: '',
    visibility: 'public',
    application: { name: 'Web', website: null },
    account:
     { id: 178236,
       username: 'hanzo05',
       acct: 'hanzo05',
       display_name: 'Hanzo',
       locked: false,
       created_at: '2017-04-27T05:08:10.517Z',
       followers_count: 2,
       following_count: 0,
       statuses_count: 5,
       note: '<p>人生一度しかない。<br />悔いのないよう、やりたい事を精一杯やるだけ。</p>',
       url: 'https://mstdn.jp/@hanzo05',
       avatar: 'https://media.mstdn.jp/images/accounts/avatars/000/178/236/original/77b6bf083f9bcfc3.jpg',
       avatar_static: 'https://media.mstdn.jp/images/accounts/avatars/000/178/236/original/77b6bf083f9bcfc3.jpg',
       header: 'https://media.mstdn.jp/images/accounts/headers/000/178/236/original/e891df0740309fd0.jpeg',
       header_static: 'https://media.mstdn.jp/images/accounts/headers/000/178/236/original/e891df0740309fd0.jpeg' },
    media_attachments: [],
    mentions: [],
    tags: [],
    uri: 'tag:mstdn.jp,2017-04-28:objectId=5030227:objectType=Status',
    content: '<p>午後の仕事に向かいます</p>',
    url: 'https://mstdn.jp/@hanzo05/5030227',
    reblogs_count: 0,
    favourites_count: 0,
    reblog: null,
    favourited: null,
    reblogged: null } ]
<https://mstdn.jp/api/v1/timelines/public?limit=5&max_id=5030227>; rel="next", <https://mstdn.jp/api/v1/timelines/public?limit=5&since_id=5030242>; rel="prev"

関連

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-mastodon, Node.js