c整理 | 小耕家的喵大仙
出品 | CSDN(ID:lichao19897314)
Q Q | 978124155
关于项目背景和微信自动化学习介绍
因为前面写了很多关于微信自动化的文章,网上有一位朋友联系我说他是做广告行业的,有时候除了微信对接客户还需要通过QQ来处理工作,因为广告行业中有大量客户还是使用QQ传送文件给他们做处理,所以需要手动下载并根据QQ用户按天归档,每天大概有上千份文件,所以他说人工接收和归档工作量特别的大,所以联系我想帮他做个自动接收QQ文件并按照联系人和发送文件的日期进行归档的功能。经过一晚上的模式和技术准备,发现是完全可行,所以在微信自动化的基础上进行了改造后实现了QQ文件自动接收的功能,并且经过生产环境的使用和验证,效果良好,大大减少了他们的工作。
如果有兴趣的朋友可以看下我微信自动化的文章!因为底层实现和微信自动化同根同源,感兴趣的点个赞关注支持下原创。
(1)C#开启探索微信自动化之路-微信UI自动化
(2)C#创建微信窗体自动化实例-微信UI自动化
(3)C#针对系统热键管理-微信UI自动化
(4)C#采集微信通讯录和联系人-微信UI自动化
(5)C#实现针对微信窗体鼠标静默点击-微信UI自动化
(6)C#搜索微信通讯录联系人-微信UI自动化
(7)C#实现微信消息群发-微信UI自动化
(8)C#监控微信进程运行状态-微信UI自动化
(9)C#监控微信网络连接状态-微信UI自动化
(11)C#实现微信窗体尺寸跟随调整-微信UI自动化
(12)C#采集微信聊天记录及历史消息-微信UI自动化
(13)C#实现自动回复微信消息-微信UI自动化
(14)C#针对微信界面元素截图操作-微信UI自动化
(15)C#实现对微信窗体的行为管控-微信UI自动化
(16)C#实现微信多开-微信UI自动化
(17)C#实现微信聊天文件接收及下载-微信UI自动化
(18)C#采集微信群成员信息-微信UI自动化(.Net)
(19)C#添加微信好友(可批量添加)--微信UI自动化(.Net)
-
QQ版本以及软件功能效果
环境要求采用的QQ版本为当前最新版本QQ👇
QQ自动接收文件的效果视频呈现
功能介绍
(1)自主监控QQ主面板的实时消息,分析实时消息中是否包含文件类型的消息,如果分析有新文件消息则自动打开与之对应的联系人聊天窗体。
(2)自动分析打开的聊天窗体中的文件传输面板UI信息,抓取待接收的文件列表信息反馈给自动化软件,并自动触发QQ接收文件按钮的事件。
(3)监控所有处于打开状态的QQ聊天窗口的文件传输面板的实时状态,将文件接收情况反馈给自动化软件。
(4)如果发现某个聊天窗口文件全部接收完毕,并且没有新文件过来则自动关闭聊天窗体,提高自动化响应速度和整体性能。
(5)按照日期和QQ用户进行文件归档操作。
注意事项
在软件执行前,请手动将【合并会话窗口】取消,防止窗口合并,本软件需要每个聊天窗口独立显示。
软件部分截图
-
技术实现思路和示例代码
以上QQ自动接收文件所实现的功能都是基于UI自动化机制,安全可靠。
- 找到QQ主窗体句柄和QQ进程ID,通过QQ进程ID构建UIA3Automation的自动化测试实例。
- 构建针对QQ自动化测试实例成功后,我们创建一个后台监听任务,监听QQ主窗体的消息面板。监听任务判断消息面板中的UI对象中的内容是否有修改,如果有修改则判断为新消息,并分析新消息是否为文件消息。
- 如果发现该新消息为文件消息,则自动打开本新消息的聊天面板,手动触发QQ接收文件的机制。
- 打开对应的聊天窗口后,我们需要实时监测每个聊天窗口的文件传输面板中的UI信息,获取到所有待接收和正在接收的文件信息,通过接收状态执行对应的操作,并收集接收状态。
- 如果发现某个聊天面板中文件全部接收完毕则强制关闭QQ聊天面板提高整体性能。
- 将成功接收到的文件从QQ默认文件夹中复制到软件的归档目录中,并按照QQ用户名和日期规则进行归档,并上传至FTP服务器保持副本,提高文件可用性。
核心示例代码
加载QQ主窗体并构建自动化测试实例
public bool Load(){int weChatID = 0;IntPtr hwnd = FindWindow(null, "QQ");if (hwnd != IntPtr.Zero){GetWindowThreadProcessId(hwnd, out weChatID);}else{return false;}application = FlaUI.Core.Application.Attach(weChatID);automation = new UIA3Automation();Window = application.GetMainWindow(automation);var windowSource = application.GetAllTopLevelWindows(automation);if (Window == null)return false;startPanel = string.Format("/Pane[{0}]", Window.FindAllChildren().Length-1);return true;}
监控QQ主面板实时文件消息
using FlaUI.QQ.FileReceive;
using FlaUI.WinForm.UIAuto.Model;
using FlaUI.QQ.FileReceive;
using FlaUI.QQ.FileReceive.Log;
using FlaUI.QQ.FileReceive.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace FlaUI.WinForm.UIAuto.Business.WX.Task
{/// <summary>/// 新文件消息检测/// </summary>public class UI_New_Receive_File_Task : UI_QQ_Element_Base_Task{private List<QQFileMessageDTO> historyMessageSource { get; set; }public override void Stop(){base.Stop();}public override void Start(){if (State == TaskState.Stop){State = TaskState.Runing;}else{return;}Thread th = new Thread(new ThreadStart(() =>{while (State == TaskState.Runing){try{LoopQQMainMessagePanel();}catch (Exception ex){SetMessage("错误:" + ex.Message);}Thread.Sleep(100);}}));th.Start();base.Start();}private void LoopQQMainMessagePanel(){var currentChatList = UI_QQ_Window.Current.Find("/Pane/Pane[4]/Pane[2]/Pane[4]/Pane/Pane/Pane/Pane/Pane/Pane/Pane/Pane/Pane/Pane/Pane/Pane");if (currentChatList == null || currentChatList.ControlType != FlaUI.Core.Definitions.ControlType.Pane){Thread.Sleep(300);return;}var source = currentChatList.FindAllChildren();var tempMessageSource = new List<QQFileMessageDTO>();source.ToList().ForEach(QQMessageItem =>{if (QQMessageItem.ControlType == FlaUI.Core.Definitions.ControlType.ListItem){var buttonName = QQMessageItem.Name;var lastMessage = QQMessageItem.Patterns.Value.PatternOrDefault?.Value;var messageitem = new QQFileMessageDTO { UserName = buttonName, Message = lastMessage };tempMessageSource.Add(messageitem);if (historyMessageSource != null){var cnt = historyMessageSource.Count(s => s.UserName == messageitem.UserName && messageitem.Message == s.Message);if (cnt == 0){var newMessage = messageitem.Message;if (IsFileMessage(newMessage)){var fileName = GetFileName(newMessage);SetMessage("来自【" + messageitem.UserName + "】新文件【" + fileName + "】");messageitem.FileName = fileName;UI_QQ_Window.Current.Focus();QQMessageItem.DoubleClick();}}}}});historyMessageSource = tempMessageSource;}private bool IsFileMessage(string message) {if(string.IsNullOrEmpty(message)) return false;if (message.Contains("分享文件\"") && message.Substring(message.Length - 1, 1).Contains("\"")){return true;}return false;}private string GetFileName(string message) {int index = message .IndexOf("分享文件\"");message= message.Substring(index, message.Length -index );message = message.Replace("分享文件\"", "");message = message.Substring(0, message.Length - 1);return message;}}
}
从打开的QQ聊天面板获取文件信息和接收情况
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using FlaUI.Core.AutomationElements;
using FlaUI.QQ.FileReceive.Log;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;namespace FlaUI.QQ.FileReceive
{/// <summary>/// 接收QQ单窗口面板里面的文件/// </summary>public class UI_Window_File_Read_Task : UI_QQ_Element_Base_Task{public override void Start(){if (State == TaskState.Stop){State = TaskState.Runing;}else{return;}Thread th = new Thread(new ThreadStart(() =>{while (State == TaskState.Runing){try{FindAllChatWindow();}catch (Exception ex){SetMessage("错误:" + ex.Message);}Thread.Sleep(100);}}));th.Start();base.Start();}private AutomationElement FindAllChatWindow(){var ss = WindowApi.GetAllDesktopWindows();foreach (var item in ss){if (item.szClassName != "TXGuiFoundation")continue;var window = UI_QQ_Window.Current.Automation.FromHandle(item.hWnd);if (window == null)continue;var current = QQUserFileManger.Current.Get(window.Name, GetChatWindowType(window));if (current.QQChatWindowType == QQChatType.User){ReceiveUserFile(window, current);}else{ReceiveGroupFile(window, current);}}return null;}/// <summary>/// 接收用户发送的文件/// </summary>/// <param name="window"></param>private void ReceiveUserFile(AutomationElement window, QQUserFileSourceDTO dto){var receiveFilePanel = window.FindFirstByXPath(@"/Pane[3]/Pane/Pane/Pane/Pane[2]/Pane/Pane[1]/Pane/Pane/Pane/Pane/Pane[1]/Pane/Pane");if (receiveFilePanel == null){//可以执行关闭dto.FileSource.Completed();//关闭CloseChatWindow(window);return;}List<string> executeingFileSource = new List<string>();foreach (var fileItem in receiveFilePanel.FindAllChildren()){if (fileItem.ControlType != Core.Definitions.ControlType.ListItem)continue;executeingFileSource.Add(fileItem.Name);ClickReceiveFile(fileItem, dto, window);}//判断是否接收完成foreach (var file in dto.FileSource){if (executeingFileSource.Count(s => file.FileName == s) <= 0){file.IsReceiveCompleted = true;}}}private void CloseChatWindow(AutomationElement window) {var closeButton = window.FindFirstByXPath("/Pane[4]/Button[3]");if (closeButton != null){closeButton.Click();}}private void ClickReceiveFile(AutomationElement fileItem, QQUserFileSourceDTO dto, AutomationElement window){//如果存在for (var i = 1; i <= 2; i++){var reviButton = fileItem.FindFirstByXPath("/Pane[2]/Pane/Pane[3]/Pane[2]/Button[" + i + "]");if (reviButton != null && reviButton.Name == "接收"){SystemLog.Info("开始接收新文件:" + fileItem.Name);window.Focus();reviButton.Click();dto.AddFile(fileItem.Name, true);break;}}}/// <summary>/// 群/// </summary>/// <param name="window"></param>/// <param name="dto"></param>private void ReceiveGroupFile(AutomationElement window, QQUserFileSourceDTO dto){CloseChatWindow(window);//if (dto.GroupExecute)// return;//dto.GroupExecute = true; //Task.Run(new Action(() =>//{// //只能从文件是否完成// //点击文件// var fileTab = window.FindFirstByXPath("/Pane[3]/Pane/Pane/Pane/Pane[1]/Pane/Pane/Pane/ToolBar[1]/TabItem[4]");// if (fileTab == null)// {// dto.GroupExecute = false;// return;// }// window.Focus();// fileTab.Click();// //刷新// var moreBtn = window.FindFirstByXPath("/Pane[3]/Pane/Pane/Pane/Pane[1]/Pane/Pane/Pane/ToolBar[1]/TabItem[4]");// if (moreBtn == null)// {// dto.GroupExecute = false;// return;// }// moreBtn.Click();// var refBtn = window.FindFirstByXPath("/Pane[3]/Pane/Pane/Pane/Pane[1]/Pane/Pane/Pane/ToolBar[1]/TabItem[4]");// if (refBtn == null)// {// dto.GroupExecute = false;// return;// }// window.Focus();// refBtn.Click();// dto.GroupExecute = false;// }));}private QQChatType GetChatWindowType(AutomationElement window){var list = window.FindFirstByXPath("/Pane[3]/Pane/Pane/Pane/Pane[2]/Pane/Pane[1]/Pane/Pane/Pane/Pane/Pane/Pane[3]/List");if (list != null && list.Name == "成员列表" && list.ControlType == Core.Definitions.ControlType.List){return QQChatType.Group;}return QQChatType.User;}public override void Stop(){base.Stop();}}public class QQUserFileManger : List<QQUserFileSourceDTO>{public event EventHandler<QQUserFileSourceDTO> DataChange;private QQUserFileManger() { }private static QQUserFileManger current = new QQUserFileManger { };public static QQUserFileManger Current { get { return current; } }/// <summary>/// 获取当前聊天对象窗体DTO/// </summary>/// <param name="name"></param>/// <param name="type"></param>/// <returns></returns>public QQUserFileSourceDTO Get(string name, QQChatType type){var current = this.FirstOrDefault(item => item.QQChatWindowName == name && item.QQChatWindowType == type);if (current == null){current = new QQUserFileSourceDTO { QQChatWindowName = name, QQChatWindowType = type };this.Add(current);DoDataChange();}return current;}public void DoDataChange(){if (DataChange != null){DataChange(this, null);}}public List<DisplayFileDTO> Show(){List<DisplayFileDTO> source = new List<DisplayFileDTO>();foreach (var item in this){foreach (var item1 in item.FileSource){var log = "等待接收";if (item1.IsReceive){log = "正在接收";if (item1.IsReceiveCompleted)log = "接收完成";}source.Add(new DisplayFileDTO{Id = item1.Id,QQChatName = item.QQChatWindowName,QQChatType = item.QQChatWindowType == QQChatType.User ? "用户文件" : "群文件",FileName = item1.FileName,RecevieLog = log});}}return source;}}public class DisplayFileDTO{public Guid Id { get; set; }public string QQChatName { get; set; }public string QQChatType { get; set; }public string FileName { get; set; }public string RecevieLog { get; set; }}/// <summary>/// 某个用户或者群下的文件集合/// </summary>public class QQUserFileSourceDTO{public QQUserFileSourceDTO(){FileSource = new QQFileSource();}/// <summary>/// QQ聊天窗口名称/// </summary>public string QQChatWindowName { get; set; }public QQChatType QQChatWindowType { get; set; }/// <summary>/// 是否在处理中/// </summary>public bool GroupExecute { get; set; } = false;public QQFileSource FileSource { get; set; }public QQFileDTO AddFile(string file, bool IsReceive = false){var item = new QQFileDTO { FileName = file, Id = Guid.NewGuid(), IsReceive = IsReceive };FileSource.Add(item);QQUserFileManger.Current.DoDataChange();return item;}}public enum QQChatType{/// <summary>/// 用户/// </summary>User,/// <summary>/// 群/// </summary>Group}public class QQFileSource : List<QQFileDTO>{public void Completed() {foreach (var item in this){item.IsReceiveCompleted = true;}}}/// <summary>/// QQ文件对象/// </summary>public class QQFileDTO{public Guid Id { get; set; }/// <summary>/// 文件名称/// </summary>public string FileName { get; set; }/// <summary>/// 是否开始接收/// </summary>public bool IsReceive { get; set; } = false;bool isReceiveCompleted = false;/// <summary>/// 是否接收完成/// </summary>public bool IsReceiveCompleted{get { return isReceiveCompleted; }set{isReceiveCompleted = value;QQUserFileManger.Current.DoDataChange();}}}
}
以上是核心组件及代码思路,因为代码关联类比较多,无法在本篇全部博客呈现!如果需要源码请加本人QQ 978124155
上一篇 C#监听QQ消息自动回复-QQ自动化https://blog.csdn.net/lichao19897314/article/details/136038468