Updated EllieHub to 1.0.3.0
This commit is contained in:
parent
7429e0298a
commit
2c37112b4a
8 changed files with 76 additions and 47 deletions
|
@ -8,7 +8,6 @@
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<WarningsAsErrors>Nullable</WarningsAsErrors>
|
<WarningsAsErrors>Nullable</WarningsAsErrors>
|
||||||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
|
||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
<BuiltInComInteropSupport>True</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>True</BuiltInComInteropSupport>
|
||||||
|
@ -16,11 +15,12 @@
|
||||||
|
|
||||||
<!--Publishing-->
|
<!--Publishing-->
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
|
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
|
|
||||||
<!--Version-->
|
<!--Version-->
|
||||||
<VersionPrefix>1.0.2.0</VersionPrefix>
|
<VersionPrefix>1.0.3.0</VersionPrefix>
|
||||||
|
|
||||||
<!--Avalonia Settings-->
|
<!--Avalonia Settings-->
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
@ -30,6 +30,13 @@
|
||||||
<UseAppHost>true</UseAppHost>
|
<UseAppHost>true</UseAppHost>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ public static class WindowExt
|
||||||
{
|
{
|
||||||
return (!activeView.TryFindResource(resourceName, theme, out var resource))
|
return (!activeView.TryFindResource(resourceName, theme, out var resource))
|
||||||
? throw new InvalidOperationException($"Resource '{resourceName}' was not found.")
|
? throw new InvalidOperationException($"Resource '{resourceName}' was not found.")
|
||||||
: (!Utilities.TryCastTo<T>(resource, out var result))
|
: (resource is T castResource)
|
||||||
? throw new InvalidCastException($"Could not convert resource of type '{resource?.GetType()?.FullName}' to '{nameof(T)}'.")
|
? castResource
|
||||||
: result;
|
: throw new InvalidCastException($"Could not convert resource of type '{resource?.GetType()?.FullName}' to '{nameof(T)}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,7 +20,7 @@ namespace EllieHub.Features.AppConfig.ViewModels;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConfigViewModel : ViewModelBase<ConfigView>
|
public class ConfigViewModel : ViewModelBase<ConfigView>
|
||||||
{
|
{
|
||||||
private static readonly string _unixNotice = Environment.OSVersion.Platform is not PlatformID.Unix
|
private static readonly string _unixNotice = (Environment.OSVersion.Platform is not PlatformID.Unix)
|
||||||
? string.Empty
|
? string.Empty
|
||||||
: Environment.NewLine + "To make the dependencies accessible to your bot instances without this updater, consider installing " +
|
: Environment.NewLine + "To make the dependencies accessible to your bot instances without this updater, consider installing " +
|
||||||
$"them through your package manager or adding the directory \"{AppStatics.AppDepsUri}\" to your PATH environment variable.";
|
$"them through your package manager or adding the directory \"{AppStatics.AppDepsUri}\" to your PATH environment variable.";
|
||||||
|
|
|
@ -69,10 +69,10 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
|
||||||
/// <exception cref="InvalidOperationException">Occurs when the visual tree has an unexpected structure.</exception>
|
/// <exception cref="InvalidOperationException">Occurs when the visual tree has an unexpected structure.</exception>
|
||||||
public void ApplyBotButtonBorder(Button button)
|
public void ApplyBotButtonBorder(Button button)
|
||||||
{
|
{
|
||||||
if (!Utilities.TryCastTo<Border>(button.Parent?.Parent, out var border))
|
if (button.Parent?.Parent is not Border border)
|
||||||
throw new InvalidOperationException("Visual tree has an unexpected structure.");
|
throw new InvalidOperationException("Visual tree has an unexpected structure.");
|
||||||
|
|
||||||
if (!Utilities.TryCastTo<ImmutableSolidColorBrush>(this.FindResource(base.ActualThemeVariant, "BotSelectionColor"), out var resourceColor))
|
if (this.FindResource(base.ActualThemeVariant, "BotSelectionColor") is not ImmutableSolidColorBrush resourceColor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
border.BorderBrush = resourceColor;
|
border.BorderBrush = resourceColor;
|
||||||
|
@ -95,7 +95,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
|
||||||
private void LoadBotViewModel(object sender, RoutedEventArgs eventArgs)
|
private void LoadBotViewModel(object sender, RoutedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
// "sender", for some reason, is not one of the buttons stored in the lateral bar's view-model.
|
// "sender", for some reason, is not one of the buttons stored in the lateral bar's view-model.
|
||||||
if (Utilities.TryCastTo<Button>(sender, out var button) && this.ViewModel!.BotButtonList.First(x => x.Content == button.Content).IsEnabled)
|
if (sender is Button button && this.ViewModel!.BotButtonList.First(x => x.Content == button.Content).IsEnabled)
|
||||||
BotButtonClick?.Invoke(button, eventArgs);
|
BotButtonClick?.Invoke(button, eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,10 +108,11 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
|
||||||
/// <exception cref="InvalidOperationException">Occurs when the visual tree has an unexpected structure.</exception>
|
/// <exception cref="InvalidOperationException">Occurs when the visual tree has an unexpected structure.</exception>
|
||||||
private void OnBotButtonLoad(object? sender, VisualTreeAttachmentEventArgs eventArgs)
|
private void OnBotButtonLoad(object? sender, VisualTreeAttachmentEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!Utilities.TryCastTo<Panel>(sender, out var panel)
|
if (sender is not Panel panel
|
||||||
|| !Utilities.TryCastTo<SKImageView>(((Border)panel.Children[0]).Child, out var botAvatar)
|
|| panel.Children[0] is not Border border
|
||||||
|| !Utilities.TryCastTo<Button>(panel.Children[1], out var button)
|
|| border.Child is not SKImageView botAvatar
|
||||||
|| !Utilities.TryCastTo<Guid>(button.Content, out var botId))
|
|| panel.Children[1] is not Button button
|
||||||
|
|| button.Content is not Guid botId)
|
||||||
throw new InvalidOperationException("Visual tree has an unexpected structure.");
|
throw new InvalidOperationException("Visual tree has an unexpected structure.");
|
||||||
|
|
||||||
// Set the avatar
|
// Set the avatar
|
||||||
|
@ -132,7 +133,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
|
||||||
/// <exception cref="InvalidOperationException">Occurs when <paramref name="sender"/> is not a <see cref="Button"/>.</exception>
|
/// <exception cref="InvalidOperationException">Occurs when <paramref name="sender"/> is not a <see cref="Button"/>.</exception>
|
||||||
private void DownsizeBotAvatar(object? sender, PointerPressedEventArgs eventArgs)
|
private void DownsizeBotAvatar(object? sender, PointerPressedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!Utilities.TryCastTo<Button>(sender, out var button))
|
if (sender is not Button button)
|
||||||
throw new InvalidOperationException($"Sender is not a {nameof(Button)}.");
|
throw new InvalidOperationException($"Sender is not a {nameof(Button)}.");
|
||||||
|
|
||||||
var botAvatar = FindAvatarComponent(button);
|
var botAvatar = FindAvatarComponent(button);
|
||||||
|
@ -149,7 +150,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
|
||||||
/// <exception cref="InvalidOperationException">Occurs when <paramref name="sender"/> is not a <see cref="Button"/>.</exception>
|
/// <exception cref="InvalidOperationException">Occurs when <paramref name="sender"/> is not a <see cref="Button"/>.</exception>
|
||||||
private void UpsizeBotAvatar(object? sender, PointerReleasedEventArgs eventArgs)
|
private void UpsizeBotAvatar(object? sender, PointerReleasedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!Utilities.TryCastTo<Button>(sender, out var button))
|
if (sender is not Button button)
|
||||||
throw new InvalidOperationException($"Sender is not a {nameof(Button)}.");
|
throw new InvalidOperationException($"Sender is not a {nameof(Button)}.");
|
||||||
|
|
||||||
var botAvatar = FindAvatarComponent(button);
|
var botAvatar = FindAvatarComponent(button);
|
||||||
|
@ -167,7 +168,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
|
||||||
/// <exception cref="InvalidOperationException">Occurs when the component's content is not a Guid.</exception>
|
/// <exception cref="InvalidOperationException">Occurs when the component's content is not a Guid.</exception>
|
||||||
private SKImageView FindAvatarComponent<T>(T component) where T : ContentControl
|
private SKImageView FindAvatarComponent<T>(T component) where T : ContentControl
|
||||||
{
|
{
|
||||||
return (!Utilities.TryCastTo<Guid>(component.Content, out var botId))
|
return (component.Content is not Guid botId)
|
||||||
? throw new InvalidOperationException($"{nameof(T)} does not contain a bot Id.")
|
? throw new InvalidOperationException($"{nameof(T)} does not contain a bot Id.")
|
||||||
: FindAvatarComponent(botId);
|
: FindAvatarComponent(botId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,7 +293,7 @@ public partial class AppView : ReactiveWindow<AppViewModel>
|
||||||
private static BotConfigViewModel GetBotConfigViewModel(Button button, IServiceScopeFactory scopeFactory)
|
private static BotConfigViewModel GetBotConfigViewModel(Button button, IServiceScopeFactory scopeFactory)
|
||||||
{
|
{
|
||||||
using var scope = scopeFactory.CreateScope();
|
using var scope = scopeFactory.CreateScope();
|
||||||
var botId = (Guid)(button.Content ?? throw new InvalidOperationException("Bot button has no valid Id."));
|
var botId = (button.Content ?? throw new InvalidOperationException("Bot button has no valid Id."));
|
||||||
var botResolver = scope.ServiceProvider.GetParameterizedService<EllieResolver>(botId);
|
var botResolver = scope.ServiceProvider.GetParameterizedService<EllieResolver>(botId);
|
||||||
|
|
||||||
return scope.ServiceProvider.GetParameterizedService<BotConfigViewModel>(botResolver);
|
return scope.ServiceProvider.GetParameterizedService<BotConfigViewModel>(botResolver);
|
||||||
|
|
|
@ -114,34 +114,10 @@ public sealed partial class EllieResolver : IBotResolver
|
||||||
await _appConfigManager.UpdateBotEntryAsync(Id, x => x with { Version = null }, cToken);
|
await _appConfigManager.UpdateBotEntryAsync(Id, x => x with { Version = null }, cToken);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(botEntry.Version))
|
|
||||||
return botEntry.Version;
|
|
||||||
|
|
||||||
// Ellie is published as a single-file binary, so we have to extract
|
return (string.IsNullOrWhiteSpace(botEntry.Version))
|
||||||
// its contents first in order to read the assembly for its version.
|
? await GetBotVersionFromAssemblyAsync(executableUri, cToken)
|
||||||
using var executableReader = new ExecutableReader(executableUri);
|
: botEntry.Version;
|
||||||
var extractDirectoryUri = Path.Combine(_tempDirectory, "EllieBotExtract");
|
|
||||||
var extractAssemblyUri = Path.Combine(extractDirectoryUri, "EllieBot.dll");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await executableReader.ExtractToDirectoryAsync(extractDirectoryUri, cToken);
|
|
||||||
|
|
||||||
var nadekoAssembly = Assembly.LoadFile(extractAssemblyUri);
|
|
||||||
var version = nadekoAssembly.GetName().Version
|
|
||||||
?? throw new InvalidOperationException($"Could not find version of the assembly at {extractAssemblyUri}.");
|
|
||||||
|
|
||||||
var currentVersion = $"{version.Major}.{version.Minor}.{version.Build}";
|
|
||||||
|
|
||||||
await _appConfigManager.UpdateBotEntryAsync(Id, x => x with { Version = currentVersion }, cToken);
|
|
||||||
|
|
||||||
return currentVersion;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Utilities.TryDeleteDirectory(extractDirectoryUri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -397,6 +373,51 @@ public sealed partial class EllieResolver : IBotResolver
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the bot version from the bot's assembly.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="executableUri">The path to the bot's executable file.</param>
|
||||||
|
/// <param name="cToken">The cancellation token.</param>
|
||||||
|
/// <returns>The version of the bot or <see langword="null"/> if the executable file is not found.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Occurs when the assembly file is not found.</exception>
|
||||||
|
private async ValueTask<string?> GetBotVersionFromAssemblyAsync(string executableUri, CancellationToken cToken)
|
||||||
|
{
|
||||||
|
if (!File.Exists(executableUri))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var directoryUri = Directory.GetParent(executableUri)?.FullName ?? Path.GetPathRoot(executableUri)!;
|
||||||
|
var assemblyUri = Path.Join(directoryUri, "EllieBot.dll");
|
||||||
|
var isSingleFile = !File.Exists(assemblyUri);
|
||||||
|
|
||||||
|
// If Ellie is published as a single-file binary, we have to extract
|
||||||
|
// its contents first in order to read the assembly for its version.
|
||||||
|
if (isSingleFile)
|
||||||
|
{
|
||||||
|
directoryUri = Path.Join(_tempDirectory, "EllieBotExtract_" + DateTimeOffset.Now.Ticks);
|
||||||
|
assemblyUri = Path.Join(directoryUri, "EllieBot.dll");
|
||||||
|
using var executableReader = new ExecutableReader(executableUri);
|
||||||
|
await executableReader.ExtractToDirectoryAsync(directoryUri, cToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ellieAssembly = Assembly.LoadFile(assemblyUri);
|
||||||
|
var version = ellieAssembly.GetName().Version
|
||||||
|
?? throw new InvalidOperationException($"Could not find version for the assembly at {assemblyUri}.");
|
||||||
|
|
||||||
|
var currentVersion = $"{version.Major}.{version.Minor}.{version.Build}";
|
||||||
|
|
||||||
|
await _appConfigManager.UpdateBotEntryAsync(Id, x => x with { Version = currentVersion }, cToken);
|
||||||
|
|
||||||
|
return currentVersion;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (isSingleFile)
|
||||||
|
Utilities.TryDeleteDirectory(directoryUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[GeneratedRegex(@"^(?:\S+\-)(\S+\-\S+)\-", RegexOptions.Compiled)]
|
[GeneratedRegex(@"^(?:\S+\-)(\S+\-\S+)\-", RegexOptions.Compiled)]
|
||||||
private static partial Regex GenerateUnzipedDirRegex();
|
private static partial Regex GenerateUnzipedDirRegex();
|
||||||
}
|
}
|
|
@ -26,7 +26,7 @@ public partial class BotConfigView : ReactiveUserControl<BotConfigViewModel>
|
||||||
/// <param name="eventArgs">The event arguments.</param>
|
/// <param name="eventArgs">The event arguments.</param>
|
||||||
private void AvatarButtonHover(object? sender, PointerEventArgs eventArgs)
|
private void AvatarButtonHover(object? sender, PointerEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!Utilities.TryCastTo<Button>(sender, out var button))
|
if (sender is not Button button)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
button.Opacity = 3.0;
|
button.Opacity = 3.0;
|
||||||
|
@ -40,7 +40,7 @@ public partial class BotConfigView : ReactiveUserControl<BotConfigViewModel>
|
||||||
/// <param name="eventArgs">The event arguments.</param>
|
/// <param name="eventArgs">The event arguments.</param>
|
||||||
private void AvatarButtonUnhover(object? sender, PointerEventArgs eventArgs)
|
private void AvatarButtonUnhover(object? sender, PointerEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!Utilities.TryCastTo<Button>(sender, out var button))
|
if (sender is not Button button)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
button.Opacity = 0.0;
|
button.Opacity = 0.0;
|
||||||
|
|
|
@ -142,7 +142,7 @@ public sealed class AppResolver : IAppResolver
|
||||||
|
|
||||||
// Rename the original file from "file" to "file_old".
|
// Rename the original file from "file" to "file_old".
|
||||||
if (File.Exists(destinationUri))
|
if (File.Exists(destinationUri))
|
||||||
File.Move(destinationUri, destinationUri + OldFileSuffix);
|
File.Move(destinationUri, destinationUri + OldFileSuffix, true);
|
||||||
|
|
||||||
// Move the new file to the application's directory.
|
// Move the new file to the application's directory.
|
||||||
if (Environment.OSVersion.Platform is not PlatformID.Unix)
|
if (Environment.OSVersion.Platform is not PlatformID.Unix)
|
||||||
|
|
Loading…
Reference in a new issue