Hubotのhubot-scriptに当たる仕組みを、Errbotではpluginといいます。
こちらにはサードパーティのPluginが並んでいますが、
せっかくなので作ってみようと思います。
pluginの基本部分
pluginは2個のファイルが必要です。
- plugファイル
- pythonモジュール
plugファイルはpluginのメタデータファイルです。
Pythonのバージョン指定、helpコマンド実行時の説明文などが記述されています。
pythonモジュールはpluginの実体です。
ロジックをこちらに書きます。
返事をするだけのplugin
hello.plug
[Core]
Name = Hello
Module = hello
[Python]
Version = 2+
[Documentation]
Description = Hello plugin
hello.py
# -*- coding:utf8 -*-
from __future__ import division, print_function, absolute_import
from errbot import BotPlugin, botcmd
class Hello(BotPlugin):
@botcmd
def hello(self, msg, args):
return 'Hello world!'
中身をもうちょっと詳しく
pluginの実体クラスはBotPluginを継承します。初期化時にもろもろの処理をしてくれるのですが、今回は省略します。
botcmd
デコレータでデコレーションしたメソッドの名前がそのままコマンドになります。
- ここでは
!hello
がそのままコマンドになります。
botコマンドになるメソッドには2個の引数が必要です。
- msg
- コマンドの全文
- args
- コマンドに引数がつけられるので、その引数部分
- 特にlistなどにパースされてはいない
botcmd
にデコレートしたメソッドが文字列を返すと、それをそのまましゃべってくれます。
楽でいいですね。
天気を教えてくれるplugin
weather.py
# -*- coding:utf8 -*-
from __future__ import division, print_function, absolute_import
from errbot import BotPlugin, botcmd
import requests
from xml.etree import ElementTree
class Weather(BotPlugin):
WEATHER_HACK_AREA_URL = \
'http://weather.livedoor.com/forecast/rss/primary_area.xml'
WEATHER_HACK_API_URL = \
'http://weather.livedoor.com/forecast/webservice/json/v1?'
@botcmd
def weather(self, msg, args):
# 引数に指定された都市を探す
city_id = self.find_city_id(args)
if city_id is None:
return u'{} は確認不可能な地域です'.format(args)
resp = requests.get(
self.WEATHER_HACK_API_URL,
{'city': city_id}
)
wt_json = resp.json()
return u'{}: {}は{}'.format(
wt_json['title'],
wt_json['forecasts'][0]['dateLabel'],
wt_json['forecasts'][0]['telop']
)
def find_city_id(self, city_name):
"""LivedoorのAPIで天気を返す都市を探す
"""
resp = requests.get(self.WEATHER_HACK_AREA_URL)
tree = ElementTree.fromstring(resp.content)
cities = {
elm.attrib['title']: elm.attrib['id']
for elm in tree.findall('.//city')
}
return cities.get(city_name, None)
この時点では雑にargsをそのまま都市名とみなして天気を返すようにしてますが、
「複数都市をスペース区切りで投げてきたら分割してそれぞれの都市の天気をまとめて返す」
といったことも可能です。
ArgumentParserを利用すれば引数の取り扱いは柔軟にできるので、いろいろな可能性がありそうですね。