This article is contributed by a netizen
Author: Listening to the Wind
Original title: WeChat Auto Reply Based on FlaUI Automation + ChatGPT
Original link: https://blog.csdn.net/ftfmatlab/article/details/132589169
1. First, see the effect
Get WeChat friend list

Auto Q&A effect

2. Features implemented in this article
This article mainly introduces how to implement auto reply:
Pin the File Transfer assistant to the top, simulate mouse click on File Transfer assistant;
Keep refreshing the conversation list. When there is a new message, a reply is needed;
When a new message is refreshed, simulate mouse click on the corresponding conversation person. At this point, determine whether it is a group chat or a person. If it is a group chat, do not reply.
After obtaining the message, forward it to ChatGPT, and wait for ChatGPT to reply.
After obtaining the content from ChatGPT, input the content into the WeChat chat box and simulate mouse click on the send button.
Simulate mouse click on the File Transfer assistant, waiting for other messages...
3. Function code
3.1. Get chat messages and send
void GetChatInfo()
{
if (!IsInit)
{
return;
}
if (wxWindow == null)
{
return;
}
//wxWindow.FindAllDescendants(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Button)).AsParallel().FirstOrDefault(s =>s!=null && s.Name == "聊天")?.Click(false);
wxWindow.FindFirstDescendant(cf => cf.ByName("聊天"))?.Click(false);
Task.Run(() =>
{
AutomationElement? assFirst = null;
object obj = new object();
while (true)
{
if (ChatListCancellationToken.IsCancellationRequested)
{
break;
}
try
{
DateTime dateTime3 = DateTime.Now;
var searchTextBox = wxWindow.FindFirstDescendant(cf => cf.ByName("会话")).AsListBoxItem();
if (searchTextBox != null)
{
var list = searchTextBox.FindAllChildren();
if (assFirst == null)
{
assFirst = list.AsParallel().FirstOrDefault(t => t != null && "文件传输助手".Equals(t.Name));// Parallel query - need to pin File Transfer to top
assFirst?.Click();
continue;
}
Parallel.ForEach(list, item =>
{
if (item != null && !string.IsNullOrEmpty(item.Name) && !"折叠置顶聊天".Equals(item.Name)
&& !"腾讯新闻".Equals(item.Name) && !"群聊".Contains(item.Name))
{
DateTime t1= DateTime.Now;
var allText = item.FindAllByXPath(".//Text");// Local search of element: .//Text; Global search: //*/Text
DateTime t2 = DateTime.Now;
Trace.WriteLine($"allText time: {(t2 - t1).TotalMilliseconds}ms");
// Reply messages that have not been replied yet in the message list
if (allText != null && allText.Length >= 4)
{
if (int.TryParse(allText[3].Name, out var count) && count > 0)
{
lock (obj)
{
var name = allText[0].Name;
var time = allText[1].Name;
var content = allText[2].Name;
if (wxWindow.Patterns.Window.PatternOrDefault != null)
{
// Set the WeChat window to default focus state
wxWindow.Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal);
}
item.Click();
DateTime t7= DateTime.Now;
var itemFirst = wxWindow.FindAllDescendants(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Text)).AsParallel()
.FirstOrDefault(t =>t!=null && t.Parent.ControlType == ControlType.Pane && !t.IsOffscreen && t.Name.Trim().IsSpecificNumbers());
DateTime t8= DateTime.Now;
Trace.WriteLine($"itemFirst: {(t8 - t7).TotalMilliseconds}ms");
// Determine whether it is a group chat
if (itemFirst == null)
{
AutoGetMesg(content);
}
assFirst?.Click();
}
}
}
}
});
DateTime dateTime4 = DateTime.Now;
Trace.WriteLine($"Task 888 time: {(dateTime4 - dateTime3).TotalMilliseconds}ms");
}
else
{
Thread.Sleep(10);
continue;
}
//ScrollEvent(-700);
}
catch (Exception ex)
{
continue;
}
finally
{
//await Task.Delay(1);
}
}
}, ChatListCancellationToken);
}
3.2. Auto reply
IChat _chat;
private void AutoGetMesg(string txt)
{
if (_chat == null)
{
_chat = new ChatAchieve();
_chat.RequestContent = GetMessage;
}
Trace.WriteLine($"Send message: {txt}");
_chat.RequestGPT(txt);
}
private FlaUI.Core.AutomationElements.TextBox _mesText;
public FlaUI.Core.AutomationElements.TextBox MesText
{
get
{
if (_mesText == null)
_mesText = wxWindow.FindFirstDescendant(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Text)).AsTextBox();
return _mesText;
}
}
private AutomationElement? _btnSend;
public AutomationElement? btnSend
{
get
{
if (_btnSend == null)
{
_btnSend = wxWindow.FindFirstDescendant(cf => cf.ByName("sendBtn"));
//_btnSend = wxWindow.FindAllDescendants(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Button)).FirstOrDefault(s => s.Name == "发送(S)");
}
return _btnSend;
}
}
const int _offSize = 300;
private void GetMessage(string mes)
{
SendMes(mes);
Trace.WriteLine($"Reply: {mes}");
}
private void SendMes(string mes)
{
if (wxWindow.Patterns.Window.PatternOrDefault != null)
{
// Set the WeChat window to default focus state
wxWindow.Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal);
}
int tempLen = 0;
string txt = string.Empty;
try
{
if (!string.IsNullOrWhiteSpace(mes))
{
string[] lines = mes.Split(Environment.NewLine);
foreach (string line in lines)
{
tempLen += line.Length;
txt += line + Environment.NewLine;
if (tempLen > _offSize)
{
MesText.Text = txt;
btnSend?.Click();
tempLen = 0;
txt = string.Empty;
}
}
if (!string.IsNullOrWhiteSpace(txt))
{
MesText.Text = txt;
Thread.Sleep(3);
btnSend?.Click();
}
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
MesText.Text = txt;
btnSend?.Click();
}
}
3.3. Other code
/// <summary>
/// Start
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
InitWechat();
}
private void FrmMain_Load(object sender, EventArgs e)
{
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
this.Dispose();
GC.Collect();
}
private CancellationToken FriendCancellationToken { get; set; }
private CancellationTokenSource FriendTokenSource { get; set; }
private CancellationToken ChatListCancellationToken { get; set; }
private CancellationTokenSource ChatListTokenSource { get; set; }
private CancellationToken GetFriendCancellationToken { get; set; }
private CancellationTokenSource GetFriendTokenSource { get; set; }
/// <summary>
/// WeChat process ID
/// </summary>
private int ProcessId { get; set; }
/// <summary>
/// WeChat window
/// </summary>
private Window wxWindow { get; set; }
private bool IsInit { get; set; }
/// <summary>
/// Get
/// </summary>
void GetWxHandle()
{
var process = Process.GetProcessesByName("Wechat").FirstOrDefault();
if (process != null)
{
ProcessId = process.Id;
}
}
/// <summary>
/// Load WeChat
/// </summary>
void InitWechat()
{
IsInit = true;
GetWxHandle();
GetFriendTokenSource = new CancellationTokenSource();
GetFriendCancellationToken = GetFriendTokenSource.Token;
ChatListTokenSource = new CancellationTokenSource();
ChatListCancellationToken = ChatListTokenSource.Token;
FriendTokenSource = new CancellationTokenSource();
FriendCancellationToken = FriendTokenSource.Token;
// Bind FLAUI based on WeChat process ID
try
{
var application = FlaUI.Core.Application.Attach(ProcessId);
var automation = new UIA3Automation();
// Get the WeChat window automation operation object
wxWindow = application.GetMainWindow(automation);
}
catch (Exception ex)
{
if (MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error) == DialogResult.OK)
this.Close();
}
// Load friends
IsListenCronyList = true;
// Load chat info
GetChatInfo();
}
/// <summary>
/// Get friend list
/// </summary>
void GetFriends()
{
if (!IsInit)
{
return;
}
if (wxWindow == null)
{
return;
}
if (wxWindow.Patterns.Window.PatternOrDefault != null)
{
// Set the WeChat window to default focus state
wxWindow.Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal);
}
wxWindow.FindAllDescendants(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Button)).AsParallel()
.FirstOrDefault(item => item != null && item.Name == "通讯录")?.Click(false);
string lastName = string.Empty;
var list = new List<AutomationElement>();
var sync = SynchronizationContext.Current;
Task.Run(async () =>
{
while (true)
{
if (GetFriendCancellationToken.IsCancellationRequested)
break;
var all = wxWindow.FindAllDescendants(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.ListItem));
var allItem = all.AsParallel().Where(s => s != null && s.Parent != null && "联系人".Equals(s.Parent?.Name)).ToList();
foreach (var item in allItem)
{
if (!string.IsNullOrWhiteSpace(item.Name) && !listBox1.Items.Contains(item.Name.ToString()))
{
sync.Post(s =>
{
listBox1.Items.Add(s);
}, item.Name.ToString());
}
}
//ScrollEvent(-700);
await Task.Delay(1);
}
}, GetFriendCancellationToken);
}
/// <summary>
/// Monitor friend list
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnListenCronyList_Click(object sender, EventArgs e)
{
IsListenCronyList = !IsListenCronyList;
}
private bool _isListenCronyList = false;
public bool IsListenCronyList
{
set
{
if (_isListenCronyList == value)
return;
_isListenCronyList = value;
string txt = string.Empty;
if (value)
{
txt = "Disable monitor friend list";
GetFriends();
}
else
{
txt = "Enable monitor friend list";
GetFriendTokenSource.Cancel();
}
btnListenCronyList.ExecBeginInvoke(() =>
{
btnListenCronyList.Text = txt;
});
}
get => this._isListenCronyList;
}
3.4. Extension methods
internal static class SystemEx
{
/// <summary>
/// Cross-thread operation control
/// </summary>
/// <param name="con"></param>
/// <param name="action"></param>
public static void ExecBeginInvoke(this Control con, Action action)
{
if (action == null) return;
if (con.InvokeRequired)
{
con.BeginInvoke(new Action(action));
}
else
{
action();
}
}
public static void ExecInvoke(this Control con, Action action)
{
if (action == null) return;
if (con.InvokeRequired)
{
con.Invoke(new Action(action));
}
else
{
action();
}
}
const string PARRERN = @"^\(\d+\)$";
public static bool IsSpecificNumbers(this string txt)
{
return Regex.IsMatch(txt, PARRERN);
}
}
Note: ChatAchieve is the implementation of IChat, which is the implementation for ChatGPT
public interface IChat
{
Action<string> RequestContent { get; set; }
void RequestGPT(string content);
}
The author is lazy and does not want to put the code in a repository, so the source code is directly sent to the site administrator. If you need it, click to download: https://img1.dotnet9.com/2023/08/WeChat.Automation.zip
For technical exchange, add QQ group: 771992300
Or scan the site administrator's WeChat (codewf, note "Join group") to join the WeChat technical exchange group:
