diff --git a/PluralKit.Bot/Services/WebhookCacheService.cs b/PluralKit.Bot/Services/WebhookCacheService.cs index 06791953..4475bba3 100644 --- a/PluralKit.Bot/Services/WebhookCacheService.cs +++ b/PluralKit.Bot/Services/WebhookCacheService.cs @@ -1,92 +1,92 @@ -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using Discord; -using Discord.Webhook; -using Discord.WebSocket; - -using Serilog; - -namespace PluralKit.Bot -{ - public class WebhookCacheService - { - public static readonly string WebhookName = "PluralKit Proxy Webhook"; - - private DiscordShardedClient _client; - private ConcurrentDictionary>> _webhooks; - - private ILogger _logger; - - public WebhookCacheService(IDiscordClient client, ILogger logger) - { - _client = client as DiscordShardedClient; - _logger = logger.ForContext(); - _webhooks = new ConcurrentDictionary>>(); - } - - public async Task GetWebhook(ulong channelId) - { - var channel = _client.GetChannel(channelId) as ITextChannel; - if (channel == null) return null; - return await GetWebhook(channel); - } - - public async Task GetWebhook(ITextChannel channel) - { - // We cache the webhook through a Lazy>, this way we make sure to only create one webhook per channel - // If the webhook is requested twice before it's actually been found, the Lazy wrapper will stop the - // webhook from being created twice. - var lazyWebhookValue = - _webhooks.GetOrAdd(channel.Id, new Lazy>(() => GetOrCreateWebhook(channel))); - - // It's possible to "move" a webhook to a different channel after creation - // Here, we ensure it's actually still pointing towards the proper channel, and if not, wipe and refetch one. - var webhook = await lazyWebhookValue.Value; - if (webhook.ChannelId != channel.Id) return await InvalidateAndRefreshWebhook(webhook); - return webhook; - } - - public async Task InvalidateAndRefreshWebhook(IWebhook webhook) - { - _logger.Information("Refreshing webhook for channel {Channel}", webhook.ChannelId); - - _webhooks.TryRemove(webhook.ChannelId, out _); - return await GetWebhook(webhook.Channel); - } - - private async Task GetOrCreateWebhook(ITextChannel channel) - { - _logger.Debug("Webhook for channel {Channel} not found in cache, trying to fetch", channel.Id); - return await FindExistingWebhook(channel) ?? await DoCreateWebhook(channel); - } - - private async Task FindExistingWebhook(ITextChannel channel) - { - _logger.Debug("Finding webhook for channel {Channel}", channel.Id); - try - { - return (await channel.GetWebhooksAsync()).FirstOrDefault(IsWebhookMine); - } - catch (HttpRequestException e) - { - _logger.Warning(e, "Error occurred while fetching webhook list"); - // This happens sometimes when Discord returns a malformed request for the webhook list - // Nothing we can do than just assume that none exist and return null. - return null; - } - } - - private Task DoCreateWebhook(ITextChannel channel) - { - _logger.Information("Creating new webhook for channel {Channel}", channel.Id); - return channel.CreateWebhookAsync(WebhookName); - } - - private bool IsWebhookMine(IWebhook arg) => arg.Creator.Id == _client.GetShardFor(arg.Guild).CurrentUser.Id && arg.Name == WebhookName; - - public int CacheSize => _webhooks.Count; - } +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Discord; +using Discord.Webhook; +using Discord.WebSocket; + +using Serilog; + +namespace PluralKit.Bot +{ + public class WebhookCacheService + { + public static readonly string WebhookName = "PluralKit Proxy Webhook"; + + private DiscordShardedClient _client; + private ConcurrentDictionary>> _webhooks; + + private ILogger _logger; + + public WebhookCacheService(IDiscordClient client, ILogger logger) + { + _client = client as DiscordShardedClient; + _logger = logger.ForContext(); + _webhooks = new ConcurrentDictionary>>(); + } + + public async Task GetWebhook(ulong channelId) + { + var channel = _client.GetChannel(channelId) as ITextChannel; + if (channel == null) return null; + return await GetWebhook(channel); + } + + public async Task GetWebhook(ITextChannel channel) + { + // We cache the webhook through a Lazy>, this way we make sure to only create one webhook per channel + // If the webhook is requested twice before it's actually been found, the Lazy wrapper will stop the + // webhook from being created twice. + var lazyWebhookValue = + _webhooks.GetOrAdd(channel.Id, new Lazy>(() => GetOrCreateWebhook(channel))); + + // It's possible to "move" a webhook to a different channel after creation + // Here, we ensure it's actually still pointing towards the proper channel, and if not, wipe and refetch one. + var webhook = await lazyWebhookValue.Value; + if (webhook.ChannelId != channel.Id) return await InvalidateAndRefreshWebhook(webhook); + return webhook; + } + + public async Task InvalidateAndRefreshWebhook(IWebhook webhook) + { + _logger.Information("Refreshing webhook for channel {Channel}", webhook.ChannelId); + + _webhooks.TryRemove(webhook.ChannelId, out _); + return await GetWebhook(webhook.Channel); + } + + private async Task GetOrCreateWebhook(ITextChannel channel) + { + _logger.Debug("Webhook for channel {Channel} not found in cache, trying to fetch", channel.Id); + return await FindExistingWebhook(channel) ?? await DoCreateWebhook(channel); + } + + private async Task FindExistingWebhook(ITextChannel channel) + { + _logger.Debug("Finding webhook for channel {Channel}", channel.Id); + try + { + return (await channel.GetWebhooksAsync()).FirstOrDefault(IsWebhookMine); + } + catch (HttpRequestException e) + { + _logger.Warning(e, "Error occurred while fetching webhook list"); + // This happens sometimes when Discord returns a malformed request for the webhook list + // Nothing we can do than just assume that none exist and return null. + return null; + } + } + + private Task DoCreateWebhook(ITextChannel channel) + { + _logger.Information("Creating new webhook for channel {Channel}", channel.Id); + return channel.CreateWebhookAsync(WebhookName); + } + + private bool IsWebhookMine(IWebhook arg) => arg.Creator.Id == _client.GetShardFor(arg.Guild).CurrentUser.Id && arg.Name == WebhookName; + + public int CacheSize => _webhooks.Count; + } } \ No newline at end of file