func main() { var deviceToken, filename, password, environment string flag.StringVar(&deviceToken, "d", "", "Device token") flag.StringVar(&filename, "c", "", "Path to p12 certificate file") flag.StringVar(&password, "p", "", "Password for p12 file.") flag.StringVar(&environment, "e", "development", "Environment") flag.Parse() cert, key, err := certificate.Load(filename, password) if err != nil { log.Fatal(err) } service := push.Service{ Client: push.NewClient(certificate.TLS(cert, key)), Host: push.Development, } if environment == "production" { service.Host = push.Production } p := payload.APS{ Alert: payload.Alert{Body: "Hello HTTP/2"}, Badge: badge.New(42), } id, err := service.Push(deviceToken, &push.Headers{}, p) if err != nil { log.Fatal(err) } log.Println("apns-id:", id) }
func main() { var filename, password string flag.StringVar(&filename, "c", "", "Path to p12 certificate file") flag.StringVar(&password, "p", "", "Password for p12 file.") flag.Parse() var err error cert, privateKey, err = certificate.Load(filename, password) if err != nil { log.Fatal(err) } service = push.Service{ Client: push.NewClient(certificate.TLS(cert, privateKey)), Host: push.Production, } r := mux.NewRouter() r.HandleFunc("/", indexHandler).Methods("GET") r.HandleFunc("/request", requestPermissionHandler) r.HandleFunc("/push", pushHandler) r.HandleFunc("/click", clickHandler).Methods("GET") // WebServiceURL endpoints r.HandleFunc("/v1/pushPackages/{websitePushID}", pushPackagesHandler).Methods("POST") r.HandleFunc("/v1/devices/{deviceToken}/registrations/{websitePushID}", registerDeviceHandler).Methods("POST") r.HandleFunc("/v1/devices/{deviceToken}/registrations/{websitePushID}", forgetDeviceHandler).Methods("DELETE") r.HandleFunc("/v1/log", logHandler).Methods("POST") http.ListenAndServe(":5000", r) }
func main() { var filename string var alert string var badgeInt int var sound string var category string var environmentString string var passphrase string var contentAvailable bool var payloadString string var payloadFile string var tokensFile string app := cli.NewApp() app.Name = "thunderstorm" // app.EnableBashCompletion = true app.Usage = "push TOKEN [...]" app.Version = "0.2.0" app.Commands = []cli.Command{ { Name: "push", Usage: "Sends an Apple Push Notification to specified devices", Flags: []cli.Flag{ cli.StringFlag{ Name: "certificate, c", Value: "", Usage: "Path to certificate (.p12) file", Destination: &filename, }, cli.StringFlag{ Name: "alert, m", Value: "", Usage: "Body of the alert to send in the push notification", Destination: &alert, }, cli.IntFlag{ Name: "badge, b", Usage: "Badge number to set with the push notification", Destination: &badgeInt, }, cli.StringFlag{ Name: "sound, s", Usage: "Sound to play with the notification", Destination: &sound, }, cli.StringFlag{ Name: "category, y", Usage: "Category of notification", Destination: &category, }, cli.StringFlag{ Name: "environment, e", Usage: "Environment to send push notification (production or development (default))", Destination: &environmentString, }, cli.StringFlag{ Name: "passphrase, p", Usage: "Provides the certificate passphrase", Destination: &passphrase, }, cli.BoolFlag{ Name: "content-available, n", Usage: "Indicates content available", Destination: &contentAvailable, }, cli.StringFlag{ Name: "payload, P", Usage: "JSON payload for notifications", Destination: &payloadString, }, cli.StringFlag{ Name: "payload-file, f", Usage: "Path of JSON file containing the payload to be sent", Destination: &payloadFile, }, cli.StringFlag{ Name: "tokens-path, t", Usage: "Path of JSON file containing the tokens", Destination: &tokensFile, }, }, // TODO: support low priority notifications // TODO: support expiration time // TODO: support id // TODO: support apns-collapse-id // TODO: support threads // TODO: support buford features https://github.com/RobotsAndPencils/buford // TODO: use a queue to send push notifications // TODO: support other parameters https://github.com/nomad/houston/blob/master/bin/apn Action: func(c *cli.Context) error { deviceToken := c.Args().First() cert, err := certificate.Load(filename, passphrase) if err != nil { // TODO detect if the error is // "pkcs12: expected exactly two safe bags in the PFX PDU" // and suggest to export the certificate from the keychain selecting // only one row log.Fatal(err) return err } // tls := certificate.TLS(cert, private) certificate.TopicFromCert(cert) commonName := cert.Leaf.Subject.CommonName bundle := strings.Replace(commonName, "Apple Push Services: ", "", 1) var environment = push.Development if environmentString == "production" { environment = push.Production } client, err := push.NewClient(cert) if err != nil { log.Fatal(err) } service := push.Service{ Client: client, Host: environment, } if payloadFile != "" { buffer, err := ioutil.ReadFile(payloadFile) if err == nil { payloadString = string(buffer) } } var p map[string]interface{} if payloadString != "" { error := json.Unmarshal([]byte(payloadString), &p) if error != nil { log.Fatal("Cannot parse payload") return err } } if p == nil { bUint := uint(badgeInt) // TODO: use Alert title, body and action or let it be with paylod // TODO: support mutable-content modifier pay := payload.APS{ Alert: payload.Alert{Body: alert}, Badge: badge.New(bUint), Sound: sound, Category: category, ContentAvailable: contentAvailable, } p = pay.Map() } headers := &push.Headers{ Topic: bundle, } tokensString := "" if tokensFile != "" { buffer, err := ioutil.ReadFile(tokensFile) if err == nil { tokensString = string(buffer) } var tokens []string if tokensString != "" { error := json.Unmarshal([]byte(tokensString), &tokens) if error != nil { log.Fatal("Cannot parse tokens array", error) return error } for _, token := range tokens { send(token, headers, p, &service) } } } else { send(deviceToken, headers, p, &service) } return nil }, }, } app.Run(os.Args) }