KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

Slack次世代プラットフォームで業務効率化に貢献する💪

こんにちは!ソフトウェアエンジニアの種岡です。 こちらの記事は カケハシ Advent Calendar 2023 Part2 の14日目の記事になります。 Part1もあるのでぜひご覧ください!

はじめに

カケハシではコミュニケーションツールとしてSlackを導入しています。 開発チームの多くはフルリモートで働いていることもあり、仕事の中心にSlackがあるのが当たり前の日常になっています。 そんな職場としての役割も果たしているSlackに、お手軽に独自ワークフローを作成する仕組みが備わっているのをご存知でしょうか? この記事ではSlack次世代プラットフォームを使った業務効率化事例を紹介しようと思います。

事例その1:メンバー指名ルーレット🎯

誰がやっても大差ない作業ってありませんか? 「誰かやりたい人いますか?」と提案しても手が上がりにくいといった状況に覚えはありませんか? そんな時に効果を発揮するワークフローです。 ランダムで指名することで公平感が生まれやすいため、一度導入すると頻度高く利用される印象があります。

事例その2:雑談の誘い☕

ロールが異なるメンバーとは、スタンドアップやレトロスペクティブなどチームイベントで顔を合わす程度という状況に遭遇したことありませんか? とくにリモートワーク環境下では、物理的に同僚が近くにいないため、偶発的にコミュニケーションが発生することが期待できません。 心理的安全性を高める手段の1つとして、意図的に会話の場を提供し、普段から個々にコミュニケーションを取って意見を言いやすい下地を作っておきたいと思って作ったワークフローです。 また、会話が得意でない人への配慮としてランダムに会話のテーマを提案するようにしたり、できるだけ参加しやすいようチームイベントの10分前に開始するといった工夫もしています。

事例その3:リリース作業周知📢

リリース作業は定型業務である一方で、手数が多く作業の抜け漏れが発生しやすかったりしないでしょうか? チームではGitHub Actionsを利用してCI/CDの自動化をしている部分はありますが、うまく汎用性を持たせて吸収させるのが難しい部分もあったので、Slackワークフローを作りました。

リリース作業ワークフローを起動させると、以下のようなフォームが表示され

必要な情報を入力後に送信ボタンを押すと、該当のチャンネルにリリース内容が通知されるというものです。

Slack次世代プラットフォームとは?

Slack次世代プラットフォームはオリジナルのSlackアプリ開発することができるプラットフォームです。

  • Slackのクラウド上で動く(もう自前でインフラを用意する必要が無くなった)
  • Denoを使った開発(開発時にTypeScriptによる型の恩恵を受けられるのはかどる)
  • ローカル環境と本番環境をそれぞれ持てる(従来はローカル環境構築だけでも一苦労)
  • Datastoreが付属してくる(Slackアプリ専用のDBが持てる嬉しさ)
  • 有料プランでしか利用できない

といった特徴を持っています。 これまでSlackアプリ開発でBoltというフレームワークを利用していた身としては、上記に挙げた点は開発者体験向上に直結しており嬉しい限りです。 有料プランでしか使えないので、利用できる環境下にいるエンジニアの皆さんには試していただきたいと思っています!

Slack CLIを使ってみる

Slack CLIの準備とローカル環境での実行

クイックスタートガイドを参考に、Slack CLIをインストールしご利用のSlackとの認証作業を行います(Step1と2)。 Step3でアプリのテンプレートを作る際に、Slack側で用意してくれているもの(View more samplesを選択すると表示される)があるのでサクッと試してみたい方にオススメです。

$ slack create test
? Select a template to build from: [Use arrows to move]

  Issue submission (default sample)
  Basic app that demonstrates an issue submission workflow

  Scaffolded template
  Solid foundation that includes a Slack datastore

❱ Blank template
  A, well.. blank project

  View more samples

  Guided tutorials can be found at api.slack.com/automation/samples

ここではBlank templateを選択しています。テンプレートを選択したらrunコマンドの実行だけでローカル環境が用意されます(ローカル用のSlackアプリをインストールしたい適切なワークスペースを選択してください) コマンドを実行したターミナルにはSlackクラウド環境とコネクションが張られます。アプリのログなどが表示されるためデバッグ時に非常に便利です。

slack run
? Choose a local environment
❱ kakehashi xxxx xxxx

Updating local app install for "KAKEHASHI"

実装

準備が整ったところで、Slack次世代プラットフォームの肝である、Functions、WorkFlows、Triggersの3つの概念を 事例その2:雑談の誘い☕ を例にひとつずつみていきます。 以下はBlank Templateを選択した後に、functions、triggers、workflowsのディレクトリを作成して、それぞれファイルを作成しました。(consts配下はただの配列なので説明省略)

├── consts
│   ├── members.ts
│   └── zatsudan_themes.ts
├── functions
│   └── zatsudan_function.ts
├── manifest.ts
├── triggers
│   └── zatsudan_trigger.ts
└── workflows
    └── zatsudan_workflow.ts

manifest.tsにWorkflowを紐づけ

manifest.tsはアプリの設定を定義するファイルで、利用できるワークフローを登録します。 また、そのワークフロー内部で利用しているSlack APIを実行するために必要な権限を設定します。

import { Manifest } from "deno-slack-sdk/mod.ts";
import ZatsudanWorkflow from "./workflows/zatsudan_workflow.ts"; // ワークフローをimport

export default Manifest({
  name: "test-slack-bot",
  description: "test slack bot",
  workflows: [
    ZatsudanWorkflow, // Slackアプリからワークフローを参照できるように定義
  ],
  outgoingDomains: [],
  botScopes: [
    "commands",
    "chat:write",
    "chat:write.public",
    "channels:history",
  ],
});

Workflowの作成

Slack次世代プラットフォームはワークフローを基本単位としてます。 ここでは、指定したチャンネル内 で、 雑談しませんか?という投稿 を行うというワークフローを定義しているといったイメージです。 雑談のテーマや誰を選ぶか?についてはビジネスロジックとしてFunctionsに切り出しているという使い方をしています。

import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { ZatsudanFunctionDefinition } from "../functions/zatsudan_function.ts";
import { memberGroup1, memberGroup2 } from "../consts/members.ts"; // メンバーのSlackユーザーIDが配列で記載されているファイル
import { zatsudanThemes } from "../consts/zatsudan_themes.ts"; // 雑談のテーマが配列で記載されているファイル

const ZatsudanWorkflow = DefineWorkflow({
  callback_id: "zatsudan_workflow",
  title: "雑談しましょう",
  description: "ランダムで雑談する相手を指名",
  input_parameters: {
    properties: {
      channel_id: { type: Schema.slack.types.channel_id },
    },
    required: ["channel_id"],
  },
});

// 雑談メンバーやテーマの選定はFunctions側に切り出し
const zatsudanFunctionStep = ZatsudanWorkflow.addStep(
  ZatsudanFunctionDefinition,
  { members: { memberGroup1, memberGroup2 }, themes: zatsudanThemes },
);

// 対象のチャンネルにメッセージを投稿する
ZatsudanWorkflow.addStep(
  Schema.slack.functions.SendMessage,
  {
    channel_id: ZatsudanWorkflow.inputs.channel_id,
    message: zatsudanFunctionStep.outputs.message,
    interactive_blocks: [
      {
        type: "actions",
        block_id: "ok-button",
        elements: [
          {
            type: "button",
            action_id: "ok",
            text: {
              type: "plain_text",
              text: "いいよ :smile:",
            },
            style: "primary",
          },
          {
            type: "button",
            action_id: "ng",
            text: {
              type: "plain_text",
              text: "スキップで :sorry:",
            },
            style: "danger",
          },
        ],
      },
    ],
  },
);

export default ZatsudanWorkflow;

Functionsの作成

Workflowから呼ばれるFunctionの中身になります。

import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";

export const ZatsudanFunctionDefinition = DefineFunction({
  callback_id: "zatsudan_function",
  title: "雑談相手をランダムで選択",
  source_file: "functions/zatsudan_function.ts",
  input_parameters: {
    properties: { // Functionの引数を定義
      members: {
        type: Schema.types.object,
        properties: {
          memberGroup1: {
            type: Schema.types.array,
            items: { type: Schema.types.string },
          },
          memberGroup2: {
            type: Schema.types.array,
            items: { type: Schema.types.string },
          },
        },
      },
      themes: {
        type: Schema.types.array,
        items: { type: Schema.types.string },
      },
    },
    required: ["members", "themes"], // 必須の引数を定義
  },
  output_parameters: { // Functionが返す値を定義
    properties: {
      message: {
        type: Schema.types.string,
        description: "投稿されるメッセージ",
      },
    },
    required: ["message"],
  },
});

// 上記のFunctionの定義を元にビジネスロジックを実装する
export default SlackFunction(
  ZatsudanFunctionDefinition,
  ({ inputs }) => {
    const { members, themes } = inputs;
    const memberGroup1 = members.memberGroup1!;
    const memberGroup2 = members.memberGroup2!;
    const member1 = memberGroup1[Math.floor(Math.random() * memberGroup1.length)];
    const member2 = memberGroup2[Math.floor(Math.random() * memberGroup2.length)];
    const theme = themes[Math.floor(Math.random() * themes.length)];
    const message =
      `<@${member1}> と <@${member2}> スタンドアップ前に10分雑談 :coffee: どうですか:question:\n場所はMeetで\nテーマは *「${theme}」* です`;
    return { outputs: { message } };
  },
);

これまでの過程をまとめると

  • manifest.tsへの追記で、SlackアプリからWorkflowを呼び出せるようになり
  • Workflowを呼び出すと雑談対象メンバーをランダムで選択してチャンネル上に通知する

のロジック実装が完了しました。

Triggerの作成

Triggerは、Slackのワークフローを何で起動させるかを定義するものになります。 Trigger Typesは4種類あります。 事例1や事例3では自分のタイミングでワークフローを起動したいのでLink triggerを使っています。 事例2は午前9時に起動したいのでScheduled triggerを使っています。 以下がzatsudan_trigger.tsの中身です。

import { Trigger } from "deno-slack-sdk/types.ts";
import { TriggerTypes } from "deno-slack-api/mod.ts";
import ZatsudanWorkflow from "../workflows/zatsudan_workflow.ts";

const startTime = new Date();
startTime.setHours(9, 0, 0, 0);

const trigger: Trigger<typeof ZatsudanWorkflow.definition> = {
  type: TriggerTypes.Scheduled,
  name: "月、火、金の朝9時に雑談相手をランダムで選択",
  workflow: `#/workflows/${ZatsudanWorkflow.definition.callback_id}`, // Scheduled triggerにワークフローを紐づけ
  inputs: {
    channel_id: {
      value: 'xxx',
    },
  },
  schedule: {
    start_time: startTime.toISOString(), // 未来日を指定しないと動作しないので注意
    end_time: "2025-12-31T23:59:59Z",
    frequency: {
      type: "weekly",
      on_days: [ // 月、火、金でワークフローが起動するように
        "Monday",
        "Tuesday",
        "Friday",
      ],
    },
  },
};

export default trigger;

TriggerをSlackアプリに紐付ける

最後に、TriggerをSlackアプリに紐づける作業が必要になります。 以下のコマンドを実行して後は定刻になるまで待ちましょう🚀

slack triggers create --trigger-def ./triggers/zatsudan_trigger.ts

開発Tips

チームでSlackアプリ開発したい時

collaboratorに追加する必要があります。

# xxxはSlackユーザーID
slack collaborator add xxxx

collaborator一覧はは以下で確認できます。

slack collaborator list

本番環境のログがみたい

ローカル環境での開発時には、slack runでターミナル上にログが表示されていました。 本番環境では以下のコマンドで実行ログやエラーログを確認できます。

slack activity --tail

Triggerはこまめに削除しておくと良い

利用しなくなったきちんと削除コマンドを実行しないとSlackクラウド環境上に残り続けます。(Triggerファイル自体を削除しただけでは駄目) 不要になったTriggerは適宜削除コマンドを実行しておくのが良いです。

# 登録されているトリガーのIDを確認
slack triggers list

# トリガーの削除
slack trigger delete --trigger-id xxxx

おわりに

Slack次世代プラットフォームを活用したワークフロー事例を紹介しました。 普段の仕事をしている中で「3回ぐらい同じことやっているな🤔」と感じたらワークフロー化できるチャンスかもしれません。 また、手軽に開発できる環境を準備することができ、アイディアをすぐ具体化できるので、開発者体験は良かったです😊 業務効率化して得られた時間をサービス成長に関連する開発に投資していけると良いですね。 余談ではありますが、ランダムでメンバーを指名するワークフローは、2回続けて指名されると、不具合か意図的に偏りがでるロジックにしていることを疑われれるため、ソースコードをメンバーに公開しておくと良いです😁

明日は同じチームの荻野さんです!お楽しみに〜👋