This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
EasyChat is a Windows-only PC WeChat automation assistant that uses UI automation to control the WeChat desktop client. It provides scheduled messaging, bulk messaging, and contact extraction through a PyQt5 GUI. The project uses uiautomation to interact with WeChat's UI controls since web WeChat is no longer available.
Multi-version support: As of 2026/04/17, the project supports multiple WeChat versions through a plugin-like architecture. Each WeChat version has its own implementation module in the versions/ directory.
Current supported versions: 4.1.9.21 (default), 4.1.8.107. Earlier versions (3.9, 4.0) are no longer supported.
Unimplemented methods: check_new_msg(), get_dialogs(), save_dialog_pictures(), and get_dialogs_by_time_blocks() raise NotImplementedError — they have not been adapted to WeChat 4.1+.
CRITICAL: Windows Narrator (讲述人) must be enabled for uiautomation to detect WeChat UI controls. This is the #1 cause of runtime failures.
# Setup
pip install -r requirements.txt # requirements.txt is UTF-16 encoded; pip handles it fine
# Run GUI
python wechat_gui.py
# Build standalone .exe (output: dist/wechat_gui.exe)
python pack.py
# Equivalent to: pyinstaller.exe -Fw --noupx --add-data "versions;versions" wechat_gui.py
# Inspect WeChat's live UI control tree (use when WeChat updates break automation)
python automation.py [-t delay] [-d depth] [-r root] [-c cursor]
# Output also written to @AutomationLog.txtTesting core automation: Edit the __main__ block in version-specific modules (e.g., versions/wechat_4_1_9_21.py), set your WeChat path, and uncomment specific test calls. No test framework is used.
The application layers are:
wechat_gui.py ← PyQt5 UI, config persistence, hotkey handling, version loader
│
├── module.py ← ClockThread (scheduler), custom widgets
│
└── versions/ ← Version-specific WeChat automation implementations
├── __init__.py ← Version registry (VERSIONS dict, helper functions)
├── wechat_4_1_9_21.py ← WeChat 4.1.9.21 automation engine
└── wechat_4_1_8_107.py ← WeChat 4.1.8.107 automation engine
│
├── wechat_locale.py ← Localized UI element names
└── clipboard.py ← File clipboard operations (win32)
The project now supports multiple WeChat versions through dynamic module loading:
-
Version registry (
versions/__init__.py): DefinesVERSIONSdict mapping user-friendly labels (e.g., "微信 4.1.9.21") to module paths (e.g., "versions.wechat_4_1_9_21"). New versions are registered here; list newest first soget_default_version()returns the latest. -
Version loader (
wechat_gui.py:_load_wechat_class()): Usesimportlibto dynamically import theWeChatclass from the selected version module. -
Version selection: Users select their WeChat version in the GUI settings. The selection is stored in
wechat_config.jsonundersettings.system_version. -
Version switching: When the user changes versions,
_reinit_wechat()reloads the appropriateWeChatclass and rebinds theprevent_offlinemethod toClockThread.
The WeChat class wraps uiautomation to control WeChat's desktop UI. Key design decisions:
- UI control depths are hardcoded (e.g.,
Depth=15for search box). These break when WeChat updates its UI structure. Useautomation.pyto re-inspect the control tree after any WeChat update. - All text input uses clipboard paste (
pyperclip→ Ctrl+V) instead of direct typing, for reliability. The 0.3s delays after clipboard operations are critical — removing them causes paste failures. - WeChat is opened via Ctrl+Alt+W (not by launching the
.exedirectly). This avoids triggering the new login popup (2026/03/09 fix). Do not change the startup logic without understanding this workaround. - Contact search skips
XTableCellitems to avoid selecting groups instead of contacts (2025/12/02 fix).search_wait(default 0.3s, configurable in settings) controls how long to wait for search results. find_all_contacts()returns a pandas DataFrame. It usesrsplit(" ", maxsplit=2)to parse contact info, which fails if names or notes contain spaces.
WechatGUI(QWidget) owns the main window. Every user action (add/delete contacts, messages, schedules) auto-saves to wechat_config.json immediately — there is no manual save.
Config structure:
{
"settings": {
"wechat_path": "",
"send_interval": 0,
"search_wait": 0.3,
"system_version": "微信 4.1.9.21",
"language": "zh-CN"
},
"contacts": ["1:name1", "2:name2"],
"messages": ["1:text:all::content", "2:file:1,2::path"],
"schedules": ["2026 4 9 16 11 1-1"]
}Interrupt hotkey: Ctrl+Alt+Q sets hotkey_pressed = True via the keyboard library global hook. Sending loops should check this flag to stop mid-process.
ClockThread(QThread) polls every second and fires scheduled tasks within a 60-second execution window (prevents duplicate fires). It also drives the anti-auto-logout feature (triggers every 60 minutes). It emits error_signal when a scheduled task fails.
Custom widgets: MyListWidget (double-click to edit), MySpinBox, MyDoubleSpinBox, MultiInputDialog, FileDialog.
WeChatLocale maps UI element names to localized strings for zh-CN, zh-TW, and en-US WeChat clients. Required when locating controls by name.
setClipboardFiles(paths) uses the Windows win32clipboard API with a DROPFILES structure to stage files for Ctrl+V sending. Known bug: line 22 references undefined variable matedata (should be metadata).
Message strings (stored in config and displayed in list widgets):
- Text:
{rank}:text:{to}:{at_names}:{content} - File:
{rank}:file:{to}:{path} to="all"or comma-separated contact indices ("1,2,3")contentsupports\nfor newlines
Schedule strings: {year} {month} {day} {hour} {min} {start}-{end}
start-end= message index range to send (1-based)
Bulk TXT loading:
- Users: one contact name per line
- Content:
all:messageor1,2,3:messageper line (text only, no files)
-
WeChat UI updates break hardcoded depths: Re-run
automation.pyon the live WeChat window to find new control depths after any WeChat update. Each version module has its own hardcoded depths. -
Do not touch WeChat launch logic: The Ctrl+Alt+W approach (not spawning
Weixin.exe) is a deliberate workaround for the new-login popup introduced in 2026/03/09. -
Clipboard timing: The 0.3s sleeps after
pyperclip.copy()andsetClipboardFiles()are load-bearing. Don't remove them. -
NotImplementedErrormethods: Callingcheck_new_msg(),get_dialogs(),save_dialog_pictures(), orget_dialogs_by_time_blocks()will raise immediately — they are not stubs with silent fallbacks. -
Backup files:
ui_auto_wechat-4.0备份.py,gui_cp.py,module_cp.pyare historical backups, not active code. The originalui_auto_wechat.pywas moved toversions/wechat_4_1_9_21.pyin commit 2ba08a4. -
PyInstaller data files: When building the .exe, the
versions/directory must be included with--add-data "versions;versions"(seepack.py).
- Chinese comments and variable names are mixed with English throughout.
- Error handling is minimal; high-level
try/exceptcatches most failures. - GUI layouts are built programmatically with
QVBoxLayout/QHBoxLayout— no.uifiles or Qt Designer. - No unit tests exist.
To add support for a new WeChat version:
- Create a new module in
versions/(e.g.,wechat_4_2_0.py) by copying an existing version module. - Update hardcoded UI control depths using
automation.pyto inspect the new WeChat UI structure. - Register the new version in
versions/__init__.pyby adding an entry to theVERSIONSdict. - Update
pack.pyif the build process needs changes. - Test thoroughly - UI automation is fragile and breaks easily with WeChat updates.