mirror of
https://github.com/lone-cloud/prism
synced 2026-06-03 08:43:10 -07:00
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package notification
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"prism/internal/signal"
|
|
)
|
|
|
|
type Dispatcher struct {
|
|
store *Store
|
|
signalClient *signal.Client
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewDispatcher(store *Store, signalClient *signal.Client, logger *slog.Logger) *Dispatcher {
|
|
return &Dispatcher{
|
|
store: store,
|
|
signalClient: signalClient,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (d *Dispatcher) Send(endpoint string, notif Notification) error {
|
|
mapping, err := d.store.GetEndpointMapping(endpoint)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get mapping: %w", err)
|
|
}
|
|
|
|
if mapping == nil {
|
|
appName := strings.TrimPrefix(endpoint, "ntfy-")
|
|
appName = strings.TrimPrefix(appName, "proton-")
|
|
|
|
if err := d.store.Register(endpoint, appName, ChannelSignal, nil, nil); err != nil {
|
|
return fmt.Errorf("failed to register endpoint: %w", err)
|
|
}
|
|
|
|
mapping, err = d.store.GetEndpointMapping(endpoint)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get mapping after registration: %w", err)
|
|
}
|
|
}
|
|
|
|
switch mapping.Channel {
|
|
case ChannelWebhook:
|
|
return d.sendWebhook(mapping, notif)
|
|
case ChannelSignal:
|
|
return d.sendSignal(mapping, notif)
|
|
default:
|
|
return fmt.Errorf("unknown channel: %s", mapping.Channel)
|
|
}
|
|
}
|
|
|
|
func (d *Dispatcher) sendSignal(mapping *Mapping, notif Notification) error {
|
|
groupID := mapping.GroupID
|
|
if groupID == nil || *groupID == "" {
|
|
newGroupID, err := d.createGroup(mapping.AppName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create group: %w", err)
|
|
}
|
|
groupID = &newGroupID
|
|
|
|
if err := d.store.UpdateGroupID(mapping.Endpoint, *groupID); err != nil {
|
|
d.logger.Warn("Failed to update group ID", "error", err)
|
|
}
|
|
}
|
|
|
|
if err := d.sendGroupMessage(*groupID, notif); err != nil {
|
|
return fmt.Errorf("failed to send group message: %w", err)
|
|
}
|
|
|
|
d.logger.Debug("Sent Signal notification", "app", mapping.AppName, "message", notif.Message)
|
|
return nil
|
|
}
|
|
|
|
func (d *Dispatcher) createGroup(appName string) (string, error) {
|
|
params := map[string]interface{}{
|
|
"name": appName,
|
|
}
|
|
|
|
result, err := d.signalClient.Call("createGroup", params)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var response struct {
|
|
GroupID string `json:"groupId"`
|
|
}
|
|
if err := json.Unmarshal(result, &response); err != nil {
|
|
return "", fmt.Errorf("failed to parse createGroup response: %w", err)
|
|
}
|
|
|
|
return response.GroupID, nil
|
|
}
|
|
|
|
func (d *Dispatcher) sendGroupMessage(groupID string, notif Notification) error {
|
|
message := notif.Message
|
|
if notif.Title != "" {
|
|
message = fmt.Sprintf("%s\n\n%s", notif.Title, notif.Message)
|
|
}
|
|
|
|
params := map[string]interface{}{
|
|
"groupId": groupID,
|
|
"message": message,
|
|
}
|
|
|
|
_, err := d.signalClient.Call("sendGroupMessage", params)
|
|
return err
|
|
}
|
|
|
|
func (d *Dispatcher) sendWebhook(mapping *Mapping, notif Notification) error {
|
|
if mapping.UpEndpoint == nil || *mapping.UpEndpoint == "" {
|
|
return fmt.Errorf("webhook endpoint not configured for %s", mapping.AppName)
|
|
}
|
|
|
|
payload, err := json.Marshal(notif)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal notification: %w", err)
|
|
}
|
|
|
|
resp, err := http.Post(*mapping.UpEndpoint, "application/json", bytes.NewReader(payload))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send webhook: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
|
|
}
|
|
|
|
d.logger.Debug("Sent webhook notification", "app", mapping.AppName, "endpoint", *mapping.UpEndpoint)
|
|
return nil
|
|
}
|