(24)实时采集微信消息(基于独立窗体)-微信UI自动化(.Net+C#)

整理 | 小耕家的喵大仙

出品 | CSDN(ID:lichao19897314)

Q Q | 978124155

 往期知识回顾

(1)开启探索微信自动化之路-微信UI自动化(.Net+C#)

(2)初始化微信窗体UI自动化实例-微信UI自动化(.Net+C#)

(3)采用热键终止微信采集任务-微信UI自动化(.Net+C#)

(4)采集微信通讯录和联系人-微信UI自动化(.Net+C#)

(5)实现对微信窗体元素静默操作-微信UI自动化(.Net+C#)

(6)搜索特定微信通讯录联系人-微信UI自动化(.Net+C#)

(7)定时群发微信图文消息-微信UI自动化(.Net+C#)

(8)监控微信进程运行状态-微信UI自动化(.Net+C#)

(9)监控微信网络连接状态-微信UI自动化(.Net+C#)

(10)实现微信窗体自动跟随移动-微信UI自动化(.Net+C#)

(11)实现微信窗体尺寸跟随自动调整-微信UI自动化(.Net+C#)

(12)采集微信消息记录及历史消息-微信UI自动化(.Net+C#)

(13)自动回复微信聊天消息-微信UI自动化(.Net+C#)

(14)微信窗体元素截图操作-微信UI自动化(.Net+C#)

(15)针对微信主窗体的行为控制-微信UI自动化(.Net+C#)

(16)微信多开-微信UI自动化(.Net+C#)

(17)自动采集微信聊天信息中的文件-微信UI自动化(.Net+C#)

(18)采集微信群成员信息-微信UI自动化(.Net+C#)

(19)批量添加微信好友-微信UI自动化(.Net+C#)

(20)批量将微信群成员添加为好友-微信UI自动化(.Net+C#)

(21)批量删除微信联系人-微信UI自动化(.Net+C#)

(22)采集微信通讯录详情面板-微信UI自动化(.Net+C#)

👆😀以上文章是以往使用自动化方案操作微信的一些案例!如有兴趣请点击浏览!

因为文章可能无法满足读者要求,如需源码和支持请联系本人 QQ 978124155 

本篇目的

      有同学看了(23)实时采集微信消息(基于主窗体)--微信UI自动化(.Net+C#) 文章后,在了解到这种方式的利弊后,联系我说他想在无人值守的环境下,实时采集某些特定的群或者联系人的消息,并且确保消息不丢失。

     所以针对这个需求,我们分析如下

  1. 无人值守的情况下我们可用和微信进行UI交互,不会影响到用户正常操作。
  2. 该同学只需要采集某些特定的群或者联系人,所以我们只需要监控特定的窗体对象即可。
  3. 需要确保消息的完整性,不能丢失对话消息。

经过对上述分析,我们在针对微信的UI自动化的过程中在软件执行之前可以手动(当然也可以自动 ,如需自动请参考 (6)搜索特定微信通讯录联系人-微信UI自动化(.Net+C#) 该文章)打开需要实时采集的群或者好友窗体,记住是以独立窗口的方式打开,这样才能保证消息的准确性和会话消息完整性,因为每个窗体的会话信息都完全呈现在我们的可视区域范围内。

      该种方式并不适合在需要和微信交互的情况下使用,所以针对无人值守的场景才适用。

软件视频和部分截图

各位朋友如果时间允许可观看视频直观感受下软件的执行过程,会更加直观清晰,本人将自动化速度调节的慢些,以便更加清晰的感受到自动化带来的魅力。

 该截图为微信会话独立窗体的对话消息记录

 该截图为软件抓取微信独立会话窗体的截图,通过两张截图对比内容发现微信发送的消息和软件采集的消息是一致的。

实现思路
  1. 获取当前微信号下面所有打开的独立微信窗体。
  2. 创建DTO对象,包含消息内容,消息类型,消息发送人,上一条消息(链路结构),用户头像,这里我们要重点强调为什么要定义【上一条消息(链路结构)】这个属性,因为我们UI对象是没有包含消息的唯一标识符的,在采集过程中你无法确定消息是不是已经采集或者是不是重复。增加一个这样的结构和维度来保证消息的准确性。
  3. 如果某个独立会话窗口时第一次采集则初始化消息缓存。
  4. 如果不是第一次采集则采集最近的15条消息(根据自己需要)形成消息链路,使用发送人,消息内容做对比,如果在缓存中不存在则视为新消息,如果存在则使用链路进行对比,如果链路中的消息和缓存中的链路消息不一致也视为新消息,链路一致则视为旧消息。

关于链路的解释,我们用一个实际的微信聊天截图来直观的描述,如下描述了一个消息的链路

在比较一个消息是否是新消息,有两种情况,一种是发送人+消息内容不存在缓存,一种是发送人+消息内容存在缓存中,那么这种存在缓存中的消息难道就不是新消息吗?请看下面的图片1,【你好啊】这个消息内容在下图出现了两次,第二次的【你好啊】从缓存中搜索是存在的,但是它又是一条新消息,你不能忽略这条消息,所有面对这种情况你需要采用消息链路的机制,如果链路中的消息中有比对不上的则视为新消息, 参考图2。

图1

图2

技术细节

获取当前微信号下面所有打开的独立微信窗体。

	/// <summary>/// 获取正在打开的聊天窗口(不包含主窗体)/// </summary>/// <returns></returns>public List<AutomationElement> GetWeChatWindows(){List<AutomationElement> weChat = new List<AutomationElement>();var source = WindowApi.GetAllDesktopWindows().ToList();foreach (var item in source){if (item.szClassName == "ChatWid"){var cc = automation.FromHandle(item.hWnd);if (cc.ControlType == FlaUI.Core.Definitions.ControlType.Window)weChat.Add(cc  );}}return weChat;}

DTO对象定义

   /// <summary>/// 微信聊天消息模型/// </summary>public class WeChatContractMessageDto{public WeChatContractMessageDto() {MessageType = 0;}/// <summary>/// 消息内容/// </summary>public string Message { get; set; }/// <summary>/// 0消息 1图片 2动画表情/// </summary>public int MessageType { get; set; }/// <summary>/// 图片二进制对象/// </summary>public Bitmap ImageBitMap { get; set; }/// <summary>/// 用户头像(准确度不行)/// </summary>public Bitmap UserHeader { get; set; }/// <summary>/// 时间/// </summary>public DateTime Time { get; set; }/// <summary>/// 1发送方 2接收方 3时间 /// </summary>public int UserType { get; set; }/// <summary>/// 微信聊天用户/// </summary>public string UserName { get; set; }/// <summary>/// 消息处理状态/// </summary>public MessageExecuteStatus ExecuteStatus { get; set; }/// <summary>/// 上一条消息(链路结构)/// </summary>public WeChatContractMessageDto PreMessage { get; set; }public static bool Equals(WeChatContractMessageDto t1, WeChatContractMessageDto t2){if (t1 == null && t2 == null) return true;if (t1 == null || t2 == null) return false;if (t1 != null && t2 != null){return t1.UserName == t2.UserName && t1.Message == t2.Message;}return false;}}

构建消息的方法

      private WeChatContractMessageDto BuildChatMessage(FlaUI.Core.AutomationElements.AutomationElement item){var my = item.FindFirstByXPath("/Pane[2]/Pane/Pane/Text");var to = my == null ? item.FindFirstByXPath("/Pane[1]/Pane/Pane/Text") : null;if (my != null){var button = item.FindFirstByXPath("/Pane[1]/Button");var map = button.Capture();return new WeChatContractMessageDto { UserHeader = map, Message = my.Name.ToString(), UserName = button.Name.ToString(), UserType = 1 };}else if (to != null){var button = item.FindFirstByXPath("/Pane[1]/Button");var map = button.Capture();return new WeChatContractMessageDto {  UserHeader= map, Message = to.Name.ToString(), UserName = button.Name.ToString(), UserType = 2 };}return null;}

第一次初始化消息缓存

      if (dto.Init == false){#region 初始化第一次打开的聊天窗口记录foreach (var chatItem in chatElementSource){if (chatItem.ControlType != FlaUI.Core.Definitions.ControlType.ListItem)continue;WeChatContractMessageDto currentMessage = null;if (chatItem.Name != "[图片]" && chatItem.Name != "[动画表情]")currentMessage = BuildChatMessage(chatItem);if (currentMessage == null || currentMessage.UserType == 3)continue;var preMessage= dto.Messages.Count > 0 ? dto.Messages[dto.Messages.Count - 1] : null;//如果是一个人连续发送多条重复记录则忽略本条if (WeChatContractMessageDto.Equals(preMessage, currentMessage))continue;//如果未初始化则全部添加到原始集合中currentMessage.PreMessage = preMessage;//上一条记录 构造消息链dto.Messages.Add(currentMessage);currentMessage.ExecuteStatus = MessageExecuteStatus.Complete;ExecuteWeChatMessageRemind(dto, currentMessage);currentMessage.ExecuteStatus = MessageExecuteStatus.Complete;}dto.Init = true;//设置为初始化完毕#endregion}

消息链路对比

   #region 构造最近15条有效记录作为比较的依据List<WeChatContractMessageDto> tempSource = new List<WeChatContractMessageDto>();var cnt = chatElementSource.Count-1;for(int i= cnt ; i >=0; i--) {var chatItem = chatElementSource[i];WeChatContractMessageDto currentMessage = null;if (chatItem.BoundingRectangle.Bottom == 0&&chatItem.BoundingRectangle.Height == 0 && chatItem.BoundingRectangle.Width == 0 &&chatItem.BoundingRectangle.Top == 0  )continue;if (chatItem.Name == "以下是新消息")continue;if (chatItem.Name != "[图片]" && chatItem.Name != "[动画表情]"){currentMessage = BuildChatMessage(chatItem);}if (currentMessage == null || currentMessage.UserType == 3)continue;if (tempSource.Count >= 15)break;var temp=   tempSource.Count>0? tempSource[tempSource.Count-1]:null;if (!WeChatContractMessageDto.Equals(currentMessage, temp)){if (temp != null)temp.PreMessage = currentMessage;tempSource.Add(currentMessage);}}tempSource.Reverse();#endregion#region 分析最近15条记录-如果不存在则视为新消息,如果最近3条记录不匹配也视为新消息foreach (var ansyTemp in tempSource){//比较最近三条记录链路 如果相等则代表消息是历史消息var hisEqualMsg = dto.Messages.Where(s => s.UserName == ansyTemp.UserName && s.Message == ansyTemp.Message);if (hisEqualMsg == null){ansyTemp.PreMessage = dto.Messages.Count > 0 ? dto.Messages[dto.Messages.Count - 1] : null;dto.Messages.Add(ansyTemp);//执行新消息记录ExecuteWeChatMessageRemind(dto, ansyTemp);Repay(current, ansyTemp);ansyTemp.ExecuteStatus = MessageExecuteStatus.Complete;}else{var newMessage = true;foreach (var hisMsg in hisEqualMsg.ToList()){//递归比较var result=  MsgEqual(ansyTemp, hisMsg); if(result)newMessage=false;}if (newMessage){ansyTemp.PreMessage = dto.Messages.Count > 0 ? dto.Messages[dto.Messages.Count - 1] : null;dto.Messages.Add(ansyTemp);//执行新消息记录ExecuteWeChatMessageRemind(dto, ansyTemp);Repay(current, ansyTemp);ansyTemp.ExecuteStatus = MessageExecuteStatus.Complete;}}}#endregion

因为文章可能无法满足读者要求,如需源码和支持请联系本人 QQ 978124155 

(23)实时采集微信消息(基于主窗体)--微信UI自动化(.Net+C#)icon-default.png?t=N7T8https://blog.csdn.net/lichao19897314/article/details/138617138

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/9091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript初了解

JS的三种书写位置&#xff1a;行内&#xff0c;内嵌&#xff0c;外部 JS的注释的书写&#xff1a;单行注释&#xff1a;// ctrl/ 多行注释&#xff1a;/**/ ShiftAltA JavaScript输入输出语句

【热门话题】如何通过AI技术提升内容生产的效率与质量

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 如何通过AI技术提升内容生产的效率与质量引言一、自然语言处理&#xff08;NLP&…

【备战软考(嵌入式系统设计师)】11 - 硬件电路基础

逻辑门电路 首先我们需要先了解三个最基础的门电路&#xff0c;可以说我们一切的电子产品的基石就是这哥仨&#xff0c;它们就与&#xff0c;或&#xff0c;非。 与门和或门有两个输入端&#xff0c;一个输出端&#xff1b;非门有一个输入端一个输出端。 在我们数字电路中&a…

【爬虫基础1.1课】——requests模块

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

Leetcode—394. 字符串解码【中等】

2024每日刷题&#xff08;131&#xff09; Leetcode—394. 字符串解码 实现代码 class Solution { public:string decodeString(string s) {string curstr;int curNum 0;stack<pair<string, int>> st; for(char c: s) {if(isdigit(c)) {curNum curNum * 10 (c…

【Linux网络编程】HTTPS协议

【Linux网络编程】HTTPS协议 目录 【Linux网络编程】HTTPS协议HTTPS介绍加密常见的加密方式HTTPS的工作过程探究&#xff08;重点&#xff09;常见问题完整流程总结 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.5.9 前言&#xff1a;本篇博客将会介绍HTTPS协议 HTTPS…

proteus使用问题

1、无法和视频里面一样新建工程 2、实验效果和视频不也一样 自己的电路图(灯不亮)&#xff1a;

C语言—深入理解指针(1)

1.内存和地址 1.1 内存 计算机上CPU&#xff08;中央处理器&#xff09;在处理数据的时候&#xff0c;需要的数据是在内存中读取的&#xff0c;处理后的数据也会放回内存中&#xff0c;那我们买电脑的时候&#xff0c;电脑上内存是8GB/16GB/32GB 等&#xff0c;其实管理方式也…

Python从0到POC编写--实用小脚本02

爆破脚本&#xff1a; 爆破脚本也是我们经常使用的东西 这里就简单讲讲后台爆破脚本的编写吧 在编写之前&#xff0c;我们先通过访问网站去看看情况 首先我们可以先登录看看 输入账号 admin &#xff0c;密码 12345 后 登录失败&#xff0c;提示 用户名或密码错误 在输入…

探秘Tailwind CSS:前端开发的加速器(Tailwind CSS让CSS编写更简洁)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Tailwind CSS 📒📝 快速体验📝 深入学习⚓️ 相关链接 ⚓️📖 介绍 📖 在这个快速迭代的互联网时代,前端开发效率和设计质量的双重要求,使得开发者们不断寻求更高效的工具和方法。今天,我们要介绍的是一个能够极大…

java面向对象实现文字格斗游戏细节完善版

为了完善上一篇的文字格斗游戏的细节&#xff0c;所以加了些代码&#xff0c;使得交互更加的具体有趣! 效果 大家可以多运行几次代码&#xff0c;得到不同的战况&#xff01;&#xff01; 代码实现 1.bean类 import java.util.Random;public class TextGame {private Strin…

[性能优化] ScrollView视图优化为循环列表

问题描述&#xff1a; 原先商城的物品栏中的item 是load在一个scrollView 下&#xff0c;用于滑动查看。仅仅在父级panel下是使用了NGUI原生的scrollview 组件&#xff0c;随着商场物品列表中新物品的增多。panel下加载的实例也非常庞大。而大部分的实例用户也无法看到&#x…

python代码无法点击进入,如何破???

python代码无法点击进入&#xff0c;如何破&#xff1f;&#xff1f;&#xff1f; 举个栗子&#xff1a; model.chat是无法进入的&#xff0c;这时可以使用如下的命令进行操作&#xff1a; ?model.chat

Day31:单元测试、项目监控、项目部署、项目总结、常见面试题

单元测试 保证独立性。 Assert&#xff1a;断言&#xff0c;一般用来比较是否相等&#xff0c;比如 Assert.assertEquals 在JUnit测试框架中&#xff0c;BeforeClass&#xff0c;Before&#xff0c;After和AfterClass是四个常用的注解&#xff0c;它们的作用如下&#xff1a; …

Vue--》从零开始打造交互体验一流的电商平台(一)

今天开始使用 vue3 ts 搭建一个电商项目平台&#xff0c;因为文章会将项目的每处代码的书写都会讲解到&#xff0c;所以本项目会分成好几篇文章进行讲解&#xff0c;我会在最后一篇文章中会将项目代码开源到我的github上&#xff0c;大家可以自行去进行下载运行&#xff0c;希…

Pyserini

文章目录 关于 Pyserini安装&#x1f3ac;如何搜索&#xff1f;&#x1f64b;如何给我的语料集建立索引 关于 Pyserini github : https://github.com/castorini/pyseriniPyserini: An Easy-to-Use Python Toolkit to Support Replicable IR Research with Sparse and Dense Re…

第09章 局域网技术(拓扑结构设计+FDDI工作机制)

9.1 本章目标 了解IEEE 802局域网标准掌握局域网拓扑结构了解10Base以太网了解快速以太网熟悉交换式以太网了解千兆位以太网了解其它种类的局域网局域网中的常用技术 9.2 局域网概述 罗伯特梅特卡夫个人简介 罗伯特梅特卡夫&#xff08;Robert Metcalfe&#xff0c;1…

【镜像仿真篇】磁盘镜像仿真常见错误

【镜像仿真篇】磁盘镜像仿真常见错误 记系统镜像仿真常见错误集—【蘇小沐】 1、实验环境 2023AFS39.E01&#xff08;Windows11系统镜像&#xff09;Arsenal Image Mounter&#xff0c;[v3.10.262]‍Vmware Workstation 17 Pro&#xff0c;[v17.5.1]Windows 11 专业工作站版…

Android ViewFlipper

Android ViewFlipper 在很多APP都有如下的头条/热榜效果&#xff08;上下自动翻滚&#xff09; 这种效果可以使用很多方式实现&#xff0c;有一个简便的方式可以使用ViewFlipper控件实现&#xff0c;ViewFlipper控件继承结果如下&#xff1a; 可以看出ViewFlipper 继承自ViewA…