このブログははてなブログからの移行記事です。
hubot choiceとは
名前の通り、与えられた引数の中から1つをランダムにchoiceするスクリプトです。
大学の先輩がゼミ用に実装していたものを拝借し、会社でコードレビュアーを決めるために導入していました。
実装は至極シンプルなので結構便利に使ってました。
※画像はイメージです
問題
しかし、シンプルなchoiceのままだとコードレビュアーの決定に使用するにはいくつか問題がありました。
毎回、レビュアー候補を入力しなければならず、めんどくさい
もし名前をtypoするとnotificationが本人に飛ばない
これらを解決するため、hubot choice
を少しだけ改良することにしました。
解決策
使用者の大半はエンジニアであることと、用途のほとんどはレビュアーの決定であることを加味して以下の様な実装にすることにしました。
hubotがグループ名とグループの要素を変数ライクに記憶できるようにする
設定したグループ名を
hubot choice
の引数に持ってくることで設定された要素からchoiceするグループ名か否かの判別は
$
がついてるか否かで判別する(弊社はPHPerの会社なので…)
実装
グループ名の記憶にはhubot brain
を使用し、シンプルにset
とdelete
コマンドを用意する。
以下実装コード。
# Description # 1つランダムに選ぶ # # Commands: # hubot choice ほげ もげ ふが -- 引数からランダムにchoice # hubot choice $<groupname> -- 登録されたグループの要素の中からランダムにchoice # hubot choice set <group name> <group elements> -- グループを設定 # hubot choice delete <group name> -- グループを削除 # hubot dump -- 登録されているグループ一覧を表示 # # Author: # @sota1235 # # Thanks: # https://github.com/masuilab/slack-hubot/blob/master/scripts/choice.coffee _ = require 'lodash' module.exports = (robot) -> CHOICE = 'choice_data' # データ取得 getData = () -> data = robot.brain.get(CHOICE) or {} return data # データセット setData = (data) -> robot.brain.set CHOICE, data # グループをセット setGroup = (groupName, groupElement) -> data = getData() data[groupName] = groupElement setData(data) return # グループを削除 deleteGroup = (groupName) -> data = getData() if data[groupName] is undefined return false delete data[groupName] return true # グループ要素を取得 getGroupElem = (groupName) -> data = getData() if data[groupName] is undefined return false else return data[groupName] robot.respond /choice (.+)/i, (msg) -> items = msg.match[1].split(/\s+/) head = items[0] # for judge command is choice or not # set, dump,deleteの場合、return if head is 'set' or head is 'dump' or head is 'delete' return # 第一引数がグループ名指定の場合 if /\$(.+)/.test items[0] items = getGroupElem items[0].substring(1) if not items msg.send "無効なグループ名です" return choice = _.sample items msg.send "厳正な抽選の結果、「#{choice}」に決まりました" # グループを設定 robot.respond /choice set (.+)/i, (msg) -> items = msg.match[1].split(/\s+/) groupName = items[0] items.shift() groupElement = items setGroup groupName, groupElement msg.send "グループ:#{groupName}を設定しました" # グループを削除 robot.respond /choice delete (.+)/i, (msg) -> groupName = msg.match[1].split(/\s+/)[0] if deleteGroup groupName msg.send "グループ:#{groupName}を削除しました。" else msg.send "グループ:#{groupName}は存在しません。" # for debug robot.respond /choice dump/i, (msg) -> data = getData() if _.size(data) is 0 msg.send "現在登録されているグループはありません" return for gname, gelm of data msg.send "#{gname}: #{gelm.join()}"
ロジックに関して特筆すべき点はないと思います。
個人的にはlodash
のsize()
関数が便利すぎて泣きました。
機能一覧
以下の4つの機能が実装されています。
ランダム選択
コマンド:hubot choice hoge moge fuga
or hubot choice $<groupename>
従来のchoice機能。引数からランダムにchoice。$
が付いている場合は該当のグループ内からランダムにchoice。
グループ追加
コマンド:choice set <groupname> hoge moge fuga
グループとその要素をセット。例えばchoice set hubot-team cat dog bird
と打つとhubot-team
グループにcat
とdog
とbird
がセットされる。
使用するときはhubot choice $hubot-team
とすることでcat
、dog
、bird
からランダムにchoiceされる。
グループ削除
コマンド:choice delete <groupname>
グループを削除する。
グループ一覧表示
コマンド:choice dump
デバッグ用。現在登録されているグループとその要素を一覧で表示する。
使い方
こんな感じ(プライバシーを配慮して僕以外のユーザ名は伏せられています)。
まとめ
hubot choice
を拡張する変数機能つけたチーム開発でレビュアーをランダムに素早く決めるために作った
その他
バグや不具合を見つけたらsota1235/slack-fresh15-hubot : issuesにマサカリ投げてください(╹◡╹)