mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-31 10:47:31 +08:00
Merge pull request #1248 from Takaranoao/main-baolan
给配置组增加执行shell的功能,可以调用外部程序实现复杂调度
This commit is contained in:
@@ -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()
|
||||
|
||||
121
BetterGenshinImpact/GameTask/Shell/ShellExecutor.cs
Normal file
121
BetterGenshinImpact/GameTask/Shell/ShellExecutor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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}"
|
||||
/>
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user