The review of 2026-10-09 (H4): a link's code shown on the desk stayed in dunst's history, which node-notifier.history serves to every caller of the mesh's verbs, agents among them; and a code sent to a paused notifier is held and shown later, to whoever is then at the screen. send now takes secret (transient, category mesh.secret) and transient; the dunstrc's rules keep both out of the history, and history leaves out any that got there. The pause, read from dunst itself, is said as the event paused at start, on change and every two minutes, for the desk to read.
72 lines
2.0 KiB
Go
72 lines
2.0 KiB
Go
package main
|
|
|
|
// Whether notifications are paused, said as this module's event `paused` {paused, machine} (the review of
|
|
// 2026-10-09, C4): at start, on every change, and again every PausedEvery. The desk shows a link's code only on
|
|
// a machine it heard is not paused lately, and says it cannot send one while none is: a code held back by a
|
|
// paused notifier would appear later, to whoever is then at the screen. The pause is read from dunst itself
|
|
// (`dunstctl is-paused`), so one set by a key or by hand is said like one set by dunst_pause.
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// PausedEvent is the event's name; PausedEvery how often it is said when nothing changed; PausedLook how
|
|
// often dunst is asked.
|
|
PausedEvent = "paused"
|
|
PausedEvery = 2 * time.Minute
|
|
PausedLook = 5 * time.Second
|
|
)
|
|
|
|
// pauseWatch says the pause; its two reaches are seams a test replaces.
|
|
type pauseWatch struct {
|
|
isPaused func() (bool, error)
|
|
say func(paused bool) error
|
|
saidAt time.Time
|
|
said bool
|
|
known bool
|
|
}
|
|
|
|
// step looks once, and says it when it changed or was said PausedEvery ago. Nothing is said while dunst
|
|
// cannot be asked: the desk then hears nothing, and takes the machine as not known.
|
|
func (w *pauseWatch) step(now time.Time) {
|
|
paused, err := w.isPaused()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if w.known && paused == w.said && now.Sub(w.saidAt) < PausedEvery {
|
|
return
|
|
}
|
|
if w.say(paused) == nil {
|
|
w.known, w.said, w.saidAt = true, paused, now
|
|
}
|
|
}
|
|
|
|
func isPaused() (bool, error) {
|
|
out, err := dunstctl("is-paused")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
switch strings.TrimSpace(out) {
|
|
case "true":
|
|
return true, nil
|
|
case "false":
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("dunstctl is-paused answered %q", out)
|
|
}
|
|
|
|
// watchPaused runs the watch for as long as the bundle runs.
|
|
func watchPaused() {
|
|
w := &pauseWatch{isPaused: isPaused, say: func(p bool) error {
|
|
return emit(PausedEvent, map[string]any{"paused": p, "machine": os.Getenv("MESH_NODE")})
|
|
}}
|
|
for {
|
|
w.step(time.Now())
|
|
time.Sleep(PausedLook)
|
|
}
|
|
}
|