mastodonのtootを原始人くんが喋ってくれるbot作った

## TL;DR
* [オレ プログラム ウゴカス オマエ ゲンシジン ナル \- Qiita](https://qiita.com/Harusugi/items/f499e8707b36d0f570c4)
    * アリガト　トモダチ

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">アリガト トモダチ <a href="https://t.co/58aOE2P9g4">https://t.co/58aOE2P9g4</a></p>&mdash; 70_10 (@70_10) <a href="https://twitter.com/70_10/status/1096233734569783296?ref_src=twsrc%5Etfw">2019年2月15日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
* ぼくも原始人くんとなかよくなりたいになった

## まずはcliで作った

* そもそもmasotodonのAPI叩いたりしたことなかったので勉強がてらcliで作った
* goでmastodonのクライアントあるんだっけって思ったけど速攻で [mattnさんのやつ](https://github.com/mattn/go-mastodon) 見つけた
* 以下原始人くんが最新のトゥートを拾ってきて翻訳してトゥートするとても簡単なコード

```golang
package main

import (
	"context"
	"fmt"
	"github.com/mattn/genshijin"
	"github.com/mattn/go-mastodon"
	"log"
)

func main() {
	c := mastodon.NewClient(&mastodon.Config{
		Server:       "https://example.com",
		ClientID:     "",
		ClientSecret: "",
	})
	err := c.Authenticate(context.Background(), "", "")
	if err != nil {
		log.Fatal(err)
	}
	s, err := c.GetAccountStatuses(context.Background(), mastodon.ID("2"), &mastodon.Pagination{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(s[0].Content)
	_, err = c.PostStatus(context.Background(), &mastodon.Toot{
		Status: genshijin.Shaberu(s[0].Content),
	})
	if err != nil {
		log.Fatal(err)
	}
}
```


## RSSを取得してtootさせるようにしてみた

* 自身がtootしたらすぐ原始人くんがtootするようにしたかった
* 「自身がtootした」っていうeventにhookさせる簡単な方法が思いつかなかったのでzapierでRSS取得して自鯖にPOSTするってZAP設置してみた
    * mastodon本体に手を入れたくなかった
    * サーバー側に新着かどうか判定するために保存するcache等もつくりたくなかった
* FTLには出したくなかったので未収載にした


```golang
package main

import (
	"context"
	"fmt"
	"github.com/mattn/genshijin"
	"github.com/mattn/go-mastodon"
	"log"
	"net/http"
	"strings"
)

func handler(w http.ResponseWriter, r *http.Request) {
	c := mastodon.NewClient(&mastodon.Config{
		Server:       "",
		ClientID:     "",
		ClientSecret: "",
	})
	err := c.Authenticate(context.Background(), "", "")
	if err != nil {
		log.Fatal(err)
	}
	r.ParseForm()
	toot := genshijin.Shaberu(strings.Join(r.Form["toot"], ","))
	_, err = c.PostStatus(context.Background(), &mastodon.Toot{
		Status:     toot,
		Visibility: "unlisted",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(w, "Hello, World")
	fmt.Println(toot)
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8888", nil)
}
```

* 作ったはいいけど15分に一度ぐらいの周期っぽいので全然おもしろくなかった

## 原始人くん自ら取りに行かせるようにしてみた

* 今現在動いてるのがこれ
* 最後のtootを last_toot に入れておいて同じであれば5秒スリープして再取得とか言うぶん投げっぷり
    * 実装中なんどかHttp Status Code 429 が帰ってきてこまりました
    * CPUが回りすぎているのでなんとかせねばならん
* boostを弾きたいのでboostかどうかを `Status.Reblog != nil` で判定してるのだけど行儀が良いのかよくわからんになってる

```golang
package main

import (
	"context"
	"fmt"
	"github.com/mattn/genshijin"
	"github.com/mattn/go-mastodon"
	"log"
	"time"
)

func main() {
	c := mastodon.NewClient(&mastodon.Config{
		Server:       "",
		ClientID:     "",
		ClientSecret: "",
	})
	err := c.Authenticate(context.Background(), "", "")
	if err != nil {
		log.Fatal(err)
	}
	last_toot := ""
	for {
		s, err := c.GetAccountStatuses(context.Background(), mastodon.ID("2"), &mastodon.Pagination{})
		if err != nil {
			fmt.Println(err)
			time.Sleep(60 * time.Second)
			continue
		}
		if s[0].Reblog != nil {
			time.Sleep(5 * time.Second)
			continue
		}
		toot := genshijin.Shaberu(s[0].Content)
		if last_toot != toot {
			c.PostStatus(context.Background(), &mastodon.Toot{
				Status:     toot,
				Visibility: "unlisted",
			})
			fmt.Println(toot)
			last_toot = toot
		}
		time.Sleep(5 * time.Second)
	}
}
```

## [追記]StreamingAPIでやった

* そういえばStreamingAPIあったなってなって[マストドンのタイムラインをgo\-mastodon のWebSocketを使用し取得する \- Qiita](https://qiita.com/S-YOU/items/cf677ae282bd6f38fbbb) を参考にサクッと実装したら無茶がなくなった。
* 公開のLTLのトゥートを拾うのでユーザ参加させれば誰でも原始人くんはつぶやくはず

```go
package main

import (
        "context"
        "fmt"
        "github.com/mattn/genshijin"
        "github.com/mattn/go-mastodon"
        "log"
)

func main() {
        c := mastodon.NewClient(&mastodon.Config{
                Server:       "",
                ClientID:     "",
                ClientSecret: "",
        })

        err := c.Authenticate(context.Background(), "", "")
        if err != nil {
                log.Fatal(err)
        }

        wsc := c.NewWSClient()
        q, err := wsc.StreamingWSPublic(context.Background(), true)
        if err != nil {
                log.Fatal(err)
        }
        for e := range q {
                if t, ok := e.(*mastodon.UpdateEvent); ok {
                        if t.Status.Sensitive {
                                continue
                        }

                        fmt.Printf("\x1b[37m[%s] \x1b[35m%-20s: \x1b[33m%s\n", t.Status.CreatedAt.Local().Format("15:04:05"), t.Status.Account..Acct, t.Status.Content)
                        c.PostStatus(context.Background(), &mastodon.Toot{
                                Status:     genshijin.Shaberu(t.Status.Content),
                                Visibility: "unlisted",
                        })
                }
        }
}
```