Edited at
NIJIBOXDay 19

ErrbotのStorageプラグインを作ってみる

More than 1 year has passed since last update.

興味本位で、ErrbotのStorageプラグインを1個作ってみた話です。

以降、このページのコードはPython3.6系のみで動きます。1


Storageプラグインの役割

Errbotのプラグインが、「永続化したいデータを管理するためのプラグイン」です。

何も指定しなければ、ビルトインのShelfが使われます。

サードパーティ製のプラグインでは、SQLAlchemyを経由してRDBMSで管理するerr-storage-sql、Firebaseを使うerr-storage-firebaseなどなどがあります。詳しくはErrbotのGithubプロジェクトページにあるWikiを見てみましょう。2


作る前に


Storageプラグインの使われ方


読み込みまで

プラグインが探索されるタイミングは起動時のみです。

また、設定上のBOT_EXTRA_PLUGIN_DIRではなくBOT_EXTRA_STORAGE_PLUGINS_DIRを追加の探索対象とします。

そこからプラグインマネージャーが認識するまでは、他のプラグインと同じです。


初期化するまで

Storageプラグイン自体の仕事は、コアプラグイン・Botプラグインが起動するタイミングで、ストレージを生成することです。

それ以降は(多分)何もしません。


Errbot稼働中

以前に書いた記事でざっくり説明した通り、Botプラグインが永続化させたいデータに対するやり取りは、オーバライドされている辞書アクセス用メソッドを介して行われます。3

この際の最終アクセス先がストレージとなります。


some_plugin.py

from errbot import BotPlugin, botcmd

class SomePlugin(BotPlugin):
@botcmd
def like(self, msg, args):
self['like'] = args # <- ここで使われる
return f"OK! You like {args}. I remembered"



Errbot終了

ストレージが持っている終了時に呼ばれるメソッドが呼ばれて役目を終えます。4


ファイル構造について

普通のプラグインと同様に、****.plugファイルとplugファイルが参照している***.pyが必要になります。

この辺は、yapsyを使っているからか、BotプラグインもBackendプラグインもStorageプラグインも同じです。


file_list.rst

* 必須

* json.plg
* jsonpg.py
* 任意
* README.md
* requirements.txt
* LICENSE

requirements.txtには依存ライブラリが書かれていることが多いのですが、起動時にこのファイルがあったら自動でpip installをすることが無いです。

ので、面倒だったらなくてもいいかもしれません。


作ってみる


プラグインの中身

errbot.storage.base内にあるStorageBase,StoragePluginBaseを継承して@abstractmethodでデコレートされているメソッドを全部実装すればいいです。


Pluginクラス

この3条件を満たしていればいいです。



  • XXXPluginという形式のクラス名であること(XXXにはplugファイルで定義したNAMEが入ります)


  • __init__が引数を1個受け取ること


  • open()が引数を1個受け取り、ストレージ本体のインスタンスを返すこと(以降、このインスタンスはStorageクラスとします)


Storageクラス


  • 次のメソッドが実装されていること



    • set: キーと値を受け取る


    • get: キーを受け取り、値を返す


    • remove: 指定したキーの値を削除する


    • len: キーの数を返す


    • keys: キーのリストを返す


    • close: 保存する




出来上がったものがこちら

「きちんと動くと思うけど、端折れるところは端折ってる」感じで記載します。

なお、個人的にはYAMLのほうが手っ取り早く見やすくできたので、YAMLストレージも作ってます。


json.plug

[Core]

Name = JSON
Module = jsonpg

[Documentation]
Description = JSON save/load storage for Errbot



jsonpg.py

import os, json

from errbot.storage.base import StorageBase, StoragePluginBase

class JSONStorage(StorageBase):
def __init__(self, save_path):
self.save_path = save_path
try:
with open(self.save_path) as fp:
self.data = json.load(fp)
except FileNotFoundError:
self.data = {}

def set(self, key, value):
self.data[key] = value

def get(self, key):
return self.data[key]

def remove(self, key):
del self.data[key]

def len(self):
return len(self.keys())

def keys(self):
return self.data.keys()

def close(self):
with open(self.save_path, 'w') as fp:
json.dump(self.data, fp)

class JSONPlugin(StoragePluginBase):
def __init__(self, bot_config):
super().__init__(bot_config)
# 保存先はShelfプラグイン等と同じ場所
self.data_dir = bot_config.BOT_DATA_DIR

def open(self, namespace):
# 各プラグインごとに呼ばれるので、namespaceをファイル名に使う
save_path = os.path.join(self.data_dir, f"{namespace}.json")
return JSONStorage(save_path)



最後に

昨年にBackendプラグインの記事を書いてる方がいましたが、Storageプラグインは考慮したほうがいい点がやや少ないので作りやすいですね。

とはいえ、保存先の仕様をちゃんと把握しないといけないので、楽とまでは言えないですが。


参考





  1. f-stringを直せば、Python3系ならだいたい動くはずです 



  2. 定期的に更新されています 



  3. https://github.com/errbotio/errbot/blob/master/errbot/storage/__init__.py 



  4. もちろん、実装によっては即時保存されることもあります。