社会人だからプロデューサー予定表を毎朝Slackに通知させる

こんにちは!この記事はアイドルマスター Advent Calendar 2016 - Adventarの21日目の記事です。
初参加になりますがよろしくお願いします。

はじめに

こんにちは、堀裕子担当プロディーサーをしています @kahun_mask です。
「早くVRでユッコの実装してくれないかな...(でもクリスマスはVRで唯ちゃんと一緒にSnow Wings生活だ!)」など最近は考えて生きています。

突然ですがプロデューサーなら今日のスケジュールくらい毎朝チェックしなきゃだめですよね。

私は副業でエンジニアをしています。
正直毎朝プロデューサー予定表を見たり、予定表を見てカレンダーを都度更新するのもしんどいです。
更に言うと通知してくれるツールとかは一つにまとめたい。

ということで、今イケてる開発現場で使われてるコミュニケーションツールのSlackに集約することにしました。

※何番煎じかわからないですがプロデューサー予定表ネタです

環境の準備

  • Node.js (LTS)
  • Heroku
  • Slack
    • Incoming WebHooks

普段使ってる言語がJavaScriptなのでここではNode.jsを使って、開発します。
アプリケーションはデプロイするのが楽かつ無料なのでHerokuを使います。
最後にお馴染みのSlackでは、インテグレーションを使います。
Incoming WebHooksですが、これは発行されるURLに向けてPOSTを送ることで任意のメッセージを特定のチャンネルに送ることのできるSlackのインテグレーションです。
Slackの通知にはこれを使います。

Web Scraping

肝心のプロデューサー予定表、毎回目視するのが辛いのでスクレイピングして見ます。
Node.js製のスクレイピングツールとして使える、cheerio-httpcliを使います。
使い方はこんな感じです。

const cheerio = require('cheerio-httpcli');
const moment = require('moment'); // 日付操作
const URL = 'http://idolmaster.jp/schedule/'; // プロデューサー予定表のURL

// URL?ey=2016&em=12 で2016年12月のスケジュールを見れる
function scrape(url, query = { ey: moment().format('YYYY'), em: moment().format('MM') }) {
  return cheerio.fetch(url, conf).then(result => {
    if (cheerio.error) {
      throw cheerio.error;
    } else {
      return result; // ここにいろいろ入ってくる
    }
  })
}

まずは、URLを指定して取ってくるところまでです。

DOMの構造ですが基本的には .List に含まれるのが予定表ですが、最初の <tr> 3つには特に欲しいデータが含まれていないので省きます。
テーブル内のそれぞれのスケジュール情報は以下のような形で構成せれています。

  • .day2: 日付.(一つ前が同日の場合はなし)
  • .week2: 曜日.(一つ前が同日の場合はなし)
  • .time2: 時間 or 何の日
  • .performance2: 765, シンデレラ, ミリオン, SideM, (+α)
    • 01 ~ 12までありそれぞれジャンルが振り分けられている
  • .genre2: ジャンル
  • .article2: 予定名
<table class="List">
  <tr>
    <td class="day2"></td>
    <td class="week2"></td>
    <td class="time2"></td>
    <td class="performance2"></td>
    <td class="genre2"></td>
    <td class="article2"></td>
  </tr>
</table>

この構造を上手く読み解いて一つの扱いやすいJSON形式にします。

{
  "schedules": [
    // ...
    {
      "datetime": 1482931800000,
      "perf": [
        "765"
      ],
      "genre": "webラジオ",
      "text": "THE IDOLM@STER STATION!!!",
      "link": "http://ch.nicovideo.jp/imas-station"
    },
    {
      "datetime": 1483012800000,
      "perf": [
        "765",
        "シンデレラ",
        "ミリオン",
        "SideM"
      ],
      "genre": "ニコ生",
      "text": "アイドルマスター年末特別ニコ生番組「ゆくM@S くるM@S 2016」",
      "link": "http://live.nicovideo.jp/gate/lv284115861"
    },
    // ...
  ]
}

Slack

Slackに送信するためにはIncoming WebHooksを有効にする必要があります。
送信先のchannelとか設定して、発行されるURLを保存。
後はそこに向けてPOSTを投げるだけです。

const axios = require('axios');
const webhookURL = 'https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx';

const moment = require('moment');

function Attachment(schedule) {
  this.title = schedule.text;
  this.title_link = schedule.link;
  this.color = 'good';
  this.text = [
    `kinds: ${schedule.perf.join(', ')}`,
    `genre: ${schedule.genre}`,
    `start: ${moment(schedule.datetime).format('HH:mm')}`
  ].join('\n');
}

function postSchedules(schedules = []) {
  var data = {};
  if (schedules.length) {
    data.text = '今日のアイマスの予定!';
    data.attachments = schedules.map(schedule => new Attachment(schedule));
  } else {
    data.text = '今日アイマスの予定はありません!';
  }
  return axios.post(webhookURL, data);
}

Attachmentを使うとなんとなく一度にLinkがきれいに別れた投稿ができてAPIの制限にかからなくていい

Heroku Scheduler

あとは、通知してほしい時間です。
Heroku Schedulerで好きな時間に通知してくれるように設定します。
(当たり前だけど迷惑のかからないように1日一回のリクエストにしています。)

起点となるjsに

#! /app/bin/node

を先頭に付け加えてあげて,
一度herokuのリモートにデプロイした後に

# スケジュールのタスクをテスト
$ heroku run node index.js

# 管理面をブラウザで開く
$ heroku addons:open scheduler

管理面にJobの登録(ここで気をつけるのはサーバーのタイムゾーンとで時差があるので差分を見て設定すること)

諸々終わっていよいよ、Slackにでてきたものがこれ!

f:id:kahun_mask:20161221013507p:plain

ちょっと簡素だけどこれで大事な予定は見逃さないはず!

これで見事に今日の公式の予定を見逃さないようになりました!

コードとかあるけど雑に作ったから見ないでね!

さいごに

こんな感じで私生活やら副業中にまでアイマス自動化をどっぷりと勧めているわけですが、おかげさまですごく充実した毎日を過ごせています。
これを実装したおかげで、アイマスへの投資額が2割ほど増えたり。 社畜!
あと、使いやすいJSON形式に出来たので、CalDAVにしてiPhoneとかGoogleカレンダーとかと同期させたり他にもいろいろできそうだなーと妄想してます。

ああ、年明けのYes! Party Time!!が楽しみだなー。ウェーブ後も「ハイ!ハイ!」と続くからみんなも予習忘れずに\(^o^)/

アイマスハッカソン、次回があれば参加したい(^q^)