@peccul is peccu

(love peccu '(emacs lisp cat outdoor bicycle mac linux coffee))

「HubotスクリプトをCommon Lispで書く」をやってみた

深町さんのこの記事をやってみた記録

blog.8arrow.org

いろいろ準備

Node.jsとRoswellのインストールは省略。

Yeomanとジェネレータ、Lakeをインストール。

npm i -g yo generator-hubot
ros install lake

プロジェクトディレクトリの初期化

Rocket.Chatで使いたかったので、アダプタも指定した。

mkdir hubot-cl
cd hubot-Common Lisp
yo hubot --name="bot" --adapter="rocketchat@1"
rm hubot-scripts.json
npm un -S hubot-{heroku-keepalive,google-images,google-translate,maps,redis-brain}
npm i -S hubot-mongodb-brain

external-scripts.jsonからhubot-{heroku-keepalive,google-images,google-translate,maps,redis-brain}を消してhubot-mongodb-brainを追加。

git repo初期化

git init
git add .
git commit -m 'Initial commit'

pm2.sh作成

PM2で起動するのと、Rocket.Chat向けに環境変数の指定がいるのでpm2.sh作成。

#!/bin/sh
export ROCKETCHAT_URL='https://hogehoge.com/'
export ROCKETCHAT_ROOM='roomname'
export ROCKETCHAT_USER=botaccount
export ROCKETCHAT_PASSWORD=botpassphrase
# use password authentication(not ldap)
export ROCKETCHAT_AUTH=password

./bin/hubot -a rocketchat

動作確認

./bin/hubotでターミナル内。./pm2.shでRocket.Chat。

bot pingPONGが帰ってくれば成功。

Common Lisp (Roswell)用に追加

Lakefile作成

$ lake-tools init
# hubotifyとdefaultのタスクを作成
$ cat Lakefile
#|-*- mode:lisp -*-|#
(in-package :cl-user)
(defpackage :lake.user
  (:use :cl :lake :cl-syntax)
  (:shadowing-import-from :lake
                          :directory))
(in-package :lake.user)

(use-syntax :interpol)

(task "default" ()
  (lake :target "hubotify"))

(task "hubotify" ()
  (dolist (file (uiop:directory-files #P"roswell/"))
    (sh `("bin/hubotify" ,(namestring file)))))

;;; here follow your tasks...

Roswellスクリプト等設置

# hubotifyをダウンロードしてビルド
$ curl -o bin/hubotify.ros https://gist.githubusercontent.com/fukamachi/ba055a9a284b181e82bf/raw/c66d65a5a18be44463ab781e669891c6a49f92c1/hubotify.ros
$ ros build bin/hubotify.ros
# whoami.rosを書く
$ mkdir roswell
$ cat roswell/whoami.ros
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#

(defun main (&rest argv)
  (declare (ignorable argv))
  (format t "~&I'm fukabot!~%"))

(ql:quickload :parenscript :silent t)
(import '(ps:ps ps:@ ps:regex))

(defun js-main ()
  (ps
    ((@ robot respond) (regex "/who are you\\?/i")
     (lambda (msg)
       (run-main
        (lambda (error stdout stderr)
          (when error
            ((@ msg send) error))
          (when stdout
            ((@ msg send) stdout))
          (when stderr
            ((@ msg send) stderr))))))))
# whoami.rosに実行権限をつけないとERROR Error: spawn EACCESとなる
$ chmod +x roswell/whoami.ros
# whoami.jsの生成
$ lake
Already have sbcl-bin.
Making core for Roswell...
building dump:lake
up to date. stop
Wrote '/Users/shimatani/Work/Repos/other/hubot-cl/scripts/whoami.js'
Command "bin/hubotify /Users/shimatani/Work/Repos/other/hubot-cl/roswell/whoami.ros" exited with error code 1.

hubotifyがエラーコード1らしいけど生成されてる。

動作確認

./bin/hubot
bot> bot who are you?
bot> I'm fukabot!

JSがroswell/whoami.rosを実行してて遅かったのでこれもビルドした。 (これだと次のlakeでビルドしたものもhubotifyの対象になるのでちょっと困る)

$ ros build roswell/whoami.ros
# scripts/whoami.jsのwhoami.rosへのパスをwhoamiに変更
$ head scripts/whoami.js
function runMain(callback) {
    var argv = Array.prototype.slice.call(arguments, 1);
    this.execFile = require('child_process').execFile;
    __PS_MV_REG = {};
    return this.execFile(__dirname + '/../' + 'roswell/whoami', argv, new(Object), function (error, stdout, stderr) {
        callback(error, stdout, stderr);
        __PS_MV_REG = {};
        return null;
    });
};

これだと実行するとエラーコードが返っているみたい。

bot> bot who are you?
bot> Error: Command failed: /path/to/hubot-cl/scripts/../roswell/whoami

I'm fukabot!

ros buildしたものを実行すると正常終了しない?

$ ./roswell/whoami; echo $?
I'm fukabot!
1
$ ./roswell/whoami.ros; echo $?
I'm fukabot!
0

ひとまずParenscriptでhubotスクリプトを書いて動かすところまでできたのでここまで。

課題

  • ParenScriptをjsclに変えてみる
  • lakeの対象をglobで指定する(roswell/*.ros)
  • ビルドするとエラーコードが返る

参考URL

blog.8arrow.org

HUBOT

github.com

qiita.com

github.com