RailsでTypetalkを定期的にスクレイピングして「いいね」数を記録しておく

# なぜこんなことをするのか
Typetalkには「いいね」つける機能があります。
しかしTypetalkには「いいね」を集計するツールがありません。
仕方ないのでapiを叩いて、調べることにしました。

ここで問題が発生しました。
Typetalkのapiは、トピック(掲示板でいうスレッド)の中の投稿の情報が
最新から200件のみしか取得できません。
従って、全件の情報をアプリケーションのデータベースに保持しておきたい場合
定期的にバッチ処理をして、データベースに記録しておきます。
その上で「いいね」数をカウントしてデータベースに格納します。

# バッチ処理にはrakeファイルを使う

以下がrakeファイルのソースコードです。

```post.rake
# encoding: utf-8

namespace :post do
  desc "投稿データを取得する。"

# $ rake post:update
# :environmentは超大事。ないとモデルにアクセスできない

  task :update => :environment do
    topics = Topic.all

    # httpインスタンスの生成
    http = Net::HTTP.new('typetalk.in', 443)
    http.use_ssl = true

    # Typetalkのトークンの代入
    client_id = ENV['CLIENT_ID']
    client_secret = ENV['CLIENT_SECRET']

    # アクセストークンの発行
    res = http.post(
        '/oauth2/access_token',
        "client_id=#{client_id}&client_secret=#{client_secret}&grant_type=client_credentials&scope=topic.read"
    )
    if res.code != '200'
      next
    end
    json = JSON.parse(res.body)
    access_token = json['access_token']

    topics.each do |topic|
      req = Net::HTTP::Get.new("/api/v1/topics/#{topic.topicId}?count=200&direction=backward")
      req['Authorization'] = "Bearer #{access_token}"
      res = http.request(req)
      if res.code != '200'
        next
      end
      res= JSON.parse(res.body)

      res['posts'].each do |post|
        if Post.where(post_id: post['id']).exists? then
          @post = Post.find_by(post_id: post['id'])
          if post['account']['isBot'] == true
            @post.destroy if @post
            next
          end
          @post.like = post['likes'].count
        else
          if post['account']['isBot'] == true
            next
          end
          @post = Post.new
          @post.topic = topic
          @post.post_id = post['id'].to_s

          @post.like = post['likes'].count
          p "UTC時間"
          p post['createdAt']
          p "JST時間"
          p post['createdAt'].in_time_zone
          @post.posted = Time.parse(post['createdAt']).in_time_zone
        end
        @post.save
      end
      topic.updated_at = Time.now()
      topic.save
    end
  end
end
```

# 解説

トピック情報を記録する`Topic`テーブル
投稿情報を記録する`Post`テーブル
の2つを使用して処理をしています。

まず前準備として、`Topic`テーブルにTypetalkから取得できるトピックのIDを入れてください。

topicオブジェクトをeachメソッドで処理して、各トピックごとに先頭200件の投稿情報を取得します。
apiの`count`パラメーターは最大値200にしておきます。
ここらへんは下記のapiのリファレンスを参照ください。
[投稿メッセージリストの取得](https://developer.nulab-inc.com/ja/docs/typetalk/api/1/get-messages/)

```ruby
    topics.each do |topic|
      req = Net::HTTP::Get.new("/api/v1/topics/#{topic.topicId}?count=200&direction=backward")
      req['Authorization'] = "Bearer #{access_token}"
      res = http.request(req)
```

返却されたjsonは、posts単位の情報が、先ほどの`count`パラメーターで設定した数の情報が返却されます。
postのIDが登録済みであれば、update処理、存在しなければcreate処理を行います。

```ruby
      res['posts'].each do |post|
        if Post.where(post_id: post['id']).exists? then
```

各投稿の['account']['isBot']には、botか人間かの判定が格納されているので、
botは必要がなければ、データベースにinsertしないようにしましょう。

```ruby
          if post['account']['isBot'] == true
```

['likes]は「いいね」をしてくれたアカウントの情報が配列で入っているので、`count`メソッドを使用して「いいね」数を計測しましょう。

```ruby
          @post.like = post['likes'].count
```

このrakeファイルの実行コマンドは下記の通り
このコマンドをジョブスケジュラーに登録して、定期実行する
これでTypetalkの全発言を補足することができる。
自分は、Herokuを利用しているので`Heroku Scheduler`を利用している。


```shell-session
$ rake post:update
```

## 最後に
毎度のことながら、Typetalkのapi関連の記事は需要があるのだろうか
一応、備忘録のために残しておこう。