Slack Channel Archiver
March 14, 2026 · View on GitHub
Auto-archive inactive Slack channels — including private channels. Configurable thresholds (default: 95-day warning + 5-day grace period).
Built from apps-script-fleet template. Inspired by 100日後にアーカイブされるSlackチャネル, extended with private channel support.
How It Works
Trigger → Fetch all channels (public + private)
→ Auto-join public channels (Bot)
→ Save snapshot to Spreadsheet
→ Classify channels:
• Inactive ≥ WARNING_THRESHOLD_DAYS → Warning list + Slack notification
• Warning ≥ GRACE_PERIOD_DAYS → Archive + Slack notification
• Became active → Remove from warning list
Apps Script Projects
| Environment | Link |
|---|---|
| dev | slack-channel-archiver-dev |
| prod | slack-channel-archiver-prod |
Quick Start
1. Create a Slack Bot
Go to api.slack.com/apps → Create New App → From an app manifest → paste slack-app-manifest.yml → Install to Workspace.
2. Set Script Property
In the Apps Script editor: Project Settings (gear icon) → Script Properties → add SPREADSHEET_ID.
3. Initialize Spreadsheet
Run initSpreadsheet in the Apps Script editor (▶). Creates the required sheets and a settings sheet with default values. First run requires OAuth authorization.
4. Fill in Settings
Open the Spreadsheet → settings sheet → fill in:
| key | value | required |
|---|---|---|
SLACK_BOT_TOKEN | Bot User OAuth Token (xoxb-...) | yes |
NOTIFY_CHANNEL_ID | Slack channel ID (C01234567 format) | yes |
WARNING_THRESHOLD_DAYS | Days of inactivity before warning | no (default: 95) |
GRACE_PERIOD_DAYS | Days between warning and archive | no (default: 5) |
TRIGGER_INTERVAL | hourly, daily, weekly | no (default: daily) |
TRIGGER_HOUR | 0–23 | no (default: 9) |
5. Set Up Trigger
Run setupTrigger in the Apps Script editor (▶). Reads the schedule from the settings sheet. First run requires script.scriptapp scope authorization.
Project Structure
src/
├── index.ts # GAS entry points (archiveInactiveChannels, setupTrigger, initSpreadsheet)
├── config.ts # Types, interfaces & default constants (WARNING=95d, GRACE=5d)
├── channel-service.ts # Channel classification logic (pure functions)
├── notifier.ts # Slack notification message builder (pure functions)
├── slack-client.ts # Slack API wrapper (stateless, token passed as argument)
└── sheet-store.ts # Spreadsheet read/write + settings loader
test/
├── channel-service.test.ts
└── notifier.test.ts
Development
| Command | Description |
|---|---|
pnpm run check | lint + typecheck + test (all checks) |
pnpm run build | Bundle TypeScript and output to dist/ |
pnpm run test | Jest with coverage |
pnpm run test -- --watch | Jest watch mode |
pnpm run deploy | check → build → deploy to dev |
pnpm run deploy:prod | check → build → deploy to production |
CI/CD
CI runs on every push and PR. CD deploys on merge to dev or main — configured via GitHub Actions secrets/variables per environment. See apps-script-fleet docs for details.
Differences from the Original Blog Post
| Feature | Original | This Project |
|---|---|---|
| Private channels | Not supported | Supported via conversations.list with types=public_channel,private_channel |
| Auto-join | Manual /invite | Bot auto-joins public channels via conversations.join |
| Reactivation | Not mentioned | Channels with new activity are removed from warning list |
| Notification | Basic | Private channels marked with :lock: icon |
| Configuration | Hardcoded | Spreadsheet settings sheet (editable without code changes) |
Notes
- Functions in
src/index.tsmust not have theexportkeyword — the GAS runtime does not support ES module syntax src/index.ts,src/slack-client.ts,src/sheet-store.tsare excluded from test coverage (GAS globals cannot run in Node.js)- Coverage threshold: 80% for all metrics (configurable in
jest.config.json) SPREADSHEET_IDis the only value stored in Script Properties; all other settings live in the Spreadsheetsettingssheet