家庭向けなスタンスでLINEのbotを作っている話その1

Posted by jolantern on Friday, January 20, 2017

この記事を書いている途中に、LINEのbotコンテストみたいなのが行われているのを知りました。

LINE BOT AWARDS

参考になれば幸い。といいつつ私も応募したいな、アイディアらしいアイディアがあるかと言われると言葉につまりますが。

動機

今、彼女と同棲しているのですが、色々お互いへの連絡事項というのが有ります。 例えば、いつ飲み会で遅くなるだとか、明日ゴミの日だぞとか、そんな些細なもの。 口頭で済むものはそれでいいんですが、スケジュールの共有とかとなると後でLINEしといて!みたいなフェーズが必要になってめんどくさいんですよね。 なのでBotに助けてもらおう、というのが動機。

なぜLINEのbotなのか

この手の事をしている記事って代替Slackを使ってるイメージが有ります。私も最初そちらを考えました。 でも、その手のことをやっている人たちの想定するユーザ(彼女とか彼氏とか)も、そういうツールを使うエンジニアなんでしょうか。 わざわざ新規でチャットツールを導入するのは億劫ですし、非エンジニア(少なくともSlackを使っていない)に新たに導入して使い方を覚えてもらうみたいなのはユーザフレンドリーではありませんよね。 だからLINEです。スタンプ使えるしね?

LINEのAPIのこと

LINEはいつだったかAPIを公開してくれています。

Messaging APIのご紹介 | LINE Business Center

ここから登録しましょう。 気をつけないといけないのは、ここでDevelopver Trialを選ばないとだめ。

なぜなら、Messaging APIを選ぶとメッセージのPushができないのです。 定期的になんかしゃべってくれるBotを作ろうと思うと、Pushは必須ですからね。 ちなみに友達を50人までしか登録できないという制約がつくので、それ以上に使いたいならお金を払いましょうね。

登録が完了すると、LINE DevelopersとLINE@Managerというのが使えるようになります。 LINE@Managerの方で、サイドバーにBot設定というのがアカウント設定の配下にあるのでそこで有効にしましょう。 それから、LINE Developersでchannel access tokenとかを生成してあげます。これは、他のWebAPIを使ったことの有る人ならなんとなくわかると思います。

LINEのAPIに於ける鬼門は、Webhookの設定ですね。 Webhookの設定はLINE Developersの設定でできるんですが、UIがいまいち微妙(一番下のEditを押さないと編集できない)なのと、SSL通信しか受け付けてくれません。 なので私はConohaVPSにSSLサーバを立ててあげました。

Conoha VPS

<code>server {
        listen 443 ssl default_server;
        server_name domain;
        ssl on;
        ssl_certificate /etc/letsencrypt/live/domain/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        location / {
             proxy_set_header Host $host;
             proxy_set_header X-Real-IP $remote_addr;
             proxy_set_header X-Forwarded-Proto https;
             proxy_set_header X-Forwarded-Host $host;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

             proxy_pass http://localhost:4567;
             proxy_redirect http:// https://;
       }

        #location / {
        #       proxy_set_header Host $http_host;
        #        proxy_pass https://localhost:4567;
        #}
}
</code>

こんな感じの設定をnginxに追加してあげました。 ちなみにSSL証明書の発行はLet’s Encryptでできます。無料でできて、ちゃんと使える。素晴らし。

Botのコード

構成はこんな感じになりました。

予定の共有はどこいったんだよって感じですが、それは後々ーとおもってとりあえず、

  1. 電車遅延を教えてくれる
  2. 天気を教えてくれる
  3. ゴミの日を通知してくれる

機能を実装しました。

毎朝教えて欲しいので、clockworksというGemを使って定期実行するようにしました。

botの基幹部分

botに発信させるための基幹部分です。rubyなどの言語で、LINEがライブラリを公開してくれているので簡単に作れるんですねー。

<code class="ruby"># app.rb
require 'sinatra'
require 'webrick'
require 'webrick/https'
require 'openssl'
require 'line/bot'
require "json"

webrick_options = {
    Port: 4567,
    #:DocumentRoot       => "./public",
    Logger: WEBrick::Log.new($stderr, WEBrick::Log::DEBUG),
    BindAddress: 'your.server.dmain'
    #:DocumentRoot       => "/ruby/htdocs",
}

class LineBot < Sinatra::Base


  def client
      @client ||= Line::Bot::Client.new do |config|
          config.channel_secret = 'LINE Developerで作れるチャンネルシークレット'
          config.channel_token = '同じく生成できるチャンネルトークン'
      end
  end


end

Rack::Handler::WEBrick.run LineBot, webrick_options
</code>

ここまでで、LINEのbotで発信をするために必要なものが揃います。 で、肝心の発信ですがまずはrepryからやりましょう。 ここでは https://domain/callback へのポストが有ると発火する用になっています。

<code class="ruby"><br></br>  post '/callback' do

      body = request.body.read

      signature = request.env['HTTP_X_LINE_SIGNATURE']
      unless client.validate_signature(body, signature)
          error 400 do 'Bad Request' end
      end

      events = client.parse_events_from(body)
      events.each do |event|
          case event
          when Line::Bot::Event::Message
              case event.type

                  message = {
                      type: 'text',
                      text: event.message['text']
                  }
                  client.reply_message(event['replyToken'], message)
                end
          end
      end

      'OK'
  end
end

</code>

これでオウム返しbotが作成できるはず。完成したやつから切り貼りしてるから、こういうのやりづらいですね。ブログに書く気が有るときはブランチ切っておいておくとかするといいんだろうか。 この状態で下記のGemfileの内容でbundle installして、bundle exec ruby app.rbとかすればbotが起動します。

<code>source "https://rubygems.org"

gem 'line-bot-api'
gem "sinatra"
gem "sinatra-contrib"
</code>

その状態で、botに話しかけてあげるとあなたの言ったことを鸚鵡返ししてくると思います。

次にPushですね。

<code>get '/push' do

    message = {
      type: 'text',
      text: 'hello'
    }

    response = client.push_message("TALK_ID", message)
    p response
end

</code>

TALK_IDには、発言先のグループや相手のIDをいれればいいようでした。私はグループでの発言に限定していたのでグループIDを取得して、それを入力していましたが、1:1で話すときはどうするんすかね。 ちなみにグループIDを調べるときは、

<code><br></br>      events = client.parse_events_from(body)
      events.each do |event|
          case event

          when Line::Bot::Event::Join
            p event
            if event["type"] == "join" && event["source"]["type"] == "group"
              File.open("group.txt", "w") do |f|
                f.puts(event["source"]["groupId"])
              end
            end
~~~~~~~~~~~~~

</code>

こんな感じのコードを書いて、botを立ち上げた状態でグループに参加させるとファイルに出力してくれます。 まあ、多人数が利用することを想定するならもうちょっとマシな書き方をするんでしょうが、今回は家庭用なのでいいのです。

ここらで代替の基幹部分は紹介できたと思うので次はお天気とかの話と、動作するとどんな感じかをお見せしようかと。気が向けば。