Posted at

Errbotのpluginを作ってみる

More than 3 years have passed since last update.

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を利用すれば引数の取り扱いは柔軟にできるので、いろいろな可能性がありそうですね。