ENZH

Full WeChat Automation on macOS: A Field Guide

📊 Slides

I wanted programmatic access to WeChat. Not the web version (deprecated). Not the Windows hooks (wrong OS). Native macOS, full read and write — pull chat history from the database, send messages through the UI. No WeChat Business API, no sandbox, no restrictions.

Turns out this is possible. It's also a minefield.

This post documents exactly what I set up, what tools I used, and the six pitfalls that cost me the most time. I also open-sourced the glue layer: wechat-mac-reader.

Why Bother

WeChat is the primary communication channel for most of my China-side relationships — investors, partners, friends, family. But it's a walled garden with zero API access for regular accounts. No webhooks, no REST API, no export.

I wanted three things:

  1. Search and summarize group chat history (some groups generate hundreds of messages a day)
  2. Pipe messages into my AI workflow — let Claude read and respond to WeChat context
  3. Send messages programmatically — not just read, but write

The result is a hybrid system: database-level access for reads, Accessibility API for writes.

The Architecture

Two independent paths, one integration layer.

Read Path: Database Decryption

WeChat on macOS stores messages in SQLCipher-encrypted SQLite databases. The encryption keys live in the WeChat process's memory. So the read pipeline is:

Step 1: Extract keyswechat-db-decrypt-macos attaches lldb to the running WeChat process, scans memory for SQLCipher encryption keys, and dumps them to a keys.json. On WeChat 4.1.x, this produces 24 separate per-database keys.

Step 2: Decrypt and servechatlog-bot (a fork of sjzar/chatlog) takes those keys, decrypts the databases on-the-fly using fsnotify file watching, and serves an HTTP API on port 5030. Endpoints include /session, /chatlog, /contact, /chatroom. It also supports webhooks for real-time push on new messages.

The result: a local REST API that gives you full access to your WeChat message history. curl localhost:5030/chatlog?wxid=...&limit=50 and you get structured JSON back.

Write Path: Accessibility API

WeChat-MCP uses macOS Accessibility API (AXUIElement) to navigate WeChat's UI tree — find the right conversation, type a message into the input field, press Enter. It registers as a Claude Code MCP server, so Claude can send WeChat messages as a tool call.

It's slower than the read path (it's literally automating the GUI), but it works reliably. You do need to grant Accessibility permissions to your terminal.

Integration Layer

The glue that ties it together:

  • A launchd service that keeps chatlog-bot running as a daemon (auto-start on login, auto-restart on crash)
  • A Claude Code skill (wechat-reader) that wraps the REST API — ask Claude to "check my WeChat messages from group X" and it curls the local endpoint
  • The WeChat-MCP server registered in Claude Code for outbound messages

The full setup is in wechat-mac-reader.

The 6 Pitfalls

These are the things that cost me the most debugging time. If you're setting this up yourself, read these before you start.

1. WeChat 4.1.x Uses Per-Database Keys

Older versions of WeChat used a single master encryption key for all databases. WeChat 4.1.x changed this — each .db file now has its own key. Tools that expect a single --data-key flag (like chatlog-bot's CLI) won't work. You need to generate a derived_key_map in chatlog-bot's config.yaml, mapping each database path to its individual key.

2. The Built-In Key Extraction Doesn't Work on 4.1.x

chatlog-bot has its own key extraction that uses the Mach VM API (vmmap) to find encryption keys in memory. On WeChat 4.1.x, this returns "no memory regions found." The lldb memory-scanning approach from wechat-db-decrypt-macos is the one that works. Don't waste time debugging the built-in extraction — skip straight to the lldb tool.

3. Key Paths Need the db_storage/ Prefix

chatlog-bot expects database paths in the derived_key_map to include the db_storage/ prefix — for example, db_storage/message/message_0.db, not message/message_0.db. The keys.json output from the extraction tool doesn't include this prefix. If the paths don't match, you get silent decryption failures — no error, just empty results.

4. Filter Out __salts__ From keys.json

The wechat-db-decrypt-macos tool outputs a keys.json where most entries are string: string mappings (database path to hex key). But there's one special entry: __salts__, which is a list, not a string. chatlog-bot's derived_key_map expects map[string]string. If you naively dump the entire keys.json into your config, it'll choke on the type mismatch. Filter __salts__ out.

5. Keys Expire on WeChat Restart

The encryption keys are extracted from the running WeChat process's memory. New process = new memory layout = new keys. Every time WeChat restarts — whether you quit it, it crashes, or macOS updates it — you need to re-run the lldb extraction and update your config. There's no permanent key. Plan for this in your automation.

6. sudo Is Required for Memory Scanning

lldb needs task_for_pid to attach to the WeChat process, which requires root privileges. This means Docker is out — you need direct access to the host machine's WeChat process. The extraction step must run with sudo. Not a dealbreaker, but it rules out certain deployment models.

What It Looks Like in Practice

Once everything is wired up, the workflow feels natural:

  • I ask Claude to summarize what happened in a WeChat group while I was asleep. It hits the local API, pulls the last 200 messages, and gives me a structured summary.
  • I tell Claude to reply to someone in WeChat with a specific message. It calls the MCP tool, which navigates the WeChat UI and sends it.
  • New messages in monitored groups trigger webhooks that feed into my notification pipeline.

The read path is fast — it's hitting a local SQLite database. The write path has a couple seconds of latency because it's automating the GUI, but for my use cases that's fine.

Feasibility Context

Before building this, I did a feasibility survey covering the landscape of WeChat automation approaches on different platforms. The macOS approach turned out to be the most viable for my setup — it avoids the fragility of protocol-level hooks and the limitations of the web API.

Should You Do This?

If you're on macOS, use WeChat as a primary communication channel, and want to integrate it with your AI workflow — yes. The setup takes about an hour once you know the pitfalls. The wechat-mac-reader repo has the config templates and launchd plist to get you started.

If you're on Windows or Linux, the read-path tools are different (and arguably more mature on Windows). The write path via Accessibility API is macOS-specific.

The bigger takeaway: desktop apps that don't offer APIs aren't actually locked down. The data is on your machine. The UI is accessible through OS-level accessibility frameworks. With the right tools, you can build a full automation layer on top of apps that were never designed for it.


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0