消息列表

聊天区里一串气泡,和左侧「会话列表」不是一回事:

会话列表消息列表(本章)
在哪窗口左侧联系人/会话右侧聊天内容区
常见控件ListControl + 会话项另一个 ListControl + 消息 ListItem
文档列表与会话本页

两条学习路线(按难度)

  1. 只操作当前屏幕上看得见的消息(推荐先学)
    bind_msg_listlist_viewport_messages / find_in_list / click_msg
    不需要 IM SDK,只要知道消息列表的 list_aid / list_name
  2. 持续监听新消息、解析 Message 对象(进阶)
    im_client(client=...)GetNextNewMessage
    需要你自己提供客户端类或适配器。

winautox 不内置具体 IM 业务 SDK;client= 需自行传入类、模块路径,或适配器名(如 'wechat',视安装包而定)。

方法一览(WinAuto)

方法说明
im_client / create_im_client / resolve_im_client_class注入 / 解析 IM 客户端
bind_msg_list / chat_message_list绑定消息 List 容器
list_viewport_messages当前视口消息项
find_in_list / find_msg_in_list视口内找 ListItem
click_msg / click_message / double_click_msg / right_click_msg点消息项
scroll_msg_into_view / scroll_message_into_view消息滚入视口
get_new_messages / get_all_messages / get_latest_messages读消息(经 IM)
get_chat_info / chat_with当前会话 / 切换会话
get_chat_message_anchor / list_friend_msgs_below_anchor / has_friend_messages_in_chat锚点辅助
make_msg_anchor / resolve_msg_by_anchor与 IM MakeMessageAnchor 等价

零基础:先绑定消息列表,再列视口

准备工作

  1. 打开目标聊天窗口,确保右侧能看见几条消息。
  2. 快速开始 的方法 dump 控件树,搜索消息列表容器的 AutomationId / Name(下面用 chat_message_list 举例,请改成你的)。
# -*- coding: utf-8 -*-
from winautox import WinAuto
import time

WIN_CLASS = 'YourMainWndClass'     # ← 改
WIN_TITLE = '目标应用'              # ← 改
LIST_AID = 'chat_message_list'     # ← 改成 dump 到的消息列表 aid

hwnd = WinAuto.find_hwnd(WIN_CLASS, WIN_TITLE)
auto = WinAuto(hwnd, mode='uia')
auto.activate()
time.sleep(0.3)

# 告诉 WinAuto:消息列表在哪
auto.bind_msg_list(list_aid=LIST_AID, search_depth=32)

# 列出「当前屏幕看得见」的消息项(滚出屏幕的不会在这里)
items = auto.list_viewport_messages()
print('视口消息条数:', len(items))
for i, m in enumerate(items):
    print(i, m)

# 按摘要文字查找并点击(文字需与 ListItem Name 一致或可匹配)
ctrl = auto.find_in_list(name='某条摘要', list_aid=LIST_AID, timeout=5)
if ctrl:
    auto.click_msg(name='某条摘要')
else:
    print('视口里没有这条。可先滚动聊天区,或检查 name 是否与控件树一致。')

# 若项在列表里但屏幕外:
# auto.scroll_msg_into_view(name='某条摘要')

用别名少写重复参数:

auto.register('msg_list', list_type='ListControl', list_aid=LIST_AID, depth=32)
auto.bind_msg_list(alias='msg_list')

bind_msg_list

参数说明
list_aid / list_name / list_cls消息列表容器定位(至少填一项)
search_depth搜索深度,默认 32;找不到就加大
aliasregister 的列表别名
client可选 IM 适配器(要锚点/好友检测时再加)
refreshTrue 强制重建

find_in_list / find_msg_in_list

只在 当前视口 里找。找不到不一定是没有这条消息,可能是被滚出去了——先 scroll / scroll_msg_into_view 再找。

index1 开始表示「第几个匹配项」;timeout 为轮询等待秒数。

读消息快捷封装(需 IM client)

# 需能 resolve 到 IM client(传入 client= 或环境已配置)
msgs = auto.get_latest_messages(n=20)
all_msgs = auto.get_all_messages(fetch_sender=False)
new_msgs = auto.get_new_messages()
info = auto.get_chat_info()
auto.chat_with('示例会话')

还不会配 client 时,先用上面的视口 list_viewport_messages / click_msg 即可完成「看见 → 点」。


API 参考(IM 客户端)

✨im_client

from winautox import WinAuto

# 流程绑定头 + im_client(须显式传入 client)
# hwnd = WinAuto.find_main_hwnd(WIN_CLASS, WIN_TITLES, ...)
# auto = WinAuto(hwnd, mode='uia')
auto.activate()
time.sleep(0.3)
im = auto.im_client(client=YourIMClient, debug=False, auto_listen=False)

参数(im_client

参数名类型默认值描述
clientclass / str(必填)IM 客户端类,或 'pkg.Module.Client'
debugboolFalseIM 客户端调试日志
auto_listenboolFalse是否启动后台子窗口监听
refreshboolFalseTrue 强制重建客户端

说明:部分 IM 客户端在 auto 绑定的不是主窗口时,可能自动查找主窗口(取决于传入的 client 实现)。

返回值:IM 客户端实例。

消息项字段(Message

字段说明
msg.attrfriend=对方发送,self=自己发送,system/time/tickle=系统或时间行
msg.typetext / image / video / file / voice / note / merge / link / quote / miniapp
msg.content气泡摘要文字
msg.sender发送者(群聊需 fetch_sender=True

✨GetNextNewMessage(推荐:轮询新消息列表)

from winautox import WinAuto
from winautox.param import WxResponse

auto = WinAuto.from_main_hwnd(WIN_CLASS, WIN_TITLES[0], mode='uia')
auto.activate()
time.sleep(0.3)
im = auto.im_client(client=YourIMClient, debug=False, auto_listen=False)

def on_message(msg):
    print(f"[{msg.attr}] type={msg.type} sender={msg.sender} content={msg.content}")
    if msg.attr == 'friend':
        pass  # 仅处理对方发来的
    elif msg.attr == 'self':
        pass  # 自己发的

while True:
    batch = im.GetNextNewMessage(
        filter_mute=False,
        fetch_sender=False,      # 群聊 True 补全 sender
        callback=on_message,     # 每条解析完立刻回调
        use_profile_sender=False,
    )
    # batch = {'chat_name': str, 'chat_type': 'friend'|'group', 'msg': [Message, ...]}
    if batch.get('msg'):
        print(batch.get('chat_name'), batch.get('chat_type'), len(batch.get('msg') or []))
    time.sleep(1)

首帧基线:循环内第一次调用通常返回空 batch(不交付历史消息),之后才会在侧栏未读或当前聊天新消息时交付。单次调用无法持续监听。

# 单次探测(非持续监听)
batch = im.GetNextNewMessage(filter_mute=False, callback=on_message)
msgs = batch.get('msg') or []

参数

参数名类型默认值描述
filter_muteboolFalse是否跳过免打扰会话
fetch_senderboolFalse群聊是否补全 msg.sender
callbackCallableNone每条消息解析后立即调用 callback(msg)
use_profile_senderboolFalseTrue 优先资料卡识别发送人

返回值

  • 类型:dict
  • 字段:chat_namechat_typemsgList[Message]

✨GetNextUnreadBarMessages

处理当前聊天内「N条新消息」跳转条(不会GetNextNewMessage 自动处理)。

def on_message(msg):
    print(msg.attr, msg.type, msg.content)

batch = im.GetNextUnreadBarMessages(fetch_sender=False, callback=on_message)
print('跳转条:', batch.get('chat_name'), len(batch.get('msg') or []))

返回值:同 GetNextNewMessage;无跳转条时 {}

✨GetAllMessage / ✨GetNewMessage

# 需已打开目标会话(如 im.ChatWith('示例会话'))
msgs = im.GetAllMessage(fetch_sender=False)
for msg in msgs:
    print(msg.attr, msg.type, msg.sender, msg.content)

new_msgs = im.GetNewMessage()  # 当前会话增量;切换 chat 后首轮为空
方法说明返回
GetAllMessage(fetch_sender=…)当前聊天全部消息List[Message]
GetNewMessage()当前聊天增量新消息(首帧建立基线,不输出历史)List[Message]
ChatInfo()当前会话名与类型dict
ChatWith(name)切换到指定会话None
# 切换会话并查看当前聊天信息
im.ChatWith('示例会话')
time.sleep(0.5)
print(im.ChatInfo())   # {'chat_name': '...', 'chat_type': 'friend', ...}

✨GetNextListenChatMessage / ✨AddListenChat

独立聊天子窗口(双击会话弹出)的消息列表:

im.AddListenChat('示例会话', callback=on_message)
im.StartListening()

# 或同步轮询(可与 GetNextNewMessage 同脚本交错)
batch = im.GetNextListenChatMessage(nickname='示例会话', callback=on_message)

✨MakeMessageAnchor / ✨ResolveMessageByAnchor(消息锚点)

处理 GetNextNewMessage callback 里的 msg 时,可先生成锚点,后续即使消息因滚动、Recycler 重绘导致 runtimeid 变化或位置移动,仍可在当前视口内按锚点找回同一条并继续操作(下载、点击、转文字等)。

原理(与 GetNextNewMessage 内部一致)

字段说明
key交付键 = 内容指纹(chat + type + attr + sender + 正文 + 同屏同文案序号)+ runtimeid
stable_key仅内容指纹,不含 runtimeid(UI 重绘后 rid 会变)
hint_rid创建时的 msg.id(ListItem runtimeid),多条同指纹时用于消歧
summary人类可读摘要,调试打印用

runtimeid 在重绘/切会话后会变,不要只靠 GetMessageById(rid) 长期追踪;应用 MakeMessageAnchor + ResolveMessageByAnchor

from winautox import WinAuto

auto = WinAuto.from_main_hwnd(WIN_CLASS, WIN_TITLES[0], mode='uia')
auto.activate()
time.sleep(0.3)
im = auto.im_client(client=YourIMClient, debug=False, auto_listen=False)

_last_anchor = None

def on_message(msg):
    global _last_anchor
    _last_anchor = im.MakeMessageAnchor(msg)
    print('[anchor]', _last_anchor['summary'])
    print('  key=', _last_anchor['key'])

batch = im.GetNextNewMessage(filter_mute=False, callback=on_message)
# ... 中间可能滚动聊天区、做其它 UI 操作 ...

msg = im.ResolveMessageByAnchor(_last_anchor)
if msg is None:
    print('视口内未找到(可能已滚出屏幕)')
else:
    print('找回:', msg.attr, msg.type, msg.content)
    msg.roll_into_view()
    # msg.download() / auto.click_bubble(msg.control) 等

WinAuto 等价调用:auto.make_msg_anchor(msg) / auto.resolve_msg_by_anchor(anchor)

type 遍历消息列表示例

msg.type 分支处理示例见上文 API,以及 调试与示例


上一页列表与会话 · 下一页控件别名 · 辅助与许可