Files
minstrel/internal/api/notify_producers.go
T
bvandeusenandClaude Opus 5.5 069baeb14d feat(notifications): requests and flags reach the people who act on them (#5340)
- A new request still pending after any auto-approval notifies the
  admins (request_pending), but not the requester if they are an admin.
  A request that dedups into one already in flight is not announced again.
- Approving or rejecting a request notifies the requester, and a
  rejection carries the admin's notes as the reason. An admin deciding
  their own request gets nothing.
- The reconciler notifies the requester when their request arrives
  (request_completed), linking the matched album or artist.
- A request the re-acquisition sweeper files and cannot approve itself
  notifies the admins.
- A quarantine flag notifies every admin except the flagger, naming the
  track, the flagger and the reason.

lidarrrequests.Service.CreateTracked reports whether a request was
inserted or deduped; Create wraps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:17:11 -04:00

74 lines
2.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package api
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
"git.fabledsword.com/bvandeusen/minstrel/internal/lidarrrequests"
"git.fabledsword.com/bvandeusen/minstrel/internal/notifications"
)
// Helpers for the handlers that produce notifications (M489). Every call is
// NotifyLogged: a notification never fails the action that caused it.
// userLabel is how a user is named to someone else: their display name, or
// their username when they have none.
func userLabel(u dbq.User) string {
if u.DisplayName != nil && *u.DisplayName != "" {
return *u.DisplayName
}
return u.Username
}
func requestPayload(row dbq.LidarrRequest) notifications.Payload {
return notifications.Payload{
RequestID: uuidToString(row.ID),
RequestKind: string(row.Kind),
Name: lidarrrequests.DisplayName(row),
}
}
// notifyRequestDecided tells the requester an admin approved or declined
// their request. An admin deciding their own request needs no notice.
func (h *handlers) notifyRequestDecided(ctx context.Context, kind notifications.Kind, admin dbq.User, row dbq.LidarrRequest, reason string) {
if row.UserID == admin.ID {
return
}
p := requestPayload(row)
p.Reason = reason
h.notifier.NotifyLogged(ctx, kind, notifications.ToUser(row.UserID), p.Map())
}
// quarantineReasonLabel is a flag reason as the flag popover words it, in
// lower case to sit inside a sentence ("flagged X: bad rip").
func quarantineReasonLabel(reason string) string {
switch reason {
case "bad_rip":
return "bad rip"
case "wrong_file":
return "wrong file"
case "wrong_tags":
return "wrong tags"
case "duplicate":
return "duplicate"
default:
return ""
}
}
// trackLabel names a track "Artist – Title" for a notification, falling back
// to the title alone, or to nothing, rather than failing the caller.
func (h *handlers) trackLabel(ctx context.Context, trackID pgtype.UUID) string {
q := dbq.New(h.pool)
t, err := q.GetTrackByID(ctx, trackID)
if err != nil {
return ""
}
if a, aerr := q.GetArtistByID(ctx, t.ArtistID); aerr == nil && a.Name != "" {
return a.Name + " – " + t.Title
}
return t.Title
}