mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
awake
This commit is contained in:
@@ -829,6 +829,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workspaces.ModuleServices",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerToys.ModuleContracts", "src\common\PowerToys.ModuleContracts\PowerToys.ModuleContracts.csproj", "{094E65D5-585E-4898-B465-97A47CD44380}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerToys.ModuleContracts", "src\common\PowerToys.ModuleContracts\PowerToys.ModuleContracts.csproj", "{094E65D5-585E-4898-B465-97A47CD44380}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Awake.ModuleServices", "src\modules\awake\Awake.ModuleServices\Awake.ModuleServices.csproj", "{2141FF78-5F51-ED6B-E11B-C7079CCA1456}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -5255,6 +5257,22 @@ Global
|
|||||||
{094E65D5-585E-4898-B465-97A47CD44380}.Release|x64.Build.0 = Release|x64
|
{094E65D5-585E-4898-B465-97A47CD44380}.Release|x64.Build.0 = Release|x64
|
||||||
{094E65D5-585E-4898-B465-97A47CD44380}.Release|x86.ActiveCfg = Release|x64
|
{094E65D5-585E-4898-B465-97A47CD44380}.Release|x86.ActiveCfg = Release|x64
|
||||||
{094E65D5-585E-4898-B465-97A47CD44380}.Release|x86.Build.0 = Release|x64
|
{094E65D5-585E-4898-B465-97A47CD44380}.Release|x86.Build.0 = Release|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|x86.ActiveCfg = Debug|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Debug|x86.Build.0 = Debug|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|x64.Build.0 = Release|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|x86.ActiveCfg = Release|x64
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456}.Release|x86.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -5588,6 +5606,7 @@ Global
|
|||||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||||
{D52AAF95-DE88-49EA-B28A-10E382BCD4AB} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
{D52AAF95-DE88-49EA-B28A-10E382BCD4AB} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||||
{094E65D5-585E-4898-B465-97A47CD44380} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
{094E65D5-585E-4898-B465-97A47CD44380} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||||
|
{2141FF78-5F51-ED6B-E11B-C7079CCA1456} = {127F38E0-40AA-4594-B955-5616BF206882}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||||
|
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||||
|
<Import Project="..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||||
|
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\common\PowerToys.ModuleContracts\PowerToys.ModuleContracts.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
79
src/modules/awake/Awake.ModuleServices/AwakeService.cs
Normal file
79
src/modules/awake/Awake.ModuleServices/AwakeService.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Common.UI;
|
||||||
|
using ManagedCommon;
|
||||||
|
using PowerToys.ModuleContracts;
|
||||||
|
|
||||||
|
namespace Awake.ModuleServices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides CLI-based Awake control for reuse across hosts.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AwakeService : ModuleServiceBase, IAwakeService
|
||||||
|
{
|
||||||
|
public static AwakeService Instance { get; } = new();
|
||||||
|
|
||||||
|
public override string Key => SettingsDeepLink.SettingsWindow.Awake.ToString();
|
||||||
|
|
||||||
|
protected override SettingsDeepLink.SettingsWindow SettingsWindow => SettingsDeepLink.SettingsWindow.Awake;
|
||||||
|
|
||||||
|
public override Task<OperationResult> LaunchAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// Default launch -> indefinite, honoring Awake's own settings for display behavior.
|
||||||
|
return SetIndefiniteAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<OperationResult> SetIndefiniteAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return InvokeCliAsync("-m indefinite");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<OperationResult> SetTimedAsync(int minutes, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (minutes <= 0)
|
||||||
|
{
|
||||||
|
return Task.FromResult(OperationResult.Fail("Minutes must be greater than zero."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvokeCliAsync($"-m timed -t {minutes}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<OperationResult> SetOffAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return InvokeCliAsync("-m passive");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<OperationResult> InvokeCliAsync(string arguments)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var basePath = PowerToysPathResolver.GetPowerToysInstallPath();
|
||||||
|
if (string.IsNullOrWhiteSpace(basePath))
|
||||||
|
{
|
||||||
|
return Task.FromResult(OperationResult.Fail("PowerToys install path not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var exePath = Path.Combine(basePath, "PowerToys.Awake.exe");
|
||||||
|
if (!File.Exists(exePath))
|
||||||
|
{
|
||||||
|
return Task.FromResult(OperationResult.Fail("Unable to locate PowerToys.Awake.exe."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var startInfo = new ProcessStartInfo(exePath, arguments)
|
||||||
|
{
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Process.Start(startInfo);
|
||||||
|
return Task.FromResult(OperationResult.Ok());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Task.FromResult(OperationResult.Fail($"Failed to invoke Awake: {ex.Message}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/modules/awake/Awake.ModuleServices/IAwakeService.cs
Normal file
16
src/modules/awake/Awake.ModuleServices/IAwakeService.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using PowerToys.ModuleContracts;
|
||||||
|
|
||||||
|
namespace Awake.ModuleServices;
|
||||||
|
|
||||||
|
public interface IAwakeService : IModuleService
|
||||||
|
{
|
||||||
|
Task<OperationResult> SetIndefiniteAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<OperationResult> SetTimedAsync(int minutes, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<OperationResult> SetOffAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
using Awake.ModuleServices;
|
||||||
|
|
||||||
|
namespace PowerToysExtension.Commands;
|
||||||
|
|
||||||
|
internal sealed partial class StartAwakeCommand : InvokableCommand
|
||||||
|
{
|
||||||
|
private readonly Func<Task<OperationResult>> _action;
|
||||||
|
private readonly string? _successToast;
|
||||||
|
|
||||||
|
internal StartAwakeCommand(string title, Func<Task<OperationResult>> action, string? successToast = null)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(action);
|
||||||
|
ArgumentException.ThrowIfNullOrWhiteSpace(title);
|
||||||
|
|
||||||
|
_action = action;
|
||||||
|
_successToast = successToast;
|
||||||
|
Name = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandResult Invoke()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = _action().GetAwaiter().GetResult();
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
return CommandResult.ShowToast(result.Error ?? "Failed to start Awake.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(_successToast) ? CommandResult.Hide() : CommandResult.ShowToast(_successToast);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return CommandResult.ShowToast($"Launching Awake failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
using Awake.ModuleServices;
|
||||||
|
|
||||||
|
namespace PowerToysExtension.Commands;
|
||||||
|
|
||||||
|
internal sealed partial class StopAwakeCommand : InvokableCommand
|
||||||
|
{
|
||||||
|
internal StopAwakeCommand()
|
||||||
|
{
|
||||||
|
Name = "Set Awake to Off";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandResult Invoke()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = AwakeService.Instance.SetOffAsync().GetAwaiter().GetResult();
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
return CommandResult.ShowToast("Awake switched to Off.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResult.ShowToast(result.Error ?? "Awake does not appear to be running.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return CommandResult.ShowToast($"Failed to switch Awake off: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
|
||||||
using PowerToysExtension.Helpers;
|
|
||||||
|
|
||||||
namespace PowerToysExtension.Commands;
|
|
||||||
|
|
||||||
internal sealed partial class StartAwakeCommand : InvokableCommand
|
|
||||||
{
|
|
||||||
private readonly Func<string> _argumentsProvider;
|
|
||||||
private readonly string? _successToast;
|
|
||||||
|
|
||||||
internal StartAwakeCommand(string title, Func<string> argumentsProvider, string? successToast = null)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(argumentsProvider);
|
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(title);
|
|
||||||
|
|
||||||
_argumentsProvider = argumentsProvider;
|
|
||||||
_successToast = successToast;
|
|
||||||
Name = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override CommandResult Invoke()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var executablePath = PowerToysPathResolver.TryResolveExecutable("PowerToys.Awake.exe");
|
|
||||||
if (string.IsNullOrEmpty(executablePath))
|
|
||||||
{
|
|
||||||
return CommandResult.ShowToast("Unable to locate PowerToys.Awake.exe.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var arguments = _argumentsProvider()?.Trim() ?? string.Empty;
|
|
||||||
|
|
||||||
var startInfo = new ProcessStartInfo(executablePath)
|
|
||||||
{
|
|
||||||
UseShellExecute = string.IsNullOrWhiteSpace(arguments),
|
|
||||||
CreateNoWindow = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(arguments))
|
|
||||||
{
|
|
||||||
startInfo.Arguments = arguments;
|
|
||||||
startInfo.UseShellExecute = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process.Start(startInfo);
|
|
||||||
return string.IsNullOrWhiteSpace(_successToast) ? CommandResult.Hide() : CommandResult.ShowToast(_successToast);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return CommandResult.ShowToast($"Launching Awake failed: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
|
||||||
|
|
||||||
namespace PowerToysExtension.Commands;
|
|
||||||
|
|
||||||
internal sealed partial class StopAwakeCommand : InvokableCommand
|
|
||||||
{
|
|
||||||
private const string AwakeWindowClass = "Awake.MessageWindow";
|
|
||||||
private const uint WmCommand = 0x0111;
|
|
||||||
private const int PassiveCommand = 0x0400 + 0x3;
|
|
||||||
|
|
||||||
internal StopAwakeCommand()
|
|
||||||
{
|
|
||||||
Name = "Set Awake to Off";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override CommandResult Invoke()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (TrySendPassiveCommand())
|
|
||||||
{
|
|
||||||
return CommandResult.ShowToast("Awake switched to Off.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandResult.ShowToast("Awake does not appear to be running.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return CommandResult.ShowToast($"Failed to switch Awake off: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TrySendPassiveCommand()
|
|
||||||
{
|
|
||||||
var handle = IntPtr.Zero;
|
|
||||||
var sent = false;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
handle = FindWindowEx(IntPtr.Zero, handle, AwakeWindowClass, null);
|
|
||||||
if (handle == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PostMessage(handle, WmCommand, new IntPtr(PassiveCommand), IntPtr.Zero))
|
|
||||||
{
|
|
||||||
sent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
||||||
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string? className, string? windowTitle);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
|
||||||
private static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ using Common.UI;
|
|||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
using PowerToysExtension.Commands;
|
using PowerToysExtension.Commands;
|
||||||
using PowerToysExtension.Helpers;
|
using PowerToysExtension.Helpers;
|
||||||
|
using Awake.ModuleServices;
|
||||||
|
|
||||||
namespace PowerToysExtension.Modules;
|
namespace PowerToysExtension.Modules;
|
||||||
|
|
||||||
@@ -19,9 +20,9 @@ internal sealed class AwakeModuleCommandProvider : ModuleCommandProvider
|
|||||||
|
|
||||||
var more = new List<CommandContextItem>
|
var more = new List<CommandContextItem>
|
||||||
{
|
{
|
||||||
new CommandContextItem(new StartAwakeCommand("Awake: Keep awake indefinitely", () => "-m indefinite", "Awake set to indefinite")),
|
new CommandContextItem(new StartAwakeCommand("Awake: Keep awake indefinitely", () => AwakeService.Instance.SetIndefiniteAsync(), "Awake set to indefinite")),
|
||||||
new CommandContextItem(new StartAwakeCommand("Awake: Keep awake for 30 minutes", () => "-m timed -t 30", "Awake set for 30 minutes")),
|
new CommandContextItem(new StartAwakeCommand("Awake: Keep awake for 30 minutes", () => AwakeService.Instance.SetTimedAsync(30), "Awake set for 30 minutes")),
|
||||||
new CommandContextItem(new StartAwakeCommand("Awake: Keep awake for 2 hours", () => "-m timed -t 120", "Awake set for 2 hours")),
|
new CommandContextItem(new StartAwakeCommand("Awake: Keep awake for 2 hours", () => AwakeService.Instance.SetTimedAsync(120), "Awake set for 2 hours")),
|
||||||
new CommandContextItem(new StopAwakeCommand()),
|
new CommandContextItem(new StopAwakeCommand()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user