この記事は hacomono advent calendar 2024 の23日目の記事です
こんにちは、hacomonoの開発基盤組織でインターンをしているドラゴンです。
hacomonoのインターンでは、社内のエンジニアや他業種の方の業務効率化のため社内ツールを開発しております。その中で、Python for Slack SDK
を活用し、Slack ワークフローや Custom function
による外部API連携の実装に挑戦する機会がありました。その際にCustom function
のドキュメントが少なく、自分では理解に手間取ってしまいました。
そのため、設定からCustom function
が動くまでの実装手順を整理したいと思い執筆いたしました。
- この記事について
- 1. はじめに
- 課題
- 実装例:社内インシデント管理システムとの連携
- 2. 環境整理
- 3. 基本設定
- 4. Callback IDの設定
- 5. Custom function のパラメータ設定
- 6. アプリケーションの実行
- 感想
この記事について
想定読者
- これから、
Custom function
を使ってみたい方 - Slackワークフローの基本知識がある方
本記事のゴール
記事の内容を通して、読者が以下の2点について具体的なイメージを持ってもらえるようになることを目指します。
Custom function
の基本的な設定手順- ワークフローからのパラメータ受け取りの実装
触れること
Custom function
の基本的な設定手順の説明- ワークフローからパラメータを受け取る方法の解説
- 外部APIとの連携例の紹介
触れないこと
- Slack APIの基本的な解説
- Python/Boltの環境構築
- トークンの取得方法の詳細
1. はじめに
Custom functionとは
Custom function
とは、Slackワークフローにカスタムステップを追加できる機能です。Bolt SDKを使用して独自の処理を実装でき、外部APIとの連携や複雑なデータ処理など、標準のワークフローステップでは実現できない機能を追加することができます。
課題
私がインターンでCustom function
を作成する中で、設定から実装までの具体的な手順をまとめた資料が少なく感じ、以下のような課題に直面することが多いです。
- 設定手順が分かりにくい
- パラメータの受け取り方が不明確
今回の記事では、これらの課題を解決するため、実際のユースケースを例に説明していきます。
実装例:社内インシデント管理システムとの連携
上記の課題をわかりやすく示すために、本記事では社内インシデント管理システムとの連携を例に挙げて解説します。
一般的に、組織内での情報共有はSlack上で行われることが多く、インシデントは専用チャンネルで報告されるケースがあります。しかし、外部のインシデント管理システムへ手動で登録するのは手間がかかります。そこで、Custom function
を活用すれば、Slack上のやりとりをトリガーとして外部APIにデータを自動送信できます。
※これは実装例のため、社内で運用されてるツールでもなく、インターン生が取り組んだタスクでもございません。
完成イメージ
- インシデント発生時、担当者はチャンネル内に固定してあるリンクからワークフローを起動
- フォームに基本情報(担当者・優先度・緊急度)を入力
Custom function
経由で外部システムに登録 → Slack上のチャンネルにも「インシデントが登録されました」とメッセージをポスト
2. 環境整理
開発するのに必要な環境
Custom function
を使用するには、以下の要件を満たす必要があります。
- Slackワークスペースの要件
- 有料プランに加入していること
- 管理者権限を持っていること
- 開発環境の要件
- Python 3.7以上
- Bolt for Python(
slack_bolt
)のインストール
Bolt for Pythonのリポジトリはこちら
- https://github.com/slackapi/bolt-python
事前準備
- アプリケーションの作成
- Slack APIのページでアプリを作成
- 基本的な設定の実施
- 必要なトークンの取得
- Bot User OAuth Token
- App Level Token
- SLACK SIGNING SECRET
Slack APIのページはこちら
3. 基本設定
ワークフローの基本設定
まず、「その他」→ 「自動化」 → 「新しいワークフロー」を選択します。
ワークフローを開始するタイミングを、任意のタイミングで始めたいため「Slack内のリンクから開始」に設定します。
インシデント報告フォームを作成します。以下のような項目を含めます。
- 優先度
- 緊急度合い
- 担当者情報
評価フォーム
組織レベルのアプリ設定
アプリをワークスペース全体で利用できるようにする必要があります。これはCustom function
を使用するための最初のステップです。
- Slackアプリの設定ページにアクセスします。
- 左サイドバーから「Org Level Apps」を選択します。
- 「Organization Level Apps」を有効化しまs。
- 要件にあった必要なスコープを承認します。
リクエストURLの設定
Custom function
を使用するために、以下のどちらかの設定が必要です。
1.Event Subscriptionsの場合
外部にホストしたサーバーのURLを指定
HTTPS
が必須- Slackからのリクエストを受け取れる必要がある
メリット
- セキュアなHTTPS通信ができる
- 本番環境での運用に適している
デメリット
- セットアップに時間とコストがかかる
- 3秒以内にSlackにレスポンスしないといけない制約がある
2.Socket Mode(今回の選択)
外部に公開されたURLが不要で、ローカル開発時でも使用できる
メリット
- 外部サーバー不要で手軽に開始
- リアルタイムな通信が可能
デメリット
- 大規模な運用には向かない
- Slackインフラへの依存度が高い
この記事では、より手軽に試せるSocket Modeを使用して実装を進めていきます。
4. Callback IDの設定
Custom function
をSlackワークフローと紐付けるために、まず一意の識別子(Callback ID)を設定します。このCallback IDはこの後説明するパラメータ取得にも必要になります。
Callback IDの設定方法
- Slackアプリの設定ページにアクセス。
- 左タブの「Workflow Steps 【NEW】」を押下します。
- Add Stepを押下し以下の画像の設定画面が開きます。
- Basic Informationを押下し、Callback IDを取得します。
5. Custom function のパラメータ設定
Custom function
はSlack ワークフローの中で扱う変数を取得・出力するために、InputとOutput Parameterの設定が必要です。
Custom function でSlack ワークフローのフォームから値を受け取るための設定方法について解説します。
パラメーターの設定方法
- Slackアプリの設定ページにアクセスします。
- 左タブの「Workflow Steps 【NEW】」を押下します。
- Add Stepを押下し以下のParameterの設定画面に遷移します。
Custom function
のInput / Output Parametersを設定する際に、ワークフロー設定画面でParameter IDが自動割り当てされます。
Input Parameters(入力パラメータ)の設定
Custom function
は入力と出力を宣言する必要があります。
各パラメータには以下の項目を設定します。
- Input type(入力タイプ)
- フォームの各項目に応じて適切なタイプを選択
- 例:User(ユーザー選択用)、Integer(数値用)など
- 必要に応じて「Make required」にチェック
- Parameter ID(パラメータID)
このParameter IDは
Custom function
でフォームの値を取得するために必要です。これらのParameter IDは、Slackワークフロー機能のWorkflow Steps設定画面でCustom functionのInput / Output Parametersを定義した際に自動生成されます。- 例:
acc4cc
(User選択用) - 例:
7412ec
(Integer入力用)
- 例:
取得した値は後述するOutput Parametersで返すことができます。
Output Parameters(出力パラメータ)の設定
Output Parametersは、Custom function
実行後の処理結果を後続のワークフローステップで利用するために設定します。
Input Parametersで取得したUser型の値を、Output Parametersでレスポンスすることもできます(InputとOutputのParameter IDは同一のものではない)。
また、Input Parametersで取得した情報を外部APIへ送信し、そのレスポンスをOutput Parametersとして返すことで、以降のステップでその値を活用できます。
アプリケーションの要件に応じて、必要なデータを適切な型でOutput Parametersとして定義してください。
パラメータの受け取り方
この関数は外部インシデント管理システムへの外部リクエストを行う想定です。
コード
def register_workflow_steps(app: App): @app.function("a2b29e") # SlackのCustom function設定で定義したcallback_id def handle_incident_report(step, complete, fail): # Input Paramatersの取得 inputs = step.inputs # Paramater IDを使って各値にアクセス slack_user_id = inputs["acc4cc"] # ユーザー情報 priority = inputs["7412ec"] # 優先度(数値) try: response = send_to_incident_system({ "reporter": slack_user_id, "priority": priority, ... }) complete(outputs={ "result": "success", "incident_id": response["id"] }) except Exception as e: fail(error=f"エラーが発生しました: {str(e)}")
注意点
- パラメータのタイプと実際の使用方法を一致させましょう。
- 必須パラメータには必ず「Make required」にチェックを入れましょう。
- Parameter ID は他のパラメータと重複しないようにしましょう。
6. アプリケーションの実行
コマンド実行
python3 app.py
上記コマンドでアプリが起動します。Socket Mode
を使用しているのでngrok
のようなローカルPC上で開発サーバーをインターネットに繋げる必要はないです。
ワークフロー起動しフォームに入力
結果
コード
def register_workflow_steps(app: App): @app.function("a2b29e") # SlackのCustom function設定で定義したcallback_id def handle_incident_report(event, inputs: dict, client, complete, fail, logger: logging.Logger): try: # Input Parametersから値を取得 person_in_charge = inputs.get("acc4cc") # 対応者IDのparam_id priority = inputs.get("7412ec") # 優先度のparam_id emergency = inputs.get("a194e7") # 緊急度のparam_id target_channel_id = inputs.get("5fc3ff") # メッセージを送信するチャンネルID # 入力値の検証 if not all([person_in_charge, priority, emergency, target_channel_id]): raise ValueError("必要な入力値が不足しています") # インシデント管理システムへ送信するデータ作成 incident_data = { "person_in_charge": person_in_charge, "priority": priority, "emergency": emergency } # 外部インシデントAPIのエンドポイント incident_api_url = "https://example.com/api/incidents" # 実際のURLを設定する headers = { "Content-Type": "application/json", "Authorization": "Bearer YOUR_API_KEY_HERE" # 必要に応じてAPIキーやトークンを設定 } try: # インシデント管理システムにPOSTリクエストを送る response = requests.post( incident_api_url, json=incident_data, headers=headers, timeout=5 ) response.raise_for_status() except requests.exceptions.RequestException as e: logger.error(f"インシデントAPI送信エラー: {e}") raise RuntimeError("インシデント登録に失敗しました") # Slackに返すメッセージテキストを組み立てる message_text = ( f"【インシデントフォームの値を自社インシデントAPIに送信】\n" f"対応者: <@{person_in_charge}>\n" f"優先度: {priority}\n" f"緊急度: {emergency}\n" ) # 指定されたチャンネルにメッセージを送信 try: client.chat_postMessage( channel=target_channel_id, text=message_text ) except Exception as e: logger.error(f"メッセージ送信エラー: {e}") raise e # 出力を設定(本例では入力パラメータをそのまま出力として返す) outputs = { "acc4cc": person_in_charge, "7412ec": priority, "a194e7": emergency, } complete(outputs=outputs) except Exception as e: logger.exception(e) fail(error=f"ワークフロー実行中にエラーが発生: {str(e)}"
感想
今回、Custom function
を用いてSlackワークフローで取得した値を、外部のインシデント管理システムに送信する実装をやってみました。
実装を進める中で特に印象的だったのは、Slackプラットフォームの拡張性の高さです。Custom functionを活用することで、かなり柔軟な機能実装が可能だと実感しました。その一方で苦戦したところもあり、パラメータの設定や取得周りは公式ドキュメントだけでは具体的なイメージを掴みづらく、実際に手を動かして試行錯誤する必要がありました。
Slack ワークフローとCustom function
を組み合わせれば、多くの方が日常的に利用するSlackから直接外部システムへデータを簡単に渡せるため、手動での作業が減らすことができます。
また、このSlack ワークフローとCustom function
を組み合わせることで、インシデント管理以外でも幅広い分野で活用が考えられます。
活用例
- 勤怠システムの打刻や報告
- ワークフローで取得した値を
Custom function
で生成AIのAPIにリクエストし、情報を付け加えてもらったり、要約してもらう。
上記に挙げた例以外の多くの活用方法があると思います。今後は社内の課題に合わせて、Custom function
で解決できる方法を模索していきたいです。
株式会社hacomonoでは一緒に働く仲間を募集しています。
エンジニア採用サイトや採用ウィッシュリストもぜひご覧ください!