Fixed logs not being written when the bot view is not active
All checks were successful
EllieBotDevs/EllieHub/pipeline/head This commit looks good
All checks were successful
EllieBotDevs/EllieHub/pipeline/head This commit looks good
This commit is contained in:
parent
2a8cd606c5
commit
6fc3eba498
6 changed files with 57 additions and 42 deletions
|
@ -15,35 +15,35 @@ public static partial class AppStatics
|
|||
/// Defines the default location where the updater configuration and bot instances are stored.
|
||||
/// </summary>
|
||||
#if DEBUG
|
||||
public static string AppDefaultConfigDirectoryUri { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "EllieHubDebug");
|
||||
public static string AppDefaultConfigDirectoryUri { get; } = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "EllieHubDebug");
|
||||
#else
|
||||
public static string AppDefaultConfigDirectoryUri { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "EllieHub");
|
||||
public static string AppDefaultConfigDirectoryUri { get; } = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "EllieHub");
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Defines the default location where the bot instances are stored.
|
||||
/// </summary>
|
||||
public static string AppDefaultBotDirectoryUri { get; } = Path.Combine(AppDefaultConfigDirectoryUri, "Bots");
|
||||
public static string AppDefaultBotDirectoryUri { get; } = Path.Join(AppDefaultConfigDirectoryUri, "Bots");
|
||||
|
||||
/// <summary>
|
||||
/// Defines the default location where the backups of bot instances are stored.
|
||||
/// </summary>
|
||||
public static string AppDefaultBotBackupDirectoryUri { get; } = Path.Combine(AppDefaultConfigDirectoryUri, "Backups");
|
||||
public static string AppDefaultBotBackupDirectoryUri { get; } = Path.Join(AppDefaultConfigDirectoryUri, "Backups");
|
||||
|
||||
/// <summary>
|
||||
/// Defines the default location where the logs of bot instances are stored.
|
||||
/// </summary>
|
||||
public static string AppDefaultLogDirectoryUri { get; } = Path.Combine(AppDefaultConfigDirectoryUri, "Logs");
|
||||
public static string AppDefaultLogDirectoryUri { get; } = Path.Join(AppDefaultConfigDirectoryUri, "Logs");
|
||||
|
||||
/// <summary>
|
||||
/// Defines the location of the application's configuration file.
|
||||
/// </summary>
|
||||
public static string AppConfigUri { get; } = Path.Combine(AppDefaultConfigDirectoryUri, "config.json");
|
||||
public static string AppConfigUri { get; } = Path.Join(AppDefaultConfigDirectoryUri, "config.json");
|
||||
|
||||
/// <summary>
|
||||
/// Defines the location of the application's dependencies.
|
||||
/// </summary>
|
||||
public static string AppDepsUri { get; } = Path.Combine(AppDefaultConfigDirectoryUri, "Dependencies");
|
||||
public static string AppDepsUri { get; } = Path.Join(AppDefaultConfigDirectoryUri, "Dependencies");
|
||||
|
||||
/// <summary>
|
||||
/// Defines a transparent color brush.
|
||||
|
@ -56,11 +56,11 @@ public static partial class AppStatics
|
|||
public static FilePickerOpenOptions ImageFilePickerOptions { get; } = new()
|
||||
{
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new FilePickerFileType[]
|
||||
{
|
||||
new("Image") { Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp" } },
|
||||
new("All") { Patterns = new[] { "*.*" } }
|
||||
}
|
||||
FileTypeFilter =
|
||||
[
|
||||
new("Image") { Patterns = ["*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp"]},
|
||||
new("All") { Patterns = ["*.*"]}
|
||||
]
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -11,29 +11,29 @@ namespace EllieHub.Common;
|
|||
internal static class Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads an image embeded with this application.
|
||||
/// Loads an image embedded with this application.
|
||||
/// </summary>
|
||||
/// <param name="uri">An uri that starts with "avares://"</param>
|
||||
/// <remarks>Valid uris must start with "avares://".</remarks>
|
||||
/// <returns>The embeded image or the default bot avatar placeholder.</returns>
|
||||
/// <exception cref="FileNotFoundException">Occurs when the embeded resource does not exist.</exception>
|
||||
public static SKBitmap LoadEmbededImage(string? uri = default)
|
||||
/// <returns>The embedded image or the default bot avatar placeholder.</returns>
|
||||
/// <exception cref="FileNotFoundException">Occurs when the embedded resource does not exist.</exception>
|
||||
public static SKBitmap LoadEmbeddedImage(string? uri = default)
|
||||
{
|
||||
return (string.IsNullOrWhiteSpace(uri) || !uri.StartsWith("avares://", StringComparison.Ordinal))
|
||||
? SKBitmap.Decode(AssetLoader.Open(new Uri(AppConstants.BotAvatarUri)))
|
||||
: SKBitmap.Decode(AssetLoader.Open(new Uri(uri)));
|
||||
? SKBitmap.Decode(AssetLoader.Open(new(AppConstants.BotAvatarUri)))
|
||||
: SKBitmap.Decode(AssetLoader.Open(new(uri)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the image at the specified location or the bot avatar placeholder if it was not found.
|
||||
/// </summary>
|
||||
/// <param name="uri">The absolute path to the image file or <see langword="null"/> to get the avatar placeholder.</param>
|
||||
/// <remarks>This fallsback to <see cref="LoadEmbededImage(string?)"/> if <paramref name="uri"/> doesn't point to a valid image file.</remarks>
|
||||
/// <param name="imagePath">The absolute path to the image file or <see langword="null"/> to get the avatar placeholder.</param>
|
||||
/// <remarks>This fallsback to <see cref="LoadEmbeddedImage(string?)"/> if <paramref name="imagePath"/> doesn't point to a valid image file.</remarks>
|
||||
/// <returns>The requested image or the default bot avatar placeholder.</returns>
|
||||
public static SKBitmap LoadLocalImage(string? uri = default)
|
||||
public static SKBitmap LoadLocalImage(string? imagePath)
|
||||
{
|
||||
return (File.Exists(uri))
|
||||
? SKBitmap.Decode(uri)
|
||||
: LoadEmbededImage(uri);
|
||||
return (File.Exists(imagePath))
|
||||
? SKBitmap.Decode(imagePath)
|
||||
: LoadEmbeddedImage(imagePath);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ namespace EllieHub.Features.AppConfig.Services.Mocks;
|
|||
internal sealed class MockAppConfigManager : IAppConfigManager
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ReadOnlyAppSettings AppConfig { get; } = new(new() { BotEntries = new() { [Guid.Empty] = new("MockBot", Path.Combine(AppStatics.AppDefaultBotDirectoryUri, "MockBot"), 0) } });
|
||||
public ReadOnlyAppSettings AppConfig { get; } = new(new() { BotEntries = new() { [Guid.Empty] = new("MockBot", Path.Join(AppStatics.AppDefaultBotDirectoryUri, "MockBot"), 0) } });
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<BotEntry> CreateBotEntryAsync(CancellationToken cToken = default)
|
||||
|
|
|
@ -15,14 +15,21 @@ public sealed class BotExitEventArgs : EventArgs
|
|||
/// </summary>
|
||||
public int ExitCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The exit message.
|
||||
/// </summary>
|
||||
public string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the event arguments when a bot process exits.
|
||||
/// </summary>
|
||||
/// <param name="botId">The bot's Id.</param>
|
||||
/// <param name="exitCode">The exit code.</param>
|
||||
public BotExitEventArgs(Guid botId, int exitCode)
|
||||
/// <param name="message">The message for the bot process that just exited.</param>
|
||||
public BotExitEventArgs(Guid botId, int exitCode, string message)
|
||||
{
|
||||
Id = botId;
|
||||
ExitCode = exitCode;
|
||||
Message = message;
|
||||
}
|
||||
}
|
|
@ -10,8 +10,9 @@ namespace EllieHub.Features.BotConfig.Services;
|
|||
/// </summary>
|
||||
public sealed class EllieOrchestrator : IBotOrchestrator
|
||||
{
|
||||
private readonly Dictionary<Guid, Process> _runningBots = new();
|
||||
private readonly ReadOnlyAppSettings _appConfig;
|
||||
private readonly ILogWriter _logWriter;
|
||||
private readonly Dictionary<Guid, Process> _runningBots = new();
|
||||
private readonly string _fileName = OperatingSystem.IsWindows() ? "EllieBot.exe" : "EllieBot";
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -27,8 +28,12 @@ public sealed class EllieOrchestrator : IBotOrchestrator
|
|||
/// Creates an object that coordinates multiple running processes of EllieBot.
|
||||
/// </summary>
|
||||
/// <param name="appConfig">The application settings.</param>
|
||||
public EllieOrchestrator(ReadOnlyAppSettings appConfig)
|
||||
=> _appConfig = appConfig;
|
||||
/// <param name="logWriter">The service that writes bot logs to disk.</param>
|
||||
public EllieOrchestrator(ReadOnlyAppSettings appConfig, ILogWriter logWriter)
|
||||
{
|
||||
_appConfig = appConfig;
|
||||
_logWriter = logWriter;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBotRunning(Guid botId)
|
||||
|
@ -39,12 +44,12 @@ public sealed class EllieOrchestrator : IBotOrchestrator
|
|||
{
|
||||
if (_runningBots.ContainsKey(botId)
|
||||
|| !_appConfig.BotEntries.TryGetValue(botId, out var botEntry)
|
||||
|| !File.Exists(Path.Combine(botEntry.InstanceDirectoryUri, _fileName)))
|
||||
|| !File.Exists(Path.Join(botEntry.InstanceDirectoryUri, _fileName)))
|
||||
return false;
|
||||
|
||||
var botProcess = Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
FileName = Path.Combine(botEntry.InstanceDirectoryUri, _fileName),
|
||||
FileName = Path.Join(botEntry.InstanceDirectoryUri, _fileName),
|
||||
WorkingDirectory = botEntry.InstanceDirectoryUri,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
|
@ -80,6 +85,7 @@ public sealed class EllieOrchestrator : IBotOrchestrator
|
|||
{
|
||||
var amount = _runningBots.Count;
|
||||
|
||||
// ReSharper disable once EmptyGeneralCatchClause
|
||||
foreach (var process in _runningBots.Values)
|
||||
try { process.Kill(true); } catch { }
|
||||
|
||||
|
@ -95,7 +101,12 @@ public sealed class EllieOrchestrator : IBotOrchestrator
|
|||
private void OnExit(object? sender, EventArgs eventArgs)
|
||||
{
|
||||
var (id, process) = _runningBots.First(x => x.Value.Equals(sender));
|
||||
OnBotExit?.Invoke(this, new(id, process.ExitCode));
|
||||
var message = Environment.NewLine
|
||||
+ $"{_appConfig.BotEntries[id].Name} stopped. Status code: {process.ExitCode}"
|
||||
+ Environment.NewLine;
|
||||
|
||||
_logWriter.TryAdd(id, message);
|
||||
OnBotExit?.Invoke(this, new(id, process.ExitCode, message));
|
||||
|
||||
_runningBots.Remove(id);
|
||||
process.CancelOutputRead();
|
||||
|
@ -116,6 +127,7 @@ public sealed class EllieOrchestrator : IBotOrchestrator
|
|||
var (id, _) = _runningBots.First(x => x.Value.Equals(sender));
|
||||
var newEventArgs = new ProcessStdWriteEventArgs(id, eventArgs.Data);
|
||||
|
||||
_logWriter.TryAdd(id, eventArgs.Data);
|
||||
OnStdout?.Invoke(this, newEventArgs);
|
||||
}
|
||||
|
||||
|
@ -132,6 +144,7 @@ public sealed class EllieOrchestrator : IBotOrchestrator
|
|||
var (id, _) = _runningBots.First(x => x.Value.Equals(sender));
|
||||
var newEventArgs = new ProcessStdWriteEventArgs(id, eventArgs.Data);
|
||||
|
||||
_logWriter.TryAdd(id, eventArgs.Data);
|
||||
OnStderr?.Invoke(this, newEventArgs);
|
||||
}
|
||||
}
|
|
@ -103,7 +103,7 @@ public class BotConfigViewModel : ViewModelBase<BotConfigView>, IDisposable
|
|||
{
|
||||
var sanitizedValue = value.ReplaceLineEndings(string.Empty);
|
||||
|
||||
DirectoryHint = $"Select the absolute path to the bot directory. For example: {Path.Combine(_appConfigManager.AppConfig.BotsDirectoryUri, sanitizedValue)}";
|
||||
DirectoryHint = $"Select the absolute path to the bot directory. For example: {Path.Join(_appConfigManager.AppConfig.BotsDirectoryUri, sanitizedValue)}";
|
||||
this.RaiseAndSetIfChanged(ref _botName, sanitizedValue);
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ public class BotConfigViewModel : ViewModelBase<BotConfigView>, IDisposable
|
|||
if (IsBotRunning)
|
||||
EnableButtons(true, false);
|
||||
else
|
||||
EnableButtons(!File.Exists(Path.Combine(botEntry.InstanceDirectoryUri, Resolver.FileName)), true);
|
||||
EnableButtons(!File.Exists(Path.Join(botEntry.InstanceDirectoryUri, Resolver.FileName)), true);
|
||||
|
||||
// Dispose when the view is deactivated
|
||||
this.WhenActivated(disposables => Disposable.Create(Dispose).DisposeWith(disposables));
|
||||
|
@ -245,7 +245,7 @@ public class BotConfigViewModel : ViewModelBase<BotConfigView>, IDisposable
|
|||
{
|
||||
_ = LoadUpdateBarAsync(Resolver, UpdateBar);
|
||||
|
||||
if (!wereButtonsUnlocked && File.Exists(Path.Combine(BotDirectoryUriBar.CurrentUri, Resolver.FileName)))
|
||||
if (!wereButtonsUnlocked && File.Exists(Path.Join(BotDirectoryUriBar.CurrentUri, Resolver.FileName)))
|
||||
EnableButtons(false, true);
|
||||
else
|
||||
EnableButtons(!wereButtonsUnlocked, true);
|
||||
|
@ -466,8 +466,6 @@ public class BotConfigViewModel : ViewModelBase<BotConfigView>, IDisposable
|
|||
if (eventArgs.Id != Resolver.Id)
|
||||
return;
|
||||
|
||||
_logWriter.TryAdd(eventArgs.Id, eventArgs.Output);
|
||||
|
||||
FakeConsole.Content = FakeConsole.Content.Length > 100_000
|
||||
? FakeConsole.Content[FakeConsole.Content.IndexOf(Environment.NewLine, 60_000, StringComparison.Ordinal)..] + eventArgs.Output + Environment.NewLine
|
||||
: FakeConsole.Content + eventArgs.Output + Environment.NewLine;
|
||||
|
@ -483,14 +481,11 @@ public class BotConfigViewModel : ViewModelBase<BotConfigView>, IDisposable
|
|||
if (eventArgs.Id != Resolver.Id)
|
||||
return;
|
||||
|
||||
var message = Environment.NewLine + ActualBotName + " stopped." + Environment.NewLine;
|
||||
|
||||
_logWriter.TryAdd(Resolver.Id, message);
|
||||
FakeConsole.Content += message;
|
||||
FakeConsole.Content += eventArgs.Message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reenables the buttons when the bot instance associated with this view-model exits.
|
||||
/// Re-enables the buttons when the bot instance associated with this view-model exits.
|
||||
/// </summary>
|
||||
/// <param name="botOrchestrator">The bot orchestrator.</param>
|
||||
/// <param name="eventArgs">The event arguments.</param>
|
||||
|
|
Loading…
Reference in a new issue