| 81 | } |
| 82 | |
| 83 | func (s *Sender) SendWebhook(ctx context.Context, job workertypes.IncomingWebhookDeliveryJob) error { |
| 84 | slog.InfoContext(ctx, "sending webhook", "channelID", job.ChannelID) |
| 85 | |
| 86 | mgr, err := s.getManager(ctx, job) |
| 87 | if err != nil { |
| 88 | // If we fail here, it's permanent when trying to get the manager. |
| 89 | s.recordFailure(ctx, job, err, true) |
| 90 | |
| 91 | return fmt.Errorf("failed to prepare webhook: %w", err) |
| 92 | } |
| 93 | |
| 94 | if err := mgr.sender.Send(ctx); err != nil { |
| 95 | isTransient := errors.Is(err, ErrTransientWebhook) |
| 96 | s.recordFailure(ctx, job, err, !isTransient) |
| 97 | |
| 98 | if isTransient { |
| 99 | return errors.Join(event.ErrTransientFailure, err) |
| 100 | } |
| 101 | |
| 102 | return fmt.Errorf("failed to send webhook: %w", err) |
| 103 | } |
| 104 | |
| 105 | if err := s.stateManager.RecordSuccess(ctx, job.ChannelID, time.Now(), job.WebhookEventID); err != nil { |
| 106 | slog.WarnContext(ctx, "failed to record success", "error", err) |
| 107 | } |
| 108 | |
| 109 | return nil |
| 110 | } |
| 111 | |
| 112 | func (s *Sender) recordFailure(ctx context.Context, job workertypes.IncomingWebhookDeliveryJob, |
| 113 | err error, permanent bool) { |