Merge pull request #1248 from Takaranoao/main-baolan

给配置组增加执行shell的功能,可以调用外部程序实现复杂调度
This commit is contained in:
辉鸭蛋
2025-03-08 15:05:23 +08:00
committed by GitHub
5 changed files with 160 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ using System.Dynamic;
using System.IO;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.Shell;
namespace BetterGenshinImpact.Core.Script.Group;
@@ -112,6 +113,11 @@ public partial class ScriptGroupProject : ObservableObject
return new ScriptGroupProject(name, name, "KeyMouse");
}
public static ScriptGroupProject BuildShellProject(string command)
{
return new ScriptGroupProject(command, command, "Shell");
}
public static ScriptGroupProject BuildPathingProject(string name, string folder)
{
return new ScriptGroupProject(name, folder, "Pathing");
@@ -165,6 +171,15 @@ public partial class ScriptGroupProject : ObservableObject
}
await pathingTask.Pathing(task);
}
if (Type == "Shell")
{
var task = ShellExecutor.BuildFromShellName(Name);
await task.Execute();
}
else
{
throw new Exception("不支持的脚本类型");
}
}
partial void OnTypeChanged(string value)
@@ -189,7 +204,8 @@ public class ScriptGroupProjectExtensions
{
{ "Javascript", "JS脚本" },
{ "KeyMouse", "键鼠脚本" },
{ "Pathing", "路径追踪" }
{ "Pathing", "路径追踪" },
{ "Shell", "Shell" }
};
public static readonly Dictionary<string, string> StatusDescriptions = new()

View File

@@ -0,0 +1,121 @@
using System;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.Common;
using Microsoft.Extensions.Logging;
namespace BetterGenshinImpact.GameTask.Shell;
[Serializable]
public class ShellExecutor
{
private string command = string.Empty;
private int maxWaitSeconds = 60;
private bool noWindow = true;
private bool output = true;
public static readonly JsonSerializerOptions JsonOptions = new()
{
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
public async Task Execute(CancellationToken ct = default)
{
if (string.IsNullOrEmpty(command))
{
TaskControl.Logger.LogWarning("无法执行Shell: Shell为空");
return;
}
var cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.Arguments = "/k @echo off";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = noWindow;
cmd.StartInfo.UseShellExecute = false;
if (ct.IsCancellationRequested)
{
TaskControl.Logger.LogError("shell {Shell} 被取消", command);
}
TaskControl.Logger.LogInformation("执行shell:{Shell},超时时间为 {Wait} 秒", command, maxWaitSeconds);
var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(maxWaitSeconds));
var mixedToken = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutSignal.Token).Token;
cmd.Start();
var outputShell = "";
var outputText = "";
var cmdCanceled = false;
try
{
await cmd.StandardInput.WriteLineAsync(command.AsMemory(), mixedToken);
await cmd.StandardInput.FlushAsync(mixedToken);
cmd.StandardInput.Close();
await cmd.WaitForExitAsync(mixedToken);
if (output)
{
outputShell = await cmd.StandardOutput.ReadLineAsync(mixedToken) ?? "";
outputText = await cmd.StandardOutput.ReadToEndAsync(mixedToken);
}
}
catch (OperationCanceledException)
{
cmdCanceled = true;
}
if (!cmd.HasExited || cmdCanceled)
{
cmd.Kill();
if (ct.IsCancellationRequested)
{
TaskControl.Logger.LogError("shell {Shell} 被取消", command);
}
else if (timeoutSignal.IsCancellationRequested)
{
TaskControl.Logger.LogWarning("shell {Shell} 超时", command);
}
else
{
TaskControl.Logger.LogWarning("shell {Shell} 出现异常输出,可能未能成功执行。", command);
}
}
if (output)
{
TaskControl.Logger.LogInformation("shell {End} 运行结束,输出:{Output}", outputShell, outputText);
}
else
{
TaskControl.Logger.LogInformation("shell {End} 运行结束", command);
}
SystemControl.ActivateWindow();
}
public static ShellExecutor BuildFromShellName(string name)
{
var obj = new ShellExecutor
{
command = name
};
return obj;
}
public static ShellExecutor BuildFromJson(string json)
{
// 留给以后玩
var task = JsonSerializer.Deserialize<ShellExecutor>(json, JsonOptions) ??
throw new Exception("Failed to deserialize ShellExecutorTask");
return task;
}
}

View File

@@ -183,6 +183,13 @@ public partial class ScriptService : IScriptService
list.Add(newProject);
hasTimer = true;
}
else if (project.Type == "Shell")
{
var newProject = ScriptGroupProject.BuildShellProject(project.Name);
CopyProjectProperties(project, newProject);
list.Add(newProject);
hasTimer = true;
}
}
return list;
@@ -234,6 +241,10 @@ public partial class ScriptService : IScriptService
_logger.LogInformation("→ 开始执行路径追踪任务: {Name}", project.Name);
await project.Run();
}
else if (project.Type == "Shell"){
_logger.LogInformation("→ 开始执行shell: {Name}", project.Name);
await project.Run();
}
}
private async Task<List<string>> ReadCodeList(List<ScriptProject> list)

View File

@@ -206,6 +206,7 @@
<MenuItem Header="JS脚本" Command="{Binding AddJsScriptCommand}" />
<MenuItem Header="路径追踪任务" Command="{Binding AddPathingCommand}" />
<MenuItem Header="键鼠脚本" Command="{Binding AddKmScriptCommand}" />
<MenuItem Header="Shell" Command="{Binding AddShellCommand}" />
</ContextMenu>
</ui:DropDownButton.Flyout>
</ui:DropDownButton>
@@ -305,6 +306,7 @@
<MenuItem Command="{Binding AddJsScriptCommand}" Header="添加JS脚本" />
<MenuItem Command="{Binding AddPathingCommand}" Header="添加路径追踪任务" />
<MenuItem Command="{Binding AddKmScriptCommand}" Header="添加键鼠脚本" />
<MenuItem Command="{Binding AddShellCommand}" Header="添加Shell" />
<MenuItem Command="{Binding AddNextFlagCommand}" Header="下一次任务从此处执行"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
/>

View File

@@ -633,6 +633,15 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
}
}
[RelayCommand]
private void OnAddShell()
{
var str = PromptDialog.Prompt("执行shell是非常危险的请不要输入你不认识的东西。\n 可能会导致安全问题并破坏你的系统。","请输入需要执行的shell");
if (!string.IsNullOrEmpty(str))
{
SelectedScriptGroup?.AddProject(ScriptGroupProject.BuildShellProject(str));
}
}
[RelayCommand]
private void OnAddPathing()