💡

Deno Slack SDKでSlack×AI×Backlog連携アプリを作った話

に公開

はじめに

Slack開発が大きく変わりました。2025年、Deno Slack SDKとサンドボックス環境の登場により、数時間で本格的なSlackアプリを開発・デプロイできる時代になりました。

本記事では、「Slackスレッドの議論をAIで要約し、Backlogに課題として自動登録するアプリ」を実際に開発した経験をもとに、Deno Slack SDKの開発体験と実践的なノウハウを紹介します。


作ったもの:Synaptask

アプリ概要

Synaptask(シナプタスク) は、Slackでの議論を自動的にBacklog課題に変換するボットです。

主な機能:

  • 🤖 ボットにメンションするだけでスレッド全体を処理
  • 🧠 AIが要約・構造化(Google Gemini使用)
  • 📋 Backlogに課題を自動作成
  • 👤 メンションされたユーザーを自動的に担当者に設定
  • 🎯 チャンネルごとのプロジェクト自動選択
  • ⚙️ Slack上で設定を動的に管理(Datastore機能)

なぜ作ったのか

開発現場でこんな経験がありました:

💬 Slack: 「ログイン画面のボタンが押せない」
💬 Slack: 「再現確認しました。レイアウト崩れが原因かも」
💬 Slack: 「週末までに直しましょう」
💬 Slack: 「じゃあ田中さんお願いします」

...3日後...

😰 「あれ、タスク化してなかった...」

この問題を解決するために、議論をリアルタイムで課題に変換できるボットを開発することにしました。


実際の動作デモ

パターン1: 基本的な使い方

Slackでの操作:

👤 ユーザー: @Synaptask ログイン画面のボタンが押せないバグを修正してください

🤖 Synaptask: 受付しました。スレッドを要約して Backlog に課題を作成します。
              完了次第ここに結果を返信します。

(2-3秒後)

🤖 Synaptask: Backlogに課題を作成しました: 
              https://your-space.backlog.com/view/FRONT-123

Backlogに作成された課題:

タイトル: ログイン画面ボタン押下不可バグ修正

説明:
ログイン画面でボタンが押せない不具合が報告されています。

【背景】
- ユーザーからの報告により発覚
- レイアウト崩れが原因の可能性

【対応】
- ボタンのクリック可能範囲を確認
- レイアウト修正
- 各種ブラウザでの動作確認

【期限】
週末まで

---
Slackスレッド: https://your-workspace.slack.com/archives/...

パターン2: 担当者自動設定

Slackでの操作:

👤 ユーザーA: ログイン機能のリファクタリングが必要です

👤 ユーザーB: UIの部分は @田中さん にお願いできますか?

👤 ユーザーC: APIは私が対応します @山田

👤 ユーザーA: @Synaptask 課題作成お願いします

🤖 Synaptask: Backlogに課題を作成しました...

結果:

  • メンションされた「田中さん」が自動的に担当者として設定される
  • SlackのEmailとBacklogのEmailが一致する場合は自動マッピング
  • 一致しない場合は名前の類似度(80%以上)で自動マッチング

パターン3: プロジェクト自動選択

設定済みの環境:

#frontend チャンネル → FRONT プロジェクト
#backend チャンネル → API プロジェクト
#design チャンネル → UX プロジェクト

Slackでの操作(#frontendチャンネル):

👤 ユーザー: @Synaptask ヘッダーメニューのアニメーション改善

🤖 Synaptask: Backlogに課題を作成しました: 
              https://your-space.backlog.com/view/FRONT-124
              👆 FRONTプロジェクトに自動登録

パターン4: 動的な設定管理(NEW!)

管理UIでの設定:

Slackのショートカットから「Configure Channel Mapping」を起動:

┌─────────────────────────────────────┐
│ チャンネル→プロジェクト設定         │
├─────────────────────────────────────┤
│ Channel: #new-feature               │
│ Project Key: FEATURE                │
│ Description: 新機能開発チャンネル    │
│                                     │
│ [保存]  [キャンセル]                │
└─────────────────────────────────────┘

メリット:

  • デプロイ不要で設定変更可能
  • チャンネル作成時に即座に対応
  • 複数ワークスペースでも共通管理

Deno Slack SDKの開発体験

実際に開発して感じた、Deno Slack SDKの素晴らしさを紹介します。

1. セットアップが爆速(5分で開始)

# Slack CLIのインストール(Denoも自動インストール)
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash

# プロジェクト作成
slack create synaptask -t slack-samples/deno-starter-template

# 認証
slack login

# 起動
cd synaptask
slack run

# これだけで開発開始!

従来のNode.js SDK:

npm install @slack/bolt
npm install dotenv
npm install nodemon
# package.jsonの設定...
# ngrokのセットアップ...
# ...(30分以上)

Deno Slack SDK:

  • ランタイムも依存関係も自動インストール
  • トンネリング不要(Slack infrastructure上で実行)
  • 環境構築ゼロで即開発

2. TypeScriptがネイティブ

// manifest.ts - 型安全なマニフェスト定義
export default Manifest({
  name: "Synaptask",
  workflows: [CreateBacklogFromThreadWorkflow],
  functions: [ProcessThreadFunction, SendThreadMessageFunction],
  botScopes: ["app_mentions:read", "chat:write"],
});
// functions/process_thread.ts - 型推論が効く
export const ProcessThreadFunction = DefineFunction({
  callback_id: "process_thread",
  input_parameters: {
    properties: {
      channel_id: { type: Schema.types.string },
      message_ts: { type: Schema.types.string },
    },
    required: ["channel_id", "message_ts"],
  },
  // ↓ 型推論により inputs.channel_id などが補完される
});

開発体験の違い:

  • コンパイル不要(.tsファイルをそのまま実行)
  • エディタの補完が完璧
  • 型エラーを事前に検知

3. ローカル開発が快適

$ slack run

? Choose a workspace
❯ MyCompany Sandbox (local)

📡 Connected, awaiting events

この状態で:

  • ✅ ファイルを編集 → 自動リロード
  • ✅ Slackでテスト → ターミナルでログ確認
  • ✅ エラー発生 → スタックトレースで即座に特定
  • console.log() → リアルタイムで確認

実際のログ出力:

ProcessThreadFunction invoked with inputs: {channel_id: "C123...", message_ts: "1234567890.123456"}
Fetched thread messages: 5
Calling Gemini for issue generation...
AI Result: {title: "ログイン画面バグ修正", should_create: true}
Selected Backlog project key: FRONT
Creating Backlog issue...
✅ Successfully created: https://your-space.backlog.com/view/FRONT-123

4. Workflowベースの設計が直感的

従来のイベントハンドラー方式:

// ❌ 複雑で追いにくい
app.event('app_mention', async ({ event, client }) => {
  // 1. 受付通知を送信
  await client.chat.postMessage({...});
  
  // 2. スレッド取得
  const thread = await client.conversations.replies({...});
  
  // 3. AI処理
  const result = await callAI(thread);
  
  // 4. Backlog作成
  const issue = await createBacklogIssue(result);
  
  // 5. 結果通知
  await client.chat.postMessage({...});
  
  // エラーハンドリングが散在...
});

Deno Slack SDKのWorkflow方式:

// ✅ ステップが明確で追いやすい
const workflow = DefineWorkflow({...});

// Step 1: 即座に受付通知
workflow.addStep(SendMessageFunction, {
  message: "受付しました..."
});

// Step 2: AI処理 + Backlog作成
const result = workflow.addStep(ProcessThreadFunction, {...});

// Step 3: 結果通知
workflow.addStep(SendMessageFunction, {
  message: result.outputs.message
});

メリット:

  • 処理の流れが一目瞭然
  • ステップごとに独立してテスト可能
  • エラー時の切り分けが容易

5. Datastoreが強力

以前は設定変更のたびにデプロイが必要でしたが、Datastore機能により動的な管理が可能に:

// datastores/channel_project_mapping.ts
export const ChannelProjectDatastore = DefineDatastore({
  name: "channel_project_mapping",
  primary_key: "channel_id",
  attributes: {
    channel_id: { type: Schema.types.string },
    project_key: { type: Schema.types.string },
    description: { type: Schema.types.string },
  },
});
// データの読み書きも簡単
const getResponse = await client.apps.datastore.get({
  datastore: ChannelProjectDatastore.name,
  id: channelId,
});

await client.apps.datastore.put({
  datastore: ChannelProjectDatastore.name,
  item: { channel_id: channelId, project_key: "FRONT" },
});

実現できたこと:

  • Slack上で設定を動的に変更
  • デプロイ不要で即座に反映
  • 複数ワークスペースで共通設定

6. デプロイが簡単

$ slack deploy

# これだけ!
  • ビルド不要
  • サーバー管理不要
  • スケーリング自動
  • SSL/TLS自動

実装のポイント(簡潔版)

アーキテクチャ

┌──────────────┐
│   Slack      │  @bot メンション
└──────┬───────┘
       │ app_mentioned event
       ▼
┌──────────────────────┐
│  Workflow (3 steps)  │
│  ┌────────────────┐  │
│  │ 1. 受付通知    │  │
│  └────────────────┘  │
│  ┌────────────────┐  │
│  │ 2. AI + 作成   │  │
│  └────────────────┘  │
│  ┌────────────────┐  │
│  │ 3. 結果通知    │  │
│  └────────────────┘  │
└──────┬───────────────┘
       │
  ┌────┴────┐
  ▼         ▼
┌────────┐ ┌──────────┐
│ Gemini │ │ Backlog  │
│  API   │ │  API v2  │
└────────┘ └──────────┘

工夫したポイント

1. Slackの15秒タイムアウト対策

// Step 1で即座に応答(0.5秒)
workflow.addStep(SendMessageFunction, {
  message: "受付しました..."
});

// Step 2以降は非同期で実行(5-8秒)
const result = workflow.addStep(ProcessThreadFunction, {...});

2. AI処理の高速化

  • OpenAI → Gemini 2.5 Flash Lite に変更
  • 応答時間: 5秒 → 1-2秒
  • コスト: 50%削減
const body = {
  contents: [{ parts: [{ text: prompt }] }],
  generationConfig: {
    temperature: 0.2,
    responseMimeType: "application/json", // JSON出力を保証
  },
};

3. Backlog API呼び出しの並列化

// ❌ 逐次処理(遅い)
const projectId = await resolveProjectId(...);
const issueTypeId = await resolveIssueTypeId(...);
const priorityId = await resolvePriorityId(...);
// 合計: 1,300ms

// ✅ 並列処理(速い)
const [projectId, issueTypeId, priorityId] = await Promise.all([
  resolveProjectId(...),
  resolveIssueTypeId(...),
  resolvePriorityId(...),
]);
// 合計: 600ms(2倍速)

4. ユーザーマッピングの自動化

// Email完全一致 → 名前類似度(80%以上)の順で自動マッチング
async function resolveSlackUserToBacklogUser(slackUserId: string) {
  const slackUser = await getSlackUserInfo(slackUserId);
  
  // 1. Email完全一致(最優先)
  if (slackUser.email) {
    const backlogUserId = await findBacklogUserIdByEmail(slackUser.email);
    if (backlogUserId) return backlogUserId;
  }
  
  // 2. 名前類似度マッチング(80%以上)
  const backlogUsers = await fetchAllBacklogUsers();
  const matched = findBestUserMatch(slackUser, backlogUsers);
  if (matched && matched.score > 0.8) return matched.id;
  
  return undefined;
}

実例:

  • "田中太郎" (Slack) ↔ "田中太郎" (Backlog) → 完全一致
  • "Tanaka Taro" (Slack) ↔ "田中太郎" (Backlog) → 85%類似度でマッチ

5. プロジェクト選択の優先順位

// 柔軟性の高い多段階フォールバック
async function resolveProjectKey(channelId, threadText) {
  // 1. スレッド内の明示指定 [backlog: PRJ]
  if (explicitOverride) return explicitOverride;
  
  // 2. Datastore設定(動的管理)
  const datastoreConfig = await getChannelMapping(channelId);
  if (datastoreConfig) return datastoreConfig.project_key;
  
  // 3. JSON設定ファイル(レガシー)
  const jsonConfig = getProjectKeyForChannel(channelId);
  if (jsonConfig) return jsonConfig;
  
  // 4. AIによる自動推定(複数プロジェクトから選択)
  const projects = await fetchBacklogProjects();
  const aiSelected = await selectProjectByAI(projects, threadText);
  if (aiSelected) return aiSelected;
  
  // 5. デフォルト
  return DEFAULT_PROJECT_KEY;
}

開発で学んだこと

1. Slack Enterprise Gridの注意点

問題: thread_tsをTrigger inputsに含めるとtoken_revokedエラー

// ❌ これをするとEnterprise Gridでエラー
inputs: {
  thread_ts: { value: TriggerContextData.Event.AppMentioned.thread_ts },
}

解決策: Permalinkからthread_tsを抽出

const permalinkRes = await client.chat.getPermalink({
  channel: channelId,
  message_ts: messageTs,
});
const url = new URL(permalinkRes.permalink);
const threadTs = url.searchParams.get("thread_ts") || messageTs;

2. ボットメッセージの除外

問題: ボット自身のメッセージを含めると無限ループ

// ❌ 全メッセージを取得するとボットの応答も含まれる
const messages = await fetchThreadMessages(channelId, threadTs);

解決策: ボットメッセージをフィルタリング

const botUserId = (await client.auth.test()).user_id;

const filtered = messages.filter(m => 
  !m.bot_id && 
  !m.app_id && 
  m.user !== botUserId &&
  m.subtype !== "bot_message"
);

3. users.info vs users.list のトレードオフ

users.info(高速だが制限あり):

// ✅ 通常のユーザー: 100ms
// ❌ ボットユーザー: エラー
const user = await client.users.info({ user: userId });

users.list(遅いが確実):

// ✅ 全ユーザー取得可能
// ⚠️ ただし全ユーザー取得で2-3秒
const allUsers = await getAllSlackUsers(client);

ベストプラクティス:

// 1. users.infoを試す
try {
  return await getUserByUsersInfo(userId);
} catch {
  // 2. 失敗時はusers.listにフォールバック
  return await getUserByUsersList(userId);
}

// 3. users.listの結果はキャッシュして再利用
const cache = new Map();

4. サンドボックス環境の価値

無料でEnterprise Grid機能を使える:

  • Datastore機能
  • 高度なWorkflow
  • 複数ワークスペース
  • 全API権限

開発フロー:

1. サンドボックスで実装・テスト(無料)
2. 十分に検証
3. 本番環境にデプロイ

節約効果:

  • Enterprise Grid: 通常 数十万円/年
  • サンドボックス: 完全無料
  • 実装期間: 8-10時間

実運用での効果

定量的な効果

指標 導入前 導入後 改善率
課題作成時間 10分 30秒 95%削減
転記ミス 30% 0% 100%改善
担当者設定漏れ 40% 5% 87%改善
週あたり課題作成数 15件 35件 2.3倍

定性的な効果

チームからのフィードバック:

「Slackで議論したらすぐタスク化されるので、議論が積極的になった」

「メンションするだけなので、誰でも気軽に課題を作成できる」

「自動で担当者が設定されるので、"誰がやるの問題"が解消された」

意外な副次効果:

  • Slack議論の質が向上(課題化を意識して具体的に)
  • ドキュメント作成が楽に(Backlog課題に議論履歴が残る)
  • 振り返りがしやすい(Slackスレッドリンクで経緯を追える)

まとめ

Deno Slack SDKの良さ

セットアップが爆速(5分で開発開始)
TypeScriptネイティブ(型安全で補完完璧)
ローカル開発が快適(ホットリロード + リアルタイムログ)
Workflowが直感的(処理の流れが明確)
Datastoreが強力(動的な設定管理)
デプロイが簡単slack deployのみ)

開発コスト

項目 時間
環境構築 15分
基本実装 3時間
AI統合 2時間
Backlog統合 2時間
高度な機能 3時間
合計 約10時間

得られた価値

  • 💰 コスト削減: 週10時間の作業時間削減(年間500時間)
  • 🚀 生産性向上: 課題作成が95%高速化
  • 😊 チーム満足度: 全員が気軽にタスク化できる文化

次のステップ

このアプリをベースに拡張できること:

  • 画像解析(Gemini Vision APIで画面キャプチャ解析)
  • 重複検知(既存課題との類似度チェック)
  • 定期レポート(週次で作成課題をサマリー送信)
  • マルチプロジェクト対応(複数のBacklogスペース)

おわりに

Deno Slack SDKは、**"Slackアプリ開発のハードルを劇的に下げた"**と感じています。

従来のNode.js SDKでは環境構築だけで数時間かかっていたのが、Deno Slack SDKでは5分でコードを書き始められる。TypeScriptネイティブなので型安全性も完璧。Workflowベースの設計で処理の流れも明確。

そして何より、Slack Developer Programのサンドボックス環境が無料でEnterprise Grid機能を提供してくれるので、個人開発者でも本格的なアプリを作成できます。

ぜひあなたも、チームの困りごとを自動化するSlackアプリを作ってみてください!


参考リンク

公式ドキュメント

コミュニティ

関連記事


🎉 あなたのSlackアプリ開発が成功することを願っています!

リバナレテックブログ

Discussion