Backlogの通知をWebhookを利用してSlackに通知する


概要

会社のBacklogがSlack連携を利用できなかったので、WebhookとGoogle Cloud Functionsを利用して、Slackへ通知する仕組みを作成した。

流れ

  1. SlackのAppを作成する。
  2. Cloud Functionsにコードを設置する。その際、SlackのAppのURLを作成したAppのURLに置き換える。
  3. Backlogの管理画面から、Webhookの通知先にCloud FunctionsのURLを登録する。

Cloud Functionsのコード

以下のコードをCloud Functionsに設置します。
適当に作っただけなので、改善の余地が多いにあります。

改善案があれば、コメント頂けると助かります。

index.js

const { IncomingWebhook } = require("@slack/client");

/**
 * Responds to any HTTP request.
 *
 * @param {!express:Request} req HTTP request context.
 * @param {!express:Response} res HTTP response context.
 */
exports.helloWorld = async (req, res) => {
  console.log('Do helloWorld')

  if (req.method !== 'POST') {
    console.log('Method Not Allowed')

    res.status(405).send('Method Not Allowed');
    return;
  }

  if (!req.body) {
    console.log('Request Body Not Found')
    res.status(400).send('Request Body Not Found');
    return;
  }

  // メッセージ生成
  const message = makeMessage(req);

  // Slack 通知部分
  // Slack の Webhooks URL を代入
  const slack_webhook = "https://hooks.slack.com/services/hoge/fuga/hogehoge"

  // こちらのURLを、各自のIncoming webhookのURLに置き換えてください。
  const webhook = new IncomingWebhook(slack_webhook);
  await webhook.send(message);

  res.send(200);

};

// メッセージ生成部分
// 通知タイプ
// 1:課題追加
// 2:課題更新
// 3:コメントのみ追加
// 5:Wikiの追加
// 6:Wikiの更新
// 7:Wikiの削除
function makeMessage(req) {

  const projectKey = req.body["project"]["projectKey"];

  const type = req.body["type"];
  const id = req.body["id"];

  const notifications = req.body["notifications"];
  const createdUser = req.body["createdUser"];
  const createdUserName = createdUser['name'];

  const summary = req.body['content']['summary'];
  const content_id = req.body['content']['id'];
  const key_id = req.body['content']['key_id'];
  const dueDate = req.body['content']['dueDate'];
  const description = req.body['content']['description'];


  const content = {
    'type': type,
    'id': id,
    'summary': summary,
    'key_id': key_id,
    'dueDate': dueDate,
    'description': description
    'createdUserName' : createdUserName
  };
  console.log(JSON.stringify(content))

  // リンク先URLに必要な要素、BacklogのURLとプロジェクトキーと課題idを取得する
  const projectBaseURL = "https://bklg.xxxx.com/backlog/view/";
  const projectBaseURLwiki = "https://bklg.xxxx.com/backlog/wiki/";

  const slack_user_map = new Map([["hoge", "@hogehoge"], ["fuga", "fugafuga"]]);
  const slack_user = slack_user_map.get(createdUserName);

  if (type == 1) { //1:課題の登録の場合
    //課題の詳細から情報を取得する
    const issueType = req.body['content']['issueType']['name'];
    const status = req.body['content']['status']['name'];

    //URLにプロジェクトKeyと課題Keyをセットし、課題にアクセスできるURLを生成する
    const projectURL = projectBaseURL + projectKey + "-" + key_id;

    //Slackに通知するメッセージの作成
    return [
    createdUserName + ' さんが「' + summary + '」を登録しました',
    '',
    ':date: 期限日: ' + dueDate || 'なし',
    ':memo: 詳細: ' + description,
    ':earth_asia: 課題URL: ' + projectURL,
    ':label: 種別: ' + issueType,
    '',
    ].join('\n');

  } else if(type == 2 || type == 3){ //2:課題更新+3:コメント追加の場合
    //課題更新とコメント追加共通で取得するコメント内容とコメントKeyを取得する
    const comment = req.body["content"]["comment"]["content"];
    const commentKey = req.body["content"]["comment"]["id"];

    //URLにプロジェクトKeyと課題KeyとコメントKeyをセットし、課題にアクセスできるURLを生成する
    const projectURL = projectBaseURL + projectKey + "-" + key_id + "#comment-" +commentKey;

    if (type == 2){ //2:課題更新のみ、更新された情報を取得する

      //Slackに通知するメッセージの作成
      return [
      createdUserName + ' さんが「' + summary + '」を更新しました',
      '',
      ':date: 期限日: ' + dueDate || 'なし',
      ':email: コメント: ' + comment,
      ':earth_asia: 課題URL: ' + projectURL,
      '',
      ].join('\n');


    } else { //3:コメント追加の場合のみの更新情報を取得する

      //Slackに通知するメッセージの作成
      return [
      createdUserName + ' さんが「' + summary + '」にコメントしました',
      '',
      ':date: 期限日: ' + dueDate || 'なし',
      ':email: コメント: ' + comment,
      ':earth_asia: 課題URL: ' + projectURL,
      '',
      ].join('\n');      
    }


  } else if(type == 4){ //4:課題の削除
    //URLにプロジェクトKeyと課題KeyとコメントKeyをセットし、課題にアクセスできるURLを生成する
    const projectURL = projectBaseURL + projectKey + "-" + key_id;

    //Slackに通知するメッセージの作成
    return [
    createdUserName + ' さんが課題を削除しました',
    '',
    ':earth_asia: 課題URL: ' + projectURL,
    '',
    ].join('\n');

  } else if (type == 5 || type == 6 || type == 7){ //5:Wikiの追加, 6:Wikiの更新, 7:Wikiの削除
    const wiki_name = req.body["content"]["name"];
    // const wiki_content = req.body["content"]["content"];  

    //URLにプロジェクトKeyと課題KeyとコメントKeyをセットし、課題にアクセスできるURLを生成する
    const projectURL = projectBaseURL + projectKey + "-" + key_id + "#comment-" +commentKey;

      return [
        createdUserName + ' さんが「' + wiki_name + '」に追加/更新/削除しました',
        '',
      ].join('\n');

  } else if (type == 8 || type == 9 || type == 10){ //8:ファイルの追加, 9:ファイルの更新, 10:ファイルの削除
      const actions_map = new Map([
      [8, "ファイルの追加"],
      [9, "ファイルの更新"],
      [10, "ファイルの削除"]
      ]);

      const action_message = actions_map.get(type);

      const content_name = req.body["content"]["name"];
      const content_dir = req.body["content"]["dir"];  

      return [
        createdUserName + ' さんが' + action_message + 'しました。',
        '',
        'ファイル名: ' + content_name,
        '場所: ' + content_dir,
        '',
      ].join('\n');

  } else {

    const actions_map = new Map([[14, "課題をまとめて更新"], 
    [17, "お知らせの追加"],
    [11, "Subversionコミット"],
    [12, "Gitプッシュ"],
    [13, "Gitリポジトリの作成"],
    [15, "プロジェクトに参加"],
    [16, "プロジェクトから脱退"],
    [18, "プルリクエストの追加"],
    [19, "プルリクエストの更新"],    
    [20, "プルリクエストにコメント"],    
    [22, "発生バージョン/マイルストーンの追加"],    
    [23, "発生バージョン/マイルストーンの更新"],    
    [24, "発生バージョン/マイルストーンの削除"]
    ]);
    const action_message = actions_map.get(type);

      return [
        createdUserName + ' さんが' + action_message + 'しました。',
        '',
        'summary: ' + summary || 'なし',
        '',
      ].join('\n');

  }

}

終わりに

リモートで会社のメールを見る機会が少なくなったので、Slackに通知を飛ばせることができて、大変助かっている。