読者です 読者をやめる 読者になる 読者になる

echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

Masto.netを使った簡単なMastodonのテスト

GWは9連休!…とか思っていたら、気づいたらもう半分過してしまい、若干凹んでいます。
働きたくないでござる

…さて、最近ネットやらTwitterで、やたら「Mastodon」という言葉を目にするようになりました。
ちなみに「Mastodon」の特徴を簡単に説明すると

てな感じです。

で、APIも公開されていて、いろんな人が各環境でのライブラリをすでに作っているわけで、あすかさんのブログにも、こんなものが紹介されていました。
kmycode.hatenablog.jp

「Mastonet」という、Guillaume Lacasa氏というフランス人の方が作成したC#用のMastodonライブラリです。
github.com

というわけで、どうせ寝てるだけだしMastodonの理解もかねて、僕も作ってみようと思いました。


Mastonetのインストール
Visual Studio使ってるなら、NuGet経由でインストールできます。
ただしGithubのREADME.md内にも「A preview version is available on Nuget : https://www.nuget.org/packages/Mastonet
とある通り、「プレリリースを含める」チェックボックスにチェックを入れないと検索されないので、それだけは注意です。

とりあえずビューとソース本文
f:id:Makky12:20170503192330p:plain

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mastonet;
using System.Diagnostics;

namespace MastodonApp
{
    public partial class Form1 : Form
    {
        private String timeLine = String.Empty;
        private TimelineStreaming stream = null;

        private enum StreamStetus
        {
            Not_Login = 0,
            Not_Start = 1,
            Now_Starting = 2,
        }

        private StreamStetus stream_status = StreamStetus.Not_Login;

        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            var id = textBox1.Text;
            var pass = textBox2.Text;

            if (String.IsNullOrEmpty(id) || String.IsNullOrEmpty(pass))
            {
                MessageBox.Show("IDまはたパスワードが未入力です。");
                return;
            }

            try
            {
                var ret = await this.MastoNet_Run(id, pass);

                timer1.Interval = 5000;
                timer1.Enabled = true;
                timer1.Tick += new System.EventHandler(timer1_Tick);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private Task<int> MastoNet_Run(String id, String pass)
        {

            return Task.Run(async () =>
            {
                if (stream_status != StreamStetus.Now_Starting)
                {
                    if (stream_status == StreamStetus.Not_Login)
                    {
                        var authClient = new AuthenticationClient("friends.nico");
                        var appRegistration = await authClient.CreateApp("MastodonApp", Scope.Read | Scope.Write | Scope.Follow); 
                        var auth = await authClient.ConnectWithPassword(id, pass);

                        var client = new MastodonClient(appRegistration, auth);
                        stream_status = StreamStetus.Not_Start;

                        stream = client.GetPublicStreaming();

                        var dispString = String.Empty;

                        stream.OnUpdate += (sender, e) =>
                        {
                            var dispName = e.Status.Account.DisplayName;
                            var content = e.Status.Content;

                            dispString = dispName + "\r\n" + content + "\r\n" + "\r\n";
                            this.timeLine += dispString;
                        };
                    }

                    stream.Start();
                    this.stream_status = StreamStetus.Now_Starting;
                }
                return 0;
            });
        }

        private void timer1_Tick(object sender, System.EventArgs e)
        {
            this.textBox3.Text = this.timeLine;
        }

        protected void button2_Click(object sender, EventArgs e)
        {
            if (stream != null && this.stream_status == StreamStetus.Now_Starting)
            {
                stream.Stop();
                this.stream_status = StreamStetus.Not_Start;
            }
        }
    }
}

と、ビューとソースを記載しましたが、重要なのはソースの「MastoNet_Run()」の内部です。

  • アプリ登録
var authClient = new AuthenticationClient("friends.nico");
var appRegistration = await authClient.CreateApp("MastodonApp", Scope.Read | Scope.Write | Scope.Follow); 

クライアント作成には「ClientID」「ClientSecret」という2つの値が必要で、これを取得するためにアプリ登録を行います。
アプリ登録は上記の通り、MastodonインスタンスのAuthenticationClientクラスを作成した上で、そのCreateApp()メソッドを実行すればOKです。

  • 認証とクライアント作成
var auth = await authClient.ConnectWithPassword(id, pass);
var client = new MastodonClient(appRegistration, auth);

認証は、OAuthを使用する方法と、メアドとパスワードで行う方法があり、後者は上記の通りauthClient.ConnectWithPassword()メソッドを実行すればOK。
※ただし後者は作者曰く「非推奨」とのことなので、問題なければOAuthを使ったほうがよいみたいです。

前者の場合、作者のREADME.mdによれば

  1. AuthenticationClientクラスのOAuthUrl()メソッドを実行して、urlを取得
  2. 1で取得したurlにアクセスして、APICodeを取得
  3. 2で取得したAPICodeを引数にして、AuthenticationClientクラスのConnectWithCode()メソッドを実行

を実行すればOK…のはずなんですが、なぜか僕の環境ではうまくいかなかったので、今後の宿題にします。*1

で、クライアント作成ですが、ここまでで実行したCreateApp()メソッドの戻り値とConnectWith***()メソッドの戻り値を引数にして、MastodonClientクラスのインスタンスを作成するだけです。

  • タイムラインのストリーミングと更新通知
stream = client.GetPublicStreaming();
var dispString = String.Empty;

stream.OnUpdate += (sender, e) =>
{
    var dispName = e.Status.Account.DisplayName;
    var content = e.Status.Content;
};

タイムラインのストリームは、先程作成したクライアントに各タイムラインストリーム取得用のメソッドが用意されているので、それを使用します。
(今回はGetPublicStreaming()メソッドで、連合タイムラインのストリームを取得しています。)
で、更新された場合、ストリームのOnUpdate()イベントハンドラーが呼ばれるので、その中に追加でやりたい処理を記載します。

なお、上記の通り、引数e(StreamUpdateEventArgs)の

  • Status.Account.DisplayNameに「ユーザー名」
  • Status.Contentに「本文」

が取得できます。

最後に、ストリームのstart()メソッドで、実際にストリーミングを開始します。(なおご想像の通り、stop()メソッドでストリーミングの停止ができます。)
※この時「この呼び出しを待たないため、現在のメソッドの実行は…」という警告が出るかもしれませんが、無視してOKです。

なお、上記ソースの実行結果はこちら。*2
f:id:Makky12:20170503200012p:plain
まあ、いろんな方がブログなどで書いていたので知っていましたが、本文がHTMLタグ付きなんですよね…
本来はその対処も追加しないといけないですが、今回はまあいいでしょう。

というわけで、Masto.netを使用した、簡単なMastodonアプリの作成でした。
せっかくのGW、いろんな方がMastodonライブラリを作成されているので、機会があればMastodonアプリやライブラリの作成に挑戦するのもいいかもしれません。

さて明日からは、そろそろ本腰入れてXamarinプロジェクトを再開しないと。

…なんか普段より、むしろGWのほうがコーディング時間が長いような???

*1:これに限らず、インスタンス環境に依存する?事項がなんか多かった気がします。
初めに使用したインスタンスでは、url&API Codeの取得は問題なく出来たんですが…

あと、初めに使用したインスタンスではCreateApp()メソッド実行時に、強制的に接続が切断されていたのですが、friends.nicoインスタンスにしたら、ソースは1行も変えてないのに、全く問題なくなった…とか。これらは今後、要調査です。

*2:他の方がコンソールアプリでやっていたため、僕はフォームアプリで作成しましたが、手っ取り早く試すだけならコンソールアプリがおすすめです。
フォームアプリだどスレッドセーフとか非同期処理時のフォームコントロールプロパティ設定とかの関係で、コンソールアプリより少々厄介です。