Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f9ac19c
创建 build
ywydog Mar 10, 2026
4e1198e
创建 build.yml
ywydog Mar 10, 2026
b7cba6a
更新 build.yml
ywydog Mar 11, 2026
a9e22a7
更新 build.yml
ywydog Mar 11, 2026
01ae509
Add build script for DutyIsland project
ywydog Mar 11, 2026
aa5426d
Create combine-artifacts.ps1
ywydog Mar 11, 2026
77bd56f
Add script to generate release notes with MD5
ywydog Mar 11, 2026
e683277
创建 b.yml
ywydog Mar 11, 2026
cf127c9
更新 b.yml
ywydog Mar 11, 2026
a7057b7
更新 b.yml
ywydog Mar 11, 2026
86c6222
更新 b.yml
ywydog Mar 11, 2026
f43e5a1
Merge branch 'Programmer-MrWang:main' into main
ywydog Mar 11, 2026
42b8d3a
Merge branch 'Programmer-MrWang:main' into main
ywydog Mar 12, 2026
782ab32
Merge branch 'Programmer-MrWang:main' into main
ywydog Mar 15, 2026
dd3c4f7
Merge branch 'Programmer-MrWang:main' into main
ywydog Mar 29, 2026
d2f6441
更新 b.yml
ywydog Apr 22, 2026
71e2b85
Merge branch 'Programmer-MrWang:main' into main
ywydog Apr 27, 2026
5e6d46c
Merge branch 'Programmer-MrWang:main' into main
ywydog May 8, 2026
b6db48e
删除 build
ywydog May 25, 2026
d0771ca
删除 b.yml
ywydog May 25, 2026
f9e0ed8
增加行动,开关自动化
ywydog May 26, 2026
412fb4e
更新 dotnet-build.yml,增加手动触发方式
ywydog May 27, 2026
39a8093
优化日志方式
ywydog May 28, 2026
b354418
删除无用文件
ywydog May 28, 2026
3423c55
删除无用文件
ywydog May 28, 2026
d49fdd0
Update toggle text for automation status control
Programmer-MrWang Jun 18, 2026
583cc03
Bump version from 2.5.0.106 to 2.5.0.107
Programmer-MrWang Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/dotnet-build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Build Project

on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
Expand Down
177 changes: 177 additions & 0 deletions Actions/ToggleWorkflowAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
using ClassIsland.Core.Abstractions.Automation;
using ClassIsland.Core.Abstractions.Services;
using ClassIsland.Core.Attributes;
using ClassIsland.Shared;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using SystemTools.Settings;
using Workflow = ClassIsland.Core.Models.Automation.Workflow;

namespace SystemTools.Actions;

[ActionInfo("SystemTools.ToggleWorkflow", "开关自动化", "\uE9A8", false)]
public class ToggleWorkflowAction(ILogger<ToggleWorkflowAction> logger) : ActionBase<ToggleWorkflowSettings>
{
private readonly ILogger<ToggleWorkflowAction> _logger = logger;

private static readonly ConcurrentDictionary<Guid, OriginalStateSnapshot> PreviousSnapshots = new();

protected override async Task OnInvoke()
{
try
{
_logger.LogDebug("ToggleWorkflowAction OnInvoke 开始");

if (Settings == null)
{
_logger.LogWarning("设置为空,无法执行");
return;
}

var automationService = IAppHost.TryGetService<IAutomationService>();
if (automationService?.Workflows == null)
{
_logger.LogError("无法获取自动化服务");
throw new InvalidOperationException("无法获取自动化服务,请确保 ClassIsland 已正确加载。");
}

var targetWorkflow = FindTargetWorkflow(automationService);
if (targetWorkflow == null)
{
_logger.LogWarning("未找到目标自动化: Index={Index}, Name={Name}",
Settings.TargetWorkflowIndex, Settings.TargetWorkflowName);
throw new InvalidOperationException($"未找到指定的自动化方案: {Settings.TargetWorkflowName}");
}

var actionSet = targetWorkflow.ActionSet;
var currentStatus = actionSet.IsEnabled;

if (IsRevertable)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the revert opt-out

If the user clears “触发器恢复时自动还原原状态”, Settings.RevertToOriginal is still ignored here: any revertable trigger records a snapshot and OnRevert will restore it anyway. In revertable trigger scenarios this makes the new checkbox ineffective and prevents users from keeping the toggled workflow state after the trigger ends.

Useful? React with 👍 / 👎.

{
PreviousSnapshots[ActionSet.Guid] = new OriginalStateSnapshot(
actionSet.Name,
automationService.Workflows.IndexOf(targetWorkflow),
currentStatus);
_logger.LogDebug("已保存自动化 \"{WorkflowName}\" 的原始状态快照", actionSet.Name);
}

var (targetStatus, operationDescription) = Settings.EnableMode switch
{
true => (true, "启用"),
false => (false, "禁用"),
_ => (!currentStatus, !currentStatus ? "启用" : "禁用")
};

if (currentStatus == targetStatus)
{
_logger.LogInformation("自动化 \"{WorkflowName}\" 已经是{Operation}状态,无需操作",
actionSet.Name, operationDescription);
}
else
{
_logger.LogInformation("正在{Operation}自动化 \"{WorkflowName}\" (原始: {OriginalStatus} -> 目标: {TargetStatus})",
operationDescription, actionSet.Name, currentStatus, targetStatus);

actionSet.IsEnabled = targetStatus;
automationService.SaveConfig($"通过行动{operationDescription}自动化 \"{actionSet.Name}\"");

_logger.LogInformation("自动化 \"{WorkflowName}\" 已成功{Operation}",
actionSet.Name, operationDescription);
}

await base.OnInvoke();
_logger.LogDebug("ToggleWorkflowAction OnInvoke 完成");
}
catch (Exception ex)
{
_logger.LogError(ex, "ToggleWorkflowAction 执行失败");
throw;
}
}

protected override async Task OnRevert()
{
try
{
await base.OnRevert();

if (!PreviousSnapshots.TryRemove(ActionSet.Guid, out var snapshot))
{
_logger.LogWarning("未找到触发前状态,跳过恢复。ActionSet={ActionSetGuid}", ActionSet.Guid);
return;
}

var automationService = IAppHost.TryGetService<IAutomationService>();
if (automationService?.Workflows == null)
{
_logger.LogError("无法获取自动化服务,恢复失败");
return;
}

var targetWorkflow = FindTargetWorkflow(automationService);
if (targetWorkflow == null)
{
_logger.LogWarning("恢复时未找到目标自动化: {Name}", snapshot.WorkflowName);
return;
}

var actionSet = targetWorkflow.ActionSet;
actionSet.IsEnabled = snapshot.IsEnabled;
automationService.SaveConfig($"通过行动恢复自动化 \"{actionSet.Name}\" 到原始状态({snapshot.IsEnabled})");

_logger.LogInformation("已恢复自动化 \"{WorkflowName}\" 为触发前状态。ActionSet={ActionSetGuid}",
actionSet.Name, ActionSet.Guid);
}
catch (Exception ex)
{
_logger.LogError(ex, "ToggleWorkflowAction 恢复失败");
throw;
}
}

private Workflow? FindTargetWorkflow(IAutomationService automationService)
{
Workflow? targetWorkflow = null;

// 1. 尝试通过索引查找
if (Settings.TargetWorkflowIndex >= 0 && Settings.TargetWorkflowIndex < automationService.Workflows.Count)
{
targetWorkflow = automationService.Workflows[Settings.TargetWorkflowIndex];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate saved index before using it

When the workflow list has been reordered or a new workflow is inserted before the saved target, this path accepts any in-range TargetWorkflowIndex and never checks it against TargetWorkflowName, so the action can toggle a different automation than the one the user selected. Falling back to the saved name when the indexed workflow's name does not match would avoid silently changing the wrong workflow.

Useful? React with 👍 / 👎.

_logger.LogDebug("通过索引 {Index} 找到自动化: {Name}",
Settings.TargetWorkflowIndex, targetWorkflow.ActionSet.Name);
}

// 2. 如果索引找不到,尝试通过名称查找
if (targetWorkflow == null && !string.IsNullOrEmpty(Settings.TargetWorkflowName))
{
targetWorkflow = automationService.Workflows
.FirstOrDefault(w => w.ActionSet.Name == Settings.TargetWorkflowName);

if (targetWorkflow != null)
{
_logger.LogDebug("通过名称 \"{Name}\" 找到自动化", Settings.TargetWorkflowName);
Settings.TargetWorkflowIndex = automationService.Workflows.IndexOf(targetWorkflow);
}
else
{
_logger.LogWarning("通过名称 \"{Name}\" 未找到自动化", Settings.TargetWorkflowName);
}
}

if (targetWorkflow == null)
{
_logger.LogWarning("未能找到任何匹配的目标自动化 (Index={Index}, Name={Name})",
Settings.TargetWorkflowIndex, Settings.TargetWorkflowName);
}

return targetWorkflow;
}

private readonly record struct OriginalStateSnapshot(
string WorkflowName,
int WorkflowIndex,
bool IsEnabled);
}
Loading
Loading