こんにちはビジネステクノロジーユニットのおかしんです。
私の所属するビジネステクノロジーユニットでは、主に社内情報システム、ITデバイスの管理、セキュリティなどを担っており、私は最近は様々なシステム間連携の構築や自動化に取り組んでいます。
Microsoft Power Automateについて
Microsoft Power Automateをご存知でしょうか?以前はMicrosoft Flowという名前でしたが、いわゆるIPaaS(Integration Platform as a Service)というジャンルで、様々なWebサービスをつなぎ合わせて、ノーコードもしくはローコードで自動化を実現するサービスです。
toC向けだとIFTTTなどが有名でしょうか。
toB向けのプロダクトでは
などが有名です。
Power Automate(以下PA)は上記の中だとZapierなどと比べて最初から用意されているコネクタ(連携済みのWebサービス)はそこまで多いわけではないのですが、HTTPのリクエストを使ってAPIを叩いたり、JSONをパースするのは比較的得意だったりします。
今回はそんなPower Automateを使ってSlackのスラッシュコマンドを作成する手法を紹介したいと思います。
なお、今回のレシピにはPAの有料版が必要になります。
Slackのスラッシュコマンドとは?
スラッシュコマンドとは、Slack内のチャットボックスで /
から始まるコマンドを実行することで特定の動作を行うSlackの機能のことです。
Slackにはいくつかの標準のスラッシュコマンドが用意されており、例えば /leave
などを自分の参加しているチャンネル上で行うと、そのチャンネルから退出することが出来ます。
これだけだと、マウスでポチポチっとやったほうが楽なのですが、AWS LambdaやGoogle Apps Scriptなどを使い、SlackのAPIや他サービスのAPIを組み合わせることで、より複雑な動作を行うことができます。
例えば弊社開発部では kyafu
というスラッシュコマンドで k8s 上に検証環境を立ち上げたり、ステージング環境にデプロイしたりといった操作をSlackだけで行えるようになっていたりします。
上記のコマンドは弊社のエンジニアがコードを書いてGitHub Actionsなどを利用することで実現しているのですが、私を含め情報システム部門に所属する人々はそんなに簡単にコードの実行環境を用意できるわけでもなく、そもそもコードを書くことに馴染みが少ない人も多いのでは無いでしょうか?
というわけで、今回は冒頭に紹介したPAを使って、ノーコードでスラッシュコマンドを実装する方法を紹介したいと思います。
今回作るコマンド
今回は、他人が作ったチャンネルの名前をAdmin権限が無くても変更できるコマンド、通称リネームちゃん(Rename Chan)を作成します。
こちらのTweetは一部誤りがあるのですが、SlackはAdmin権限以上の権限が無いと、自身が作ったチャンネル以外はオープンチャンネル、プライベートチャンネル問わずリネームすることが出来ません。
組織変更などの際に、部署チャンネルのリネームなどは比較的発生しやすいと思いますが、Admin権限を持った人でないとリネームできないので、Admin権限を持った人に作業が集中してしまうという問題がありました。
弊社では苦肉の策で、リーダー以上の役職者の方全員にAdmin権限を付与することで作業を分散していたのですが、「チャンネルをリネームしたい」という目的を達成するにはAdmin権限は権限が大きすぎるという問題がありました。
スラッシュコマンドを用いて、Admin権限を持った管理アカウントがリネームを代行することで、Admin多すぎ問題に終止符を打つことができます。
レシピ概要
Slack Appの設定
このアプリはAdmin権限を持ったユーザーのUser Tokenを使って実行する必要があるので、Admin権限を持ったユーザーで作成してください。
利用するAPIは conversations.rename です。ドキュメントにはRequired scopesに以下4つのBot tokens scopes が必要となっています。
- channels:manage
- groups:write
- im:write
- mpim:write
しかし、実際にはこのAPIはBot tokenで叩くことはできないので、これらのScopeは不要です。
Scope
Slashコマンドの設定は以下のような感じです。コマンドの文字列は好きに決めてください。
PAのフローの概要
レシピ詳細 Step by Step
SlackのスラッシュコマンドはHTTPのリクエストを設定したRequest URLに対して投げます。そのリクエストを受けるためにPAでは 「HTTP 要求の受信時」というトリガーから作成します。
このトリガーは、一度フロー全体を保存しないとURLが発行されません。トリガーを保存するには、アクションが最低一つ必要なので、「JSONの解析」というアクションを追加し、以下のようにコンテンツとスキーマを設定して、一旦フローを保存します。
フローを保存すると、URLが発行されるので、スラッシュコマンドのRequest URLに設定します
JSONの解析
これで、フローはとりあえずリクエストを受けれる状態になるので、アプリをワークスペースにインストールして、コマンドを実行してみてください。
フローの実行ログには「失敗」のログが出ます。これは、JSONの解析の中で設定しているスキーマ {}
が間違っているからなのですが、では実際どういうリクエストが飛んできていたのか、実行ログから確認しましょう。
リクエストの本文に合致するスキーマを設定するために、本文内をコピーし、フローを再度編集します
PAの「JSONの解析」アクションにはサンプルのJSONを入力するとスキーマを自動で生成してくれる素敵な機能があります。
サンプルから生成をクリックして、先程コピーした本文を貼り付けて「完了」します
すると、いい感じのスキーマを作ってくれます
これで、JSONをうまく解析してくれるはずです。再度スラッシュコマンドを実行してみましょう。
なんと、スキーマを正しく設定したはずが、またエラーが出てしまいます。
BadRequest. The property ‘content’ must be of type JSON in the ‘ParseJson’ action inputs, but was of type ‘application/x-www-form-urlencoded’.
「JSON型じゃないといけないのにapplication/x-www-form-urlencoded型が届いてるよ」というエラーが出ています。
application/x-www-form-urlencoded
に /
が入ってること、もしくはプロパティ名に $
が入っていることが原因じゃないかと疑っているのですが、とにかく、PAの「JSONの解析」はスラッシュコマンドのリクエストをそのままでは処理出来ないようなのです。
四苦八苦した結果、 $formdata
のプロパティだけに絞れば処理できることがわかったので、「JSONの解析」に全文を渡すのではなく、少しプロパティを減らしてから渡すことにします。
オブジェクトのプロパティを減らしてくれる removeProperty 関数
Slackのスラッシュコマンドから送られてくるリクエストは必ず以下のようなJSONになっています。
今回のフローでこの中で使いたい要素は $formdata
という配列の中の以下の3つの値だけです
- channel_id(リネームを実行する対象)
- user_id (誰がリネームを実施したのかを通知するのに利用)
- text(変更後のチャンネル名をコマンドの引数から取得)
$content-type
と $content
はプロパティそのものが不要かつ、エラーの元になっているので、「本文」のオブジェクトからこの2つのプロパティを減らします。
PAにはオブジェクトのプロパティを減らしてくれる removeProperty という関数があります。
removeProperty(<object>, '<property>')
という書式で、 <object>
から <property>
を削除してくれます。
よって今回は「本文」というオブジェクトから$content-type
と $content
を削除するので、2回 removeProperty
を行えば良いということになります。(もっと良いやり方があったら教えて下さい)
removeProperty(removeProperty(triggerBody(),'$content-type'),'$content')
このようになります。
triggerBody()
は「動的なコンテンツ」から「本文」を選択することで自動入力することも出来ます。
プロパティを2つ減らしたので、設定するスキーマは以下のようになります
これで、一度保存して再度コマンドを実行してみましょう。
今度は正しく処理されています。入力されたオブジェクトの一番最初のプロパティが $formdata
に変わっていることが分かりますね。
$formdata
は Array型なので、まずは $formdata
から formdata
というArray変数を作成します。
ちなみに変数を作成することはマストではありません。 $formdata
のまま後続の処理をすることもできますが、フローの視認性が悪くなるので私は他の人が見ても直感的に理解しやすいように変数を作成するステップを挟むようにしています。
変数の初期化
「変数の初期化」は新しく変数を作成するアクションです。プログラミングで言うところの変数の宣言ですね。Typescriptだと let arr: string[] = ['text1', 'text2', 'text3']
といった具合です。
今回は配列変数として初期化します。
初期化と同時に、 $formdata
という配列を初期値として設定しておきます。
ここで、一つ前のステップで行った「JSONの解析」の解析結果を次のアクションである「変数の初期化」で利用できていることが分かります。
JSONから特定の要素を取り出し、後続のアクションで利用することができる「JSONの解析」はPAでAPIを叩いたり、Webhookを受け取って処理を行う際に最も重要になってくるアクションの1つです。これが無いと生きていけません。
さて、ここで formdata
という変数に格納した $formdata
の中身をおさらいしましょう。
$formdata
の中には Key, Value のプロパティを持つオブジェクトが13個入っていることが分かりますね。
この中でも channel_id
, user_id
, text
の key に対応する value こそが後続のアクションで使いたい部品です。
formdata
という配列変数から4番目の要素を取り出しただけでは以下のようなオブジェクトが取り出されるだけなので、そのオブジェクトから更に value
だけ取り出す必要があります。
{"key": "channel_id, "value":"[実行したチャンネルのID]"}
- 配列変数からX番目の要素を取り出す
- 取り出した要素(オブジェクト)から
value
だけを取り出す
この2STEPが必要ということになります。
配列変数からX番目の要素を取り出す
配列からX番目の要素を取り出すには variables
関数を使います。
variables(変数)[アドレス]
という書式です。 formdata
内の4番目の要素 channel_id
のアドレスは 3
になる(0から始めて4番目)ので、
variables('formdata')[3]
で {"key": "channel_id, "value":"[実行したチャンネルのID]"}
が取り出されることになります
取り出した要素(オブジェクト)から value
だけを取り出す
さて、オブジェクトから特定のプロパティを取り出すにはどうすればよかったでしょうか?
オブジェクトはJSONでもあるので「JSONの解析」が使えますね。
スキーマは以下のようになります
channel_id という key の value を channel_id という変数に格納する
「JSONの解析」によって フロー上で使えるようになった value
を使って新しい変数を作ります。新しい変数を作るのは「変数の初期化」ですね。今度は文字列変数になります。
同様にして、 userid
と text
も作成してしまいましょう。それぞれアドレスは 5
と 8
です。(6番目と9番目)
Slackの conversations.rename APIを使ってチャンネルをリネーム
PAに最初から用意されているSlackコネクタにはチャンネル名を変更するアクションは用意されていません。よって、APIを叩いてチャンネル名変更を行う必要があります。レシピ概要にも書いたように利用するAPIは conversations.rename です。
PAからAPIを叩くには「HTTP」アクションを利用します。
conversations.renameのドキュメントを見ると https://slack.com/conversations.rename に対して POST のリクエストを送れば良いことが分かります。
https://api.slack.com/methods/conversations.rename/test
こちらのURLからテストを手動で行うことができるので、冒頭で作成しておいた User OAuth Tokenを使って、APIを実行してみます。
テスターでテストを行うと、リクエストのパラメータを含んだURLやヘッダーが表示されるので、それをそのままPAの「HTTP」アクションにコピペします。
ただし、 channel=
の部分と name=
の部分には作成した変数を使います。
ヘッダーの左側には Authorization
、右側には Bearer xoxp-************************
を入力します。
ここまで出来ていれば、スラッシュコマンドを実行することでチャンネル名を変更できることが確認できます。なお、プライベートチャンネルをリネームしたい場合は、プライベートチャンネルにアプリを作成したユーザーを一度招待する必要があります。(このユーザーをシステムアカウントとして作っておくと良いでしょう)
リネーム通知とチャンネルからの退出
誰がリネームを行ったのか、の通知とリネームを実行したシステムアカウントのチャンネルからの退出のアクションを作成します。
通知は標準のSlackコネクタの「メッセージの投稿(V2)」を利用します。メンションを行うために concat
という関数を使い、 concat('<@',variables('user_id'),'>')
とすることでメンションできます。
最後にHTTPで conversations.leave というAPIを叩くと完了です。
スラッシュコマンドを実行すると以下のような結果になります。
今後の改善点
エラーハンドリングが全くできていないので、たとえば text
が空だったり、プライベートチャンネルにAPIを叩くユーザーが招待されていなかったりした際に条件分岐して、コマンドをエラーを返すような処理を追加していきたいと思います。
まとめ
いかがでしたでしょうか?
スラッシュコマンドをPAで実行するには、おそらく一番最初の部分でリクエストのJSONから余計なプロパティを削除しなければいけないところがハマりポイントではないかと思います。
複数の引数を取るようなコマンドを作るさいには text
を更に整形するような処理が必要になりますが、本記事のように user_id
, channel_id
, text
の変数を作るところまで出来ていれば、色んな用途に応用できるのでは無いかと思います。
変数の初期化の部分については、私が知らないだけでもっとスマートに value
を取り出す関数などがあるやもしれませんので、もしそういった方法を知ってる方がいましたら、@okash1n までご連絡いただければ幸いです。
今回作ったアプリのサンプルをGitHubにアップしているので、自分の環境で試してみたい方はZipファイルでダウンロードして、Power Automate にインポートしてみてください。
GitHub - Studist/sample-rename-chan: Slackのチャンネル名をAdmin権限なくてもリネーム出来るSlackアプリをMicrosoft Power…Slackのチャンネル名をAdmin権限なくてもリネーム出来るSlackアプリをMicrosoft Power Automateで作るサンプルです - GitHub - Studist/sample-rename-chan…
github.com
追記(エラーハンドリング)
エラーハンドリングを実装してみました。 conversations.rename の実行結果から ok
プロパティと error
プロパティの値を取得して条件分岐しています。
エラーハンドリング
エラー処理に必要な値
conversations.rename の実行結果のうち、エラー処理に必要な値は
ok
is_channel
error
という3つの値です。
実行結果を「JSONの解析」して、これらの値から変数を初期化しておきましょう。
※「条件」を作る際に、変数を作らずに直接「JSONの解析」の結果を条件式に入れてもうまくいきません
正常系
リネームをしたチャンネルに通知を行って、終了です。
プライベートチャンネルの場合はBotユーザーを自分でチャンネルから退出させます。( conversations.leave を使用)
異常系
異常系は、今のところ以下の4つを想定してます
channel_not_found
: プライベートチャンネルにフロー実行ユーザーを招待していないか、DMなどの場合name_taken
: すでに他のチャンネルで使われている名前を指定しているinvalid_name_specials
: チャンネル名に使えない文字や空白入っている(引数が2つ以上)invalid_name_required
: コマンドに引数が無い
これらの場合、エラー通知はエラーコードとTeachme BizのマニュアルのURLを添えてコマンド実行者に対してDMで送るようにしています。
最後に、想定していないエラーが出た時を想定して、エラーコードと問い合わせへの誘導を行っています。
フローの見通しを良くする
スコープというアクションを使うと、もう少しフローの見通しをよくできるそうなので、複雑な条件分岐になる場合はこういった機能を使うことを検討しても良いでしょう。