Slack Channel Archiver

March 14, 2026 · View on GitHub

CI License: MIT Node.js TypeScript Google Apps Script

日本語

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

EnvironmentLink
devslack-channel-archiver-dev
prodslack-channel-archiver-prod

Quick Start

1. Create a Slack Bot

Go to api.slack.com/appsCreate New AppFrom 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:

keyvaluerequired
SLACK_BOT_TOKENBot User OAuth Token (xoxb-...)yes
NOTIFY_CHANNEL_IDSlack channel ID (C01234567 format)yes
WARNING_THRESHOLD_DAYSDays of inactivity before warningno (default: 95)
GRACE_PERIOD_DAYSDays between warning and archiveno (default: 5)
TRIGGER_INTERVALhourly, daily, weeklyno (default: daily)
TRIGGER_HOUR023no (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

CommandDescription
pnpm run checklint + typecheck + test (all checks)
pnpm run buildBundle TypeScript and output to dist/
pnpm run testJest with coverage
pnpm run test -- --watchJest watch mode
pnpm run deploycheck → build → deploy to dev
pnpm run deploy:prodcheck → 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

FeatureOriginalThis Project
Private channelsNot supportedSupported via conversations.list with types=public_channel,private_channel
Auto-joinManual /inviteBot auto-joins public channels via conversations.join
ReactivationNot mentionedChannels with new activity are removed from warning list
NotificationBasicPrivate channels marked with :lock: icon
ConfigurationHardcodedSpreadsheet settings sheet (editable without code changes)

Notes

  • Functions in src/index.ts must not have the export keyword — the GAS runtime does not support ES module syntax
  • src/index.ts, src/slack-client.ts, src/sheet-store.ts are excluded from test coverage (GAS globals cannot run in Node.js)
  • Coverage threshold: 80% for all metrics (configurable in jest.config.json)
  • SPREADSHEET_ID is the only value stored in Script Properties; all other settings live in the Spreadsheet settings sheet

License

MIT