#nullable enable using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Client.Models; using BTCPayServer.Data; using BTCPayServer.HostedServices; using BTCPayServer.Plugins.Emails; using BTCPayServer.Plugins.Emails.HostedServices; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using StoreData = BTCPayServer.Data.StoreData; namespace BTCPayServer.Plugins.Webhooks.HostedServices; /// /// This class listen to events and create webhook notifications and trigger event from it. /// public class WebhookProviderHostedService( EventAggregator eventAggregator, ApplicationDbContextFactory dbContextFactory, IEnumerable webhookTriggerProviders, WebhookSender webhookSender, ILogger logger) : EventHostedServiceBase(eventAggregator, logger) { class WebhookTriggerOwner(WebhookTriggerProvider provider, WebhookTriggerContext ctx) : ITriggerOwner { public Task BeforeSending(EmailRuleMatchContext context) => provider.BeforeSending(context, ctx); } protected override void SubscribeToEvents() { SubscribeAny(); } protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken) { var (provider, webhookEvent) = webhookTriggerProviders .Select(o => (o, o.GetWebhookEvent(evt))) .FirstOrDefault(o => o.Item2 is not null); if (webhookEvent is null || provider is null) return; var webhooks = await webhookSender.GetWebhooks(webhookEvent.StoreId, webhookEvent.Type); foreach (var webhook in webhooks) { var ev = Clone(webhookEvent); ev.WebhookId = webhook.Id; var delivery = Data.WebhookDeliveryData.Create(webhook.Id); ev.DeliveryId = delivery.Id; ev.OriginalDeliveryId = delivery.Id; ev.Timestamp = delivery.Timestamp; ev.IsRedelivery = false; webhookSender.EnqueueDelivery(new(webhook.Id, ev, delivery, webhook.GetBlob())); } if (await GetStore(webhookEvent) is {} store) { var ctx = provider.CreateWebhookTriggerContext(store, evt, webhookEvent); var triggerEvent = new TriggerEvent(webhookEvent.StoreId, EmailRuleData.GetWebhookTriggerName(webhookEvent.Type), await provider.GetEmailModel(ctx), new WebhookTriggerOwner(provider, ctx)); EventAggregator.Publish(triggerEvent); } } private StoreWebhookEvent Clone(StoreWebhookEvent webhookEvent) => (StoreWebhookEvent)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(webhookEvent), webhookEvent.GetType(), WebhookSender.DefaultSerializerSettings)!; private async Task GetStore(StoreWebhookEvent webhookEvent) { await using var ctx = dbContextFactory.CreateContext(); return await ctx.Stores.FindAsync(webhookEvent.StoreId); } }