togatttiのエンジニアメモ

過度な期待はしないでください.

Slackでアニメ放送時間の通知Botを作ってみた

最近、Slackを使ったアプリネタを見る機会が増えてきた気がします。

なので、自分でもやってみようと思いました。

今回は、アニメ放送時間の一時間前になったら、定期通知するBotを作ってみます。

具体的には、

  1. Web UIにアニメの放送時間を登録
  2. Cronファイルを更新
  3. 放送時間の一時間前になったら、Slack APIを叩いてPost

という仕組みのものを作ります。

手順

Web UIはRailsで簡単なものを用意します。

必要なモジュールの準備、Scaffold作成

$ rails new animelovers --skip-bundle
$ cd animelovers

Gemfileには、以下を追記します。

slack-apiは、文字通りSlackの操作に使い、wheneverは、crontabの設定内容を管理するモジュールです。

# for slack
gem 'slack-api'

# for crontab
gem 'whenever', require: false

bundle installします。

bundle install --path vendor/bundle

次に、scaffoldでひな形を作ります。

$ bundle exec rails g scaffold Anime title:string days_of_the_week:string start_time:time
$ bundle exec rake db:migrate

Slackの設定

SlackのAPIトークンを、config/initializers/slack.rb に下記のように書きます。

APIは、Slack Web API | Slack から発行します。

#config/initializers/slack.rb
require 'slack'

Slack.configure do |config|
  config.token = 'xoxp-9469336594-9469676821-9469781495-******'
end

これで、Rails内の各所から、Slackにポストできます。

Postは、次の一行で行うことができます。text, username, channelは、適宜変更します。

Slack.chat_postMessage(text: 'テスト', username: 'kenta', channel: '#random')

Wheneverの設定

UIから、新規登録、更新、削除が行われたときに、config/schedule.rbが更新され、crontabファイルも更新されるようにします。

下記のような、ヘルパーを書きます。

何点か補足しておくと、crontabに書く、* * * * * のような、実行時間はcrontimeで、適当に整形しています。

Slackのusernameや、channelは適宜変更します。

config/schedule.rbを書き換えた後は、whenever --update-crontabを実行しています。

# app/helpers/whenever_helper.rb
module WheneverHelper
  def update_schedule 
    schedule_path = File.join(Rails.root, 'config/schedule.rb')

    File.open(schedule_path, 'w') do |f| 
      f.flock File::LOCK_EX
      header = "set :output, 'log/output.log'\n"
      header << "set :environment, :development\n"
      f.write(header)
    end

    animes = Anime.all
    animes.each do |anime|
      content = <<-FILE
# minute, hour, day_of_month, month, day_of_week
every '#{crontime(anime)}' do
    runner "Slack.chat_postMessage(text: '#{anime.title} が あと一時間で始まるよ!!', username: 'kenta', channel: '#random')"
end

      FILE
      File.open(schedule_path, 'a') do |f|
        f.flock File::LOCK_EX
        f.write(content)
      end
    end
    system("bundle exec whenever --update-crontab")

  end 

  private
  def crontime anime
     if anime.start_time.to_s.match(/\s(\d{2}):(\d{2}):/)
       hour = "%01d" % $1
       hour = hour.to_i
       minute = "%01d" % $2
     end

     wday = week_list[anime.days_of_the_week]

     if hour == 0
       hour = 23
       if wday == 0
         wday = 6
       else
         wday = wday - 1
       end
     else
       hour = hour - 1
     end

    "%s %s * * %s" % [minute, hour, wday]
  end

  def week_list
    {'Sun' => 0, 'Mon' => 1, 'Tue' => 2, 'Wen' => 3, 'Thr' => 4, 'Fri' => 5, 'Sat' => 6}
  end

end

次に、コントローラーに、反映させます。

WheneverHelperはこのあたりに、

# app/controllers/animes_controller.rb
class AnimesController < ApplicationController
  include WheneverHelper

update_scheduleはcreateと

  # POST /animes
  # POST /animes.json
  def create
    @anime = Anime.new(anime_params)

    respond_to do |format|
      if @anime.save
        update_schedule

updateと

  # PATCH/PUT /animes/1
  # PATCH/PUT /animes/1.json
  def update
    respond_to do |format|
      if @anime.update(anime_params)
        update_schedule

destroyに

  # DELETE /animes/1
  # DELETE /animes/1.json
  def destroy
    @anime.destroy
    update_schedule

それぞれ、追記します。

ソースコードGitHubに挙げました。

github.com

実演

このように登録しておくと、

f:id:togattti1990:20150823215345p:plain

こうやって通知してくれます。

f:id:togattti1990:20150823215324p:plain

ちなみに、config/schedule.rbには、次の行が追記されます。

# minute, hour, day_of_month, month, day_of_week
every '52 21 * * 0' do
    runner "Slack.chat_postMessage(text: 'テストアニメ が あと一時間で始まるよ!!', username: 'kenta', channel: '#random')"
end

これで、アニメを見逃すことが無くなる、やったー\(^o^)/

参考にしたリンク

10分コーディング! Rubyでつくる Slack時報 | マネーフォワード エンジニアブログ