pull/21/merge
Banghao Chi 2026-05-14 13:50:47 +08:00 committed by GitHub
commit e1d2f67594
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 118 additions and 2 deletions

View File

@ -8,7 +8,7 @@
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey.svg)](#安装)
[![Rust](https://img.shields.io/badge/built%20with-Rust-orange.svg)](https://www.rust-lang.org)
会话 · 聊天记录 · 搜索 · 联系人 · 群成员 · 收藏 · 统计 · 导出
会话 · 聊天记录 · 搜索 · 发送 · 联系人 · 群成员 · 收藏 · 统计 · 导出
</div>
@ -38,6 +38,7 @@ npx skills add jackwener/wx-cli -g
- **毫秒级响应** — 后台 daemon 持久缓存解密数据库mtime 不变则复用
- **AI 友好** — 默认 YAML 输出,更省 token & 易读;`--json` 可切换为 JSON方便 `jq` 处理等)
- **完全本地** — 数据不出本机,实时解密,无需全量预解密
- **macOS 发送** — 通过微信搜索快捷键打开聊天并发送文本消息(需辅助功能权限)
---
@ -150,10 +151,13 @@ wx history "AI群" --since 2026-04-01 --until 2026-04-15
wx search "关键词" # 全库搜索
wx search "关键词" -n 500 # 放宽搜索结果条数
wx search "会议" --in "工作群" --since 2026-01-01
wx send "张三" "你好,今晚 8 点见" # 发送消息macOS
```
`history` / `search` / `export` 都支持 `-n` / `--limit` 指定条数。默认值只是为了避免一次性输出过多消息,不是硬上限。
`send` 是 macOS 屏幕自动化命令:它会激活微信,用 `⌘F` 聚焦搜索框,打开目标聊天后发送文本。使用前需保持微信已登录,并给当前终端或 agent 应用开启"辅助功能"权限;发送过程中会临时使用剪贴板。
会话/消息输出里都带 `chat_type` 字段,取值为 `private` / `group` / `official_account` / `folded`。`official_account` 涵盖公众号、订阅号、服务号及 `mphelper` / `qqsafe` 等系统通知;`folded` 对应微信里的"订阅号折叠"和"折叠群聊"两个聚合入口。
### 朋友圈SNS

View File

@ -1,6 +1,6 @@
---
name: wx-cli
description: "wx-cli — 从本地微信数据库查询聊天记录、联系人、会话、收藏等。用户提到微信聊天记录、联系人、消息历史、群成员、收藏内容时,使用此 skill 安装并调用 wx-cli。"
description: "wx-cli — 从本地微信数据库查询聊天记录、联系人、会话、收藏等,并可在 macOS 通过 WeChat 屏幕自动化发送消息。用户提到微信聊天记录、联系人、消息历史、群成员、收藏内容、发送微信消息或回复微信时,使用此 skill 安装并调用 wx-cli。"
---
# wx-cli
@ -16,6 +16,8 @@ description: "wx-cli — 从本地微信数据库查询聊天记录、联系人
- wx-cli
- 帮我看看微信里
- 搜索微信消息
- 发送微信消息
- 回复微信
## Prerequisites
@ -23,6 +25,7 @@ description: "wx-cli — 从本地微信数据库查询聊天记录、联系人
- 微信桌面版 4.x 已安装并登录
- Node.js >= 14npm 安装方式)或 curlshell 安装方式)
- 首次 `wx init` 需要 `sudo`(内存扫描提取密钥)
- macOS 发送消息需要给当前终端或 agent 应用开启"辅助功能"权限
---
@ -122,10 +125,15 @@ wx history "AI群" --since 2026-04-01 --until 2026-04-15 -n 100
wx search "关键词"
wx search "关键词" -n 500
wx search "会议" --in "工作群" --since 2026-01-01
# 发送消息macOS
wx send "张三" "你好"
```
`history` / `search` / `export` 都支持 `-n` / `--limit` 指定返回条数。默认值只是为了避免一次输出过多,不是硬上限。
`send` 是 macOS 屏幕自动化命令:它会激活微信,用 `⌘F` 聚焦搜索框,打开目标聊天后发送文本。发送前必须由用户明确确认收件人和消息正文;命令会真实发送消息给第三方。发送过程中会临时使用文本剪贴板,并在常见情况下恢复原文本剪贴板内容。
`sessions` / `unread` / `history` / `new-messages` / `stats` 的输出都带 `chat_type` 字段agent 可据此分流:
| 取值 | 含义 | username 特征 |

View File

@ -2,6 +2,7 @@ mod init;
pub mod sessions;
pub mod history;
pub mod search;
pub mod send;
pub mod contacts;
pub mod export;
pub mod daemon_cmd;
@ -92,6 +93,14 @@ enum Commands {
#[arg(long)]
json: bool,
},
/// 发送微信消息macOS 屏幕自动化)
Send {
/// 聊天对象名称
chat: String,
/// 要发送的消息
#[arg(allow_hyphen_values = true)]
message: String,
},
/// 查看联系人
Contacts {
/// 按名字过滤
@ -282,6 +291,7 @@ fn dispatch(cli: Cli) -> Result<()> {
Commands::Search { keyword, chats, limit, since, until, msg_type, json } => {
search::cmd_search(keyword, chats, limit, since, until, msg_type, json)
}
Commands::Send { chat, message } => send::cmd_send(chat, message),
Commands::Contacts { query, limit, json } => contacts::cmd_contacts(query, limit, json),
Commands::Export { chat, since, until, limit, format, output } => {
export::cmd_export(chat, since, until, limit, format, output)

94
src/cli/send.rs 100644
View File

@ -0,0 +1,94 @@
use anyhow::Result;
#[cfg(target_os = "macos")]
use anyhow::{bail, Context};
#[cfg(target_os = "macos")]
use std::process::Command;
#[cfg(target_os = "macos")]
const SEND_SCRIPT: &str = r#"
on run argv
if (count of argv) < 2 then error "chat and message are required"
set chatName to item 1 of argv
set messageText to item 2 of argv
set previousClipboard to missing value
try
set previousClipboard to the clipboard as text
end try
try
tell application id "com.tencent.xinWeChat" to activate
delay 0.3
tell application "System Events"
set wxProc to first application process whose bundle identifier is "com.tencent.xinWeChat"
set frontmost of wxProc to true
delay 0.2
keystroke "f" using command down
delay 0.1
keystroke "a" using command down
delay 0.05
my pasteText(chatName)
delay 1.5
key code 36
delay 0.8
my pasteText(messageText)
delay 0.1
key code 36
end tell
my restoreClipboard(previousClipboard)
on error errorMessage number errorNumber
my restoreClipboard(previousClipboard)
error errorMessage number errorNumber
end try
end run
on pasteText(textValue)
tell application "System Events"
set the clipboard to textValue
delay 0.05
keystroke "v" using command down
end tell
end pasteText
on restoreClipboard(previousClipboard)
if previousClipboard is not missing value then set the clipboard to previousClipboard
end restoreClipboard
"#;
#[cfg(target_os = "macos")]
pub fn cmd_send(chat: String, message: String) -> Result<()> {
if chat.trim().is_empty() {
bail!("聊天对象名称不能为空");
}
if message.is_empty() {
bail!("消息不能为空");
}
let output = Command::new("osascript")
.arg("-e")
.arg(SEND_SCRIPT)
.arg(&chat)
.arg(&message)
.output()
.context("无法运行 osascript")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
let reason = if stderr.is_empty() {
format!("osascript exited with status {}", output.status)
} else {
stderr
};
bail!(
"发送微信消息失败:{}。请确认微信已登录,并已给当前终端/应用开启“辅助功能”权限",
reason
);
}
println!("已发送到 {}", chat);
Ok(())
}
#[cfg(not(target_os = "macos"))]
pub fn cmd_send(_chat: String, _message: String) -> Result<()> {
anyhow::bail!("send 命令目前只支持 macOS");
}