タイトルの通り。
- GitHub の PR のレビュアーを、開発チームの特定のメンバー数名から一人指名したい
- 開発チームの一部のメンバーで持ち回りでやらないといけないタスクがあって、毎回誰がやるか決めるのめんどい
といったことがあって、せっかくなので bot に決めてもらえないかなあという気分になりました。
「どの人たちから選ぶか」は目的によってまちまちなので、それなら Slack のチャンネルのメンバーから選ぶようにすれば汎用性が高いんじゃないかと思って作ってみました。
コード
先にコードから。
なお、フレームワークとして Hubot を使ってますが、CoffeeScript を書くのが嫌だったので ES2015 で書いてます。
そのあたりの設定の話はこちらを参考にしてください。
import request from 'request'; module.exports = (robot) => { robot.respond(/.*(random|抽選|選ぶ).*/, (msg) => { const url = 'https://slack.com/api/channels.list?token=' + process.env.HUBOT_SLACK_TOKEN; // チャンネル一覧を取得 request(url, (err, res, body) => { // msg.message.room で現在の channel 名が取れる const channel = findChannel(JSON.parse(body).channels, msg.message.room); console.log('Channel found'); console.log(channel); // bot 自身を除外して抽選 const botId = robot.adapter.self.id; const filterdMembers = channel.members.filter((member) => { return member !== botId; }); console.log(filterdMembers); const member = msg.random(filterdMembers); msg.send('選ばれたのは、 <@' + member + '> でした :tea:'); }); }); } function findChannel(channels, targetName) { for (const channel of channels) { if (channel.name === targetName) { return channel; } } return null; };
(PR は #1)
動作の様子
bot に向かって random/抽選/選ぶ といった単語を含めてメンションすると適当に選んでくれます。
実装上のポイントなど
現在のチャンネル名は msg.message.room
で取れる
最初に苦労したのは、自分が今いるチャンネルを取得するという処理です。
以下の記事を読んで msg.message.room
で取れるんだーということを知りました。
自分でも console.log(msg)
して中身を確認してみました。
message
以外にも robot
に色々情報が入ってそうです。
Response { robot: Robot { name: 'hubot', events: EventEmitter { ... _maxListeners: undefined }, brain: Brain { ... _eventsCount: 3 }, alias: false, adapter: SlackBot { customMessage: [Function], message: [Function], ... message: SlackTextMessage { user: User { id: 'U1R73F7DL', name: 'zaki-yama', ... room: 'bot-channel' }, text: '@hubot: random', rawText: '<@U1R7NPEKU>: random', rawMessage: Message { _client: [Object], _onDeleteMessage: [Function], deleteMessage: [Function], _onUpdateMessage: [Function], updateMessage: [Function], type: 'message', channel: 'C1VTH4FPD', user: 'U1R73F7DL', text: '<@U1R7NPEKU>: random', ts: '1469699396.000062', team: 'T1R7BJHV3' }, id: '1469699396.000062', done: false, room: 'bot-channel' },
Slack の channel オブジェクトからチャンネルに所属するメンバーを取得する
Slack API の channels.info でチャンネル一覧を取得し、チャンネル名が msg.message.room
と一致するものを探します。
レスポンスの channels
プロパティの中にすべてのチャンネルが配列で入ってます。
また、channel オブジェクトの中身はこちらに記載されています。
https://api.slack.com/types/channel
channel.members
でメンバーの一覧が取得できますね。
Bot 自身の情報は robot.adapter.self.id
で取れる
Bot もチャンネルに含まれているため、ランダムで一人選ぶときには自分を除外しなければなりません。
このためだけに Slack API を叩かないとだめなのかなーと思っていたんですが、なんとなく robot.adapter
あたりを console.log()
で見てたら見つかりました。
このあたりのドキュメントはどこかにまとまってるんですかね。。。
配列からランダムで1つ選ぶ、は msg.random(array)
が使える
らしいです。Hubot の公式ドキュメントに書いてあります。
https://hubot.github.com/docs/scripting/#random
(Hubot のドキュメントは見づらい...!)
メンションは名前でもIDでも送れる
channels.info で取得した channel オブジェクトの members
からはユーザーの ID しか取れないので、
てっきり最初は別途 users.info を叩いてユーザー名を取得し直さないといけないんだと思ってました。
が、Slack API のドキュメントをちゃんと読むと、メンションをとばすときは ID で OK ということが書かれています。
https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
むしろ
The readable name can be included after the ID, by separating them with a pipe (
|
) character.
なので、ID の後ろに name をつけても良いよ、ぐらいでしょうか。
ただ、 ユーザー名だけでも一応メンションはとばせるみたいです。
メンションは @userId
でなく <@userId>
とする必要があるみたいですね。
TODO
毎回同じ返事だと寂しいので、メッセージにバリエーションを持たせたいです。
→対応しました。GitHub 参照 https://github.com/zaki-yama/hubot-es2015/issues/3
リファレンス
- hubotでslackに参加しているユーザーからランダムに抽選するやつを書いた - Qiita
- Slack API
- 今回使用した channels.list API
- メンションの書き方
- Channel オブジェクト
- Hubot の robot, msg オブジェクトの中身はこのへんがんばって読むしかない?
- https://hubot.github.com/docs/scripting/
- これ参考に自分でも console.log した