LoginSignup
6
4

More than 1 year has passed since last update.

Slack 次世代プラットフォーム機能を少しずつ試す - 標準フォーム編

Last updated at Posted at 2022-12-19

こんにちは、Slack の公式 SDK 開発と日本の Developer Relations を担当している瀬良 (@seratch) と申します :wave:

この記事は Slack の次世代プラットフォーム機能を少しずつ試しながら、ゆっくりと理解していくシリーズの記事です。

「次世代プラットフォーム機能って何?」という方は、以下の記事で詳しく解説しましたので、まずはそちらをお読みください。

この記事では、標準ファンクションとして提供されているフォーム(OpenForm)の使い方をご紹介します。

プロジェクトを作成

いつものようにブランクプロジェクトを作成してゼロからコードを足していきましょう。slack create コマンドを実行して、選択肢から「Blank Project」を選択してください。作成したプロジェクトの構成は以下の通りです。

$ tree
.
├── LICENSE
├── README.md
├── assets
│   └── default_new_app_icon.png
├── deno.jsonc
├── import_map.json
├── manifest.ts
└── slack.json

ワークフローを追加

今回は標準ファンクションだけを使いますので workflow.ts というファイルだけを作成します。

// ----------------
// ワークフロー定義
// ----------------

import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
export const workflow = DefineWorkflow({
  callback_id: "form-demo-workflow",
  title: "OpenForm Demo Workflow",
  input_parameters: {
    properties: {
      interactivity: { type: Schema.slack.types.interactivity },
      user_id: { type: Schema.slack.types.user_id },
      channel_id: { type: Schema.slack.types.channel_id },
    },
    required: ["interactivity", "user_id", "channel_id"],
  },
});

// ここにフォームを使ったステップを追加
const formStep = workflow.addStep(Schema.slack.functions.OpenForm, {
  title: "Send a greeting",
  interactivity: workflow.inputs.interactivity,
  submit_label: "Send greeting",
  fields: {
    elements: [
      {
        name: "recipient",
        title: "Recipient",
        // users_select ブロックに変換される
        type: Schema.slack.types.user_id,
        default: workflow.inputs.user_id,
      },
      {
        name: "channel",
        title: "Channel to send message to",
        // channels_select ブロックに変換される
        type: Schema.slack.types.channel_id,
        default: workflow.inputs.channel_id,
      },
      {
        name: "message",
        title: "Message to recipient",
        // plain_text_input ブロックに変換される
        type: Schema.types.string,
        // multiline: true に変換される
        long: true,
      },
    ],
    required: ["recipient", "channel", "message"],
  },
});

// 正しく outputs を受け取った確認
workflow.addStep(Schema.slack.functions.SendEphemeralMessage, {
  user_id: formStep.outputs.fields.recipient, // name がキー名になる
  channel_id: formStep.outputs.fields.channel, // name がキー名になる
  message: "OpenForm's `outputs.fields`: ```" +
    formStep.outputs.fields +
    "```",
});

// ----------------
// トリガー定義
// ----------------

import { Trigger } from "deno-slack-api/types.ts";
const trigger: Trigger<typeof workflow.definition> = {
  type: "shortcut",
  name: "Form Demo Trigger",
  workflow: `#/workflows/${workflow.definition.callback_id}`,
  inputs: {
    // interactivity は OpenForm を利用するためには必須
    // この記事投稿時点で interactivity を提供できるのはリンクトリガーのみ
    interactivity: { value: "{{data.interactivity}}" },
    // OpenForm を利用する上では必要ないが、受け取った内容の出力をするために利用
    user_id: { value: "{{data.user_id}}" },
    channel_id: { value: "{{data.channel_id}}" },
  },
};
export default trigger;

そして manifest.ts に今作成したワークフローを登録します。

import { Manifest } from "deno-slack-sdk/mod.ts";
import { workflow as DemoWorkflow } from "./workflow.ts";

export default Manifest({
  name: "frosty-mink-263",
  description: "Demo workflow",
  icon: "assets/default_new_app_icon.png",
  workflows: [DemoWorkflow],
  outgoingDomains: [],
  botScopes: ["commands", "chat:write", "chat:write.public"],
});

slack run でアプリを起動してみて、エラーが発生していないことを確認してください。

リンクトリガーを作成してワークフローを試す

slack triggers create --trigger-def ./workflow.ts を実行して、リンクトリガーを作成して、それをチャンネル内のメッセージかブックマークとして共有してください。

実行すると以下のようにあなただけに見えるメッセージとしてフォームで受け取った情報を返してくれます。

基本的な利用法のまとめ

OpenForm の基本的な利用法のポイントは、

  • リンクトリガーを使って interactivity をワークフローに渡す
  • ワークフローに Schema.slack.functions.OpenForm ファンクションを追加
  • OpenForm の設定項目は、こちらこちらのページを参照のこと
  • OpenFormworkflow#addStep() した戻り値を変数として保持、その変数の outputs.fields に送信された情報は含まれる

という点になります。

ここまで、とりあえず使い方は理解できたと思いますが、もう少し具体例をご紹介していきます。

組み込みの入力バリデーションを追加

先ほどのフォームでは、すべての項目を必須項目として設定していました。"message" を必須項目から外してみましょう。そのためには required として設定されている三つの要素から "message" を削除するだけです。

    required: ["recipient", "channel"],

これで "message" に何も入力しなくても送信できるようになります。入力せずに送信した場合、空文字になるわけではなく outputs.fields にそのキー自体が存在しなくなりますので、ご注意ください。

次に「"message" は必須ではないが、入力するなら 5 文字以上 20 文字以下」という条件をつけてみましょう。以下のように設定することで 1-4 文字、21 文字以上の入力がされた状態ではフォームを送信できなくなります。

      {
        name: "message",
        title: "Message to recipient",
        // plain_text_input ブロックに変換される
        type: Schema.types.string,
        // multiline: true に変換される
        long: true,
        minLength: 5,
        maxLength: 20,
      },

(まだあまり詳しい説明が書かれていませんが・・・ :bow:こちらのページに利用可能な属性の一覧がありますので、ご参照ください。

組み込みのセレクトメニューの使い方

Block Kit でいうところの static_select のような機能は設定方法が若干異なりますので、具体例とともご紹介しておきます。

以下のような要素を追加するとセレクトメニューが生成されます。

      {
        name: "favorite",  // 必須
        title: "Favorite",  // 必須
        type: Schema.types.string,  // 必須
        choices: [
          { value: "dog", title: "Dog" },
          { value: "cat", title: "Cat" },
        ],  // 必須
        enum: ["dog", "cat"],  // 必須
        default: "cat",  // 必須でない
      },

実際の見た目はこのようになります。

注意点としては、(この記事投稿時点ではまだ機能として利用されていませんが) enum は必須項目となっており、省略した場合、フリーテキストの plain_text_input ブロックにフォールバックされてしまいますので、必ず指定するようにしてください。

また、その他のセレクトメニューとしては、上のコード例でもすでに出てきていますが、user_idchannel_id といった type を指定すると自動的にユーザー一覧からの選択、チャンネル一覧からの選択に変換されるようになっています。

また、複数選択には Schema.types.array とその要素の type を指定します。

      {
        name: "people",
        title: "People",
        type: Schema.types.array,
        items: {
          type: Schema.slack.types.user_id,
        },
        default: [workflow.inputs.user_id, "USLACKBOT"],
      },

以下のような UI に変換されます。

ここまでで試した fields.elements をすべて含む完全なコードは以下の通りです。これを貼り付けて試してみてください。

// ここにフォームを使ったステップを追加
const formStep = workflow.addStep(Schema.slack.functions.OpenForm, {
  title: "Send a greeting",
  interactivity: workflow.inputs.interactivity,
  submit_label: "Send greeting",
  fields: {
    elements: [
      {
        name: "recipient",
        title: "Recipient",
        // users_select ブロックに変換される
        type: Schema.slack.types.user_id,
        default: workflow.inputs.user_id,
      },
      {
        name: "channel",
        title: "Channel to send message to",
        // channels_select ブロックに変換される
        type: Schema.slack.types.channel_id,
        default: workflow.inputs.channel_id,
      },
      {
        name: "message",
        title: "Message to recipient",
        // plain_text_input ブロックに変換される
        type: Schema.types.string,
        // multiline: true に変換される
        long: true,
        minLength: 5,
        maxLength: 20,
      },
      {
        name: "favorite",
        title: "Favorite",
        // static_select ブロックに変換される
        type: Schema.types.string,
        choices: [
          { value: "dog", title: "Dog" },
          { value: "cat", title: "Cat" },
        ],
        enum: ["dog", "cat"], // 省略しないでください
        default: "cat",
      },
      {
        name: "people",
        title: "People",
        // multi_users_select ブロックに変換される
        type: Schema.types.array,
        items: {
          // ここの型を変えると他のブロックに変化する
          type: Schema.slack.types.user_id,
        },
        default: [workflow.inputs.user_id, "USLACKBOT"],
      },
    ],
    required: ["recipient", "channel"],
  },
});

// 正しく outputs を受け取った確認
workflow.addStep(Schema.slack.functions.SendEphemeralMessage, {
  user_id: formStep.outputs.fields.recipient, // name がキー名になる
  channel_id: formStep.outputs.fields.channel, // name がキー名になる
  message: "OpenForm's `outputs.fields`: ```" +
    formStep.outputs.fields +
    "```",
});

繰り返しとなりますが、指定可能な値についてはこちらのドキュメントをご参照ください。

終わりに

いかがだったでしょうか?制約はあるものの、この標準のフォームで、多くの用途はカバーできるのではないかと思います。特に既存の Block Kit では標準でサポートしてなかった最小値・最大値のバリデーションなどはよくあるユースケースだと思います。

より詳細な情報は、以下のページをご参照ください。

別の記事では、制約のない高度なモーダル利用についても解説する予定ですので、そちらの情報をお探しの方は少々お待ちください。

(追記)記事を公開しました。

それでは!

6
4
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
6
4