2021-11-09 06:48:47 +00:00
|
|
|
using System.Diagnostics;
|
2020-09-20 20:36:04 +00:00
|
|
|
|
2020-11-16 09:07:57 +00:00
|
|
|
using App.Metrics;
|
|
|
|
|
2020-12-23 01:19:02 +00:00
|
|
|
using Myriad.Builders;
|
|
|
|
using Myriad.Rest;
|
2021-11-27 02:10:56 +00:00
|
|
|
using Myriad.Rest.Types.Requests;
|
|
|
|
using Myriad.Types;
|
2020-09-20 20:36:04 +00:00
|
|
|
|
|
|
|
using NodaTime;
|
|
|
|
|
2020-11-16 09:07:57 +00:00
|
|
|
using Serilog;
|
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
namespace PluralKit.Bot;
|
|
|
|
|
|
|
|
public class ErrorMessageService
|
2020-09-20 20:36:04 +00:00
|
|
|
{
|
2021-11-27 02:10:56 +00:00
|
|
|
// globally rate limit errors for now, don't want to spam users when something breaks
|
|
|
|
private static readonly Duration MinErrorInterval = Duration.FromSeconds(10);
|
2022-01-21 05:10:51 +00:00
|
|
|
private static readonly Duration IntervalFromStartup = Duration.FromMinutes(2);
|
2021-11-27 02:10:56 +00:00
|
|
|
|
2022-03-30 08:36:22 +00:00
|
|
|
private readonly ILogger _logger;
|
2021-11-27 16:09:08 +00:00
|
|
|
private readonly BotConfig _botConfig;
|
2021-11-27 02:10:56 +00:00
|
|
|
private readonly IMetrics _metrics;
|
|
|
|
private readonly DiscordApiClient _rest;
|
|
|
|
|
2021-11-27 16:09:08 +00:00
|
|
|
public ErrorMessageService(BotConfig botConfig, IMetrics metrics, ILogger logger, DiscordApiClient rest)
|
2020-09-20 20:36:04 +00:00
|
|
|
{
|
2021-11-27 16:09:08 +00:00
|
|
|
_botConfig = botConfig;
|
2021-11-27 02:10:56 +00:00
|
|
|
_metrics = metrics;
|
|
|
|
_logger = logger;
|
|
|
|
_rest = rest;
|
2021-11-09 06:48:47 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
lastErrorTime = SystemClock.Instance.GetCurrentInstant();
|
|
|
|
}
|
2021-08-27 15:03:47 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
// private readonly ConcurrentDictionary<ulong, Instant> _lastErrorInChannel = new ConcurrentDictionary<ulong, Instant>();
|
|
|
|
private Instant lastErrorTime { get; set; }
|
2021-08-27 15:03:47 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public async Task SendErrorMessage(ulong channelId, string errorId)
|
|
|
|
{
|
|
|
|
var now = SystemClock.Instance.GetCurrentInstant();
|
|
|
|
if (!ShouldSendErrorMessage(channelId, now))
|
2020-11-16 09:07:57 +00:00
|
|
|
{
|
2021-11-27 02:10:56 +00:00
|
|
|
_logger.Warning("Rate limited sending error message to {ChannelId} with error code {ErrorId}",
|
|
|
|
channelId, errorId);
|
|
|
|
_metrics.Measure.Meter.Mark(BotMetrics.ErrorMessagesSent, "throttled");
|
|
|
|
return;
|
2020-11-16 09:07:57 +00:00
|
|
|
}
|
2020-09-20 20:36:04 +00:00
|
|
|
|
2021-11-27 16:09:08 +00:00
|
|
|
var channelInfo = _botConfig.IsBetaBot
|
|
|
|
? "**#hi-please-break-the-beta-bot** on **[the support server *(click to join)*](https://discord.gg/THvbH59btW)**"
|
|
|
|
: "**#bug-reports-and-errors** on **[the support server *(click to join)*](https://discord.gg/PczBt78)**";
|
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
var embed = new EmbedBuilder()
|
|
|
|
.Color(0xE74C3C)
|
|
|
|
.Title("Internal error occurred")
|
2021-11-27 16:09:08 +00:00
|
|
|
.Description($"For support, please send the error code above in {channelInfo} with a description of what you were doing at the time.")
|
2021-11-27 02:10:56 +00:00
|
|
|
.Footer(new Embed.EmbedFooter(errorId))
|
|
|
|
.Timestamp(now.ToDateTimeOffset().ToString("O"));
|
|
|
|
|
|
|
|
try
|
2020-09-20 20:36:04 +00:00
|
|
|
{
|
2021-11-27 02:10:56 +00:00
|
|
|
await _rest.CreateMessage(channelId,
|
2022-02-26 21:28:20 +00:00
|
|
|
new MessageRequest { Content = $"> **Error code:** `{errorId}`", Embeds = new[] { embed.Build() } });
|
2020-11-16 09:07:57 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
_logger.Information("Sent error message to {ChannelId} with error code {ErrorId}", channelId, errorId);
|
|
|
|
_metrics.Measure.Meter.Mark(BotMetrics.ErrorMessagesSent, "sent");
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
2020-11-16 09:07:57 +00:00
|
|
|
{
|
2021-11-27 02:10:56 +00:00
|
|
|
_logger.Error(e, "Error sending error message to {ChannelId}", channelId);
|
|
|
|
_metrics.Measure.Meter.Mark(BotMetrics.ErrorMessagesSent, "failed");
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
2021-11-09 06:48:47 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
private bool ShouldSendErrorMessage(ulong channelId, Instant now)
|
|
|
|
{
|
|
|
|
// if (_lastErrorInChannel.TryGetValue(channelId, out var lastErrorTime))
|
2021-11-09 06:48:47 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
var startupTime = Instant.FromDateTimeUtc(Process.GetCurrentProcess().StartTime.ToUniversalTime());
|
|
|
|
// don't send errors during startup
|
|
|
|
// mostly because Npgsql throws a bunch of errors when opening connections sometimes???
|
2021-11-27 16:09:08 +00:00
|
|
|
if (now - startupTime < IntervalFromStartup && !_botConfig.IsBetaBot)
|
2021-11-27 02:10:56 +00:00
|
|
|
return false;
|
2020-11-16 09:07:57 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
var interval = now - lastErrorTime;
|
|
|
|
if (interval < MinErrorInterval)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// _lastErrorInChannel[channelId] = now;
|
|
|
|
lastErrorTime = now;
|
|
|
|
return true;
|
2020-09-20 20:36:04 +00:00
|
|
|
}
|
|
|
|
}
|