44 "context"
55 "errors"
66 "fmt"
7+ "iter"
78 "log/slog"
9+ "maps"
810 "net/url"
911 "time"
1012
@@ -16,42 +18,63 @@ var (
1618 ErrInvalidURI = errors .New ("invalid URI" )
1719)
1820
21+ type Destination struct {
22+ URI string
23+ SuppressFor time.Duration // wait this long after manual actions before sending
24+ }
25+
1926type Notifications struct {
20- mappings map [string ][]string
27+ mappings map [string ][]Destination
2128}
2229
23- func (n * Notifications ) AddURI (event string , uri string ) error {
30+ func (n * Notifications ) AddDestination (event string , uri string , suppressFor time. Duration ) error {
2431 if n .mappings == nil {
25- n .mappings = map [string ][]string {}
32+ n .mappings = map [string ][]Destination {}
2633 }
2734 if _ , err := url .Parse (uri ); err != nil {
2835 return fmt .Errorf ("parse uri: %w" , err )
2936 }
30- n .mappings [event ] = append (n .mappings [event ], uri )
37+ n .mappings [event ] = append (n .mappings [event ], Destination { URI : uri , SuppressFor : suppressFor } )
3138 return nil
3239}
3340
34- func (n * Notifications ) IterMappings (f func (string , string )) {
35- for event , uris := range n .mappings {
36- for _ , uri := range uris {
37- f (event , uri )
41+ func (n * Notifications ) Destinations () iter.Seq2 [string , Destination ] {
42+ mappings := maps .Clone (n .mappings )
43+
44+ return func (yield func (string , Destination ) bool ) {
45+ for event , destinations := range mappings {
46+ for _ , destination := range destinations {
47+ if ! yield (event , destination ) {
48+ return
49+ }
50+ }
3851 }
3952 }
4053}
54+
4155func (n * Notifications ) Sendf (ctx context.Context , event string , f string , a ... any ) {
4256 n .Send (ctx , event , fmt .Sprintf (f , a ... ))
4357}
4458
4559// Send a simple string for now, maybe later message could instead be a type which
4660// implements a notifications.Bodyer or something so that notifiers can send rich notifications.
4761func (n * Notifications ) Send (ctx context.Context , event string , message string ) {
48- if actionTime , ok := ctx .Value (actionKey {}).(time.Time ); ok && time .Since (actionTime ) < 30 * time .Second {
49- slog .DebugContext (ctx , "suppressing notification for recent manual action" ,
50- "event" , event , "since_manual" , time .Since (actionTime ))
62+ destinations := n .mappings [event ]
63+ if len (destinations ) == 0 {
5164 return
5265 }
5366
54- uris := n .mappings [event ]
67+ var timeSinceAction time.Duration
68+ if actionTime , ok := ctx .Value (actionKey {}).(time.Time ); ok {
69+ timeSinceAction = time .Since (actionTime )
70+ }
71+
72+ var uris []string
73+ for _ , dest := range destinations {
74+ if timeSinceAction == 0 || timeSinceAction >= dest .SuppressFor {
75+ uris = append (uris , dest .URI )
76+ }
77+ }
5578 if len (uris ) == 0 {
5679 return
5780 }
0 commit comments