Errbotを使って、自社のAdventCalendarへの参加を促す

# 前書き

2016年もAdvent Calendarの季節が近づいてきましたね。
今年もニジボックスではQiitaを利用させてもらってアドベントカレンダーをやるのですが、
「自分のErrbot知見」というお題目を掲げて、

平日定時にアドベントカレンダーの枠状況を伝えるだけのBotスクリプトを用意してみました。

- 自分の好みでErrbotを使用
- すでに、なんとも言えない機能を持ったErrbotは稼働しているのでプラグインをつくるだけ

# コードを晒す

```py3:adventcarendar.py
from datetime import datetime
from errbot import BotPlugin, botcmd, arg_botcmd, webhook
import pytz
import requests
from bs4 import BeautifulSoup
from errcron.bot import CrontabMixin
from errcron.cronjob import CronJob


class Adventcalendar(BotPlugin, CrontabMixin):
    """
    Qiita AdventCalendar
    """
    # 主張1: crontabが使える
    CRONTAB = [
        '30 18 * * 1,2,3,4,5 .report_calendar_members',
    ]
    TIMEZONE = 'Asia/Tokyo'

    CHANNEL = '#general' # Qiita晒し用
    CALENDAR_URL = 'http://qiita.com/advent-calendar/2016/nijibox'

    def activate(self):
        super().activate()
        self.activate_crontab()

    def report_calendar_members(self, polled_time):
        """デイリーで枠情報をしゃべる
        """
        # 主張2: スクレイピングするとパース
        resp = requests.get(self.CALENDAR_URL)
        soup = BeautifulSoup(resp.content, 'html.parser')
        items = soup.select('.adventCalendarItem')
        # 主張3: 枠数と投稿者数を切り離す
        reserved = [
            item
            for item in items
            if len(item.select('.adventCalendarItem_author')) > 0
        ]
        authors = set([
            item.select('.adventCalendarItem_author')[0].text.strip()
            for item in reserved
        ])

        if len(reserved) < 25:
            message = '{}\n{} 人によって {}個の投稿が掲載される予定です。\n引き続き参加のほど、よろしくお願いします。'.format(
                self.CALENDAR_URL,
                len(authors),
                len(reserved),
            )
            channel = self.build_identifier(self.CHANNEL)
            self.send(channel, message)
```

# 少しコードについての概説を書く

## 主張1: errcron によってcrontab的に毎日定時に喋ってもらう

以前書いた記事にあるように、Errbotにはcrontabのような機能はないので、
自分が作った`errcron`を使って簡易的なCrontab的な動作をするようにしています。
そのため、

```
30 18 * * 1,2,3,4,5 .report_calendar_members
```
といった、crontab記法をそのまま使っています。
今回は休日まで喋らせてもうるさいので、平日の18:30という夜の区切りっぽい時間にしてみました。

## 主張2: requestsとBeautifulSoup4で手っ取り早くスクレイピング

今回、自社のアドベントカレンダーの情報はRSSからでも、APIとかでもなく、HTMLをそのままみに行っています。~~(RSSは公開記事しかもたないし、APIはそもそもないし)~~

`requests`なら`requests.get`で一発でレスポンスとれすし、BeautifulSoupがあるので、「`adventCalendarItem`クラスの要素を数える」というさっくりとした手段でカレンダーの各アイテムが数が取れました。
構造が綺麗なQiitaに感謝です。

## 主張3: 枠数と投稿者数を切り離す

今はそうでもないですが、一人のメンバーが複数の記事を書くこともあるので、一旦**投稿者数**を**投稿数**とは分けて考えてみることにしました。

### 投稿数について

`.adventCalendarItem`なboxの中身はすでに枠が埋まっているときに限り`.adventCalendarItem_author`クラスの投稿者名枠が出てきます。
つまり、`Item`の中から`author`の中身をとりあえず取りだせば、投稿リストがつくれます。

```py3:このへん
reserved = [
    item
    for item in items
    if len(item.select('.adventCalendarItem_author')) > 0
]
```

### 重複を除去して、投稿者数にする

上記の投稿リストはから投稿者リストを重複なく作るのは直接だと面倒なので

1. 投稿リストから名前を抽出してリストにする
1. list -> setにして名前の重複を除去する

という手段で投稿者リスト(重複なし)を用意します。

```py3:このへん
authors = set([
    item.select('.adventCalendarItem_author')[0].text.strip()
    for item in reserved
])
```

## 最後にSlackに投げる

投稿数リストも投稿者リストもつくれたので、あとはそれを元にSlackにポストするだけです。
Errbotの標準機能なので、今回は細かい説明はパス。


<img width="664" alt="スクリーンショット 2016-10-31 22.18.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/6805/a24923ac-15c7-8714-c9ee-7fb67292dbe9.png">

これを見て、うちのカレンダーが賑やかになるといいですね。