refactor: split node agent and Go control plane
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
using FlaUI.Core.AutomationElements;
|
||||
using FlaUI.Core.Definitions;
|
||||
using WxAgent.Core;
|
||||
|
||||
namespace WxAgent.Windows;
|
||||
|
||||
public static partial class WechatChatClient
|
||||
{
|
||||
private static FlaUI.Core.AutomationElements.TextBox GetSearchBox(AutomationElement main) =>
|
||||
(main.FindFirstDescendant(cf => cf.ByControlType(ControlType.Edit).And(cf.ByName(WechatLocators.Search)))
|
||||
?? throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "Search input was not found.")).AsTextBox();
|
||||
|
||||
private static AutomationElement[] GetSearchRows(AutomationElement main) =>
|
||||
(FindByAutomationId(main, WechatLocators.SearchList)
|
||||
?? throw new WxAgentException(WxAgentErrorCode.UiStructureChanged, "Local search list was not found."))
|
||||
.FindAllChildren(cf => cf.ByControlType(ControlType.ListItem));
|
||||
|
||||
private static WechatSearchPage ReadSearchPage(AutomationElement[] rows, string query, string? category = null) =>
|
||||
WechatSearchRules.Parse(rows.Select(row => (SafeAutomationId(row), SafeName(row))).ToArray(), query, category);
|
||||
|
||||
private static async Task<WechatSearchPage> ResetLocalSearchAsync(AutomationElement main, string query, CancellationToken cancellationToken)
|
||||
{
|
||||
GetSearchBox(main).Text = string.Empty;
|
||||
GetSearchBox(main).Text = query;
|
||||
if (!string.Equals(GetSearchBox(main).Text, query, StringComparison.Ordinal))
|
||||
throw new WxAgentException(WxAgentErrorCode.ResultUnconfirmed, "Search input did not retain the exact query.");
|
||||
await Task.Delay(500, cancellationToken).ConfigureAwait(false);
|
||||
return ReadSearchPage(GetSearchRows(main), query);
|
||||
}
|
||||
|
||||
private static async Task<WechatSearchPage> ExpandLocalCategoryAsync(
|
||||
AutomationElement main, string query, WechatSearchExpansion expansion, CancellationToken cancellationToken)
|
||||
{
|
||||
var rows = GetSearchRows(main);
|
||||
var current = ReadSearchPage(rows, query).Expansions.SingleOrDefault(item => item.Category == expansion.Category)
|
||||
?? throw new WxAgentException(WxAgentErrorCode.UiStructureChanged, "Local expansion control changed before activation.");
|
||||
// This row was classified under a local header, not found by a global keyword/name search.
|
||||
rows[current.RowIndex].Click();
|
||||
for (var attempt = 0; attempt < 25; attempt++)
|
||||
{
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
var page = ReadSearchPage(GetSearchRows(main), query, current.Category);
|
||||
if (page.Results.Count(result => result.Category == current.Category) >= current.ExpectedCount
|
||||
&& !page.Expansions.Any(item => item.Category == current.Category)) return page;
|
||||
}
|
||||
throw new WxAgentException(WxAgentErrorCode.UiStructureChanged, "Not all local search results became available; refusing a partial selection.");
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<WechatSessionSearchResult>> SearchLocalSessionsCoreAsync(
|
||||
AutomationElement main, string query, CancellationToken cancellationToken)
|
||||
{
|
||||
var overview = await ResetLocalSearchAsync(main, query, cancellationToken).ConfigureAwait(false);
|
||||
var results = overview.Results.ToList();
|
||||
foreach (var expansion in overview.Expansions)
|
||||
{
|
||||
await ResetLocalSearchAsync(main, query, cancellationToken).ConfigureAwait(false);
|
||||
var full = await ExpandLocalCategoryAsync(main, query, expansion, cancellationToken).ConfigureAwait(false);
|
||||
results.RemoveAll(result => result.Category == expansion.Category);
|
||||
results.AddRange(full.Results.Where(result => result.Category == expansion.Category));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static async Task<WechatSessionSearchResult> OpenLocalSearchSessionAsync(AutomationElement main, string name, CancellationToken cancellationToken, string? query = null)
|
||||
{
|
||||
query ??= name;
|
||||
var results = await SearchLocalSessionsCoreAsync(main, query, cancellationToken).ConfigureAwait(false);
|
||||
var selected = WechatSearchRules.RequireExact(results, name);
|
||||
var page = await ResetLocalSearchAsync(main, query, cancellationToken).ConfigureAwait(false);
|
||||
var expansion = page.Expansions.SingleOrDefault(item => item.Category == selected.Category);
|
||||
if (expansion is not null)
|
||||
await ExpandLocalCategoryAsync(main, query, expansion, cancellationToken).ConfigureAwait(false);
|
||||
var rows = GetSearchRows(main);
|
||||
var current = ReadSearchPage(rows, query, expansion?.Category);
|
||||
var fresh = WechatSearchRules.RequireExact(current.Results, name);
|
||||
if (fresh.Category != selected.Category)
|
||||
throw new WxAgentException(WxAgentErrorCode.InvalidOperationState, "Local search category changed before selection.");
|
||||
var matches = rows.Where(row => SafeAutomationId(row) == fresh.AutomationId && SessionNameMatches(SafeName(row), name)).ToArray();
|
||||
if (matches.Length != 1)
|
||||
throw new WxAgentException(WxAgentErrorCode.InvalidOperationState, "Local search target is no longer unique.");
|
||||
if (matches[0].IsOffscreen)
|
||||
{
|
||||
var scroll = matches[0].Patterns.ScrollItem.PatternOrDefault
|
||||
?? throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "Local result is offscreen and cannot be scrolled into view.");
|
||||
scroll.ScrollIntoView();
|
||||
}
|
||||
matches[0].Click();
|
||||
await WaitForSessionPageAsync(main, name, cancellationToken).ConfigureAwait(false);
|
||||
return fresh;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user