server_firebase.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package server
  2. import (
  3. "context"
  4. firebase "firebase.google.com/go"
  5. "firebase.google.com/go/messaging"
  6. "fmt"
  7. "google.golang.org/api/option"
  8. "heckel.io/ntfy/auth"
  9. "strings"
  10. )
  11. func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subscriber, error) {
  12. fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(credentialsFile))
  13. if err != nil {
  14. return nil, err
  15. }
  16. msg, err := fb.Messaging(context.Background())
  17. if err != nil {
  18. return nil, err
  19. }
  20. return func(m *message) error {
  21. fbm, err := toFirebaseMessage(m, auther)
  22. if err != nil {
  23. return err
  24. }
  25. _, err = msg.Send(context.Background(), fbm)
  26. return err
  27. }, nil
  28. }
  29. func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, error) {
  30. var data map[string]string // Mostly matches https://ntfy.sh/docs/subscribe/api/#json-message-format
  31. switch m.Event {
  32. case keepaliveEvent, openEvent:
  33. data = map[string]string{
  34. "id": m.ID,
  35. "time": fmt.Sprintf("%d", m.Time),
  36. "event": m.Event,
  37. "topic": m.Topic,
  38. }
  39. case messageEvent:
  40. allowForward := true
  41. if auther != nil {
  42. allowForward = auther.Authorize(nil, m.Topic, auth.PermissionRead) == nil
  43. }
  44. if allowForward {
  45. data = map[string]string{
  46. "id": m.ID,
  47. "time": fmt.Sprintf("%d", m.Time),
  48. "event": m.Event,
  49. "topic": m.Topic,
  50. "priority": fmt.Sprintf("%d", m.Priority),
  51. "tags": strings.Join(m.Tags, ","),
  52. "click": m.Click,
  53. "title": m.Title,
  54. "message": m.Message,
  55. "encoding": m.Encoding,
  56. }
  57. if m.Attachment != nil {
  58. data["attachment_name"] = m.Attachment.Name
  59. data["attachment_type"] = m.Attachment.Type
  60. data["attachment_size"] = fmt.Sprintf("%d", m.Attachment.Size)
  61. data["attachment_expires"] = fmt.Sprintf("%d", m.Attachment.Expires)
  62. data["attachment_url"] = m.Attachment.URL
  63. }
  64. } else {
  65. // If anonymous read for a topic is not allowed, we cannot send the message along
  66. // via Firebase. Instead, we send a "poll_request" message, asking the client to poll.
  67. data = map[string]string{
  68. "id": m.ID,
  69. "time": fmt.Sprintf("%d", m.Time),
  70. "event": pollRequestEvent,
  71. "topic": m.Topic,
  72. }
  73. }
  74. }
  75. var androidConfig *messaging.AndroidConfig
  76. if m.Priority >= 4 {
  77. androidConfig = &messaging.AndroidConfig{
  78. Priority: "high",
  79. }
  80. }
  81. return maybeTruncateFCMMessage(&messaging.Message{
  82. Topic: m.Topic,
  83. Data: data,
  84. Android: androidConfig,
  85. }), nil
  86. }