興味本位で、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
この際の最終アクセス先がストレージとなります。
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プラグインも同じです。
* 必須
* 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ストレージも作ってます。
[Core]
Name = JSON
Module = jsonpg
[Documentation]
Description = JSON save/load storage for Errbot
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プラグインは考慮したほうがいい点がやや少ないので作りやすいですね。
とはいえ、保存先の仕様をちゃんと把握しないといけないので、楽とまでは言えないですが。
参考
- 公式のドキュメントと、ビルトインのクラス
- SQLプラグイン(プラグインプロジェクトの構造についての参照元)
-
f-stringを直せば、Python3系ならだいたい動くはずです ↩
-
定期的に更新されています ↩
-
https://github.com/errbotio/errbot/blob/master/errbot/storage/__init__.py ↩
-
もちろん、実装によっては即時保存されることもあります。 ↩