9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LIFULLAdvent Calendar 2019

Day 10

ふりかえりのための備忘にSlackを活用する

Posted at

原題「Cloud FunctionsをバックエンドにSlackのアクションを作る」

:book: あらまし

チームでの開発に定期的なふりかえりは不可欠です。

私が所属するチームでは主にKPT方式でふりかえりを行っています。
しかし加齢のためか、ふりかえりを実施するタイミングでは
既にイテレーション内で起こったことを忘れ去っているという大きなProblemがあります。

そこでSlackのActions(アクション)機能を使って
思いついたその場でKeepやProblemを登録できるフォームを作成します。

:gear: 仕組み

Slackの アクション は、メッセージに対して任意の操作を定義する機能です。

例えば、チャット上でサイトの不具合の報告を受けたときに
その場でJIRAのチケットやGitHubのIssueを作成するような活用例があります。

公式の記事の ダイアグラム にあるように
Slackアプリから送信されるペイロードを自前のアプリケーションで処理することにより
メッセージに紐づく各種データの処理を行うことができます。

本記事では コチラ の記事を参考にインタラクティブなフォームを実装します。
運用コストを下げる為にバックエンドにはGCPのCloudFunctionsを利用します。

:computer: 作業

Slackアプリの作成

参考記事 の「アプリの作成と設定」に従います。

Request URL には自前のアプリケーションの受け口となるURLを記載します。
今回はトリガータイプをHTTPに設定したCloudFunctionsのURLを指定します。
(作成方法は後述のため、一旦仮のURLを設定して後で変更します)

作成が完了すると、メッセージ右上の「その他」メニューからアクションを選択できるようになります。
Slack___test-for-slack-actions___mockmock.png

アプリケーションの実装

関数の作成

GCPのコンソールからCloudFunctionsの関数を作成します。
ペイロードを受け取るため、トリガーはHTTPで設定します。
Cloud_Functions_-nature-_Google_Cloud_Platform.png

(AWSのLambdaと比べて、単体でもHTTPリクエストを受け付けられるのが嬉しい)

なお、記事に書く都合上ソースコードをインラインエディタで記載しますが
運用上はCloud Source Repositoriesなどを活用した方が圧倒的に楽です。
動作はNode.js 10(ベータ版)で確認しています。

モジュールの準備

package.json に最低限必要なモジュールを記載しておきます。
CloudFunctions自体が機能を持っていることにより、 参考記事 の内容より少なめです。

{
  "name": " slack-actions-kp",
  "version": "0.0.1",
  "dependencies": {
    "axios": "^0.19.0",
    "qs": "^6.9.1"
  }
}

主な処理

アプリケーションの受け口として複数のURLを設定することはできないため
ペイロードから取得する type によって処理を分ける必要があります。

今回のアプリケーションが受け付けるリクエストは2通りあります。

  1. アクションを選択されたときのリクエスト
  2. フォームが送信されたときのリクエスト
/**
 * Responds to any HTTP request.
 *
 * @param {!express:Request} req HTTP request context.
 * @param {!express:Response} res HTTP response context.
 */
exports.slackActionsKP = (req, res) => {
  const payload = JSON.parse(req.body.payload);
  const {type, submission} = payload;

  if (type === 'message_action') {
    // アクションが選択されたときの処理: Slack上でフォーム(ダイアログ)を開く
  } else if (type === 'dialog_submission') {
    // フォームが送信されたときの処理: 送信された内容をどこかに記録する
  }
}

リクエストの前者では、Slack上でフォーム(ダイアログ)を開く処理を行います。
ダイアログ上の項目の設定については コチラ が詳しいです。

if (type === 'message_action') {
  const dialogData = {
    token: SLACK_ACCESS_TOKEN,  // 環境変数から読み込んで事前に復号する
    trigger_id: payload.trigger_id,
    dialog: JSON.stringify({
      title: 'KとPを忘れない',
      callback_id: 'do_not_forget_kp',
      submit_label: '登録する',
      elements: [
        {
          label: '種別(K/P)',
          type: 'select',
          name: 'type',
          value: 'Keep',
          options: [
            { label: 'Keep', value: 'Keep' },
            { label: 'Problem', value: 'Problem' }
          ],
        },
        {
          label: '内容',
          type: 'textarea',
          name: 'message',
          value: payload.message.text
        },
      ]
    })
  };

  // SlackのAPIでフォーム(ダイアログ)を開く
  // https://api.slack.com/methods/dialog.open
  axios.post('https://slack.com/api/dialog.open', qs.stringify(dialogData))
    .then((result) => {
      if(result.data.error) {
        res.sendStatus(500);
      } else {
        res.sendStatus(200);
      }
    })
    .catch((err) => {
      res.sendStatus(500);
    });
} ...

リクエストの後者では、送信された内容を記録する処理を行います。
今回はIncomingWebhook経由で、専用のチャンネルに内容を投稿します。

} else if (type === 'dialog_submission') {
  // 結果だけ先に返す
  res.send('');

  // Slackにメッセージを投稿する
  const emoji = submission.type === 'Keep' ? ':ok_woman:' : ':no_good:'
  const data = {
    text: `${emoji} ${submission.type} ${submission.message}`
  };

  // WebhookのURLは環境変数から読み込んで事前に復号する
  axios.post(SLACK_WEBHOOK_URL, data)
    .then((result) => {
      console.log(result.data);
    })
    .catch((err) => {
      console.log(err);
    });
}

成果物

Slack上のフォームを通してK/Pを記録することができるようになりました。
メッセージ右上の「その他」からアクションを選択するとフォームが表示されます。
sl.png

「登録する」を実行すると、それぞれこのように投稿されました。
kp.png

長ったらしい投稿でも、ソースコード込みでも、数ポチで記録できます。
また、payload.message には投稿者のIDも含まれるため
他のメンバーの投稿をそっと取り上げることもできます。※コードには未反映

:warning: 注意書き

動くようにはなりましたが、運用の前にいくつか注意があります。

アクセス元の認証

メイン処理の前段で、Slackからの正しいアクセスであることを確認する必要があります。
参考記事 の「リクエスト情報の認証」の箇所を参考に実装を加えます。

各種設定の暗号化

CloudFunctionsの環境変数にアクセストークンやシークレットを埋めますが
事前に KMS で暗号化を行い、使用前に復号して利用するようにします。

:checkered_flag: むすび

メッセージをKeep/Problemとして記録するアクションを作成しました。

今回は他のチャンネルに投稿しただけですので
「コピペで良いのでは?」とか「ピン留めしたら?」という声が聞こえてきそうですが
スプレッドシートやDBにデータを保存すればもっと活用の幅は広がりそうです。

(私は付箋が好きです。備忘しても声を出して書き出すべきだと思います:relaxed:


明日以降もLIFULLアドベントカレンダーは続きます。引き続きお楽しみに!
https://qiita.com/advent-calendar/2019/lifull

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?