rust
mastodon
MastodonDay 22

RustでマストドンのBotを作ったお話

分散型SNSマストドン

私は分散型SNSのマストドンによく参加しており最近ではよくRustを用いてマストドン関連のサービスを書いていたりしました。その中の一つでBotを作ったので使い方などを説明したいと思います。

リポジトリ
https://github.com/nacika-ins/nanachibot

Mastodonのベースとなるライブラリ「Mammut

Mammutは、Mastodon APIのラッパーライブラリです。マストドンにアプリを登録したり、フォローやタイムラインの取得などができます。一通りのエンティティも揃っており、これだけでマストドンのAPIが簡単に叩く事ができます。

Mammutで出来なかったこと

Mammutでは一通りのマストドンのAPIを叩く事ができますが、マストドンの特徴であるリアルタイムなストリーミングによるタイムラインの自動取得が出来ません。通信をキープすることで自動取得が実現できるのですがそのようなコードが見当たりませんでした。😢

ないなら作れとforkしたのがこちらです。

nacika-ins/Mammut

こちらのリポジトリでは、まだちゃんとコード整理出来てないですが get_user_streaming というメソッドを用意しました。threadloopchannel を用いて websocket によって常に通信を監視するようになっています。かなり試行錯誤しました。😓

マストドンがまだ1系の頃は pingに対してpongを送らなくても通信が維持されていたりしていたのがいつのまにかpingに対して正しくpongを返さないと切断されるようになっていたり、rust の websocket自体が仕様変更で改修が必要だったりとなかなかうまく行きませんでした。

もしもwebsocketの通信が切断されても、loop によって再試行されます。

数回の実行でアプリの簡単登録出来るようにした

Bot側のコードでは、まだconfig.tomlが作成されていない場合はアプリ作成の問い合わせを行うようになっています。複数回実行することで、config.tomlの中のキーが自動的に設定されます。ただ、アクセストークンだけは手動で貼り付ける必要があります。
config.tomlが満たされているとBotが起動します。

ストリーミングの実行部分

let (srx, nrx) = mastodon.get_user_streaming();

ユーザータイムラインのストリーミングを開始すると、2つのReceiverが渡されます。
左がHTL(ホームタイムライン)、右が通知です。それぞれ Statusというエンティティを扱いますが、

HTLと通知では若干構成がことなるので注意が必要です。Mammut標準のエンティティだと型が違っていてエラーになっている箇所があったので修正している部分があります。

Receiverから受信するとThreadがそこで待機状態になってしまうので、新しいThreadを作成しそこで受信するようにしています。肝心のBotの部分は、

let nanachi_arc = Arc::new(Mutex::new(nanachi));

Arc Mutexを用いて、スレッドを一時的にロックしHTLと通知用のスレッドでそれぞれ共通で使っています。
そこまでリソース食わないし状態を持たないので2つ作っても問題はないです。

Botの投稿

Botに何か投稿させる処理は別なチャンネルで行うようにしました。投稿自体はMammutの機能を使い、StatusBuilderStatusを作成し、new_statusで投稿します。 .in_reply_to_idは、ユーザーIDではなく、返信先のステータスIDのようです。

今後やってみたいこと

まだLTLや連合のストリーミングに対応していないので今後対応させて、自動的に学習させたりしてみようかと考えています。