牛逼:使用C#組合FlaUI和chatGPT實現微信AI問答

牛逼:使用C#組合FlaUI和chatGPT實現微信AI問答

基於FlaUI自動化+chatGPT實現微信自動回覆

最後更新 2023/8/30 下午7:57
且听风吟
預計閱讀 8 分鐘
分類
.NET
標籤
.NET C# AI

本文由網友投稿

作者:且聽風吟

原文標題:基於 FlaUI 自動化+chatGPT 實現微信自動回覆

原文連結:https://blog.csdn.net/ftfmatlab/article/details/132589169

1. 先看效果圖

取得微信好友列表

自動問答效果

2. 本文實現功能

本次主要介紹如何實現自動回覆:

  1. 將檔案傳輸助手置頂,模擬滑鼠點擊檔案傳輸助手;

  2. 一直重新整理會話列表,有新的訊息就需要回覆內容;

  3. 當重新整理到新的訊息時,模擬滑鼠點擊到對應的會話人,此時判斷是群聊還是人,如果是群聊則不回覆。

  4. 取得訊息後轉發給 chatGPT,同時等待 chatGPT 回覆內容。

  5. 取得 chatGPT 的內容後將內容輸入到微信聊天框,並模擬滑鼠點擊傳送按鈕。

  6. 模擬滑鼠點擊檔案傳輸助手,等待其他訊息。。。。

3. 功能程式碼

3.1. 取得聊天訊息並傳送

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));// 並行查詢---需將檔案傳輸助手置頂
                         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");//   定位元素的局部搜尋: .//Text;   全局搜尋: //*/Text
                              DateTime t2 = DateTime.Now;
                              Trace.WriteLine($"allText用時:{(t2 - t1).TotalMilliseconds}ms");
                              // 回覆訊息列表還未回覆的
                              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)
                                        {
                                             //將微信視窗設定為預設焦點狀態
                                             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");
                                        // 判斷是否為群聊
                                        if (itemFirst == null)
                                        {
                                             AutoGetMesg(content);
                                        }
                                        assFirst?.Click();
                                   }
                              }
                              }
                         }
                    });

                    DateTime dateTime4 = DateTime.Now;
                    Trace.WriteLine($"任務888耗時:{(dateTime4 - dateTime3).TotalMilliseconds}ms");
               }
               else
               {
                    Thread.Sleep(10);
                    continue;
               }

               //ScrollEvent(-700);

          }
          catch (Exception ex)
          {
               continue;
          }
          finally
          {
               //await Task.Delay(1);
          }
          }
     }, ChatListCancellationToken);
}

3.2. 自動回覆

IChat _chat;
private void AutoGetMesg(string txt)
{
     if (_chat == null)
     {
          _chat = new ChatAchieve();
          _chat.RequestContent = GetMessage;
     }
     Trace.WriteLine($"傳送訊息:{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($"回覆:{mes}");
}
private void SendMes(string mes)
{
     if (wxWindow.Patterns.Window.PatternOrDefault != null)
     {
          //將微信視窗設定為預設焦點狀態
          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. 其他程式碼

/// <summary>
/// 啟動
/// </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>
/// 微信的處理序ID
/// </summary>
private int ProcessId { get; set; }
/// <summary>
/// 微信視窗
/// </summary>
private Window wxWindow { get; set; }
private bool IsInit { get; set; }
/// <summary>
/// 取得
/// </summary>
void GetWxHandle()
{
     var process = Process.GetProcessesByName("Wechat").FirstOrDefault();
     if (process != null)
     {
          ProcessId = process.Id;
     }
}
/// <summary>
/// 載入微信
/// </summary>
void InitWechat()
{
     IsInit = true;
     GetWxHandle();
     GetFriendTokenSource = new CancellationTokenSource();
     GetFriendCancellationToken = GetFriendTokenSource.Token;
     ChatListTokenSource = new CancellationTokenSource();
     ChatListCancellationToken = ChatListTokenSource.Token;
     FriendTokenSource = new CancellationTokenSource();
     FriendCancellationToken = FriendTokenSource.Token;
     //根據微信處理序ID繫結FLAUI
     try
     {
          var application = FlaUI.Core.Application.Attach(ProcessId);
          var automation = new UIA3Automation();
          //取得微信視窗自動化操作物件
          wxWindow = application.GetMainWindow(automation);
     }
     catch (Exception ex)
     {
          if (MessageBox.Show(ex.Message, "異常", MessageBoxButtons.OK, MessageBoxIcon.Error) == DialogResult.OK)
          this.Close();
     }


     // 載入好友
     IsListenCronyList = true;
     // 載入聊天資訊
     GetChatInfo();
}
/// <summary>
/// 取得好友列表
/// </summary>
void GetFriends()
{
     if (!IsInit)
     {
          return;
     }
     if (wxWindow == null)
     {
          return;
     }

     if (wxWindow.Patterns.Window.PatternOrDefault != null)
     {
          //將微信視窗設定為預設焦點狀態
          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>
/// 監聽好友列表
/// </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 = "關閉監聽好友列表";
          GetFriends();
          }
          else
          {
          txt = "開啟監聽好友列表";
          GetFriendTokenSource.Cancel();
          }
          btnListenCronyList.ExecBeginInvoke(() =>
          {
          btnListenCronyList.Text = txt;
          });
     }
     get => this._isListenCronyList;
}

3.4. 擴充方法

internal static class SystemEx
{
     /// <summary>
     /// 跨執行緒操作控制項
     /// </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);
     }
}

註:ChatAchieve 是 IChat 的實作,是對 chatGPT 的實作

public interface IChat
{
     Action<string> RequestContent { get; set; }
     void RequestGPT(string content);
}

作者懶,不願意將程式碼放倉庫,原始碼直接丟站長了,大家需要直接點擊下載:https://img1.dotnet9.com/2023/08/WeChat.Automation.zip

技術交流新增 QQ 群:771992300

或掃站長微信(codewf,備註「加群」)加入微信技術交流群:

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2026/1/5

寫給所有 .NET 開發者的 2025 年度總結

相信今年大家沒少看到 《抱歉,C# 已經跌出第一梯隊》類似的文章,.NET 生態到底如何,本文將為你系統梳理 2025 年 .NET 開發者最應該關注的技術趨勢和重要事件,涵蓋AI發展、.NET演進及兩者融合的最新動態和趨勢,以求幫助大家找准定位,迎接未來的挑戰與機遇。

繼續閱讀
同分類 / 同標籤 2023/11/17

.NET8 正式發佈,C#12 新變化

雖然 8 又帶來了許多方面的增強,比如:人工智慧、雲原生、效能、native AOT 等,但我還是最關注 C# 語言和一些框架層面的變化,下面介紹下 C# 12 和框架中我認為比較實用的新增功能。

繼續閱讀