Ruby製の軽量Botフレームワーク Mobb をリリースしました


TL;DR

Sinatra-isiに記述できるBotフレームワークMobbと、Botとサービスの間を取り持つRackに相当するReppを作って公開しました。

作ろうと思った理由

RubyでBotを作ろうと思うと、大体の人はRubotyを選択すると思います。GoogleでRubyを使ってSlack Botを作る方法なんかをいくつか探していると、そのうち自然とRubotyに行き着くと思います。ドキュメントやサンプルも日本語で多数用意されており、日本のRuby界隈におけるBotの選択肢としてはデファクトスタンダードではないかと思っています。

Rubotyはとてもよく作られたBotフレームワークですが、個人的にはプラグインのロードにBundlerを使って$LOADPATHから順次プラグインをロードしている点がやや使いづらいと感じていました。公にできない自作プラグインや業務レベルのロジックを乗せるには、それを回避する手順が煩雑になってしまい、Rubotyのロードの仕組みを調べることで本来やりたかったことに使うべき時間を奪われてしまうのが悩ましいところでした。

また、RubotyはHandlerとActionというクラスをきちんと定義しており、その流れに乗っていれば特に迷うことは無いのですが、本当にどうしようもないほどにくだらない機能(例えば、渡された言葉をシャッフルしてechoするだけのしょうもない単機能のbot)しか持っていないbotを作るには、あまりにもリッチ過ぎる制約を持っていました。また、プラグインのロードの都合上、Handlerは自分たちのロジックとは違うモジュール名空間に置かなくてはいけないことも気になる点でした。

この煩雑さを回避したい、毎秒クソボットを作りたいと思うようになると、まるでRailsのようなRubotyの壮大な世界観が少し扱いづらくなってきました。そこで、この悩みを解決するためにSinatraのような手軽さで書けるRuby製Botフレームワークが必要だと感じたために、Mobb プロジェクトを開始しました。

また、世の中には数多のBotフレームワークがありますが、その殆どが自らのコードに特化したサービス接続用のプラグインを持っており、他の世界観で使い回すことができないことも大きな問題だと思いました。これはちょうど、Web ApplicationとWeb Serverの接続部分を解決したRackのようなものがBotの世界にも必要だということであり、Rack(のような)インターフェイスにしたがってBotエンジンを記述すれば、サービスとの接続部分を使いまわせると考えたことが、ReppというBot用の共通のサービス接続インターフェイスを作ろうと思った動機です。

Sinatra DSL

Sinatraのコア実装は、おおよそ2000行のPure Rubyで書かれています。たった2000行でWeb Application Framework が作れることにとても感心します。所々で非常にトリッキーなテクニックを使っていて、きちんと全体を把握するには数日かかると思いますが、それでもその気があれば数日で全体を理解できる素晴らしいコードだと思います。

実際のところ、Sinatraはその2000行のコードでWeb Application Framework として成立しているわけではなく、強力なルートのパターンマッチング実装であるmustermannが別に存在します。また、起動に関してもRack側にかなり助けられている箇所もあります。ですが、SinatraのDSLそのものは2000行の実装の中にすべてが現れていて、かつそれなりに読めるコードなので、今回はその周辺には触れません。

Sinatra DSLで最も理解が難しいと思ったのは、settingsの正体を知るところと、DSLでロジックを記述した時にSinatra側の変数を参照するためにバインドをするためのラッパーが存在しているところだと思います。Sinataraコードリーディング日記は、また改めてブログのエントリで詳細に書ければいいかなと思います。

Mobb

Mobb は、Sinatra DSLの影響を100%受けて作られた、Bot Frameworkです。

最も簡単なサンプルコードは、エントリの先頭に書かれたコードで、これは実際に機能します。その他のサンプルに関しては、リポジトリのexamplesディレクトリに書かれています。

正規表現のパターンマッチングが拾えたり、自身の設定を参照したりもできます。

インストールは、gem経由で行いますが、Bundler推奨です。次のようなGemfileを用意します。

インストールします。

app.rbなどの適当なファイルに、次の内容を記述します。

実行します。

app.rbで、サービス名にslackを指定しているため、環境変数SLACK_TOKENにBot用のトークン文字列を渡しておいてください。シェルで試しに動かすだけであれば、 set :service の行を削ると自動的にシェルで起動します。

現状では、渡された文字列か正規表現にマッチするとブロックを実行する on と、そのエイリアスの receive しか機能しませんが、徐々に機能追加して、 cron 的な役割を果たす every や、特定の日時に発火する at などを実装していく予定です。

Repp

Repp は、Rackの影響を100%受けて作られた、Botフレームワークとサービスのインターフェイスです。

とりあえず、今はv0.1.0ということで、ドキュメント化された仕様もないし、そもそも頭の中で固まっている仕様もありません。

というのも、Bot Framework の場合は、 Web Application 用のRackと違い、リクエストとかならず対になるレスポンスがあることが保証されていなかったり、リクエストが無いのにレスポンスをしなくてはいけなかったりするため、そう簡単にRackの仕様を真似ることはできないのです。

また、これは最も大きな問題なのですが、チャットサービスによって使える機能がかなり違うため、ここのサービスは意識しつつ全体として破綻しないインターフェイスが必要です。具体的に言うと、Slackのアタッチメント機能なんかは他のサービスからすると参照できない情報だけど、似たように変換はできるかもしれないような情報を、どうやってサービス間で共有するのか? という話です。

これに関しては、もうバッサリと互換を切り捨てて、Botを書く人が何のサービスに向けて書いているのかを明確にするしか無いのではと思っていますが、それもまあどうなんだという気持ちもあり、たくさん考えています。

とりあえず、ReppはRackのようなインターフェイスと言いつつ、まったくインターフェイスとして決まった仕様が存在せず、ドラフトで今の動きをやっている状態です。

そのうち整備していきます。

RejectKaigi2018の話

Mobb のプロジェクト自体は、すこし前からその構想を持っていたのですが、Sinatraをコードリーディングをして似た実装のDSLを作り上げるのにかなり時間がかかってしまい、最初にアイディアを発表したのはRejectKaigi2018の発表でした。

この発表の時には、大体の現在公開されているコードの基礎部分が完成しており、Slack用のRepp実装がなかったために公開を見送っていた状態です。RubyKaigi2018までには初版を作り上げて公開したいとセッション内で話しましたが、残念ながら私生活の状況によってそれは叶わず、6月末の公開になりました。

RubyKaigi2018の話

今年のRubyKaigiは仙台でした。初日にという牛タンやさんに行きましたが、最高に美味しかったです。

Ruby25thでなんとなくテンションがあがり、このMobbの話をしたくてCFPを送りましたが、残念ながら落ちてしまったことの反省を前回のエントリで書きました。

Mobbのファーストリリースの話

Mobb のファーストリリースのためのコード整備やリリース作業は、所属しているドワンゴという会社が年一で行っているハッカソンの時間にやりました。一応業務の時間の中で好き勝手やってリリースしたので、宣伝の意味も兼ねて会社の採用情報の未承諾広告を貼っておきます。
【未承認広告ここから】

ドワンゴ採用情報サイト

「あなたの本気が、あなたを変えて、未来を少し変えていく。」ドワンゴの採用情報をおしらせします。

紹介制度とかもあるので、直接Twitterで呼びかけてもらっても大丈夫です。

【未承認広告ここまで】

MobbとReppの今後

まず、両方共テストがありません。テストを書いていこうと思います。

他にも、Reppはきちんとしたドキュメントが必要です。それに、まだこれだっていう仕様もしっかり決まっていません。ドラフトです。底を固めるために色々考えるのが、当面の課題になりそうです。

一応、頑張ってMobbとReppをメンテナンスしていく気持ちはあるので、便利だと思ったらぜひ使ってみてフィードバックをおねがいします。


この記事はRuby, ポエムに投稿されました. このパーマリンクをブックマークする。 コメントを投稿するか、トラックバックをどうぞ: トラックバック URL.

コメントする

あなたのメールは 絶対に 公開されたり共有されたりしません。 * が付いている欄は必須項目です

次の HTML タグと属性が使用できます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

*
*