Просмотр исходного кода

Support for Firebase ~poll keepalive topic that wakes up iOS devices every 20 minutes

Philipp Heckel 3 лет назад
Родитель
Сommit
af76a2606d
4 измененных файлов с 33 добавлено и 2 удалено
  1. 4 0
      docs/releases.md
  2. 4 1
      server/config.go
  3. 7 1
      server/server.go
  4. 18 0
      server/server_firebase.go

+ 4 - 0
docs/releases.md

@@ -38,6 +38,10 @@ some known issues, which will be addressed in follow-up releases).
 
 ## ntfy server v1.24.0 (UNRELEASED)
 
+**Features:**
+
+* Regularly send Firebase keepalive messages to ~poll topic to support self-hosted servers (no ticket)
+
 **Bugs:**
 
 * Support emails without `Content-Type` ([#265](https://github.com/binwiederhier/ntfy/issues/265), thanks to [@dmbonsall](https://github.com/dmbonsall))

+ 4 - 1
server/config.go

@@ -13,7 +13,8 @@ const (
 	DefaultAtSenderInterval          = 10 * time.Second
 	DefaultMinDelay                  = 10 * time.Second
 	DefaultMaxDelay                  = 3 * 24 * time.Hour
-	DefaultFirebaseKeepaliveInterval = 3 * time.Hour // Not too frequently to save battery
+	DefaultFirebaseKeepaliveInterval = 3 * time.Hour    // ~control topic (Android), not too frequently to save battery
+	DefaultFirebasePollInterval      = 20 * time.Minute // ~poll topic (iOS), max. 2-3 times per hour (see docs)
 )
 
 // Defines all global and per-visitor limits
@@ -67,6 +68,7 @@ type Config struct {
 	WebRootIsApp                         bool
 	AtSenderInterval                     time.Duration
 	FirebaseKeepaliveInterval            time.Duration
+	FirebasePollInterval                 time.Duration
 	SMTPSenderAddr                       string
 	SMTPSenderUser                       string
 	SMTPSenderPass                       string
@@ -117,6 +119,7 @@ func NewConfig() *Config {
 		MaxDelay:                             DefaultMaxDelay,
 		AtSenderInterval:                     DefaultAtSenderInterval,
 		FirebaseKeepaliveInterval:            DefaultFirebaseKeepaliveInterval,
+		FirebasePollInterval:                 DefaultFirebasePollInterval,
 		TotalTopicLimit:                      DefaultTotalTopicLimit,
 		VisitorSubscriptionLimit:             DefaultVisitorSubscriptionLimit,
 		VisitorAttachmentTotalSizeLimit:      DefaultVisitorAttachmentTotalSizeLimit,

+ 7 - 1
server/server.go

@@ -91,6 +91,7 @@ var (
 
 const (
 	firebaseControlTopic     = "~control"                // See Android if changed
+	firebasePollTopic        = "~poll"                   // See iOS if changed
 	emptyMessageBody         = "triggered"               // Used if message body is empty
 	defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
 	encodingBase64           = "base64"
@@ -1074,7 +1075,12 @@ func (s *Server) runFirebaseKeepaliver() {
 		select {
 		case <-time.After(s.config.FirebaseKeepaliveInterval):
 			if err := s.firebase(newKeepaliveMessage(firebaseControlTopic)); err != nil {
-				log.Printf("error sending Firebase keepalive message: %s", err.Error())
+				log.Printf("error sending Firebase keepalive message to %s: %s", firebaseControlTopic, err.Error())
+			}
+		case <-time.After(s.config.FirebasePollInterval):
+			log.Printf("Sending to timer topic %s", firebasePollTopic)
+			if err := s.firebase(newKeepaliveMessage(firebasePollTopic)); err != nil {
+				log.Printf("error sending Firebase keepalive message to %s: %s", firebasePollTopic, err.Error())
 			}
 		case <-s.closeChan:
 			return

+ 18 - 0
server/server_firebase.go

@@ -80,6 +80,24 @@ func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, erro
 			"event": m.Event,
 			"topic": m.Topic,
 		}
+		// Silent notification; only 2-3 per hour are allowed; delivery not guaranteed
+		// See https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
+		apnsData := make(map[string]interface{})
+		for k, v := range data {
+			apnsData[k] = v
+		}
+		apnsConfig = &messaging.APNSConfig{
+			Headers: map[string]string{
+				"apns-push-type": "background",
+				"apns-priority":  "5",
+			},
+			Payload: &messaging.APNSPayload{
+				Aps: &messaging.Aps{
+					ContentAvailable: true,
+				},
+				CustomData: apnsData,
+			},
+		}
 	case messageEvent:
 		allowForward := true
 		if auther != nil {