Skip to content

Repository files navigation

mattermost-archive

CLI tool that archives Mattermost channel messages to local markdown files.

Each channel is written to <output_dir>/<channel>/YYYY/MM/YYYY-MM-DD.md. State is tracked in <output_dir>/state.json so interrupted runs resume where they left off.

Requirements

  • Python 3.11+
  • uv (package manager)

Installation

git clone <repo-url>
cd <cloned-dir>
make build

This installs the tool and its dependencies into a local .venv.

Configuration

Copy the example config and edit it:

cp config.example.toml config.toml

config.toml fields:

Field Required Description
server_url yes Mattermost server URL, no trailing slash (e.g. https://chat.example.com)
access_token no* Personal access token. Can be set via MM_ACCESS_TOKEN env var instead
team yes Team name or 26-character team ID
channels yes List of channel names or IDs to archive
earliest_date yes Oldest date to retrieve on first run, YYYY-MM-DD (UTC)
output_dir yes Directory to write files into. Must already exist and be writable
max_retries no Retry attempts on HTTP 429 rate limiting. Default: 5

*access_token is required either in the config file or via the environment variable.

Getting a personal access token

In Mattermost: Profile > Security > Personal Access Tokens > Create Token.

Using an environment variable for the token

export MM_ACCESS_TOKEN="your-token"

The env var takes precedence over any access_token value in the config file.

Running

uv run mattermost-archive

This reads ./config.toml by default. To specify a different config file:

uv run mattermost-archive /path/to/config.toml

First run

The first run performs a full backfill from earliest_date to today, fetching messages newest-first. Progress is written to disk as each day completes. Expect a lot of API pages for large channels.

Interrupting and resuming

Press Ctrl-C to stop. Progress is saved to state.json after each completed day. Re-run the tool to continue the backfill from where it stopped -- already-archived days are not re-fetched.

Subsequent runs

Once the backfill is complete, re-running fetches only messages newer than the last archived post. The most recent day's file is updated in-place.

Output format

Each day file looks like:

# channel-name - 2026-04-15

**[2026-04-15T09:12:34Z] Alice Smith**: Good morning

**[2026-04-15T09:15:02Z] Bob Jones** *(reply)*: Morning!

**[2026-04-15T10:00:00Z] System**: Alice Smith joined the channel.

Messages from bots and webhooks that use Slack-style attachments are rendered from props.attachments fields when the message body is empty.

Development

make test       # pytest with coverage (80% minimum enforced)
make lint       # ruff check + format check
make typecheck  # pyright strict
make clean      # remove .venv and build artifacts

About

Archive mattermost channels to local filesystem

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages