QQ NT 自动发消息(单次运行版 v5.2)
针对 QQ NT / 9.x 桌面版(Windows) 的自动发消息技能。v5 完成架构级重构(修复“重复输入”“发送后跳页”);v5.1 补齐输入后 OCR 校验(防“假发送”)与打码范围修正;v5.2 修正了本技能最关键的坑——托盘态恢复窗口的方式。
> ### 🔑 v5.2 核心修复:托盘态必须用 QQ 原生热键唤出,绝不能 Raw ShowWindow
> 实测确认:QQ 收在托盘后,若用 ShowWindow(SW_RESTORE) 把窗口“拉”出来,得到的是假活窗口——IsWindowVisible=True、前台也是它,但渲染进程没被唤醒:画面停在旧帧、悬停/点击零重绘、键盘输入全被吞(表现得就像“卡死”,且任务栏里也不出现该窗口)。
> 正确做法:按 QQ 自带的“呼出主面板”全局热键(默认 Ctrl+Alt+Z),等同于人工点击系统托盘图标——QQ 会走它自己的完整显示流程,渲染与交互都正常。
> 对照实验(同一实例、同一托盘态):热键恢复 → 活性自检 ✅ 通过;ShowWindow 恢复 → 活性自检 ❌ 连续零重绘。
⚠️ 隐私与安全
| 操作 | 风险 | 防护措施 |
|---|---|---|
| 屏幕截图 | 会捕获 QQ 窗口内容 | 脚本内部临时截图仅用于 OCR 定位,用完立即 os.remove 删除,不留存、不展示、不上传到任何外部服务 |
| 回传截图(如需) | 会话内容含隐私 | 二次确认截图对左栏联系人列表 + 中部聊天记录整体高斯模糊,仅保留顶部“接收方”与底部“输入框+发送按钮”供核对 |
| 确认截图落盘 | 打码图仍含接收方与消息摘要 | 确认截图写入 %TEMP%\qq_confirm.png、发送后写入 %TEMP%\qq_after_send.png(均已打码,但顶部接收方与底部内容保持清晰)。核对完成后请手动删除,勿长期留存或转发 |
| 剪贴板覆盖 | 目标联系人与消息会留在剪贴板 | 脚本用剪贴板粘贴搜索词/消息,结束后不恢复原剪贴板内容;介意可在用后手动清空 |
| 剪贴板 | 临时覆盖剪贴板 | 发送完成后不恢复(粘贴内容即最终消息) |
| 键盘/鼠标模拟 | 控制 QQ 发送 | 发送期间请勿操作鼠标键盘 |
| 自动发送 | 消息直接发出 | 默认发送前需确认(--confirm-wait / 终端交互);仅 --yes 才跳过 |
原则:不主动截图 QQ 消息界面;确需截图排障须重新申请授权,用完即删;绝不在含敏感信息的会话中使用。
功能特性
- ✅ 先校验 QQ 是否启动:未启动(无进程)→ 直接返回“QQ 未启动”提示并停止,不做任何后续操作
- ✅ 托盘态用 QQ 原生热键唤出:
wake_qq_by_hotkey()按Ctrl+Alt+Z(可改常量)让 QQ 自己把主面板显示出来;已可见的窗口只置前、绝不 ShowWindow,从根上避免“假活窗口” - ✅ 渲染活性自检 + 四级阶梯恢复:恢复窗口后先【悬停联系人行】比对前后截图重绘(不点击、不改状态),悬停无果再【点击搜索框】二次确认。识别渲染卡死(画面是旧帧、点击/输入被吞,
IsHungAppWindow检测不出)→ 依次尝试 ① QQ 原生热键恢复(先隐藏→Ctrl+Alt+Z 唤出,最对症)② 重绘抖动(尺寸±1px)③ 隐藏/显示、最小化/还原 ④ 只结束渲染子进程(保留登录态);四级都失败才停止并提示手动重启,绝不自动重启 QQ - ✅ 置前降噪:窗口已在前台且确认在最上层时,
_ensure_foreground零操作返回,不再重复ShowWindow/AttachThreadInput/TOPMOST切换(这些对 Electron 窗口属强刺激,一次运行中会被调用多次) - ✅ 单次运行:搜索 → 进入会话 → 输入 → 截图 → 发送,全在同一进程内完成,绝不重复搜索/重复输入
- ✅ 输入后 OCR 校验(防假发送):写入后立刻 OCR 读输入框,读到内容才继续;没读到就换下一个候选点重试一次(覆盖式,不会叠加);仍失败则停止并非发送,绝不出现“脚本说已发、其实没发出去”
- ✅ 发送前会话校验:输入前与回车前各确认一次“仍在目标会话”,避免内容被粘进搜索框、回车后消息没发反而把页面切走
- ✅ 通用窗口置前:用 Windows 标准 API 把 QQ 顶到最前,不依赖“最小化某个软件”
- ✅ 自动搜索并通过结果卡片进入目标会话(QQ 号 / 昵称)
- ✅ 输入单次不重试:只点击一次、只粘贴一次,杜绝“测试6测试6测试6”式叠加与重复输入动作
- ✅ 发送前、后各生成一张二次确认截图(顶部接收方 + 底部输入框内容清晰,左栏列表与中部聊天记录打码),供核对;任何对外展示的截图都已打码
- ✅ 临时截图用完即删,符合隐私红线
系统要求
- Windows 10 / 11
- 已安装并登录 QQ NT / 9.x 桌面版(旧版 QQ 路径不同,未覆盖)
- QQ 需处于已启动状态(未启动时脚本会明确提示,不会自动拉起)
- 目标联系人 / QQ 号可被搜索到
依赖安装
基础依赖(脚本主流程用的 python,含 pyautogui):
pip install pyautogui pygetwindow pyperclip pillow
OCR 依赖(必须装在该 default 虚拟环境,脚本用固定路径调用):
%USERPROFILE%\.workbuddy\binaries\python\envs\default\Scripts\python.exe -m pip install rapidocr_onnxruntime
> 说明:脚本会按当前用户目录自动推导该 default 环境路径(%USERPROFILE%\.workbuddy\...),不再写死用户名;若路径不同,用环境变量 QQ_OCR_PYTHON 指定即可。
使用方法
# 推荐(供 Agent/外部控制):单次运行,输入完成后输出 READY_FOR_CONFIRM:<截图路径>,
# 等待确认信号文件出现才发送(默认 300s 超时自动取消)
python qq_send_message.py 12345678 "你好" --confirm-wait
# 仅预览:进入会话并输入后截图即退出,不发送
python qq_send_message.py 12345678 "你好" --prepare
# 自动化:跳过确认直接发送(需用户知情)
python qq_send_message.py 12345678 "你好" --yes
# 终端交互:截图后按提示输入 y 才发送
python qq_send_message.py 12345678 "你好"
# 托盘态会用 QQ 自带热键 Ctrl+Alt+Z 唤出主面板(无需参数)
# 若你在 QQ 里改过“呼出主面板”热键,改脚本里的 QQ_WAKE_HOTKEY 常量即可
# 卡死时的「只结束渲染子进程」免登录恢复【v5.1 起默认开启】,
# 需要关闭时显式加 --no-revive-renderer
python qq_send_message.py 12345678 "你好" --confirm-wait --no-revive-renderer
外部确认机制(--confirm-wait)
脚本进入会话并输入内容后,会打印 READY_FOR_CONFIRM:<截图路径>,然后轮询等待信号文件:
- 确认发送 → 创建
%TEMP%\qq_send_go.flag - 取消发送 → 创建
%TEMP%\qq_send_cancel.flag - 300 秒内无信号则自动取消(不发送)
这样 Agent 可以:运行脚本 → 取截图给用户核对 → 用户确认后创建 go 文件 → 脚本在同一进程内仅按回车发送,不重新搜索、不重新输入。
工作原理(单次运行 8 步)
- 校验 QQ 状态:
check_qq_status()判断not_running/tray/visible。未启动 → 直接返回并停止。 - 找窗口 + 原生唤出 + 置前:若
tray(无可见窗口)→wake_qq_by_hotkey()按Ctrl+Alt+Z让 QQ 自己唤出主面板;再定位已存在的 QQ 进程、枚举窗口,已可见的只SetForegroundWindow/BringWindowToTop(绝不 ShowWindow),否则才退化为_restore_to_foreground。最后用WindowFromPoint校验确实在最上层,并把窗口拉进屏幕内。 - 渲染活性自检:
probe_renderer_alive()先悬停联系人行比对截图重绘(无损);悬停无果再点击搜索框二次确认。判定卡死 → 阶梯恢复:①revive_by_hotkey()(先隐藏→Ctrl+Alt+Z 唤出,最对症)→ ②_nudge_repaint()尺寸 ±1px → ③revive_without_restart()隐藏/显示、最小化/还原 → ④ (默认)revive_by_killing_renderer()只结束渲染子进程;四级都失败才停止并提示手动重启。 - 开搜索:不依赖失效的
Ctrl+Alt+S,鼠标点击顶部搜索入口(OCR 定位优先,left + width*0.20, top + 45比例兜底,自适应窗口尺寸)。 - 输入账号 + 进入会话:清空搜索框粘贴目标 → OCR 点击结果卡片 → 必要时点「发消息」→
_in_session()只检查右侧聊天区顶部标题栏确认进入正确会话(不被左侧常驻联系人列表误导)。 - 覆盖式输入 + OCR 校验:点击输入框(多候选点,OCR 定位发送按钮优先)→ Ctrl+A 清空 → 粘贴 → 立刻 OCR 校验输入框是否真含该文本;没读到就换候选点重试一次;仍失败则停止(退出码 5),绝不盲发。
- 二次确认截图:顶部接收方 + 底部输入框内容保持清晰,左栏列表与中部聊天记录打码,供远程核对。
- 确认:
--confirm-wait等信号文件 /--yes直接过 / 终端y。 - 发送 + 校验:同一进程内仅按回车(发送前再确认仍在目标会话)→ OCR 校验输入框已清空、且聊天区出现该消息 → 再生成一张已打码的“发送后”截图供核对。
关于"二次确认"
二次确认截图必须让核对者看清两件事:① 发给谁(顶部会话名/接收方)② 发什么(底部输入框内容)。因此脚本把左栏联系人列表与中部聊天记录整体高斯模糊打码,但顶部会话标题栏与底部输入框(含发送按钮)保持清晰。确认通过后再发送。
实测打码强度(边缘能量,越低越模糊):中部聊天区 ≈2.7,顶部标题 ≈14.7,底部输入区 ≈4.6 —— 只有需要核对的两块是清晰的。
卡死根因实测(2026-09-10,本机 QQ NT)—— 结论已修正
最终结论:"托盘态调用就卡死"不是 QQ 的锅,是本技能早期用 ShowWindow 恢复窗口的方式不对。
| 实验 | 操作 | 结果 |
|---|---|---|
| A | 用 QQ 自身动作把窗口收进托盘(SC_CLOSE),再按 Ctrl+Alt+Z 热键唤出 |
✅ 活性自检通过、可交互 |
| B | 同样收进托盘,改用 ShowWindow(SW_RESTORE) 唤出 |
❌ 连续多次零重绘(假活窗口) |
| C | 手动 SW_HIDE 后跑完整流程 |
✅ 通过(SW_HIDE 不等价于 QQ 自己的收托盘) |
| D | 已卡死窗口:SW_HIDE → Ctrl+Alt+Z |
✅ 救活 |
| E | 已卡死窗口:单纯 ShowWindow / 尺寸抖动 / 隐藏显示链 | ❌ 救不回 |
机理:QQ NT 收进托盘时,窗口是被 QQ 的应用层逻辑隐藏的(渲染被挂起/节流)。ShowWindow 只能把 OS 窗口重新“显示”,却唤不醒 Chromium 渲染进程 → 画面停在旧帧、输入事件无响应;且这种状态的窗口任务栏也不会出现按钮。而 QQ 自带热键走的是它自己的完整显示流程,会同时恢复渲染与输入。
用户直觉是对的:人工(点托盘图标)打开从不出问题,脚本调用才出问题 —— 差别就在恢复方式。
QQ 渲染卡死后的恢复(人工/半自动重启)
四级自动恢复都失败时,才需要重启 QQ(正常情况已不会走到这里)。若 QQ 是"记住密码"状态,可按下述步骤半自动完成,无需重新输入密码:
taskkill /F /IM QQ.exe /T(再Stop-Process -Name QQ -Force兜底),等待 3~5s;- 启动 QQ(常见路径
%ProgramFiles%\Tencent\QQNT\QQ.exe,以你的实际安装路径为准),等待约 15s; - 若停在登录页:OCR 定位"已阅读并同意服务协议"文字 → 点其左侧复选框勾选(未勾选时登录按钮是灰的)→ OCR 找文本恰为"登录"的主按钮(注意排除"账密登录/扫码登录")→ 点击;
- 轮询直到出现宽 > 900、高 > 700 的可见 QQ 主窗口,即登录成功。
> 若登录页要求扫码/短信验证,则需人工介入。技能脚本本身永不自动重启 QQ。
关于"搜索位置变化 / 窗口放大"
- 窗口放大/缩小:搜索入口用相对比例定位,尺寸变化不影响命中(只要窗口没被极端缩放)。
- QQ 大版本更新导致搜索框位移:调整
SEARCH_X_RATIO / SEARCH_Y_OFFSET两个常量即可。
文件说明
| 文件 | 功能 |
|---|---|
qq_send_message.py |
QQ 自动发消息(主脚本) |
注意事项
- QQ 必须先启动:未启动时脚本直接返回提示并停止,不会自动拉起。请先手动打开并登录。
- QQ 收在系统托盘(后台)也能用:脚本会用 QQ 自带呼出热键(默认
Ctrl+Alt+Z) 把主面板唤出,等价于点托盘图标。 - 发送期间不要操作鼠标键盘。
- 首次 OCR 加载稍慢(RapidOCR 推理),属正常。
- 若报"未找到联系人",请确认该 QQ 号/昵称可在搜索结果中出现。
故障排除
| 问题 | 解决方案 |
|---|---|
| 提示“QQ 未启动” | 请先手动打开 QQ 并登录后重试(脚本不会自动拉起) |
| 托盘态无法恢复窗口 | 脚本默认按 Ctrl+Alt+Z(QQ“呼出主面板”热键)唤出,等价于点托盘图标;若你改过该热键,改脚本里的 QQ_WAKE_HOTKEY |
| 唤出的窗口“可见但点不动/输不进” | 说明走了 Raw ShowWindow 的退化路径(热键失效时)。点一下系统托盘 QQ 图标即可正常 |
| 搜索未打开(误报) | 小窗口下 OCR 可能漏读占位文字,若后续能搜到联系人则忽略;否则调 SEARCH_X_RATIO / SEARCH_Y_OFFSET |
| 未找到联系人 | 确认目标可被搜索到;昵称尽量用唯一标识(QQ 号最稳) |
| 输入框内容不对/为空 | 脚本已内置输入后 OCR 校验:读不到内容会自动换候选点重试,仍失败就停止不发;也可查看二次确认截图人工核对 |
| 退出码 5(输入校验未通过) | 输入框多次写入后仍读不到内容。多为窗口尺寸/缩放导致候选点偏移,或聊天区被取消选中;脚本会打印具体原因 |
| 提示“QQ 界面卡死,请手动重启 QQ” | 四级恢复(原生热键 → 重绘抖动 → 非破坏性唤醒 → 只结束渲染子进程)都没救活。此时只能人工重启 QQ:右键托盘图标 → 退出 → 重新打开(一般记住密码;若登录页出现“已阅读并同意服务协议”需先勾选再点登录)。脚本不会自动重启 QQ,以免触发重新登录 |
| OCR 失败 | 确认 RapidOCR 装在 default 虚拟环境;检查 OCR_PYTHON 路径 |
许可证
MIT License