SODA Engineering Blog
🏂

Next-gen Slack platformを完全に理解する

2022/10/10に公開

株式会社SODAの社内勉強会で使用した資料です

こちらは株式会社SODAのエンジニア社内勉強会にて @rinchsan が発表したときの資料です。
株式会社SODAについては以下リンクなどをご覧ください。

https://recruit.soda-inc.jp

https://recruit.soda-inc.jp/engineer

何が便利か完全に理解する

  • 自作のSlack AppをSlackのクラウド上で実行できる
    • LambdaやCloud Runなど用意しなくてもよい
    • Denoでちょろっとコード書けばSlack Appを動かせる
  • クラウド上でLoggingやDatastoreの利用も出来る
    • 7日間のログを勝手に保存してくれる
    • データ保存もちょろっとコード書くだけ

ひとまず完全に理解する

Trigger

なにそれ

  • 何かしらのタイミングでWorkflowを呼び出すやつ
  • 種類によって受け取れるデータがいろいろ

  • スケジュールで定期的に
  • 新しいチャンネルが作成された
  • 新しい絵文字が作成された
  • メッセが投稿された
  • メッセにリアクションが付いた
  • etc

なんで分離されてる?

  • 同じWorkflowを色々なトリガで呼び出すため
    • channel_createdchannel_renamed で同じWorkflowを呼び出すとか

Workflow

なにそれ

  • FunctionをStepとして組み合わせてやりたいことを実装するやつ
  • Triggerから入力を受け取る

  • 新しく作成されたチャンネルをお知らせ
  • 新しく作成された絵文字をお知らせ

なんで分離されてる?

  • TriggerとFunction群を橋渡しするため

Function

なにそれ

  • テスト可能な単位に処理を分割して実装するやつ
  • 入力と出力を持つ

  • メンションされた文字列からコマンドを抽出する
  • 投稿するメッセージ文字列を作る
  • Built-inなFunctionも
    • Schema.slack.functions.SendMessage
    • Schema.slack.functions.OpenForm

なんで分離されてる?

  • テスト可能にするため
  • 再利用性を上げるため
  • 配布しやすくするため

始め方を完全に理解する

↓を実行するだけで必要なツールは全部入ります(Mac / Linuxの場合)(deno はHomebrewでインストールされます)。

curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash

↓を実行して表示されるスラッシュコマンドをSlackに貼り付けて投稿するだけで認証完了。

slack login

↓を実行して使ってるWorkspaceが表示されたら準備OKです。

slack info

↓を実行して雛形をとりあえず作成できます。

slack create [name] --template https://github.com/slack-samples/deno-starter-template

Triggerを完全に理解する

例えば「チャンネルが新規作成されたとき」のTriggerはこちら。

import { Trigger } from "deno-slack-api/types.ts";
import NotifyWorkflow from "../workflows/notify.ts";

const notifyTrigger: Trigger<typeof NotifyWorkflow.definition> = {
  type: "event",
  name: "Notify channel created",
  description: "Notify channel created",
  workflow: "#/workflows/notify_workflow",
  inputs: {
    new_channel_id: {
      value: "{{data.channel_id}}",
    },
    new_channel_name: {
      value: "{{data.channel_name}}",
    },
  },
  event: {
    event_type: "slack#/events/channel_created",
  },
};

export default notifyTrigger;

Triggerを作成・更新・削除します。TriggerはWorkflowとは別に独立してデプロイ(?)する必要があります。

slack trigger create --trigger-def triggers/channel_created.ts
slack trigger update --trigger-def triggers/channel_created.ts --trigger-id ABCDE12345
slack trigger delete --trigger-id ABCDE12345

Workflowを完全に理解する

例えば「自分で実装した SendMessageFunctionDefinition を呼び出す」のWorkflowはこちら。

Workflowが受け取るデータを DefineWorkflowinput_parameters で指定して、 addStep するときに XxxWorkflow.inputs から取り出します。

addStep したFunctionの出力を別のStepで使いたい場合は、 addStep の返り値を変数 xxxStep に格納して xxxStep.outputs で取り出します。

import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { SendMessageFunctionDefinition } from "../functions/send_message.ts";

const NotifyWorkflow = DefineWorkflow({
  callback_id: "notify_workflow",
  title: "Notify new channel",
  description: "Notify new channel",
  input_parameters: {
    properties: {
      new_channel_id: {
        type: Schema.types.string,
      },
      new_channel_name: {
        type: Schema.types.string,
      },
    },
    required: ["new_channel_id", "new_channel_name"],
  },
});

NotifyWorkflow.addStep(SendMessageFunctionDefinition, {
  new_channel_id: NotifyWorkflow.inputs.new_channel_id,
  new_channel_name: NotifyWorkflow.inputs.new_channel_name,
});

export default NotifyWorkflow;

Triggerとは別にWorkflowをデプロイします。Workflowは↓のように manifest.tsworkflows に持たせて $ slack deploy することでデプロイできます。

import { Manifest } from "deno-slack-sdk/mod.ts";
import NotifyWorkflow from "./workflows/notify.ts";

export default Manifest({
  name: "notify-new-channel",
  description: "Notify new channel created/renamed",
  icon: "assets/icon.png",
  workflows: [NotifyWorkflow],
  outgoingDomains: [],
  botScopes: [
    "commands",
    "chat:write",
    "chat:write.public",
    "channels:read",
  ],
});

ローカル開発環境でデバッグする場合は $ slack run が便利です。

Functionを完全に理解する

例えば「チャンネル名とチャンネルIDを受け取ってメッセージを作って投稿する」のFunctionはこちら。
https://github.com/rinchsan/notify-new-channel/blob/main/functions/send_message.ts

inputs とは別で受け取れる env は↓を実行して設定できます。Slackのクラウド上に暗号化されて保存されます。

slack env add my-key secret-value

さらに、 inputsenv に加えて受け取れる client はSlack APIを呼び出せるので便利です。

Functionのテストを完全に理解する

↑で例に出したFunctionのテストはこちら。
https://github.com/rinchsan/notify-new-channel/blob/main/functions/send_message_test.ts

deno-slack-sdk/mod.tsSlackFunctionTester がとても便利で、返り値の createContext を使えばとても簡単にテストが書けて便利です。

createContext を使うと client をデフォルトでStub(?)してくれるんですが、おそらく正常系しかテスト出来ないので、API Callを柔軟にモックしたい場合は deno.land/x/mock_fetch などを使うと良いでしょう。

ちなみに、↓のコマンド群でテストを実行しつつカバレッジを計測することが出来て便利です。 coverage オプションはまだ UNSTABLE なのでご注意を。

deno test --allow-net --coverage=cover
deno coverage cover/
rm -rf cover/

その他便利な機能を完全に理解する

Logging

console.log で出力したログを↓で見れます。便利。

slack activity --tail

Datastore

詳細は書くのめんどくさいので多機能すぎるので割愛しますが、なんとデータストアも使えます。
DynamoDB Syntaxで使えるらしい。なんかすごそう。

VSCode Extension

DenoのVSCode Extensionがとても便利です。めっちゃ色々補完が効くのでドキュメント見なくてもコード書けます。

Permissions

Workspace内のどのUserにデプロイ権限を付けるか選べます。

Collaborations

複数人で同じSlack Appを開発することも出来ます。
ただチームで開発するならいい感じにCI/CDを組んで自動デプロイのパイプラインを組みたいところ。
そうなってくると $ slack login をどうやって通すか、 $ slack trigger create が冪等性ないのどうするか、とか面倒そう。

完全に理解したぜ!

じゃあなんか作ろう!

例に使った「新しくチャンネルがCreate or Renameされたら通知する」のSlack Appのコード全体はこちら。
https://github.com/rinchsan/notify-new-channel

あと、ここまで読んでくれて「作るぞ!」となってる無料版Slack利用中の方々、すみません、有料版Slackでないと利用できないのです。
有料版SlackであればNext-gen Slack platform自体は追加料金なしで使えるので、偉い人に課金をお願いしましょう。

GitHubで編集を提案
SODA Engineering Blog
SODA Engineering Blog

Discussion