XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Reflection;
using System.Text.Json;
using Microsoft.Web.WebView2.Core;
using Windows.Storage.Streams;

namespace XFEEDToolBox;

// Isolated news-only host: no toolbox runtime, game connection or automation.
public partial class App : Application
{
    public static NewsPreviewRuntime Runtime { get; } = new();
    private Window? window;
    public App() => InitializeComponent();
    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        var panel = new Controls.GalnetNewsPanel();
        window = new() { Title = "银河新闻预览", Content = new ScrollViewer { Content = panel } };
        var arguments = Environment.GetCommandLineArgs(); var index = Array.IndexOf(arguments, "--verify-news");
        if (index >= 0 && index + 1 < arguments.Length)
        {
            window.AppWindow.Resize(new(1440, 1080));
            window.AppWindow.Move(new(-20000, -20000));
            window.AppWindow.Show(false);
            _ = VerifyAsync(panel, arguments[index + 1]);
        }
        else { window.AppWindow.Resize(new(1280, 700)); window.Activate(); }
    }

    private async Task VerifyAsync(Controls.GalnetNewsPanel panel, string report)
    {
        string? failure = null; JsonElement? carousel = null, article = null;
        var started = DateTimeOffset.UtcNow;
        try
        {
            async Task<JsonElement> Wait(Func<WebView2?> getView, string condition)
            {
                var deadline = DateTimeOffset.UtcNow.AddSeconds(60);
                while (DateTimeOffset.UtcNow < deadline)
                {
                    if (getView() is { CoreWebView2: not null } view)
                    {
                        try
                        {
                            var result = await view.ExecuteScriptAsync(condition);
                            using var json = JsonDocument.Parse(result);
                            if (json.RootElement.ValueKind == JsonValueKind.Object) return json.RootElement.Clone();
                        }
                        catch (Exception) { }
                    }
                    await Task.Delay(100);
                }
                throw new TimeoutException("Native news WebView did not reach the expected state.");
            }
            async Task Capture(WebView2 view, string path)
            {
                // Allow the WebView compositor to present freshly decoded media.
                await Task.Delay(500);
                using var stream = new InMemoryRandomAccessStream();
                await view.CoreWebView2.CapturePreviewAsync(CoreWebView2CapturePreviewImageFormat.Png, stream);
                using var reader = new DataReader(stream.GetInputStreamAt(0));
                await reader.LoadAsync((uint)stream.Size); var pixels = new byte[(int)stream.Size]; reader.ReadBytes(pixels);
                await File.WriteAllBytesAsync(path, pixels);
            }
            var browser = (WebView2)panel.FindName("NewsBrowser");
            carousel = await Wait(() => browser, "window.galnet?.inspect().count===6 && /[\\u3400-\\u9fff]/.test(document.getElementById('slide-title').textContent) && document.getElementById('cover').naturalWidth>0 ? ({...window.galnet.inspect(),title:document.getElementById('slide-title').textContent}) : null");
            await Capture(browser, Path.Combine(Path.GetDirectoryName(report)!, "native-carousel.png"));
            await browser.ExecuteScriptAsync("document.getElementById('read-more').click()");
            WebView2? Reader() => typeof(Controls.GalnetNewsPanel).GetField("articleBrowser", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(panel) as WebView2;
            article = await Wait(Reader, "document.body.className==='reader-mode' && innerWidth>=600 && /[\\u3400-\\u9fff]/.test(document.getElementById('article-body').textContent) && document.getElementById('article-cover').naturalWidth>0 ? ({width:innerWidth,title:document.getElementById('article-title').textContent,paragraphs:document.querySelectorAll('#article-body p').length,translation:document.getElementById('article-translation').textContent}) : null");
            await Capture(Reader()!, Path.Combine(Path.GetDirectoryName(report)!, "native-article.png"));
            await Reader()!.ExecuteScriptAsync("document.getElementById('language').click()");
            var original = await Reader()!.ExecuteScriptAsync("document.getElementById('article-translation').textContent");
            if (!original.Contains("英文原文", StringComparison.Ordinal) && !JsonSerializer.Deserialize<string>(original)!.Contains("英文原文", StringComparison.Ordinal))
                throw new InvalidOperationException("Native article original-language toggle failed.");
        }
        catch (Exception error) { failure = error.ToString(); }
        await File.WriteAllTextAsync(report, JsonSerializer.Serialize(new { passed = failure is null, started, finished = DateTimeOffset.UtcNow, carousel, article, failure, gameCallsInvoked = false }, new JsonSerializerOptions { WriteIndented = true }));
        window?.Close(); Environment.Exit(failure is null ? 0 : 1);
    }
}
public sealed class NewsPreviewRuntime
{
    public string DataDirectory { get; } = Path.Combine(AppContext.BaseDirectory, "preview-data");
    public void Log(string category, string message) => System.Diagnostics.Debug.WriteLine(category + ": " + message);
}