(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

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
微信小程序是一种基于微信平台的应用程序,通过微信开发者工具进行开发。在微信小程序开发中,如果需要实现用户注册功能,可以使用.NET平台进行开发。 在.NET平台中,可以使用C#语言编写代码来实现微信小程序的注册功能。首先,需要引入微信小程序的开发接口,可以通过NuGet等方式引入相关的SDK。 注册功能主要包括用户输入信息、后台处理和反馈等步骤。首先,可以在微信小程序前端页面添加一个注册表单,用户可以在该注册表单中填写相关信息,例如用户名、密码等。通过微信小程序提供的API,可以获取用户输入的信息。 接下来,在后台使用C#语言编写代码,可以通过编写API接口来处理用户注册的请求。在API接口中,可以使用数据库操作进行数据的存储和管理。例如,可以将用户注册的信息存储到关系型数据库中,例如SQL Server,或者非关系型数据库中,例如MongoDB。 在注册功能的代码中,还需要进行一些验证和安全处理措施。例如,需要对用户输入的用户名进行唯一性验证,以防止重复注册。同时,可以对用户密码进行加密处理,增加安全性。 最后,可以在注册成功后给用户发送反馈信息,例如注册成功的提示或者跳转到其他页面等。 需要注意的是,以上只是简单介绍了.NET平台在微信小程序注册功能开发中的一些关键步骤,具体的代码实现可能根据具体的需求和业务逻辑有所不同。在实际开发过程中,可以参考微信小程序的开发文档和.NET的相关文档,结合具体的业务需求进行开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小耕家的喵大仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值