Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

この記事は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

友だち追加

teitei_tk
Software Engineer
https://teitei-tk.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした