187 lines
5.6 KiB
C#
187 lines
5.6 KiB
C#
namespace Zcbot.WindowsNode;
|
|
|
|
internal sealed class TrayApplicationContext : ApplicationContext
|
|
{
|
|
private readonly NodeConfigStore store;
|
|
private readonly ConfigurationForm form;
|
|
private readonly NotifyIcon tray;
|
|
private readonly ToolStripMenuItem statusItem;
|
|
private CancellationTokenSource? connectionStop;
|
|
private Task? connectionTask;
|
|
private NodeConfig? config;
|
|
private NodeStatus current = NodeStatus.Create(NodeState.NotRegistered, "尚未注册");
|
|
|
|
internal TrayApplicationContext(NodeConfigStore store)
|
|
{
|
|
this.store = store;
|
|
form = new ConfigurationForm();
|
|
form.RegisterRequested += RegisterAsync;
|
|
form.ReconnectRequested += RestartConnection;
|
|
form.ResetIdentityRequested += ResetIdentity;
|
|
|
|
statusItem = new ToolStripMenuItem("尚未注册") { Enabled = false };
|
|
var menu = new ContextMenuStrip();
|
|
menu.Items.Add(statusItem);
|
|
menu.Items.Add(new ToolStripSeparator());
|
|
menu.Items.Add("打开配置", null, (_, _) => ShowConfiguration());
|
|
menu.Items.Add("立即重连", null, (_, _) => RestartConnection());
|
|
menu.Items.Add(new ToolStripSeparator());
|
|
menu.Items.Add("退出", null, (_, _) => ExitNode());
|
|
|
|
tray = new NotifyIcon
|
|
{
|
|
ContextMenuStrip = menu,
|
|
Icon = TrayIconFactory.Create(NodeState.NotRegistered),
|
|
Text = "zcbot Windows Node - 尚未注册",
|
|
Visible = true,
|
|
};
|
|
tray.DoubleClick += (_, _) => ShowConfiguration();
|
|
|
|
if (store.Exists)
|
|
{
|
|
try
|
|
{
|
|
config = store.Load();
|
|
StartConnection();
|
|
}
|
|
catch (NodeConfigurationException exception)
|
|
{
|
|
UpdateStatus(NodeStatus.Create(NodeState.AuthenticationRequired, exception.Message));
|
|
ShowConfiguration();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateStatus(current);
|
|
ShowConfiguration();
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
connectionStop?.Cancel();
|
|
tray.Visible = false;
|
|
tray.Dispose();
|
|
form.Dispose();
|
|
connectionStop?.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
private async Task RegisterAsync(EnrollOptions options)
|
|
{
|
|
UpdateStatus(NodeStatus.Create(NodeState.Connecting, "正在注册节点…"));
|
|
try
|
|
{
|
|
await EnrollmentClient.EnrollAsync(options, store, CancellationToken.None);
|
|
config = store.Load();
|
|
UpdateStatus(NodeStatus.Create(NodeState.Connecting, "注册成功,正在连接…"));
|
|
StartConnection();
|
|
}
|
|
catch
|
|
{
|
|
UpdateStatus(NodeStatus.Create(NodeState.NotRegistered, "注册失败,请检查配置"));
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private void StartConnection()
|
|
{
|
|
if (config is null || connectionTask is { IsCompleted: false })
|
|
{
|
|
return;
|
|
}
|
|
connectionStop?.Dispose();
|
|
connectionStop = new CancellationTokenSource();
|
|
connectionTask = RunConnectionAsync(config, connectionStop.Token);
|
|
}
|
|
|
|
private async Task RunConnectionAsync(NodeConfig nodeConfig, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
await new NodeConnectionLoop(nodeConfig, status => PostStatus(status))
|
|
.RunAsync(cancellationToken);
|
|
}
|
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
|
{
|
|
PostStatus(NodeStatus.Create(NodeState.Stopped, "连接已停止"));
|
|
}
|
|
catch (NodeConfigurationException exception)
|
|
{
|
|
PostStatus(NodeStatus.Create(NodeState.AuthenticationRequired, exception.Message));
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
PostStatus(NodeStatus.Create(NodeState.Offline, $"节点异常:{exception.Message}"));
|
|
}
|
|
}
|
|
|
|
private void RestartConnection()
|
|
{
|
|
if (config is null)
|
|
{
|
|
ShowConfiguration();
|
|
return;
|
|
}
|
|
connectionStop?.Cancel();
|
|
_ = RestartAfterStopAsync();
|
|
}
|
|
|
|
private void ResetIdentity()
|
|
{
|
|
connectionStop?.Cancel();
|
|
store.DeleteLocalIdentity();
|
|
config = null;
|
|
UpdateStatus(NodeStatus.Create(NodeState.NotRegistered, "本机身份已清除,请使用新注册码注册"));
|
|
}
|
|
|
|
private async Task RestartAfterStopAsync()
|
|
{
|
|
if (connectionTask is not null)
|
|
{
|
|
await connectionTask;
|
|
}
|
|
StartConnection();
|
|
}
|
|
|
|
private void PostStatus(NodeStatus status)
|
|
{
|
|
if (form.IsHandleCreated)
|
|
{
|
|
form.BeginInvoke(() => UpdateStatus(status));
|
|
}
|
|
}
|
|
|
|
private void UpdateStatus(NodeStatus status)
|
|
{
|
|
current = status;
|
|
form.ApplyStatus(status, config);
|
|
statusItem.Text = status.Message;
|
|
tray.Text = Limit($"zcbot Windows Node - {status.Message}", 63);
|
|
var oldIcon = tray.Icon;
|
|
tray.Icon = TrayIconFactory.Create(status.State);
|
|
oldIcon?.Dispose();
|
|
}
|
|
|
|
private void ShowConfiguration()
|
|
{
|
|
form.ApplyStatus(current, config);
|
|
form.Show();
|
|
form.WindowState = FormWindowState.Normal;
|
|
form.Activate();
|
|
}
|
|
|
|
private void ExitNode()
|
|
{
|
|
connectionStop?.Cancel();
|
|
tray.Visible = false;
|
|
ExitThread();
|
|
}
|
|
|
|
private static string Limit(string value, int length) =>
|
|
value.Length <= length ? value : value[..length];
|
|
}
|