sinctechmemo

個人的メモ

hubotで定例会議のcronとおやすみ週を設定した

hubotとは

github製のチャットbotで、Node.jsを実行環境に動作します。
使い方は至ってシンプルで、hubot用のAPIを取得し、あとはscripts/配下のディレクトリにファイルを作って好きなようにコーディングしていくだけです。公式の推奨はCoffeeScriptですがCoffeeじゃなくても書けます。

導入手順の詳細は↓の記事を参考にしました。
qiita.com

やったこと

1. 定例会議を隔週(グループがAとBに分かれているため)でお知らせしてくれる
2. おやすみ週を設定してくれる
3. 任意のタイミングでA,Bを設定してくれる


今回は公式推奨のCoffeeScriptを使って社内チャットツールであるslackに導入しました。
1から順に説明します。

1. 定例会議を隔週でお知らせ
module.exports = (robot) ->
  cronJob = require("cron").CronJob
  MEETING_CNT = 1
  STORE_MEETING_CNT = 1
  CALLED_OFF_FLG = false
  NOTICE = ''

まずは必要なmoduleとstateを宣言しておきます。
基本的な方針として、ここのstate値を出し分けの判定に使います。
cronというnode moduleは定期タスクの実行を行います。

 MEETING_CNT = 1
 STORE_MEETING_CNT = 1

ここで同じようなstateを宣言していますが、理由は2.で後述します。

module.exports = (robot) ->
  Meeting =
    sendMsg: (channel) ->
      this.sendMsgType()
      robot.send(room: channel, NOTICE)

    sendMsgType: ->
      switch MEETING_CNT
        when 1
          notice = "今週の定例はAグループだよ。メンバーは\n#{ Meeting.memberList.A.members } だよ"
          MEETING_CNT = 2
        when 2
          notice = "今週の定例はBグループだよ。メンバーは\n#{ Meeting.memberList.B.members } だよ"
          MEETING_CNT = 1
      NOTICE = notice

Meetingというオブジェクトを生成し、ここのオブジェクトに関数をもたせます(Meeting.memberList.A.membersは同じオブジェクト内で記述していますが、個人情報入っているので割愛)。
ここでは、MEETING_CNTというstateを元に、送信するメッセージの出し分けを行い、処理の最後にはMEETING_CNTを1(もしくは2)にしてAとBがループするようにします(※課題1)。
メッセージはNOTICEのstateに渡し、実際にメッセージを送信する関数は sendMsg: (channel) -> に記述します。
ここで、第一引数のchannelは発信先のチャンネル名を指定します。

上記で出し分けのロジックは完了です。
あとは、cronの設定をします。
cronJobの第一引数で送信する時間の設定ができ、下記のように*/10とすると10秒毎に定期cronします。

new cronJob('*/10 * * * * *', () ->
    Meeting.sendMsg("random")
  ).start()
2. おやすみ週の設定

おやすみは突発的なものです。
なので、hubotにリプライを飛ばしたらその週をおやすみにしてくれるような仕組みにしたいです。
ソースに下記を追加します。

    sendMsgType: ->
      switch MEETING_CNT
        when 0
          notice = "今週の定例はお休みです。"
          MEETING_CNT = STORE_MEETING_CNT
        when 1
          notice = "今週の定例はAグループだよ。メンバーは\n#{ Meeting.memberList.A.members } だよ"
          MEETING_CNT = 2
          STORE_MEETING_CNT = 2
        when 2
          notice = "今週の定例はBグループだよ。メンバーは\n#{ Meeting.memberList.B.members } だよ"
          MEETING_CNT = 1
          STORE_MEETING_CNT = 1
      NOTICE = notice

    checkCalledOff: ->
      if CALLED_OFF_FLG
        MEETING_CNT = 0
        CALLED_OFF_FLG = false

 #ここまでがMeeting object
 
 new cronJob('*/10 * * * * *', () ->
    Meeting.checkCalledOff()
    Meeting.sendMsg("random")
  ).start()

robot.respond /つぎおやすみ/i, (msg) ->
    CALLED_OFF_FLG = true
    msg.send "OK わかった 次回の定例はお休みにするね"

Meeting object内にcheckCalledOff という関数を用意しました。
この関数をcronJob内で呼び出し、中止のフラグ判定を行います。
robot object内のrespond関数を使い、hubotに「つぎおやすみ」とリプライを飛ばすとフラグがtrueになり、一度おやすみになると、直ちにフラグはfalseに戻ります。
ここで、STORE_MEETING_CNT というstateを用意した理由として、「おやすみ後のグループ設定は、以前の状態を保持したい」という意図がありました。
たとえば、前回の会議がBグループで終わっていたら、次はAにしたい、逆も然りという具合です。
つまり、MEETING_CNTは実際(現在)の状態で、STORE_MEETING_CNTは前回までの状態という役割ですね(※課題2)。

3. 任意のタイミングでA,Bを設定

1. 2.で基本的なロジックは出来ているので、あとはrobot.respondでリプライ飛ばすだけです。

  robot.respond /つぎA/i, (msg) ->
    MEETING_CNT = 1
    STORE_MEETING_CNT = 1
    msg.send "次回の定例をAグループに設定したよ"

  robot.respond /つぎB/i, (msg) ->
    MEETING_CNT = 2
    STORE_MEETING_CNT = 2
    msg.send "次回の定例をBグループに設定したよ"


※課題1: A,B以外の変則的なループが発生した際に拡張性がないようにみえる。
※課題2: グローバル変数の濫用にみえる。実際にはmodule内の変数なので問題なし?わからん。



以上です( ・ิϖ・ิ)