10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

山の天気を教えてくれるLinebotを作る on Heroku(Freeプラン)

Last updated at Posted at 2019-08-01

登山仲間と登山に行くときに天気をググってURLをLineグループで共有することがよくあるので、少しでも手順を簡略化するために山の天気を教えてくれるLinebotを作ることにしました。

※注意
この記事ではHerokuやLINE Developersを使っています。
HerokuやLINE Developersは次々に仕様が変わっていくので料金など重要な情報は公式情報を確認するようにしてください。


2022/08/26追記
この記事で使っていたHerokuの無料プランは2022年11月に廃止されることが決定しました。


仕様

Lineで「○○山 天気」とコメントすると、Linebotがその山の天気予報ページをコメントしてくれる。
botがコメントする天気ページは下記の「てんきとくらす」を使っています。山の天気がわかるので大変お世話になっているサイトです。
サイトポリシーに『・本サイトへのリンクは原則自由であり、事前連絡も必要ありません。』と書いてあるので堂々と使って大丈夫でしょう。
https://tenkura.n-kishou.co.jp/tk/policy.html

システム構成

システム構成は下記の通りです。
スクリーンショット 2019-07-25 14.45.51.png

インフラ

Lineメッセージを解析して該当の天気URLを返却するAPIサーバーは、1アプリを動かすだけであれば料金がかからないherokuのFreeプランを使うことにしました。
DBも10,000行までであれば無料で使えます。
(※2019年7月時点)
https://jp.heroku.com/pricing

Line Developers

Linebotを作るにはLine Developersに登録する必要があります。
https://developers.line.biz/

登録方法は下記記事と同じなのでこの記事では省略します。
GCPを使って日次のLine push通知を作ってみた

上記の他にWebhookの設定が必要ですが、それはherokuの環境構築ができた後に設定するので後述します。

開発環境

Ruby on Railsで開発しました。
DBはherokuにあわせてpostgres。
ソース管理はheroku内のgithubを使うこともできるが、自分のgithubで管理したかったので自分のgithub経由でherokuへデプロイするようにしました。
開発環境ではdockerを使っています。

開発

開発環境構築

Dockerを使って開発環境を構築します。
最初にDockerfile, docker-compose.yml, Gemfile, Gemfile.lockを作成します。

Dockerfile
FROM ruby:2.6.3

RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y build-essential nodejs yarn postgresql-client

RUN mkdir -p /app
WORKDIR /app

COPY Gemfile Gemfile.lock ./

RUN gem update bundler
RUN bundle install
COPY . .

CMD ["rails", "server", "-b", "0.0.0.0"]
docker-compose.yml
version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 5432
      DATABASE_USER: postgres
      DATABASE_PASSWORD:
    links:
      - db
    volumes:
      - .:/app
    ports:
      - 3000:3000
    tty: true
    stdin_open: true
  db:
    image: postgres:11.3
    ports:
      - "5432"
    volumes:
      - db-volume:/var/lib/postgresvolumes
volumes:
  db-volume:

Railsのバージョンはなんでもよかったので現時点で最新の6にしてみた。

Gemfile
source 'https://rubygems.org'

gem 'rails', '6.0.0.rc1'

Gemfile.lockは空

ファイルが準備できたらdocker上にrails環境を構築します。
まずは環境をビルドします。

# build
docker-compose build

続いてdocker上にrails環境を生成します。

# rails new
docker-compose run web bundle exec rails new . --database=postgresql --skip-test

データベースの設定を行います。
development, test環境のアカウントは適当にpostgres/passwordにしました。(環境変数で変更もできます)
productionはherokuで発行されるurlを環境変数DATABASE_URLで指定できるようにしています。

config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
development:
  <<: *default
  database: db_development
  host: <%= ENV.fetch('DATABASE_HOST') { 'localhost' } %>
  port: <%= ENV.fetch('DATABASE_PORT') { 5432 } %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('DATABASE_USER') { 'postgres' } %>
  password: <%= ENV.fetch('DATABASE_PASSWORD') { 'password' } %>
test:
  <<: *default
  database: db_test
  host: <%= ENV.fetch('DATABASE_HOST') { 'localhost' } %>
  port: <%= ENV.fetch('DATABASE_PORT') { 5432 } %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('DATABASE_USER') { 'postgres' } %>
  password: <%= ENV.fetch('DATABASE_PASSWORD') { 'password' } %>
production:
  <<: *default
  url: <%= ENV['DATABASE_URL'] %>
  database: db_production

rails環境の準備ができたのでサーバーを起動します。

# サーバー起動(docker-composeのcommandをバックグランドで起動)
docker-compose up --build -d

http://localhost:3000/ で接続確認。
下記が表示されれば疎通OKです。
スクリーンショット 2019-07-25 14.59.37.png

実装

line-botを実装します。
rubyで実装する場合、'line-bot-api'というgemを使うと良いです。
githubにLinebotのサンプルもあるので参考にして作りました。sinatraを使っているのでRailsで作る場合は少し読み替える必要はあります。
https://github.com/line/line-bot-sdk-ruby

私は下記のように実装しました。基本、サンプルと同じように作っています。

config/routes.rb
  post '/callback' => 'linebot#callback'
app/models/yama.rb
#
# yamasテーブルに山の名称(name)、URLを作るためのcode、typeを保持しています。
#
class Yama < ApplicationRecord
  self.inheritance_column = :_type_disabled

  class << self
    def find_by_message(message)
      # あんまりイケてないけど全部の山の名称を1件ずつメッセージに含まれているか確認して
      # 含まれていたらその山情報を返却する。
      # 本当は全文検索とかした方が良いと思うけどデータ数少なくてこれでも困っていないのでとりあえずこれで;;
      all.find_each do |yama|
        return yama if message =~ /#{yama.name}/
      end
    end
  end

  def url
    "https://tenkura.n-kishou.co.jp/tk/kanko/kad.html?code=#{code}&type=#{type}&ba=hk"
  end
end
app/controllers/linebot_controller.rb
class LinebotController < ApplicationController
  require 'line/bot'

  protect_from_forgery except: [:callback]

  def callback
    body = request.body.read

    signature = request.env['HTTP_X_LINE_SIGNATURE']
    unless client.validate_signature(body, signature)
      head :bad_request
      return
    end

    events = client.parse_events_from(body)

    events.each { |event|
      case event
      when Line::Bot::Event::Message
        case event.type
        when Line::Bot::Event::MessageType::Text
          # メッセージに"天気"という文字が入っているか確認。入っていなければSKIP
          next unless event.message['text'] =~ /天気/

          if yama = Yama.find_by_message(event.message['text'])
            # 山が見つかればメッセージを生成してreplyする
            reply_text = "#{yama.name}の天気\n#{yama.url}"
            message = {
              type: 'text',
              text: reply_text
            }
            client.reply_message(event['replyToken'], message)
          end
        end
      end
    }

    head :ok
  end

  private

  def client
    @client ||= Line::Bot::Client.new do |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    end
  end
end

実装が終わったら自身のgithubへPushしましょう。

heroku

環境構築

app新規作成

herokuのdashboardを開き、"Create new app"を選択します。
スクリーンショット 2019-08-01 14.43.41.png

適当な名前をつけてappを作成します。
スクリーンショット 2019-08-01 14.43.20.png

Deployタブ

Deployment methodで"GitHub"を選択して、Connect to GitHubで先ほどpushしたリポジトリを選択します。
スクリーンショット 2019-08-01 14.45.46.png

Resourcesタブ

Add-onesで"Heroku Postgres"を検索して選択します。
スクリーンショット 2019-08-01 14.52.00.png
Hobby Dev-Freeを選択して、Provision
スクリーンショット 2019-08-01 14.53.16.png
これでpostgresが利用できます。
念のため、同ページに記載されているEstimated Monthly Costが$0.00(費用がかかっていない)ことも確認しておきましょう。
スクリーンショット 2019-08-01 14.55.38.png

Postgres

先ほど作成したHeroku PostgresがリンクになっているのでそこをクリックしてPostgresの情報を確認します。
リンク先のSettingsタブにDatabase Credentialsという項目があるのでView Credentials...をクリックします。
その中に表示される"URI"をDB接続で利用します。
スクリーンショット 2019-08-01 14.58.45.png

Settingsタブ

SettingsタブのConfig Varsに必要な環境変数を足します。
DATABASE_URLはすでに設定されていると思いますが、もしなければ先ほど確認したpostgresのURIを設定します。
LINE_CHANNEL_SECRET、LINE_CHANNEL_TOKENはLineDevelopersで作成したチャンネルページに記載されているものを設定します。(LINE_CHANNEL_TOKENはLINE Developersのアクセストークンのことです)
あとはRailsで必要な諸々の環境変数を設定します。
スクリーンショット 2019-08-01 15.04.20.png

同じタブに"Domains and certificates"にURLが記載されているので、それに"/callback"をつけたものがWebhookURLです。

Deploy

Deployタグの下部にある"Manual deploy"をクリックしてデプロイします。

データを入れる

Postgresを使っているので必要に応じてデータを入れてください。
Rails consoleが使えるのでHerokuのドキュメントを参考にしてください。
https://devcenter.heroku.com/articles/getting-started-with-rails5#run-the-rails-console

Webhookの設定

HerokuのSettingsタブで確認したWebhookURLをLINEDevelopersのWebhook URLに設定します。
スクリーンショット 2019-08-01 15.17.05.png

個別で利用するだけならこれだけでOKですが、もしLINEグループにこのチャンネルを友達追加したい場合はすぐ下にある"Botのグループトーク参加"を利用するにする必要があります。

最後に

ここまできたらあとはLine上で話しかけるだけです。Let's talking!!

実際に作成した時とこの記事を書くまでに時差があり、手順が間違っていたり抜けていたりするかもしれないのでおかしな点があればご指摘いただけるとありがたいです。
herokuのログはheroku logsで確認できるのでrailsがうまく動かない場合は参照してみてください。
https://devcenter.heroku.com/articles/getting-started-with-rails5#view-logs

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?