プログラマブルでないグループウェアだってスケジュールをSlackへ通知できるんですよ

そう、WebExtensionならね。

### お使いのグループウェア(GW)はプログラマブルですか?

登録していたスケジュールを ~~たまったRSSを読んでいる間に~~ すっかり忘れていたという事態がまれによくありますよね。
開始数分前にSlackへリマインドが来ればこの病にも対処できるかと思ったのですが、残念ながら僕の会社で導入しているGWでは、スケジュールのリマインダは日毎で、そもそもメールでしか通知してくれません。

GWがプログラマブルならプラグイン機能的なものがあったりして、きっと楽々実現できるのでしょうがそれも見当たらず。
仕方がないのでWebExtensionでブラウザの拡張機能として実装してみることにします。

### 条件

- GWがブラウザで見られる
- インターネットに接続している
- Chromeが使える(たぶんFireFoxでも大丈夫)


### さっそく実装

まずは、http://qiita.com/vmmhypervisor/items/18c99624a84df8b31008 あたりを参考に参考にしてIncomingWebhookのURLを取得しておきます。
通知先は自分のDMがよいと思います。

実装の詳しいところは http://qiita.com/narupo/items/0a15577d25ad00e3e5e3 とか、公式の[チュートリアル](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Your_first_WebExtension) を見ていただくとして、最低限必要なファイルは2つです。

```json:manifest.json
{
  "manifest_version": 2,
  "name": "NotifyYourGwSchedule",
  "version": "1.0",
  "description": "Notify your schedule",
  "icons": {},
  "content_scripts": [
    {
      "matches": ["{Your GW's URL}"],
      "js": ["main.js"],
      "run_at": "document_idle"
    }
  ]
}
```

```js:main.js(エントリポイント)
'use strict';

const WEBHOOK_URL = '{Set your webhook url}';
const BUFFER_TO_START_MIN = 5;

const postToSlack = (json, msgKey) => {
  const request = new XMLHttpRequest();
  request.onreadystatechange = () => localStorage[msgKey] = 'already sent';
  request.open('POST', WEBHOOK_URL);
  request.send(JSON.stringify(json));
}

const buildSlackMessage = (text, link) => ({
  username: 'GWスケジュールお知らせ',
  icon_url: 'https://goo.gl/FGbijb',
  attachments: [{
    fallback: text,
    pretext: `${BUFFER_TO_START_MIN}分後に始まるそうです!`,
    color: "#3366CC",
    title: text,
    title_link: link
  }]
})

const findTodaySchedules = () => {
  const content = document.querySelector('div.portal-cal');
  if (!content) {
    return false;
  }
  const today = content.querySelector('td.cal-day.co-today');
  if (!today) {
    return false
  }

  // 本日のスケジュール抽出&整形
  return Array.prototype.map.call(today.querySelectorAll('a.cal-item'), item => {
    const term = item.querySelector('span.cal-term-text').textContent.split('-').map(s => s.trim());
    return {
      text: item.textContent,
      link: item.getAttribute('href'),
      from: term[0],
      to: term[1]
    };
  });
}

// main
(() => {
  const schedules = findTodaySchedules();
  if (!schedules) {
    return;
  }
  const nowSec = Date.now() / 1000;
  schedules.filter(schedule => !localStorage[schedule.link]).forEach(schedule => {
    const startTime = schedule.from.split(':');
    // 規定時間後に通知
    const start = new Date;
    start.setHours(startTime[0]);
    start.setMinutes(startTime[1]);
    const startSec = (start.getTime() / 1000) - BUFFER_TO_START_MIN * 60;
    if (nowSec < startSec) {
      setTimeout(() => postToSlack(buildSlackMessage(schedule.text, schedule.link), schedule.link), (startSec - nowSec) * 1000);
    }
  });
})();
```

今回は、ポータル内のスケジュールから開始時間を取得し、現在から通知したい時刻までの秒数を計算して、その時間後にSlackへPOSTするように指定しています。
その辺のコードをお使いのGWと、通知したい内容に合わせて修正してください。
( desknet's ならほぼそのままで動くと思います)

あとは、GWへのURLとIncomingWebhookのURLを埋め込んだら完成です。
それらを適当なフォルダに保存して、Chromeのメニューから 設定 > 拡張機能タブ > パッケージ化されていない拡張機能を読み込む で保存したフォルダを指定して読み込んでください。
`manifest.json`の `matches` で指定したURLを開いたときに `main.js` が実行されます。簡単ですね。

ちなみに、常に指定のページを開いていないとなりません(たぶん)。
あと、いつリロードするか(されるか)分からないので、リロードされても大丈夫なように作らないとならないのは気をつけたいところです。