読者です 読者をやめる 読者になる 読者になる

Raspberry Piに電車遅延通知用のSlack Bot仕込んでみた

我が家でもRaspberry Piを常時起動し始めました。Raspbery Pi+CentOSという組み合わせからくる苦労もありますが、楽しく付き合っています。

Raspberry Piといえば電子工作ですが、単純に常時起動のLinuxとしても有効活用したいと思ったところから、電車遅延通知用のSlack Bot作ることにしました。

元になる電車遅延データ

元となるデータはこちらの鉄道コムさんのRSSデータを使わせていただくことにしました。

www.tetsudo.com

使わせていただくデータはこのあたりです。

関連ファイル

関連するファイルは以下の3ファイルです。RubyスクリプトYAML設定ファイルを準備して同じディレクトリに配置します。

ファイル名 内容
notify_train_delay.rb Rubyスクリプト(systemdによるスケジュール実行)
notify_train_delay.yml YAML形式の設定ファイル
notify_train_delay.dat 重複通知回避用の履歴情報(自動生成)

Rubyスクリプト

Slack通知のために以下のライブラリをインストールしておきます。

gem install slack-incoming-webhooks

スクリプトは以下の通りです。notify_train_delay.rbという名前で保存します。

require 'rss'
require 'yaml'
require 'slack/incoming/webhooks'

config_file = File.expand_path(__FILE__).sub(/\.[^.]+$/, '.yml')
history_file = File.expand_path(__FILE__).sub(/\.[^.]+$/, '.dat')
config = YAML.load_file(config_file)

items = RSS::Parser.parse(config['url']).items
DelayInfo = Struct.new(:title, :pubDate, :description, :link)
delay_data = []
config['railways'].each do |r|
  items.select do |i|
    i.category.content.include?(r['domain']) &&
      i.title.include?(r['railway'])
  end.each do |item|
    delay_data << DelayInfo.new(item.title,
                              item.pubDate,
                              item.description,
                              item.link)
  end
end

history_data = File.exist?(history_file) ? YAML.load_file(history_file) : []
updated_data = delay_data - history_data
open(history_file,'w') { |f| YAML.dump(delay_data,f) }

updated_data.each do |data|
  slack = Slack::Incoming::Webhooks.new "#{config['slack']['webhookurl']}", \
    channel: "#{config['slack']['channel']}", \
    username: "#{config['slack']['username']}", \
    icon_emoji: "#{config['slack']['icon_emoji']}"

  attachments = [{
    title: "#{data.title}",
    text: "#{data.pubDate} \n ``` #{data.description} ``` \n #{data.link}",
    color: "warning",
    mrkdwn_in: ["text"]
  }]
  slack.post "", attachments: attachments
end

YAML形式の設定ファイル

設定ファイルをnotify_train_delay.ymlというファイル名で保存します。

例えば今現在、こちらを確認すると湘南新宿ラインで遅延が発生しています。

設定ファイルの項目における、domainは画面左側の「JR東日本(関東地区)」や「東京メトロ」と、railwayは右側の「東海道線高崎線湘南新宿ライン」と文字列一致を確認して、遅延情報をチェックする仕様です。

---
url: http://api.tetsudo.com/traffic/rss20.xml
railways:
- domain: JR東日本
  railway: 埼京線
- domain: JR東日本
  railway: 湘南新宿ライン
- domain: 東武鉄道
  railway: 東上
slack:
  webhookurl: https://hooks.slack.com/services/XXXXXX/XXXXX/XXXXXX 
  channel: "@yusuke.otomo"
  username: Raspberry Pi
  icon_emoji: ":train:"

systemd timerによるスケジュール起動

スクリプトと設定ファイルの配置が完了したら、スケジュール実行のためにsystemdの設定に入ります。

まずは、サービス定義を作ります。ExecStartに実行するコマンドを定義しておきます。

サービス定義とはなっていますが、一回コマンドが実行したらサービスも停止します。(つまり通常のコマンド実行とほぼ一緒です)

cat <<EOF > /etc/systemd/system/NotifyTrainDelay.service
[Unit]
Description=NotifyTrainDelay

[Service]
Type=simple
ExecStart=/root/.rbenv/shims/ruby /root/tools/notify_train_delay.rb

[Install]
WantedBy=multi-user.target
EOF

タイマーからサービスをスケジュール起動させてます。 重要なのはOnCalendarの項目で、月曜から金曜まで、7-10時まで毎時実行と、17-21時まで毎時チェックかけています。

Unitにはタイマーの実行対象として先ほど作成したServiceを定義します。

cat <<EOF > /etc/systemd/system/NotifyTrainDelay.timer
[Unit]
Description=Runs NotifyTrainDelay Script

[Timer]
OnCalendar=Mon-Fri *-*-* 7,8,9,10,17,18,19,20,21:00:00
Persistent=true
Unit=NotifyTrainDelay.service

[Install]
WantedBy=multi-user.target
EOF

最後にserviceとtimerを有効化しておきます。

systemctl daemon-reload
systemctl enable NotifyTrainDelay.service
systemctl start NotifyTrainDelay.timer
systemctl enable NotifyTrainDelay.timer

タイマーが登録されているか確認します。

systemctl --system list-timers

電車遅延のSlack通知

Slack通知はこのような感じでです。

Slackメッセージ内のリンクに飛ぶと詳細が見れます。