Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ scripts/*.json
scripts/*.txt
scripts/*.xlsx

# Member registry (may contain sensitive WeChat IDs)
group_members.json
*_members.json
member_registry.json

# Temporary files
*.tmp
*.bak
Expand All @@ -58,6 +63,26 @@ desktop.ini
.AppleDouble
.LSOverride

# Debug and test scripts (not part of the package)
debug_*.py
/test_*.py
test_*.ps1
debug_*/
*.png
ocr_runner.cs
ocr_simple.py
compile_cs.ps1
analyze_dll.py
check_dependencies.py
find_winkit.ps1

# Audit logs (may contain sensitive data)
*_audit.jsonl
wx4py_send_audit.jsonl

# UV lock file (optional)
uv.lock

CLAUDE.md
AGENTS.md
.claude-plugin/
Expand Down
66 changes: 65 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,41 @@ with WeChatClient(auto_connect=True) as wx:

---

### 群聊消息发送者识别(OCR)

```python
from wx4py import MemberRegistry, WeChatClient
from wx4py.features.messaging.listener import WeChatGroupListener

# 创建成员注册表(首次会自动注册群成员)
registry = MemberRegistry()
registry.load_from_file("group_members.json")

def on_message(event):
if event.sender_name:
if event.sender_wxid:
print(f"[{event.group}] {event.sender_name} ({event.sender_wxid}): {event.content}")
else:
print(f"[{event.group}] {event.sender_name}: {event.content}")
else:
print(f"[{event.group}] 未知发送者: {event.content}")

with WeChatClient(auto_connect=True) as wx:
listener = WeChatGroupListener(
client=wx,
groups=["工作群", "项目群"],
member_registry=registry,
on_message=on_message,
)
listener.start(block=True)
```

**效果**:群聊消息可以识别发送者昵称和微信ID,支持接入 AI 做更智能的回复。

**原理**:通过 OCR 截图识别消息上方的昵称,结合 MemberRegistry 昵称匹配获取微信ID。

---

### 监听群消息并转发给指定联系人或群

```python
Expand Down Expand Up @@ -325,7 +360,36 @@ AI 会自动完成操作。
<details>
<summary><b>Q: 聊天记录能获取发送者吗?</b></summary>

微信 4.x 的 UI 不暴露发送者信息,这是技术限制,暂无法获取。
微信 4.x 的 UI Automation 不直接暴露发送者信息,但 wx4py 通过 **OCR 截图识别 + MemberRegistry 昵称匹配** 的方式,可以识别群聊消息的发送者昵称和微信ID。

**实现原理**:
1. 使用 PaddleOCR 对消息区域截图识别昵称
2. 通过 MemberRegistry 精确匹配或模糊匹配获取微信ID
3. 自动注册群成员,建立昵称与微信ID的映射

**使用示例**:
```python
from wx4py import MemberRegistry, WeChatClient
from wx4py.features.messaging.listener import WeChatGroupListener

# 创建成员注册表
registry = MemberRegistry()
registry.load_from_file("group_members.json")

with WeChatClient(auto_connect=True) as wx:
listener = WeChatGroupListener(
client=wx,
groups=["工作群"],
member_registry=registry, # 启用发送者识别
on_message=lambda e: print(f"{e.sender_name}: {e.content}"),
)
listener.start(block=True)
```

**注意事项**:
- 需要安装 PaddleOCR:`pip install paddlepaddle paddleocr`
- 首次监听会自动注册群成员(需要一些时间)
- 成员信息保存在 `group_members.json`

</details>

Expand Down
100 changes: 100 additions & 0 deletions examples/messaging/listen_with_sender_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""
群聊消息监听示例:OCR 识别发送者昵称和微信ID

本示例展示了如何使用 OCR + MemberRegistry 来识别消息发送者。

发送者识别流程(自动):
1. 使用 PaddleOCR 截图识别消息发送者昵称
2. 通过 MemberRegistry 精确匹配或模糊匹配获取微信ID
3. 如果 MemberRegistry 中没有注册成员,自动注册群成员

自动注册流程:
- 首次监听某个群时,会自动获取群成员列表
- 逐个点击成员头像获取微信ID(需要成员公开微信ID)
- 保存到 group_members.json 文件

前置要求:
- 微信 4.x 的 UI Automation 不暴露发送者信息
- 解决方案:使用 OCR 识别昵称 + MemberRegistry 关联微信ID
- PaddleOCR 需要安装:pip install paddlepaddle paddleocr
"""

from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from src import MemberRegistry, WeChatClient
from src.features.messaging.listener import WeChatGroupListener


# 配置要监听的群聊列表(可以添加多个群)
GROUPS = [
"群名称1", # 修改为你要监听的群名称
# "群名称2",
# "群名称3",
]


def on_message(event):
"""消息回调函数,演示如何获取发送者信息"""
print(f"\n{'='*60}")
print(f"收到消息!")
print(f" 群聊: {event.group}")
if event.sender_name:
if event.sender_wxid:
print(f" 发送者: {event.sender_name} ({event.sender_wxid})")
else:
print(f" 发送者: {event.sender_name} (微信ID未注册)")
else:
print(f" 发送者: [未知]")
print(f" 消息内容: {event.content[:100]}")
print(f" 是否 @ 我: {event.is_at_me}")
print(f"{'='*60}")


def main():
"""主函数"""
# 创建成员注册表
registry = MemberRegistry()

# 尝试从文件加载已保存的成员信息
members_file = "group_members.json"
if Path(members_file).exists():
registry.load_from_file(members_file)
total_members = sum(len(members) for members in registry._members.values())
if total_members > 0:
print(f"✓ 已从 {members_file} 加载 {total_members} 名群成员")

# 创建客户端并启动监听
with WeChatClient(auto_connect=True) as wx:
print(f"\n开始监听群聊: {', '.join(GROUPS)}")
print("=" * 60)
print("说明:")
print(" - 首次监听会自动注册群成员(需要一些时间)")
print(" - 成员信息保存在 group_members.json")
print(" - 发送者识别使用 OCR + 模糊匹配")
print("=" * 60)
print()

# 创建监听器(会自动注册群成员)
listener = WeChatGroupListener(
client=wx,
groups=GROUPS,
on_message=on_message,
member_registry=registry,
auto_reply=False, # 只监听,不自动回复
# 如果需要 AI 自动回复,可以设置:
# auto_reply=True,
# reply_on_at=True, # 只在被 @ 时回复
)

# 开始监听
listener.start(block=True)


if __name__ == "__main__":
main()
105 changes: 105 additions & 0 deletions examples/messaging/load_group_members.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
"""
群成员管理示例:自动获取群成员信息并关联微信ID

本示例展示了如何:
1. 获取群成员列表
2. 自动获取每个成员的微信ID(通过资料卡)
3. 保存和加载成员信息

使用说明:
运行本脚本后,会自动获取群成员列表,然后逐个点击成员头像获取微信ID。
注意:由于需要打开资料卡获取微信ID,速度较慢(约3-5秒/成员)。
"""

import sys
from pathlib import Path

# 添加项目根目录到路径
sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from src import MemberRegistry, WeChatClient


def main():
"""主函数"""
group_name = "家庭龙虾"
members_file = "group_members.json"

# 创建成员注册表
registry = MemberRegistry()

# 尝试从文件加载已保存的成员信息
if Path(members_file).exists():
registry.load_from_file(members_file)
print(f"已加载 {len(registry._members.get(group_name, {}))} 名已注册的成员")
else:
print("未找到成员信息文件,将创建新文件")

with WeChatClient(auto_connect=True) as wx:
# 获取群成员列表
print(f"\n正在获取群成员: {group_name}")
members = wx.group_manager.get_group_members(group_name)

if not members:
print("未获取到群成员,请检查群名是否正确")
return

print(f"\n获取到 {len(members)} 名群成员:")
for i, member in enumerate(members, 1):
print(f" {i}. {member}")

# 检查需要获取微信ID的成员
members_to_fetch = []
for member in members:
existing_wxid = registry.get_wxid(group_name, member)
if existing_wxid:
print(f"\n[已有] {member} -> {existing_wxid}")
else:
members_to_fetch.append(member)

if members_to_fetch:
print(f"\n需要获取微信ID的成员: {len(members_to_fetch)} 人")
print("-" * 60)
print("提示: 这将逐个打开成员资料卡获取微信ID")
print(" 速度较慢(约3-5秒/成员),请耐心等待...")
print("-" * 60)

# 逐个获取微信ID
for i, member in enumerate(members_to_fetch, 1):
print(f"\n[{i}/{len(members_to_fetch)}] 获取 {member} 的微信ID...")

wxid = wx.group_manager.get_member_wxid(group_name, member)

if wxid:
registry.add_member(group_name, member, wxid)
print(f" ✓ 成功: {member} -> {wxid}")
else:
print(f" ✗ 失败: 无法获取 {member} 的微信ID")

# 短暂等待让UI稳定
import time
time.sleep(1)

# 保存成员信息到文件
print(f"\n保存成员信息到 {members_file}...")
registry.save_to_file(members_file)

# 显示当前注册表内容
registered_count = len(registry._members.get(group_name, {}))
print(f"\n完成!当前已注册 {registered_count} / {len(members)} 名成员")

# 显示所有成员
print(f"\n所有成员信息:")
print("=" * 60)
for member in members:
wxid = registry.get_wxid(group_name, member)
if wxid:
print(f" {member:20s} -> {wxid}")
else:
print(f" {member:20s} -> (未获取微信ID)")
print("=" * 60)


if __name__ == "__main__":
main()
39 changes: 39 additions & 0 deletions examples/ocr/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""
PaddleOCR 使用示例
"""
import os
import sys

# 添加项目根目录到路径
project_root = r'D:\yeafel\pycharmWorkspace\wx4py'
sys.path.insert(0, project_root)

# 确保项目包可以被正确导入
os.chdir(project_root)

# 现在可以直接导入
from src.utils.ocr_utils import recognize_text, recognize_sender

# 测试图片
test_image = r'D:\yeafel\pycharmWorkspace\wx4py\ocr_debug_screenshot.png'

print("=" * 50)
print("PaddleOCR 使用示例")
print("=" * 50)

# 识别所有文字
print("\n【识别所有文字】")
texts = recognize_text(test_image)
for text, confidence in texts:
print(f" {text} (置信度: {confidence:.2f})")

# 识别发送者
print("\n【识别发送者昵称】")
sender = recognize_sender(test_image)
if sender:
print(f" 发送者: {sender}")
else:
print(" 未识别到发送者")

print("\n" + "=" * 50)
Loading