LINEBOT SDK GOを使って月曜日のたわわBOTを作った。

  • 19
    いいね
  • 0
    コメント

この記事はGo (その2) Advent Calendar 2016の8日目の記事です。

皆さんは普段Messengerツールとして何を利用しているでしょうか。
仕事ならSlackやChatwork。プライベートならFacebook Messengerなどいろいろと種類があります。
その中でも日本ではLINEが多いのではないでしょうか。
日本ではLINEの月間アクティブ率が96.6%になったという記事もあります。

http://gaiax-socialmedialab.jp/post-30833/

自分もプライベートではLINEを利用しています。
そして、そのLINEではAPIを利用し、BOTを作成することができます。
既にいくつかの事例もあるようです。

http://blog.minato.jp.net/entry/linebot

この記事では、GoでLINEのBOTを書いたという事例を紹介したいと思います。


what is monday on tawawa?

で、まず月曜日のたわわって何なのという話からですが、

「月曜朝の社畜諸兄にたわわをお届けします」として、2015年2月23日から毎週月曜日にTwitterに投稿されているWeb漫画。「たわわ」とは胸が大きい、つまり巨乳を指し、憂鬱な月曜日に巨乳の女の子のイラスト・漫画を投稿することで、社畜の皆さんを少しでも楽にしようと言うもの。

月曜日のたわわとは

という感じのものです。

Botとしてはこんな感じになっています。
IMG_3142.PNG

チャットにたわわをおくれと投稿すると、月曜日のたわわのテキストと画像URLを返すようになっています。

情報の収集方法

記事を作る際にどうやってURLを取得するかと考えたのですが、
ここはオーソドックスにTwitter APIを使って取得しようと決めました。
ここではanacondaというライブラリを使用しました。

package twitter

import (
    "github.com/teitei-tk/tawawa-bot/config"

    "github.com/ChimeraCoder/anaconda"
)

type Client struct {
    APIClient *anaconda.TwitterApi
    Config    config.Twitter
}

func NewClient() (Client, error) {
    var client Client

    conf, err := config.InitTwitter()
    if err != nil {
        return client, err
    }

    anaconda.SetConsumerKey(conf.ConsumerKey)
    anaconda.SetConsumerSecret(conf.ConsumerSecret)
    api := anaconda.NewTwitterApi(conf.AccessToken, conf.AccessTokenSecret)

    client.APIClient = api
    client.Config = conf

    return client, nil
}

とTwitterClientを作成し、APIを叩きます。

func FetchAllTawawaTweets(client Client, param RequestParametor) (filterdTweets []anaconda.Tweet, err error) {
    var maxRequestTweetID int64
    var sinceID int64
    var tweestCount int
    var tweets []anaconda.Tweet

    for {
        if maxRequestTweetID != 0 {
            param.MaxID = maxRequestTweetID
        }

        res, err := getOwnerTimelineFromTwitter(client, param)
        tweestCount = tweestCount + len(res.Tweets)
        if err != nil {
            return tweets, err
        }

        filterdRes := FilterTawawaTweets(res)
        for _, t := range filterdRes.Tweets {
            if maxRequestTweetID == 0 || maxRequestTweetID > t.Id {
                maxRequestTweetID = t.Id
            }

            if sinceID == 0 || t.Id > sinceID {
                sinceID = t.Id
            }

            tweets = append(tweets, t)
        }

        if maxRequestTweetID == sinceTweetID || tweestCount >= 3200 {
            break
        }
    }

    var showTweetRequestCount = 0
    var requestTweetID = maxRequestTweetID
    for {
        resTweet, err := showFromOwnerTweet(requestTweetID, client)
        if err != nil {
            return tweets, err
        }

        tweets = append(tweets, resTweet)

        requestTweetID = resTweet.InReplyToStatusID
        showTweetRequestCount = showTweetRequestCount + 1
        if showTweetRequestCount >= 100 || resTweet.Id == sinceTweetID {
            break
        }
    }

    return tweets, nil
}

この処理でやっていることは、
まず、投稿者である比村奇石さんのHomeTimelineを取得します。
その際に正規表現でTextを抽出し、フィルタリングを書けます。
が、Hometimelineだけでは全てのURLを取得出来ません。そこで、投稿しているTweetに注目しました。
投稿しているTweetはin_reply_to_statusIDを含んでおり、それを利用することで全ての内容を遡ることが出来ます。
なので、最後のTweetに含まれているin_reply_to_status_idを使い、最初に投稿した内容までを遡っています。

しかし、毎回ユーザーからメッセージが来る度にTwitterAPIを叩くのではすぐにRateLimitが来ます。
なので取得したあとは、RedisにCacheとしてJSON形式で保存をしました。

// Get Tawawa Owner Timeline it's use Cache
func GetAllTawawaTweets(client Client, param RequestParametor) (res UserTimelineResponse, err error) {
    res = UserTimelineResponse{}

    redisClient := config.InitRedis()
    val, err := redisClient.Get(TwitterResponseCacheKey).Result()
    if val != "" {
        err = json.Unmarshal([]byte(val), &res)
        if err != nil {
            return res, err
        }
        return res, err
    }

    result, err := FetchAllTawawaTweets(client, param)
    if err != nil {
        return res, err
    }
    res.Tweets = result

    bytes, err := json.Marshal(res)
    if err != nil {
        return res, err
    }

    err = redisClient.Set(TwitterResponseCacheKey, string(bytes), time.Duration(time.Millisecond*ResponseCacheExpiredAt)).Err()
    if err != nil {
        return res, err
    }

    return res, err
}

所感

最初プライベート用にBOTを作りたいなと思って練習とネタがてら書いてみました。
Goはチュートリアル以外触ったことがなく、今回始めてちゃんと作りましたが、
書いていて面白い言語でした。やはり型は偉大だった。

今回は一部の人以外、正直実用性が薄いBotでしたが、他にもLineBOTでやれることはあると思います。
今後は自分用のLINEBOTを作りたいなと思っています。
例えば、弊社のfreeeを利用し家計簿をつけて、そのAPIを利用し1日の終わりに家計簿を通知したり、
GoogleCalendarの内容を集計し、1日の始まりに通知をするなど用途はいろいろあるはずです。

この記事で少しでも興味を持ってもらえたなら幸いです。

最後にソースコードを置いておくので、興味があればご確認ください。
https://github.com/teitei-tk/tawawa-bot

友だち追加