Skip to content

Commit

Permalink
AzuraCast fixes (#255)
Browse files Browse the repository at this point in the history
* Remove unneeded log line

* Separate the AzuraCast settings embeds

* Add warning when self-signed cert is detected

* Add empty cert able to be replaced

* Enrich the error message more
  • Loading branch information
Sella-GH authored Jan 19, 2025
1 parent 53d0594 commit 58ab9fc
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 43 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
- We're now using the new ARM64 GitHub Actions runner for the Docker images
- This results in a faster build time and more compact actions workflow

### Improvements
- Removed some unneded and wrong placed logging messages
- The AzuraCast Settings embed is now shown even when the instance is offline
- The AzuraCast Stations embed however continues to be not shown
- When the AzuraCast instance has a self-signed SSL certificate Azzy will now warn about it

## 2.2.3 - 2025-01-15
### Dependencies
- Updated [DSharpPlus](https://github.com/DSharpPlus/DSharpPlus) to version 5.0.0-nightly-02448
Expand Down
3 changes: 3 additions & 0 deletions dev.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ RUN sed -i "s\Commit not found\\$COMMIT\g" /app/Modules/Core/Files/AppStats.json
&& sed -i "s\Compilation date not found\\$TIMESTAMP\g" /app/Modules/Core/Files/AppStats.json \
&& sed -i "s\Lines of source code not found\\$LOC_CS\g" /app/Modules/Core/Files/AppStats.json

# Dev Build only: Add empty certificate for local testing
RUN touch /etc/ssl/certs/azzybot.crt

# Add new user
RUN useradd -M -U azzy && chown -R azzy:azzy /app && chmod 0755 -R /app
USER azzy
Expand Down
74 changes: 38 additions & 36 deletions src/AzzyBot.Bot/Commands/ConfigCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ public async ValueTask AddAzuraCastAsync
AzuraCastEntity? dAzuraCast = guild.AzuraCast;
if (dAzuraCast is not null)
{
_logger.DatabaseAzuraCastNotFound(guildId);
await context.DeleteResponseAsync();
await context.FollowupAsync(GeneralStrings.ConfigInstanceAlreadyExists);
return;
Expand Down Expand Up @@ -587,50 +586,53 @@ public async ValueTask GetSettingsAsync(SlashCommandContext context)
await using DiscordMessageBuilder messageBuilder = new();
messageBuilder.AddEmbeds([guildEmbed]);

if (guild.AzuraCast?.IsOnline is true)
if (guild.AzuraCast is not null)
{
AzuraCastEntity ac = guild.AzuraCast;
Dictionary<ulong, string> stationRoles = new(ac.Stations.Count);
Dictionary<int, string> stationNames = new(ac.Stations.Count);
Dictionary<int, int> stationRequests = new(ac.Stations.Count);
foreach (AzuraCastStationEntity station in ac.Stations)
{
DiscordRole? stationAdminRole = roles.FirstOrDefault(r => r.Id == station.Preferences.StationAdminRoleId);
DiscordRole? stationDjRole = roles.FirstOrDefault(r => r.Id == station.Preferences.StationDjRoleId);
stationRoles.Add(stationAdminRole?.Id ?? 0, stationAdminRole?.Name ?? "Name not found");
stationRoles.Add(stationDjRole?.Id ?? 0, stationDjRole?.Name ?? "Name not found");
DiscordRole? instanceAdminRole = roles.FirstOrDefault(r => r.Id == ac.Preferences.InstanceAdminRoleId);
DiscordEmbed azuraCastEmbed = EmbedBuilder.BuildGetSettingsAzuraInstanceEmbed(ac, $"{instanceAdminRole?.Name} ({instanceAdminRole?.Id})");

AzuraStationRecord? stationRecord = null;
try
{
stationRecord = await _azuraCastApi.GetStationAsync(new(Crypto.Decrypt(ac.BaseUrl)), station.StationId);
}
catch (HttpRequestException)
{
await _azuraCastPing.PingInstanceAsync(ac);
break;
}
messageBuilder.AddEmbed(azuraCastEmbed);

if (stationRecord is null)
if (ac.IsOnline)
{
Dictionary<ulong, string> stationRoles = new(ac.Stations.Count);
Dictionary<int, string> stationNames = new(ac.Stations.Count);
Dictionary<int, int> stationRequests = new(ac.Stations.Count);
foreach (AzuraCastStationEntity station in ac.Stations)
{
await _botService.SendMessageAsync(guild.AzuraCast.Preferences.NotificationChannelId, $"I don't have the permission to access the **station** ({station.StationId}) endpoint.\n{AzuraCastApiService.AzuraCastPermissionsWiki}");
continue;
DiscordRole? stationAdminRole = roles.FirstOrDefault(r => r.Id == station.Preferences.StationAdminRoleId);
DiscordRole? stationDjRole = roles.FirstOrDefault(r => r.Id == station.Preferences.StationDjRoleId);
stationRoles.Add(stationAdminRole?.Id ?? 0, stationAdminRole?.Name ?? "Name not found");
stationRoles.Add(stationDjRole?.Id ?? 0, stationDjRole?.Name ?? "Name not found");

AzuraStationRecord? stationRecord = null;
try
{
stationRecord = await _azuraCastApi.GetStationAsync(new(Crypto.Decrypt(ac.BaseUrl)), station.StationId);
}
catch (HttpRequestException)
{
await _azuraCastPing.PingInstanceAsync(ac);
break;
}

if (stationRecord is null)
{
await _botService.SendMessageAsync(guild.AzuraCast.Preferences.NotificationChannelId, $"I don't have the permission to access the **station** ({station.StationId}) endpoint.\n{AzuraCastApiService.AzuraCastPermissionsWiki}");
continue;
}

stationNames.Add(station.Id, stationRecord.Name);

int stationBotRequests = await _dbActions.GetAzuraCastStationRequestsCountAsync(guildId, station.StationId);
stationRequests.Add(station.Id, stationBotRequests);
}

stationNames.Add(station.Id, stationRecord.Name);
IEnumerable<DiscordEmbed> azuraCastStationsEmbed = EmbedBuilder.BuildGetSettingsAzuraStationsEmbed(ac, stationRoles, stationNames, stationRequests);

int stationBotRequests = await _dbActions.GetAzuraCastStationRequestsCountAsync(guildId, station.StationId);
stationRequests.Add(station.Id, stationBotRequests);
messageBuilder.AddEmbeds(azuraCastStationsEmbed);
}

DiscordRole? instanceAdminRole = roles.FirstOrDefault(r => r.Id == ac.Preferences.InstanceAdminRoleId);
IEnumerable<DiscordEmbed> azuraEmbed = EmbedBuilder.BuildGetSettingsAzuraEmbed(ac, $"{instanceAdminRole?.Name} ({instanceAdminRole?.Id})", stationRoles, stationNames, stationRequests);

messageBuilder.AddEmbeds(azuraEmbed);
}
else if (guild.AzuraCast is not null)
{
messageBuilder.WithContent("Apparently you have an AzuraCast instance configured, but it's not online.");
}

await member.SendMessageAsync(messageBuilder);
Expand Down
17 changes: 14 additions & 3 deletions src/AzzyBot.Bot/Services/Modules/AzuraCastPingService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Net.Http;
using System.Security.Authentication;
using System.Threading.Tasks;

using AzzyBot.Bot.Utilities.Records.AzuraCast;
Expand All @@ -18,6 +19,7 @@ public sealed class AzuraCastPingService(ILogger<AzuraCastPingService> logger, A
private readonly AzuraCastApiService _azuraCast = azuraCast;
private readonly DbActions _dbActions = dbActions;
private readonly DiscordBotService _botService = discordBotService;
private const string ValidCertNeeded = "The certificate for AzuraCast instance **URI** is self-signed and therefore not valid!\nYou need a valid HTTPS certificate for your AzuraCast instance so AzzyBot can safely connect to it.";

public async Task PingInstanceAsync(AzuraCastEntity azuraCast)
{
Expand All @@ -29,12 +31,21 @@ public async Task PingInstanceAsync(AzuraCastEntity azuraCast)
{
status = await _azuraCast.GetInstanceStatusAsync(uri);
}
catch (HttpRequestException)
catch (HttpRequestException ex)
{
_logger.BackgroundServiceInstanceStatus(azuraCast.GuildId, azuraCast.Id, "offline");
string message = $"AzuraCast instance **{uri}** is **down**!";
if (ex.InnerException is AuthenticationException)
{
_logger.BackgroundServiceInstanceStatus(azuraCast.GuildId, azuraCast.Id, "invalid because of a self-signed certificate");
message = ValidCertNeeded.Replace("**URI**", uri.ToString(), StringComparison.OrdinalIgnoreCase);
}
else
{
_logger.BackgroundServiceInstanceStatus(azuraCast.GuildId, azuraCast.Id, "offline");
}

await _dbActions.UpdateAzuraCastAsync(azuraCast.Guild.UniqueId, isOnline: false);
await _botService.SendMessageAsync(azuraCast.Preferences.OutagesChannelId, $"AzuraCast instance **{uri}** is **down**!");
await _botService.SendMessageAsync(azuraCast.Preferences.OutagesChannelId, message);
}

if (status is not null)
Expand Down
13 changes: 9 additions & 4 deletions src/AzzyBot.Bot/Utilities/EmbedBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -538,12 +538,11 @@ public static DiscordEmbed BuildGetSettingsGuildEmbed(string serverName, GuildEn
return CreateBasicEmbed(title, description, DiscordColor.White, fields: fields);
}

public static IEnumerable<DiscordEmbed> BuildGetSettingsAzuraEmbed(AzuraCastEntity azuraCast, string instanceRole, IReadOnlyDictionary<ulong, string> stationRoles, IReadOnlyDictionary<int, string> stationNames, IReadOnlyDictionary<int, int> stationRequests)
public static DiscordEmbed BuildGetSettingsAzuraInstanceEmbed(AzuraCastEntity azuraCast, string instanceRole)
{
ArgumentNullException.ThrowIfNull(azuraCast);

const string title = "AzuraCast Settings";
List<DiscordEmbed> embeds = new(1 + azuraCast.Stations.Count);
Dictionary<string, AzzyDiscordEmbedRecord> fields = new(6)
{
["Base Url"] = new($"||{((!string.IsNullOrEmpty(azuraCast.BaseUrl)) ? Crypto.Decrypt(azuraCast.BaseUrl) : "Not set")}||"),
Expand All @@ -554,9 +553,15 @@ public static IEnumerable<DiscordEmbed> BuildGetSettingsAzuraEmbed(AzuraCastEnti
["Automatic Checks"] = new($"- Server Status: {Misc.GetReadableBool(azuraCast.Checks.ServerStatus, ReadableBool.EnabledDisabled)}\n- Updates: {Misc.GetReadableBool(azuraCast.Checks.Updates, ReadableBool.EnabledDisabled)}\n- Updates Changelog: {Misc.GetReadableBool(azuraCast.Checks.UpdatesShowChangelog, ReadableBool.EnabledDisabled)}")
};

embeds.Add(CreateBasicEmbed(title, color: DiscordColor.White, fields: fields));
return CreateBasicEmbed(title, color: DiscordColor.White, fields: fields);
}

public static IEnumerable<DiscordEmbed> BuildGetSettingsAzuraStationsEmbed(AzuraCastEntity azuraCast, IReadOnlyDictionary<ulong, string> stationRoles, IReadOnlyDictionary<int, string> stationNames, IReadOnlyDictionary<int, int> stationRequests)
{
ArgumentNullException.ThrowIfNull(azuraCast);

const string stationTitle = "AzuraCast Stations";
List<DiscordEmbed> embeds = new(azuraCast.Stations.Count);
foreach (AzuraCastStationEntity station in azuraCast.Stations)
{
string stationName = stationNames.FirstOrDefault(x => x.Key == station.Id).Value;
Expand Down Expand Up @@ -593,7 +598,7 @@ public static IEnumerable<DiscordEmbed> BuildGetSettingsAzuraEmbed(AzuraCastEnti
string showPlaylist = Misc.GetReadableBool(station.Preferences.ShowPlaylistInNowPlaying, ReadableBool.EnabledDisabled);
string fileChanges = Misc.GetReadableBool(station.Checks.FileChanges, ReadableBool.EnabledDisabled);

fields = new(11)
Dictionary<string, AzzyDiscordEmbedRecord> fields = new(11)
{
["Station Name"] = new(stationName),
["Station ID"] = new(stationId),
Expand Down

0 comments on commit 58ab9fc

Please sign in to comment.