adish intelligence

アディッシュ株式会社のエンジニアブログです。

Real Time Messaging APIで秘書botを作ってみる

はじめに

みなさま、こんにちは。来年いよいよ生まれて半世紀を迎えます、adish技術開発部.雪風チーム所属の田原と申します。 12月1日に始まったGaiax Advent Calendar 2016 - Qiitaも、折り返し地点を過ぎました。 紅白の出場者も決まり、流行語大賞も決まり、今年を表す漢字一文字も決まり、どんどん年末に向かって時間が進んでいきますね。 そうそう、去年は有馬記念で10万馬券をGetして大興奮したのですが、あれから1年経つなんて、本当に早いですね... 今年も当んないかな...

...失礼しました。技術ブログの話に戻します。

私は普段は主にPerlで仕事をしていますが、今度Pythonのお仕事をすることになり、50の手習いではないですが、最近Pythonの勉強を始めました。「タプル」とか「イミュータブル」とかまだ馴れないですね。 そんなレベルなのですが、無謀にもPython系の記事を書いてみたいと思います。

みんな大好きSlackのbotを、Real Time Messaging APIというものを利用して作ってみます。

Real Time Messaging APIとは

Real Time Messaging API | Slack

リアルタイムメッセージングAPIはWebSocketベースのAPIで、スラックからのイベントをリアルタイムで受信し、ユーザーとしてメッセージを送信できます。単に「RTM API」と呼ばれることもあります。
※本家サイト冒頭より抜粋

以降「RTM API」と呼ぶことにします。

事前準備

Slack環境の準備

まずは、botのテストができるslackの環境を準備します。会社で使っていて、なおかつ管理者権限とかあるならばそこで試してもいいですが、そうでない場合はFreeプランを契約してしまうのもいいと思います。

ご存じの方も多いと思いますが、Slackにはインテグレーションサービス(という名のbot)がラインナップされており、実は自分でbotを作らなくても、そこそこ秘書みたいな仕事をさせることは可能なように思えます。

  • インテグレーションサービスストア

App Directory | Slack

  • Slackの料金プラン

Pricing | Slack

制限はありますが、botサービスを試したり、自作botを試したり、スケジュール管理とかメモの投稿とか、ちょっとした秘書ツールとして使うには充分だと思います。

SlackのSign Inの方法については、ネット上にあふれていると思いますので、そちらを参考にしてください。

Slack botアカウントの準備

Slackのbotアカウントを作成します。ここは管理者権限が必要です。

bot用アカウントを作る

権限のあるアカウントでログインしている場合、以下の画面が出ると思います。

f:id:hige-hage:20161214154234p:plain

「@username」に、botアカウントのユーザー名を入れて、「Add bot integration」ボタンを押下します。

f:id:hige-hage:20161214145245p:plain

「API Token」は、botを作成する際に必要になりますので控えておいてください。「Customize Icon」や「Customize Name」で作成したbotの名前やアイコン画像を変更できます。変更内容を「Save Integration」で保存します。

テスト用のチャンネルの作成とボットアカウントの登録

テスト用にプライベートなチャンネルを作成します。

f:id:hige-hage:20161214145437p:plain

「Send invite to」の欄に、先ほど作ったボットアカウントを指定します。

bot稼働環境の準備

Python3が動く環境を準備してください。

私の場合は、Windows8.1 + Vagrant の環境に以下を準備しました。

  • Ubuntu16.04 LTS
  • Python3

※環境構築にあたっては以下のページを参考にさせていただきました。どうもありがとうございました。

VirtualBox + VagrantでUbuntu16.04の環境を構築する - Yoshinorin in HEXO

データサイエンティストを目指す人のpython環境構築 2016 - Qiita

RTM APIのインストール

RTM APIのソースコードは以下にあります。

GitHub - lins05/slackbot: A chat bot for Slack (https://slack.com).

pipでインストールできます。

ubuntu@test1:~$ sudo apt-get pip install slackbot

これで、botを作る準備が整いました。

さっそく作ってみる

それでは、簡単なbotを作ってみたいと思います。

作るにあたっては、以下のページを参考にさせていただきました。どうもありがとうございました。

PythonでSlackbotを作る(1)~(4) – ビットログ

簡単なbot

任意の場所にBot用のディレクトリを任意の名称で作ります。 以下のように作りました。

ubuntu@test1:~$ mkdir -p bot/test0020
ubuntu@test1:~$ tree bot
bot
└── test0020
ubuntu@test1:~$ 
ubuntu@test1:~$ cd bot/test0020

bot用の設定ファイルを作り、先ほど控えた「API Token」を入れておきます。

test0020/slackbot_settings.py

API_TOKEN = "test0020のAPI TOKEN"
default_reply = "すみません、聞き取れませんでした"

default_reply は想定していない言葉を投げられた場合に返すメッセージになります。

次に、botを起動するコードを記述します。

test0020/run.py

from slackbot.bot import Bot


def main():
    bot = Bot()
    bot.run()

if __name__ == "__main__":
    main()

では、botを起動してみます。

ubuntu@test1:~/bot/test0020$ python run.py

slackbotライブラリのデフォルトプラグイン(slackbot.plugin)に、疎通確認用に設定されているものがあるので、メンションの特定の言葉に反応したり、チャンネルに投稿された言葉に反応します。

f:id:hige-hage:20161214145633p:plain

Pythonのslackbotライブラリは、このようにプラグインを作ることで、独自にダイレクトメッセージやbotが参加しているチャンネル内でのメンションや投稿内の特定の言葉に反応する機能を実装することができます。

忠臣蔵ごっこ

botが動く環境が確認できたので、ちょっと遊んでみましょう。

ところで、今日は何の日かご存知でしょうか?

今日12月14日は、忠臣蔵のクライマックス、赤穂浪士討ち入りの日です。 若い方たちはご存じないかもしれませんが、昔は「紅白」「レコ大」「忠臣蔵」と言われたくらい、年末の風物詩でした。

忠臣蔵 主君の仇を討った忠義の士たちの戦いの全容

秘密裏にことを進めるために、合言葉を設けたといいます。 「やま」といえば、「かわ」と返す、まさにbotで遊ぶのにぴったりのネタではないでしょうか。 ということで、忠臣蔵ごっこができるbotを作ってみましょう。

まず、kuranosukeというbotアカウントを作り、テスト用の部屋に招待しておきます。

次に、ごっこ遊び用のbotディレクトリとごっこ遊び用プラグインを配置するためのディレクトリを作成します。

ubuntu@test1:~/bot mkdir -p ako47/plugins

ちなみに、ako47というのは討ち入りに参加した人数が47人というのにちなんでです。A●B48とは関係ないです。 次に、Slackbotのプラグインとして読み込まるディレクトリはモジュールである必要があるので、plugins配下に「_init__.py」を作成しておきます。

ubuntu@test1:~/bot touch ako47/plugins/__init__.py

_init__.py は空ファイルで構いません。

ごっこ遊び用のプラグインを作ります。

plugins/aukotoba.py

from slackbot.bot import respond_to

@respond_to('山')
@respond_to('やま')
def res(message):
    message.reply('川!')

関数"res()"に対して、"respond_to()"というslackbotにあるデコレータを付与します。 "respond_to"の引数に、マッチするキーワードを指定することで、プラグインロード時に"res()"関数がbotに対するメンションに反応するように登録されます。

次に、slackbot_settings.pyを作ります。プラグインロードが行われるように「PLUGINS」を指定します。

slackbot_settings.py

API_TOKEN = "kuranosukeのAPI TOKEN"
PLUGINS = [
    'plugins',
]
default_reply = "おのれ、くせ者!!~"

ここまでで、以下のような状態になります。

ubuntu@test1:~/bot$ tree ako47
ako47
├── plugins
│   ├── aikotoba.py
│   └── __init__.py
├── run.py
└── slackbot_settings.py

3 directories, 7 files
ubuntu@test1:~/bot$ 

それでは、slackbotを起動して遊んでみましょう。

f:id:hige-hage:20161214145822p:plain

あれ、逆じゃない?...とか、細かいことは言わないでください...どうしても、今日の出来事に結び付けたかったので。

大きく寄り道してしまいましたが、秘書botを作りましょう。

秘書botを作る

秘書の仕事にはいろいろあると思いますが、一番に浮かぶのはスケジュールを確認してくれることかと思います。 そこで、スケジュール確認できる秘書のbotを作ってみます。

弊社ではiQubeというクラウド型グループウエアを利用しており、基本的にスケジュールもここで管理しています。 iQubeの自分の設定画面から発行されたAPIのURLをたたくことで、iCalenderフォーマットで自分のスケジュールを取得することができますので、今回はこれを使って今日のスケジュールを教えてくれる秘書を用意したいと思います。

「今日のスケジュール」と問い合わせたら、iQube上に登録されている今日のスケジュールを教えてくれる秘書を作ってみます。 最初に作ったtest0020のbotに機能を追加する形で作ります。

  • iCalenderフォーマットをpythonで扱うためのパッケージ

iCalendar package — icalendar 3.9.1.dev0 documentation

※iCalenderフォーマットを扱うのであれば、このページを参考にパッケージをインストールしておいてください。

まず、slackbot_settings.pyを修正します。

slackbot_settings.py

API_TOKEN = "test0020のAPI TOKEN"
default_reply = "すみません、聞き取れませんでした"

PLUGINS = [
    'plugins',
]

次に、実際にデータを取得してメッセージングするプラグインスクリプト「secretary.py」を作成します。

secretary.py

import codecs ----------(1)
from pytz import timezone ----------(2)
from datetime import date, datetime ----------(3)

from slackbot.bot import respond_to
from slackbot.bot import listen_to ----------(4)
from icalendar import Calendar, Event ----------(5)

@listen_to('(.*)のスケジュール') ----------(6)
def check_schedule(message, something):
    param = "{0}".format(something)

    # 日付のセット
    if param == '今日':
        p_date = datetime.now()
        s_date = p_date.strftime('%Y/%m/%d')
        fin = codecs.open('plugins/schedule.txt') ----------(7)
        cal = Calendar.from_ical(fin.read())
        schedules = []

        for ev in cal.walk():
            if ev.name == 'VEVENT': ----------(8)
                start_dt = ev.decoded("dtstart") ----------(9)
                print(start_dt.astimezone(timezone('Asia/Tokyo')))
                summary = ev['summary']
                if start_dt.astimezone(timezone('Asia/Tokyo')).strftime("%Y/%m/%d") == s_date:
                    schedules.append(start_dt.astimezone(timezone('Asia/Tokyo')).strftime("%Y/%m/%d %H:%M") + ' ' + summary)
        # ユーザーにメッセージを返す
        send_user = message.channel._client.users[message.body['user']][u'name']

        post = {
            'pretext': '{}さんの今日のスケジュールです。'.format(send_user),
            'title': '今日のスケジュール',
            'author_name': send_user,
            'text': '\n'.join(schedules),
            'color': 'good'
        }

        ret = message._client.webapi.chat.post_message(
            message._body['channel'],
            '',
            username=message._client.login_data['self']['name'],
            as_user=True,
            attachments=[post]
        )
    else:
        message.reply('申し訳ありません。今日のスケジュールしか答えられません')

初心者の身分で畏れ多いですが、スクリプトを簡単に解説します。

PEP8の流儀は勉強中なので、そこはご容赦ください...

(1) テキストエンコード用の基底クラスをimportしてます。(9)のicalデータから開始日データを抽出する際に使います。

(2) タイムゾーンパッケージをimportしてます。icalデータに入ってる日付データのタイムゾーンを「Asia/Tokyo」にする際に使います。

(3) 日付ハンドリング用のパッケージをimportしてます。

(4) listen_toデコレータをimportします。respond_toデコレータがメンションに対して反応するのに対し、listen_toデコレータは部屋に投稿された特定のキーワードに対して反応します。パターンマッチングを使って値を切り出すことができます。

(5) icaleder形式のデータを扱うためのパッケージをimportしてます。

(6) パターンマッチングを使って値を切り出すことができます。

(7) iQubeから取得した、iCalenderフォーマットのデータファイルを読み込んでます。「urllib.request」パッケージを使って、取得したストリームを直接処理するのが正解ですが、なぜかエラーとなってしまうので、ここではサンプルとして出すことができればいいので、ファイルを読むようにしてます。

(8) これが含まれるforブロックでは、iCalenderオブジェクトを順番に見ています。「VEVENT」というキーが、スケジュールイベントに該当します。

(9) イベントオブジェクトのイベント開始日を取得してます。今日の日付に該当したら、メッセージ出力用の変数(schedules)に入れてます。

全部準備したら、下記のようなディレクトリ&ファイル構成になります。

test0020
├── plugins
│   ├── __init__.py
│   ├── schedule.txt
│   └── secretary.py
├── run.py
└── slackbot_settings.py

1 directory, 5 files
ubuntu@test1:~/bot$ 

それでは、秘書に働いてもらいましょう。

ubuntu@test1:~/bot/test0020$ python run.py 

f:id:hige-hage:20161214154526p:plain

いかがでしょうか? ソースを見ての通り、今日のスケジュールしか答えられないのはプアですが、botアプリを作るだいたいの感じはつかめたのではないでしょうか?

このように、RTM API + Python3 を使うと、割と簡単にbotアプリが作れますので、みなさんも自分の秘書を作ってみてください。