Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Discord Webhook for error messages + fix bug with confirmaton dialog #69

Merged
merged 2 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env-sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ AF_PublicContemptChannel=
AF_HallOfShameChannel=
AF_RecruitInfoChannel=
AF_RecruitAskChannel=
AF_ServerManagerUrl=
AF_ServerManagerApiKey=
AF_LogWebhookId=
AF_LogWebhookToken=
AF_LogWebhookLevel=
2 changes: 1 addition & 1 deletion .github/workflows/build_bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Build solution
run: msbuild /p:Configuration=Release
- name: Upload ArmaforcesMissionBot build artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: ArmaforcesMissionBot
path: ArmaforcesMissionBot/bin/Release
4 changes: 4 additions & 0 deletions ArmaforcesMissionBot/ArmaforcesMissionBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
<PackageReference Include="RestSharp" Version="106.12.0" />
<PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Discord.Lite" Version="0.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 6 additions & 2 deletions ArmaforcesMissionBot/Controllers/ApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Discord.WebSocket;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Expand All @@ -24,21 +25,24 @@ public class ApiController : ControllerBase
private readonly BanHelper _banHelper;
private readonly SignupHelper _signupHelper;
private readonly MiscHelper _miscHelper;
private readonly ILogger<ApiController> _logger;

public ApiController(
MissionsArchiveData missionsArchiveData,
SignupsData signupsData,
DiscordSocketClient client,
BanHelper banHelper,
SignupHelper signupHelper,
MiscHelper miscHelper)
MiscHelper miscHelper,
ILogger<ApiController> logger)
{
_missionsArchiveData = missionsArchiveData;
_signupsData = signupsData;
_client = client;
_banHelper = banHelper;
_signupHelper = signupHelper;
_miscHelper = miscHelper;
_logger = logger;
}

[HttpGet("currentMission")]
Expand Down Expand Up @@ -345,7 +349,7 @@ public void Users()
[HttpPost("createMission")]
public async Task CreateMissionAsync(Mission mission)
{
Console.WriteLine(JsonConvert.SerializeObject(mission));
_logger.LogTrace($"Create mission request: {JsonConvert.SerializeObject(mission)}");

mission.Editing = ArmaforcesMissionBotSharedClasses.Mission.EditEnum.New;
_signupsData.Missions.Add(mission);
Expand Down
79 changes: 68 additions & 11 deletions ArmaforcesMissionBot/DataClasses/Config.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using dotenv.net;
using Newtonsoft.Json;
#nullable enable
using dotenv.net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Serilog;
using Serilog.Events;

namespace ArmaforcesMissionBot.DataClasses
{
Expand All @@ -19,6 +18,22 @@ public class Config
public ulong BotRole { get; set; }
public ulong RecruiterRole { get; set; }
public ulong RecruitRole { get; set; }

/// <summary>
/// Discord webhook ID for log messages.
/// </summary>
public ulong? LogWebhookId { get; set; }

/// <summary>
/// Discord webhook token for log messages.
/// </summary>
public string? LogWebhookToken { get; set; }

/// <summary>
/// Minimum level that will be logged to Discord webhook.
/// </summary>
public LogEventLevel LogWebhookLevel { get; set; } = LogEventLevel.Error;

public string KickImageUrl { get; set; }
public string BanImageUrl { get; set; }
public string ServerManagerUrl { get; set; }
Expand All @@ -28,19 +43,61 @@ public class Config
public ulong PublicContemptChannel { get; set; }
public ulong HallOfShameChannel { get; set; }
public ulong RecruitInfoChannel { get; set; }
public ulong RecruitAskChannel { get; set; }
public ulong RecruitAskChannel { get; set; }

public void Load()
{
DotEnv.Config(false);

PropertyInfo[] properties = typeof(Config).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in properties)
foreach (var propertyInfo in properties)
{
if(propertyInfo.PropertyType == typeof(string))
TrySetStringValue(propertyInfo);
else if (propertyInfo.PropertyType == typeof(ulong) || propertyInfo.PropertyType == typeof(ulong?))
TrySetUlongValue(propertyInfo);
else if (propertyInfo.PropertyType == typeof(LogEventLevel))
TrySetEnumValue<LogEventLevel>(propertyInfo);
}
}

private void TrySetEnumValue<T>(PropertyInfo propertyInfo) where T : struct
{
var stringValue = GetVariable(propertyInfo.Name);
if (stringValue != null && Enum.TryParse(stringValue, out T valueToSet))
{
propertyInfo.SetValue(this, valueToSet);
}
}

private void TrySetStringValue(PropertyInfo propertyInfo)
{
var stringValue = GetVariable(propertyInfo.Name);
if (stringValue != null)
{
propertyInfo.SetValue(this, stringValue);
}
}

private void TrySetUlongValue(PropertyInfo propertyInfo)
{
var stringValue = GetVariable(propertyInfo.Name);
if (stringValue != null && ulong.TryParse(stringValue, out var valueToSet))
{
propertyInfo.SetValue(this, valueToSet);
}
}

private static string? GetVariable(string variableName)
{
try
{
return Environment.GetEnvironmentVariable("AF_" + variableName);
}
catch (Exception exception)
{
if(prop.PropertyType == typeof(string))
prop.SetValue(this, Environment.GetEnvironmentVariable("AF_" + prop.Name));
if (prop.PropertyType == typeof(ulong))
prop.SetValue(this, ulong.Parse(Environment.GetEnvironmentVariable("AF_" + prop.Name)));
Log.Warning(exception, "Failed to read environment variable {Name}", variableName);
return null;
}
}
}
Expand Down
28 changes: 18 additions & 10 deletions ArmaforcesMissionBot/Handlers/CommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Serilog;

namespace ArmaforcesMissionBot.Handlers
{
Expand Down Expand Up @@ -50,17 +51,24 @@ private async Task HandleCommandAsync(SocketMessage messageParam)

// Keep in mind that result does not indicate a return value
// rather an object stating if the command executed successfully.
var result = await _commands.ExecuteAsync(
context: context,
argPos: argPos,
services: _services);
try
{
var result = await _commands.ExecuteAsync(
context: context,
argPos: argPos,
services: _services);

// Optionally, we may inform the user if the command fails
// to be executed; however, this may not always be desired,
// as it may clog up the request queue should a user spam a
// command.
if (!result.IsSuccess)
await context.Channel.SendMessageAsync(result.ErrorReason);
// Optionally, we may inform the user if the command fails
// to be executed; however, this may not always be desired,
// as it may clog up the request queue should a user spam a
// command.
if (!result.IsSuccess)
await context.Channel.SendMessageAsync(result.ErrorReason);
}
catch (Exception exception)
{
Log.Error(exception, "An error occured while handling a command {@Message}", message);
}
}
}
}
33 changes: 18 additions & 15 deletions ArmaforcesMissionBot/Handlers/LoadupHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ArmaforcesMissionBot.Features.Modsets;
using ArmaforcesMissionBot.Features.Modsets.Legacy;
using Microsoft.Extensions.Logging;
using static ArmaforcesMissionBot.DataClasses.SignupsData;

namespace ArmaforcesMissionBot.Handlers
Expand All @@ -22,9 +22,12 @@ public class LoadupHandler : IInstallable
private Config _config;
private ModsetProvider _newModsetProvider;
private LegacyModsetProvider _legacyModsetProvider;

private ILogger<LoadupHandler> _logger;

public async Task Install(IServiceProvider map)
{
_logger = map.GetRequiredService<ILogger<LoadupHandler>>();

_client = map.GetService<DiscordSocketClient>();
_config = map.GetService<Config>();
_newModsetProvider = new ModsetProvider(map.GetService<IModsetsApiClient>());
Expand All @@ -36,7 +39,7 @@ public async Task Install(IServiceProvider map)

private async Task Load(SocketGuild guild)
{
Console.WriteLine($"[{DateTime.Now.ToString()}] Loading up from: {guild.Name}");
_logger.LogInformation("Loading up from server: {ServerName}", guild.Name);

await LoadMissions(guild);
await LoadBans(guild);
Expand All @@ -50,7 +53,7 @@ private async Task LoadMissions(SocketGuild guild)

var channels = guild.CategoryChannels.Single(x => x.Id == _config.SignupsCategory);

Console.WriteLine($"[{DateTime.Now.ToString()}] Loading missions");
_logger.LogInformation("Loading missions");

foreach (var channel in channels.Channels.Where(x => x.Id != _config.SignupsArchive && x.Id != _config.CreateMissionChannel && x.Id != _config.HallOfShameChannel).Reverse())
{
Expand Down Expand Up @@ -118,7 +121,7 @@ await messages.ForEachAsync(async x =>
pattern += $"{match.Groups[0]} ";
team.Slots.Add(slot);

Console.WriteLine($"New slot {slot.Emoji} [{slot.Count}] {slot.Name}");
_logger.LogDebug("New slot {Emoji} [{Count}] {Name}", slot.Emoji, slot.Count, slot.Name);
}

team.Name = team.Name.Replace("|", "");
Expand All @@ -133,13 +136,13 @@ await messages.ForEachAsync(async x =>
{
var signedID = ulong.Parse(match.Groups[2].Value);
mission.SignedUsers.Add(signedID);
Console.WriteLine($"{match.Groups[1].Value} : {match.Groups[2].Value} ({signedID})");
_logger.LogTrace("{Match1} : {Match2} ({UserId})", match.Groups[1].Value, match.Groups[2].Value, signedID);
team.Slots.Single(x => x.Emoji == match.Groups[1].Value).Signed.Add(signedID);
}
}
catch(Exception e)
catch (Exception exception)
{
Console.WriteLine($"Failed loading team {team.Name} : {e.Message}");
_logger.LogWarning(exception, "Failed loading team {Name}", team.Name);
}
}

Expand Down Expand Up @@ -197,7 +200,7 @@ private async Task LoadBans(SocketGuild guild)
{
var signups = _services.GetService<SignupsData>();

Console.WriteLine($"[{DateTime.Now.ToString()}] Loading bans");
_logger.LogInformation("Loading bans");

var banChannel = guild.Channels.Single(x => x.Id == _config.HallOfShameChannel) as SocketTextChannel;
var messages = banChannel.GetMessagesAsync();
Expand Down Expand Up @@ -269,7 +272,7 @@ private async Task LoadBanHistory(SocketGuild guild)

var channels = guild.CategoryChannels.Single(x => x.Id == _config.SignupsCategory);

Console.WriteLine($"[{DateTime.Now.ToString()}] Loading ban history");
_logger.LogInformation("Loading ban history");
// History of bans
var shameChannel = guild.Channels.Single(x => x.Id == _config.HallOfShameChannel) as SocketTextChannel;
var messages = shameChannel.GetMessagesAsync();
Expand Down Expand Up @@ -305,7 +308,7 @@ await messages.ForEachAsync(async x =>
uint.Parse(match.Groups[2].Value),
uint.Parse(match.Groups[3].Value)));
}
Console.WriteLine($"[{DateTime.Now.ToString()}] Loaded signup ban history");
_logger.LogInformation("Loaded signup ban history");
}
}
finally
Expand Down Expand Up @@ -335,7 +338,7 @@ await messages.ForEachAsync(async x =>
DateTime.Parse(match.Groups[3].Value),
(BanType)Enum.Parse(typeof(BanType), match.Groups[4].Value)));
}
Console.WriteLine($"[{DateTime.Now.ToString()}] Loaded reaction spam ban history");
_logger.LogInformation("Loaded reaction spam ban history");
}
}
finally
Expand All @@ -352,7 +355,7 @@ private async Task LoadMissionsArchive(SocketGuild guild)

var channels = guild.CategoryChannels.Single(x => x.Id == _config.SignupsCategory);

Console.WriteLine($"[{DateTime.Now.ToString()}] Loading mission history");
_logger.LogInformation("Loading mission history");
archive.ArchiveMissions.Clear();

// History of missions
Expand Down Expand Up @@ -389,7 +392,7 @@ await messages.ForEachAsync(async x =>
DateTimeStyles.RoundtripKind,
out date))
{
Console.WriteLine($"Loading failed on mission date: {embed.Footer.Value.Text}");
_logger.LogWarning("Failed to parse archive mission date {Date}", embed.Footer.Value.Text);
continue;
}

Expand Down Expand Up @@ -436,7 +439,7 @@ await messages.ForEachAsync(async x =>
return x.Date.CompareTo(y.Date);
});

Console.WriteLine($"[{DateTime.Now.ToString()}] Loaded {archive.ArchiveMissions.Count} archive missions");
_logger.LogInformation("Loaded {Count} archive missions", archive.ArchiveMissions.Count);
}

private string GetModsetNameFromUnknownUrl(string unknownUrl)
Expand Down
Loading
Loading