slack-rs 0.1.70

A Slack CLI tool with OAuth authentication, profile management, and API access
Documentation
---
title: Slack Web API CLI(Rust)仕様書(案)
date: 2026-02-03
status: draft
tags:
  - slack
  - cli
  - rust
  - oauth
  - spec
---

# Slack Web API CLI(Rust)仕様書(案)

## 概要
複数の Slack ワークスペースに対して、**tumf本人権限(OAuth / User Token)**で Slack の Public Web API を操作できる **汎用CLI** を Rust で実装する。

gog のように「プロファイル切替 + サブコマンド + JSON出力 + パイプ前提」の使い心地を目指す。

> 重要な現実制約
> - Slack Web API は「網羅=必要スコープが爆増」しやすい。
> - ワークスペース(特に取引先)では **スコープ承認が通らない**ことがある。
> - したがって本プロジェクトの方針は **“API網羅はCLI機能として”**(= どのメソッドも叩ける)を提供しつつ、
>   **デフォルト運用は最小スコープ(Read-only)**、必要に応じて段階的に拡張する。

---

## 1. ゴール / 非ゴール

### 1.1 ゴール
- **Slack Web API の任意メソッドを呼び出せる**(網羅性)
  - 例: `slack-rs --profile acme api call conversations.history channel=C123 limit=200`
- 主要なよく使う操作は **人間向けの薄いラッパーコマンド**として提供
  - 例: `slack-rs search ...`, `slack-rs conv history ...`, `slack-rs msg post ...`
- 出力は **JSON優先**(textはサマリ用途)
- **複数ワークスペース**を profile として扱う
- 認証は **OAuth(PKCE + localhost callback)**

### 1.2 非ゴール(初期)
- RTM / Socket Mode 等の常時接続によるイベント購読(別プロダクト)
- Enterprise Grid 管理系(監査ログ等)は要検討

---

## 2. アーキテクチャ方針(「網羅」を現実的にする)

### 2.1 2階建て
1) **汎用レイヤ**: `api call <method> [key=value...]`
   - Slack Web API の *どのメソッドでも* 叩ける
   - 未実装領域も即カバーできる
2) **高頻度コマンド**: `search`, `conv`, `msg`, `users`, `files` など
   - 使い勝手と事故防止(引数チェック、既定値、整形)

### 2.2 メソッド定義の扱い(任意)
- “網羅”を補助するため、可能なら **メソッドメタデータ(引数・説明)を取り込み**
  - 取り込み元は開発時に選定(Slack公式のメタ一覧/HTMLスクレイプ/手動スナップショット等)
- 最低限は **メタ無しでも動く**(key=value をそのままPOST)

---

## 3. 認証方式(OAuth)

- `slack-rs auth login --profile <name>`
  - ブラウザを起動して OAuth
  - `oauth.v2.access` で token を取得
  - `team.id` / `team.name` を profile に保存

### 3.1 Token保存
- OS file storage/SecretService を優先(平文禁止)
- debugログは token を必ずマスク

### 3.2 複数ワークスペース
- profile = 1 workspace
- `--profile` 省略時はエラー(混同事故防止)

---

## 4. スコープ戦略(段階的)

「Slack Public API を網羅」= すべてのスコープを最初から要求しない。

### 4.1 スコープセット(例)
- **Read-only セット(初期推奨)**
  - `search:read`
  - `channels:read`, `groups:read`, `im:read`, `mpim:read`
  - `channels:history`, `groups:history`, `im:history`, `mpim:history`
  - `users:read`

- **Write セット(今回: 必須)**
  - `chat:write`(投稿/返信)
  - `reactions:write`(リアクション)
  - (必要なら)`chat:write.public`(参加していないパブリックチャンネルへの投稿が必要な場合のみ)

- **Admin/Enterprise系**
  - 取引先WSでは通らない前提で別扱い

CLI側は
- `slack-rs auth status` で現在のスコープを可視化
- 目的コマンド実行時に「不足スコープ」を表示

---

## 5. CLI コマンド仕様(提案)

### 5.1 グローバル
- `--profile <name>`(必須)
- `--format json|text`(デフォルト: json)
- `--no-color`
- `--debug`(HTTPログはマスク)

### 5.2 auth
- `slack-rs auth login --profile <name>`
- `slack-rs auth status [--profile <name>]`
- `slack-rs auth logout --profile <name>`

### 5.3 api(網羅の核)
- `slack-rs api call <method> [key=value ...] [--json '{...}']`
  - method例: `search.messages`, `conversations.history`, `users.info`
  - `key=value` は form-urlencoded を基本、`--json` で JSON body も可能に
  - Slackの仕様に合わせて GET/POST を切替(原則POSTでも可)

- `slack-rs api methods`(任意:メタ取り込みができたら)
  - メソッド一覧表示、fuzzy検索

### 5.4 よく使うラッパー(最初に実装する候補)
- `slack-rs search "<query>" [--limit N] [--sort timestamp|score] [--order asc|desc]`
- `slack-rs conv list [--types public,private,im,mpim] [--limit N]`
- `slack-rs conv history --channel <C...> [--oldest <ts>] [--latest <ts>] [--limit N]`
- `slack-rs users info --user <U...>`

Write(今回入れる)
- `slack-rs msg post --channel <C...> --text "..." [--thread-ts <ts>]`
- `slack-rs msg update --channel <C...> --ts <ts> --text "..."`
- `slack-rs msg delete --channel <C...> --ts <ts>`
- `slack-rs react add --channel <C...> --ts <ts> --emoji ":white_check_mark:"`
- `slack-rs react remove --channel <C...> --ts <ts> --emoji ":white_check_mark:"`

---

## 6. レート制限 / リトライ
- 429 + Retry-After を尊重
- exponential backoff + jitter
- 高負荷コマンド(history大量取得等)は並列度制御

---

## 7. セキュリティ / 事故防止
- token を出力・ログしない
- profile必須 + 出力に `team.name` を必ず含める
- write系は **事故防止の二重ガード**
  - デフォルトで write 無効
  - 実行には `SLACKRS_ENABLE_WRITE=1` が必要(環境変数ガード)
  - さらに破壊的操作(delete等)は `--yes` なしでは実行しない
  - `--lang` に応じて確認プロンプトも翻訳(i18n)

---

## 8. 実装(Rust)

### 8.1 利用可能な crate 候補(調査結果の追記)

#### Slack API クライアント
- **`slack-morphism`**
  - Slack Web API / Events API / Socket Mode / Block Kit まで含む “現役” のクライアントライブラリ
  - docs / examples がある(公式サイト: https://slack-rust.abdolence.dev
  - OAuth ルートの例もある(`/auth/install`, `/auth/callback` 等)
  - 本プロジェクトでは **Web API(user token)呼び出し部分**で採用候補
  - 注意: 依存が大きくなりがちなので「軽量CLI」を優先するなら **reqwest直叩き**も選択肢

#### HTTP / OAuth / 秘密情報保管
- HTTP: `reqwest`
- OAuth/PKCE: `oauth2`
- Token/Secret保管: `file storage`(macOS file storage / Windows / Linux Secret Service など)

#### CLI/出力
- CLI: `clap`
- JSON: `serde`, `serde_json`
- 端末表示(任意): `colored` / `owo-colors`

#### ローカル callback server
- `axum`(推奨)/ `tiny_http`(軽量)

### 8.2 i18n(国際化)対応

#### 方針
- CLI のメッセージ(エラー、案内、確認文、ヘルプ補助など)を i18n 化する
- 初期対応言語: **英語(en)/ 日本語(ja)**
- 出力データ(JSON)は言語非依存(フィールド名は固定)。i18n は *人間向けテキスト*のみ。

#### 仕様
- 言語決定順(優先度順)
  1) `--lang <tag>`(例: `ja`, `en-US`  2) `SLACKRS_LANG` 環境変数
  3) OS ロケール(`LANG`, `LC_ALL` 等)
  4) default: `en`

#### 実装候補 crate
- **`i18n-embed`**(Fluent / gettext の両方式をサポート)
  - バイナリに翻訳資産を埋め込める
  - `cargo-i18n` と組み合わせ可能
- 併用候補:
  - `fluent-bundle`, `unic-langid`(Fluent採用時)

#### 翻訳資産
- `locales/en-US/...` `locales/ja-JP/...` のように配置
- 最低限、以下をキー化:
  - `auth.login.open_browser`
  - `auth.login.callback_wait`
  - `auth.status.ok`
  - `error.missing_scope`
  - `error.rate_limited`
  - `confirm.write_action`

### 8.3 内部構造
- `slack_api`:HTTP client、認証、レート制限(`slack-morphism`採用時は薄くラップ)
- `token_store`:file storage/SecretService抽象
- `i18n`:LanguageLoader + メッセージ取得
- `commands`:auth/search/conv/api/msg...

---

## 9. 受け入れ条件(DoD)

- 2つ以上のワークスペースで profile を作って切り替えできる
- `api call search.messages` が動く(成功するWSで)
- `search` / `conv history` など最低限のラッパーが動く
- 429 を正しく扱う
- tokenが安全に保存され、マスクされる
- `--lang ja` / `--lang en` でCLIメッセージ言語が切り替わる(少なくとも主要メッセージ)
- write系コマンドが安全装置付きで動く(`SLACKRS_ENABLE_WRITE=1` + 必要に応じて `--yes`
---

## 付録: 典型コマンド例

```bash
# OAuth(WSごと)
slack-rs auth login --profile acme
slack-rs auth login --profile partner

# 網羅:任意メソッド
slack-rs --profile acme api call search.messages query="invoice in:#finance" sort=timestamp count=20
slack-rs --profile acme api call conversations.history channel=C123 limit=50

# ラッパー
slack-rs --profile acme search "invoice in:#finance" --limit 20
slack-rs --profile partner conv history --channel C123 --limit 200
```