はじめに
Bolt for JSでは、Slackのワークフロービルダーで使えるWorkflow Stepsを実装することができます。(以下、Custom Stepと呼びます。)
~~リファレンスがありますが日本語化がまだのため、~~私が実装した経験から解説していきます。
日本語化されました!
https://slack.dev/bolt-js/ja-jp/concepts#steps
事前設定
前提としてSlackアプリを作成するのですが、アプリの作成は様々な記事があがっているのでそちらを参照してください。
Custom Stepを作成するために必要な設定は以下の2点です。
ワークスペースでWorkflow stepsの利用を許可する
ワークスペースの設定 > 権限 > アプリからのワークフロービルダーステップ
「ワークフロービルダーで、このワークスペースにインストールされたアプリのステップを表示する」にチェック。
アプリのWorkflow Steps
アプリのWorkflow Stepsの「Turn on workflow steps」をONの状態にします。
ON状態にすると、workflow.steps:execute
のスコープが必ず追加されるため、アプリのReinstallが促されます。
Reinstallしておきましょう。
Stepsの「Add Step」から追加するStepを登録します。
- Step name
- 追加するCustom Stepの名前
- Callback ID
- あとで実装するCallback IDと一致させる
Create up to 10 custom steps for your app to be used within Workflow builder.
Custom Stepは1つのアプリに対して10こまでしか実装できないみたいですね。
実装していく
実装の基本の形は
const { App, WorkflowStep } = require('@slack/bolt');
const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET,
token: process.env.SLACK_BOT_TOKEN,
});
const wsEdit = ({ ack, step, configure }) => {};
const wsSave = ({ ack, view, update }) => {};
const wsExecute= ({ step, complete, fail, client }) => {};
const ws = new WorkflowStep('custom_step', {
edit: wsEdit,
save: wsSave,
execute: wsExecute,
});
app.step(ws);
です。
'custom_step'
の部分が事前設定にあるCallback IDと一致するようにします。
edit(wsEdit
), save(wsSave
), execute(wsExecute
)のそれぞれの役割とサンプルコードを解説します。
edit(wsEdit)
ワークフロービルダーで、Custom Stepを追加したり、編集したりするときに実行されます。
カスタムステップの設定をするモーダルを定義し、開きます。
サンプルコード
const wsEdit = ({ ack, step, configure }) => {
await ack();
const blocks = [
{
type: 'input',
block_id: 'title_input',
element: {
type: 'plain_text_input',
action_id: 'title',
},
label: {
type: 'plain_text',
text: 'タイトル',
}
},
{
type: 'input',
block_id: 'message_input',
element: {
type: 'plain_text_input',
action_id: 'message',
multiline: true,
},
label: {
type: 'plain_text',
text: 'メッセージ',
},
}
];
const { inputs } = step;
const title = inputs.title;
const message = inputs.message;
if (title !== void 0) {
blocks[0].element.initial_value = title.value;
}
if (message !== void 0) {
blocks[1].element.initial_value = message.value;
}
await configure({ blocks });
};
順に説明します。
await ack();
応答です。とりあえず返しておきましょう。
const blocks = [
// 省略
];
モーダルに表示するBlockを定義します。
Block Kit Builderを使うと便利です。(Modal Previewで!)
const { inputs } = step;
const title = inputs.title;
const message = inputs.message;
if (title !== void 0) {
blocks[0].element.initial_value = title.value;
}
if (message !== void 0) {
blocks[1].element.initial_value = message.value;
}
この部分は、編集モードで開いたときの初期値を定義しています。
これがないと、編集で開いたときに前回保存した内容が表示されません。
inputs
はこの後にでてくるsave(wfSave
)で定義しています。
(この部分はなんかもっとうまいやり方があるかもしれません。)
await configure({ blocks });
最後にconfigure
にblocks
を渡すことで、モーダルが表示されます。
save(wsSave)
Custom Stepで、表示されたモーダルの「保存する」をクリックした時に実行されます。
モーダルからの値を受け取って、利用します。
サンプルコード
const wsSave = async ({ ack, view, update }) => {
await ack();
const { values } = view.state;
const title = values.title_input.title;
const message = values.message_input.message;
const inputs = {
title: { value: title.value },
message: { value: message.value },
};
const outputs = [
{
type: 'text',
name: 'title',
label: 'タイトル',
},
{
type: 'text',
name: 'message',
label: 'メッセージ',
},
];
await update({ inputs, outputs });
}
順に説明します。
await ack();
応答です。とりあえず返しておきましょう。
const { values } = view.state;
const title = values.title_input.title;
const message = values.message_input.message;
const inputs = {
title: { value: title.value },
message: { value: message.value },
};
view.state.values
にはモーダルに入力した値がわたってきます。
それをもとにinputs
を構成します。
それぞれvalue
に値を設定すればよいです。
このinputs
はedit(wsEdit
)でstep.inputs
にわたります。
inputs
を詳しく知りたい方はリファレンスを参照ください。
※私にはvariables
を使うシーンがよくわかりませんでした
const outputs = [
{
type: 'text',
name: 'title',
label: 'タイトル',
},
{
type: 'text',
name: 'message',
label: 'メッセージ',
},
];
outputs
には後続のワークフローステップにわたす変数を定義します。
(変数を挿入するをクリックしたときにでてきます。)
type
にはtext
,channel
,user
のいずれかが入ります。
実際に渡す値はexecute(wsExecute
)で設定します。
await update({ inputs, outputs });
最後にupdate
にinputs
とoutputs
を渡すことで、モーダルが表示されます。
execute(wsExecute)
ワークフローが実際にCustom Stepに到達した際に実行されます。
サンプルコード
const wsExecute = async ({ step, complete, fail, client }) => {
const { inputs } = step;
const outputs = {
title: inputs.title.value,
message: inputs.message.value,
};
try {
await client.chat.postMessage({
channel: '###',
blocks: [
{
type: 'header',
text: {
type: 'plain_text',
text: inputs.title.value,
emoji: true,
}
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: inputs.message.value,
}
},
]
});
await complete({ outputs });
}
catch (e) {
fail({ error: { message: 'fail...' } });
}
}
順に説明します。
const { inputs } = step;
const outputs = {
title: inputs.title.value,
message: inputs.message.value,
};
save(wsSave
)で定義したoutputsに対して、次のステップにわたす値を設定します。
try {
// 省略
await complete({ outputs });
}
catch (e) {
fail({ error: { message: 'fail...' } });
}
try
で処理を実行します。
サンプルコードでは、Custom Stepで設定したタイトルとメッセージを固定のチャンネルにポストしています。
最後に
await complete({ outputs });
でoutputsを次のステップにわたします。
エラーがでた場合は
fail({ error: { message: 'fail...' } });
でエラーメッセージを返します。
おそらく、ワークフローのアクティビティにエラーメッセージが表示されるのだと思います。(未確認)
さいごに
これでCustom Stepが実装できました!
ワークフロービルダーでちょっと手の届かないところだけサクッと作れるので、大変重宝しています。
アプリのショートカットはabc順で並ぶので、探すのが大変ですが、ワークフローは一番上に表示されるので、その点でもワークフロービルダーでサクッと作ってしまいたいですよね。
汎用的に使えそうなCustom Stepを複数作ってみて、Slack Lifeを充実させましょう!
最後になりましたが、間違っているところなどありましたら、ばしばしご指摘くださいませ。