外部サーバー不要!Slack次世代プラットフォームでランダムメンションBotを作った話
はじめに
「このタスク誰がやる?」——チームのSlackチャンネルで、ちょっとしたタスクの担当者をランダムに決めたい場面。既存のslackの機能だとタイミングによって偏りが出てしまっていたり、自分にメンションが飛んできたりするケースがあったので、ラウンドロビン形式でもメンションできるBotが欲しくなりました。
今回、Slackの次世代プラットフォーム(Slack Hosted)を使って、外部サーバーもAWSアカウントも不要なランダムメンションBotを作りました。この記事では、Slack Hostedの特徴と、実際の開発体験を紹介します。
実際のBot

Slack Hostedとは
Slack Hostedは、Slackが提供するアプリケーション実行基盤です。従来のSlack Botとの大きな違いは以下の通りです。
| 従来のSlack Bot | Slack Hosted | |
|---|---|---|
| 実行環境 | 自前のサーバー / AWS Lambda等 | Slackが提供(Deno Runtime) |
| データベース | 自前で用意(DynamoDB等) | Slack Datastore |
| 言語 | 任意 | TypeScript(Deno) |
| インフラコスト | あり | 有料プランに含まれる |
| デプロイ | 自前で構築 | slack deploy 一発 |
つまり、Slackの有料プランさえあれば、追加のインフラなしでBotを動かせるのが最大の魅力です。
作ったもの:Random Mention Bot
Botにメンションすると、登録メンバーからランダムに1人を選んでメンションするBotです。
主な機能
- ランダム選出 —
@Botでメンバーから1人を選出 - ラウンドロビン選出 — 順番に選出するモードも搭載
- メンバー管理 —
@Bot add @ユーザー/@Bot remove @ユーザーで追加・削除 - グループ機能 — 用途別にメンバーグループを分けられる
- ユーザーグループ同期 — Slackのユーザーグループからメンバーを一括インポート
- 自分除外 — メンションした本人は候補から自動的に除外
使い方
@Bot add @田中 → メンバー追加
@Bot add @佐藤 → メンバー追加
@Bot → ランダムに1人選出(自分は除外)
@Bot mode round_robin → ラウンドロビンに切替(グループ単位)
@Bot list → メンバー一覧を表示
@Bot help → コマンド一覧を表示
アーキテクチャ
@Bot メンションTrigger → Workflow → Function の3層構造
Slack Hostedのアプリは、Trigger・Workflow・Function の3つの要素で構成されます。
Trigger(トリガー) はイベントの起点です。今回はBotへのメンションをイベントトリガーで検知しています。
// triggers/mention_trigger.ts
const mentionTrigger: Trigger<typeof RandomPickWorkflow.definition> = {
type: TriggerTypes.Event,
name: "Random pick on mention",
workflow: `#/workflows/${RandomPickWorkflow.definition.callback_id}`,
event: {
event_type: TriggerEventTypes.AppMentioned,
all_resources: true,
},
inputs: {
channel_id: { value: TriggerContextData.Event.AppMentioned.channel_id },
user_id: { value: TriggerContextData.Event.AppMentioned.user_id },
message_ts: { value: TriggerContextData.Event.AppMentioned.message_ts },
text: { value: TriggerContextData.Event.AppMentioned.text },
},
};
Workflow(ワークフロー) はTriggerから受け取った入力をFunctionに渡すパイプラインです。
// workflows/random_pick_workflow.ts
export const RandomPickWorkflow = DefineWorkflow({
callback_id: "random_pick_workflow",
title: "Random Pick",
input_parameters: {
properties: {
channel_id: { type: Schema.slack.types.channel_id },
user_id: { type: Schema.slack.types.user_id },
message_ts: { type: Schema.types.string },
text: { type: Schema.types.string },
},
required: ["channel_id", "user_id", "message_ts"],
},
});
RandomPickWorkflow.addStep(RandomPickFunction, {
channel_id: RandomPickWorkflow.inputs.channel_id,
user_id: RandomPickWorkflow.inputs.user_id,
message_ts: RandomPickWorkflow.inputs.message_ts,
text: RandomPickWorkflow.inputs.text,
});
Function(ファンクション) が実際のビジネスロジックです。メンションのテキストをパースして、コマンドに応じた処理を実行します。
Slack Datastore:サーバーレスなデータ永続化
外部DBを用意せずに、Slack Datastoreでデータを永続化できます。定義はシンプルです。
// datastores/members.ts
export const MembersDatastore = DefineDatastore({
name: "members",
primary_key: "id",
attributes: {
id: { type: Schema.types.string }, // "group_name:user_id"
group_name: { type: Schema.types.string },
user_id: { type: Schema.types.string },
},
});
操作もSDK経由で直感的に行えます。
// データの書き込み
await client.apps.datastore.put({
datastore: MembersDatastore.name,
item: { id: `${group}:${userId}`, group_name: group, user_id: userId },
});
// データの検索
await client.apps.datastore.query({
datastore: MembersDatastore.name,
expression: "#g = :g",
expression_attributes: { "#g": "group_name" },
expression_values: { ":g": group },
});
DynamoDBライクなクエリ構文で、フィルタリングも可能です。
開発体験
ローカル開発が快適
slack run コマンドで、ローカルのコードをそのままSlackワークスペースで動かせます。コードを変更するとホットリロードされるため、開発サイクルが非常に速いです。
slack run
デプロイは一行
本番デプロイも slack deploy だけ。CI/CDパイプラインの構築も不要です。
slack deploy
manifest.tsで全体を宣言的に管理
アプリの構成(Function、Workflow、Datastore、Bot権限)はすべて manifest.ts に宣言的に記述します。
export default Manifest({
name: "Random Mention Bot",
description: "Botにメンションするとランダムに1人を選出してメンションします",
functions: [RandomPickFunction, AddMemberFunction, ...],
workflows: [RandomPickWorkflow, AddMemberWorkflow, ...],
datastores: [MembersDatastore, ChannelConfigDatastore, RoundRobinStateDatastore],
botScopes: [
"commands", "app_mentions:read", "chat:write",
"datastore:read", "datastore:write", "usergroups:read",
],
});
必要な権限スコープもここで管理するため、設定の見通しが良いです。
ディレクトリ構成
├── manifest.ts # App設定(宣言的)
├── datastores/
│ ├── members.ts # メンバーデータストア
│ ├── channel_config.ts # グループごとのモード設定
│ └── round_robin_state.ts # ラウンドロビン状態管理
├── functions/
│ ├── random_pick.ts # メインロジック(コマンドパース+選出)
│ ├── add_member.ts # メンバー追加
│ ├── remove_member.ts # メンバー削除
│ ├── list_members.ts # メンバー一覧
│ ├── sync_usergroup.ts # ユーザーグループ同期
│ └── set_mode.ts # 選出モード切替
├── workflows/ # Trigger → Function のパイプライン
├── triggers/ # イベント検知(メンション等)
└── slack.json # Slack CLI設定
Slack Hostedの良いところ・注意点
良いところ
- インフラ管理ゼロ — サーバー、DB、デプロイパイプラインすべて不要
- コスト追加なし — Slack有料プランに含まれている
- 型安全 — TypeScript + Deno SDKで型の恩恵を受けられる
- ローカル開発が快適 —
slack runでホットリロード付き開発 - 宣言的な構成管理 — manifest.tsで全体像が把握しやすい
注意点
- Slack有料プランが必須(Pro / Business+ / Enterprise Grid)
- Denoランタイム限定 — Node.jsのnpmパッケージはそのまま使えない場合がある
- Datastoreの制約 — RDBのような複雑なクエリやJOINはできない
- 実行時間制限 — 長時間実行するバッチ処理には向かない
まとめ
Slack Hostedは、「ちょっとしたBot」を作るのに最適なプラットフォームです。
- 外部インフラの構築・運用が不要
- TypeScriptで型安全に開発できる
- Datastoreでデータ永続化も完結
slack deployでデプロイ完了
今回のRandom Mention Botのように、チーム内のちょっとした課題を解決するBotを、最小限の手間で作って運用できるのがSlack Hostedの魅力です。
「サーバーを立てるほどでもないけど、Botがあると便利なんだよな」——そんな場面にぜひ試してみてください。