using BetterGenshinImpact.Core.Config; using BetterGenshinImpact.Core.Script.Group; using BetterGenshinImpact.Core.Script.Project; using BetterGenshinImpact.View.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Controls; using BetterGenshinImpact.Core.Recorder; using BetterGenshinImpact.Core.Script; using BetterGenshinImpact.GameTask; using BetterGenshinImpact.GameTask.Model.Enum; using Wpf.Ui; using Wpf.Ui.Controls; using System.Text.RegularExpressions; namespace BetterGenshinImpact.ViewModel.Pages; public partial class ScriptControlViewModel : ObservableObject, INavigationAware, IViewModel { private readonly ISnackbarService _snackbarService; private readonly ILogger _logger = App.GetLogger(); private readonly HomePageViewModel _homePageViewModel; /// /// 脚本组配置 /// [ObservableProperty] private ObservableCollection _scriptGroups = []; /// /// 当前选中的脚本组 /// [ObservableProperty] private ScriptGroup? _selectedScriptGroup; public readonly string ScriptGroupPath = Global.Absolute(@"User\ScriptGroup"); public void OnNavigatedFrom() { } public void OnNavigatedTo() { ReadScriptGroup(); } public ScriptControlViewModel(ISnackbarService snackbarService, HomePageViewModel homePageViewModel) { _snackbarService = snackbarService; _homePageViewModel = homePageViewModel; ScriptGroups.CollectionChanged += ScriptGroupsCollectionChanged; } [RelayCommand] private void OnAddScriptGroup() { var str = PromptDialog.Prompt("请输入脚本组名称", "新增脚本组"); if (!string.IsNullOrEmpty(str)) { // 检查是否已存在 if (ScriptGroups.Any(x => x.Name == str)) { _snackbarService.Show( "脚本组已存在", $"脚本组 {str} 已经存在,请勿重复添加", ControlAppearance.Caution, null, TimeSpan.FromSeconds(2) ); } else { ScriptGroups.Add(new ScriptGroup { Name = str }); } } } [RelayCommand] public void OnDeleteScriptGroup(ScriptGroup? item) { if (item == null) { return; } try { ScriptGroups.Remove(item); File.Delete(Path.Combine(ScriptGroupPath, $"{item.Name}.json")); _snackbarService.Show( "脚本组删除成功", $"脚本组 {item.Name} 已经被删除", ControlAppearance.Success, null, TimeSpan.FromSeconds(2) ); } catch (Exception e) { _logger.LogDebug(e, "删除脚本组配置时失败"); _snackbarService.Show( "删除脚本组配置失败", $"脚本组 {item.Name} 删除失败!", ControlAppearance.Danger, null, TimeSpan.FromSeconds(3) ); } } [RelayCommand] private void OnAddScript() { var list = LoadAllScriptProjects(); var combobox = new ComboBox(); foreach (var scriptProject in list) { combobox.Items.Add(scriptProject.FolderName + " - " + scriptProject.Manifest.Name); } var str = PromptDialog.Prompt("请选择需要添加的脚本", "请选择需要添加的脚本", combobox); if (!string.IsNullOrEmpty(str)) { var folderName = str.Split(" - ")[0]; SelectedScriptGroup?.Projects.Add(new ScriptGroupProject(new ScriptProject(folderName))); } } private List LoadAllScriptProjects() { var path = Global.ScriptPath(); // 获取所有脚本项目 var projects = Directory.GetDirectories(path) .Select(x => new ScriptProject(Path.GetFileName(x))) .ToList(); return projects; } [RelayCommand] public void OnDeleteScript(ScriptGroupProject? item) { if (item == null) { return; } SelectedScriptGroup?.Projects.Remove(item); _snackbarService.Show( "脚本配置移除成功", $"{item.Name} 的关联配置已经移除", ControlAppearance.Success, null, TimeSpan.FromSeconds(2) ); } private void ScriptGroupsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (ScriptGroup newItem in e.NewItems) { newItem.Projects.CollectionChanged += ScriptProjectsCollectionChanged; } } if (e.OldItems != null) { foreach (ScriptGroup oldItem in e.OldItems) { oldItem.Projects.CollectionChanged -= ScriptProjectsCollectionChanged; } } // 补充排序字段 var i = 1; foreach (var group in ScriptGroups) { group.Index = i++; } // 保存脚本组配置 foreach (var group in ScriptGroups) { WriteScriptGroup(group); } } private void ScriptProjectsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { // 补充排序字段 if (SelectedScriptGroup is { Projects.Count: > 0 }) { var i = 1; foreach (var project in SelectedScriptGroup.Projects) { project.Index = i++; } } // 保存脚本组配置 if (SelectedScriptGroup != null) { WriteScriptGroup(SelectedScriptGroup); } } private void WriteScriptGroup(ScriptGroup scriptGroup) { try { if (!Directory.Exists(ScriptGroupPath)) { Directory.CreateDirectory(ScriptGroupPath); } var file = Path.Combine(ScriptGroupPath, $"{scriptGroup.Name}.json"); File.WriteAllText(file, scriptGroup.ToJson()); } catch (Exception e) { _logger.LogDebug(e, "保存脚本组配置时失败"); _snackbarService.Show( "保存脚本组配置失败", $"{scriptGroup.Name} 保存失败!", ControlAppearance.Danger, null, TimeSpan.FromSeconds(3) ); } } private void ReadScriptGroup() { try { if (!Directory.Exists(ScriptGroupPath)) { Directory.CreateDirectory(ScriptGroupPath); } ScriptGroups.Clear(); var files = Directory.GetFiles(ScriptGroupPath, "*.json"); List groups = new(); foreach (var file in files) { try { var json = File.ReadAllText(file); var group = ScriptGroup.FromJson(json); groups.Add(group); } catch (Exception e) { _logger.LogDebug(e, "读取单个脚本组配置时失败"); _snackbarService.Show( "读取脚本组配置失败", "读取脚本组配置失败:" + e.Message, ControlAppearance.Danger, null, TimeSpan.FromSeconds(3) ); } } // 按index排序 groups.Sort((a, b) => a.Index.CompareTo(b.Index)); foreach (var group in groups) { ScriptGroups.Add(group); } } catch (Exception e) { _logger.LogDebug(e, "读取脚本组配置时失败"); _snackbarService.Show( "读取脚本组配置失败", "读取脚本组配置失败!", ControlAppearance.Danger, null, TimeSpan.FromSeconds(3) ); } } [RelayCommand] public void OnGoToScriptGroupUrl() { Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/") { UseShellExecute = true }); } [RelayCommand] public void OnGoToScriptProjectUrl() { Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/") { UseShellExecute = true }); } [RelayCommand] public void OnImportScriptGroup(string scriptGroupExample) { ScriptGroup group = new(); if ("AutoCrystalflyExampleGroup" == scriptGroupExample) { group.Name = "晶蝶示例组"; group.Projects.Add(new ScriptGroupProject(new ScriptProject("AutoCrystalfly"))); } if (ScriptGroups.Any(x => x.Name == group.Name)) { _snackbarService.Show( "脚本组已存在", $"脚本组 {group.Name} 已经存在,请勿重复添加", ControlAppearance.Caution, null, TimeSpan.FromSeconds(2) ); return; } ScriptGroups.Add(group); } [RelayCommand] public async Task OnStartScriptGroupAsync() { if (SelectedScriptGroup == null) { _snackbarService.Show( "未选择脚本组", "请先选择一个脚本组", ControlAppearance.Caution, null, TimeSpan.FromSeconds(2) ); return; } // 没启动时候,启动截图器 await _homePageViewModel.OnStartTriggerAsync(); // 重新加载脚本项目 var projects = SelectedScriptGroup.Projects.Select(project => new ScriptProject(project.FolderName)).ToList(); var codeList = await ReadCodeList(projects); var hasTimer = HasTimerOperation(codeList); if (hasTimer) { _logger.LogInformation("脚本组 {Name} 包含实时任务操作调用", SelectedScriptGroup.Name); } _logger.LogInformation("脚本组 {Name} 加载完成,共{Cnt}个脚本,开始执行", SelectedScriptGroup.Name, projects.Count); // 循环执行所有脚本 var timerOperation = hasTimer ? DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty : DispatcherTimerOperationEnum.UseSelfCaptureImage; await new TaskRunner(timerOperation) .RunAsync(async () => { foreach (var project in projects) { try { if (hasTimer) { TaskTriggerDispatcher.Instance().ClearTriggers(); } _logger.LogInformation("------------------------------"); _logger.LogInformation("→ 开始执行脚本: {Name}", project.Manifest.Name); await project.ExecuteAsync(); await Task.Delay(1000); } catch (Exception e) { _logger.LogDebug(e, "执行脚本时发生异常"); _logger.LogError("执行脚本时发生异常: {Msg}", e.Message); } finally { _logger.LogInformation("→ 脚本执行结束: {Name}", project.Manifest.Name); _logger.LogInformation("------------------------------"); } } }); _logger.LogInformation("脚本组 {Name} 执行结束", SelectedScriptGroup.Name); } private async Task> ReadCodeList(List list) { var codeList = new List(); foreach (var project in list) { var code = await project.LoadCode(); codeList.Add(code); } return codeList; } private bool HasTimerOperation(IEnumerable codeList) { var regex = new Regex(@"^(?!\s*\/\/)\s*dispatcher\.\s*addTimer", RegexOptions.Multiline); return codeList.Any(code => regex.IsMatch(code)); } }