@@ 2,6 2,12 @@
**Note:** Please add newest entries on top. Use ISO date format YYYY-MM-DD and markdown formatting.
+## 2024-06-02
+- feat: Continue implementing `verify_recipients` function.
+- feat: Implement JSON unmarshalling to a data structure for `verify_recipients` function.
+- feat: Complete Validation API request function.
+- refactor: Add "dryrun mode" line to runtime configuration dump.
+
## 2024-05-27
- feat: Continue implementing `verify_recipients` function.
- feat: Get user, device and license information from Pushover.net.
@@ 107,6 107,20 @@ type ValidConfigurationOptions struct {
attachableFileTypes []string // Stores the file types which can be attached to a notification.
}
+// Following struct mirrors Pushover.net's validation API (see https://pushover.net/api#validate).
+// It's used in the codebase during recipient verification and listing available recipients.
+// Note: All the fields in the following struct are exported in order to make struct tags function.
+//
+// struct tags cannot function on non-exported names, since other libraries can't peek into the struct.
+type pushoverValidationResponse struct {
+ Status int `json:"status"` // Tells us how the request went. 1 is OK, 0 is Not OK.
+ Group int `json:"group"` // Denotes whether the submitted user key belongs to a group. Groups are comprised of many users and used by commercial customers.
+ Devices []string `json:"devices"` // Contains the list of devices (or recipients) under that user key.
+ Licenses []string `json:"licenses"` // Contains the list of licenses that the user has (e.g. iOS, desktop, etc.)
+ Error []string `json:"errors"` // Contains any error if the request didn't return 1. Not present if everything went OK.
+ Request string `json:"request"` // Contains the request UUID.
+}
+
// This function determines our input method (direct call or pipe).
// The idea is to get features (Stat) of stdin, and look whether it's a "character device"
// (e.g.: TTY or anything we can enter characters on). If not, this means our input is
@@ 537,6 551,7 @@ func printState(runtimeConfiguration *RuntimeConfiguration, notification *Notifi
logger.Infof("Pushover API key: %s", runtimeConfiguration.apiKey)
logger.Infof("Pushover user key: %s", runtimeConfiguration.userKey)
logger.Infof("Current logging level is %s.", runtimeConfiguration.logLevel)
+ logger.Infof("Dry run mode: %t", runtimeConfiguration.dryrun)
logger.Infof("Verify recipients before sending: %t", runtimeConfiguration.verifyRecipients)
logger.Infof("Called via pipe: %t", isInputFromPipe(logger))
logger.Infof("") // Leave an empty line.
@@ 593,27 608,48 @@ func printState(runtimeConfiguration *RuntimeConfiguration, notification *Notifi
*/
func getDeviceList(runtimeConfiguration *RuntimeConfiguration, logger *zap.SugaredLogger) (body []byte) {
logger.Debugf("Getting list of users from Pushover.net")
-
+
valuesToSend := url.Values{} // We will build our URL object and send it to the API.
-
+
// Let's add required fields.
valuesToSend.Add("token", runtimeConfiguration.apiKey)
valuesToSend.Add("user", runtimeConfiguration.userKey)
-
+
response, err := http.PostForm("https://api.pushover.net/1/users/validate.json", valuesToSend)
if err != nil {
logger.Panicf("Something went wrong, exiting (error is %s).", err)
}
-
+
defer response.Body.Close() // Automatically close response.Body when the function exits.
body, err = io.ReadAll(response.Body)
logger.Debugf("Got the following response: %s", body)
+
+ // Need to convert this JSON to an object to be able to interact with it nicely.
+ var responseStruct pushoverValidationResponse
+ err = json.Unmarshal(body, &responseStruct)
+
+ if err != nil {
+ logger.Panicf("Cannot unmarshal JSON to a struct (error is %s).", err)
+ }
+
logger.Infof("Pushover Response for User List")
logger.Infof("-------------------------------")
+ logger.Infof("Status: %d", responseStruct.Status)
+
+ if responseStruct.Status == 1 {
+ logger.Infof("Group: %d", responseStruct.Group)
+ logger.Infof("Device(s): %s", responseStruct.Devices)
+ logger.Infof("License(s): %s", responseStruct.Licenses)
+ } else if responseStruct.Status == 0 {
+ // This is not the best way to handle this, but good enough for now.
+ // TODO: Revisit here and polish if necessary
+ logger.Panicf("There's an error during validation (error is %s).", responseStruct.Error[0])
+ }
+ logger.Infof("Request ID: %s", responseStruct.Request)
logger.Infof("") // Always leave an empty line at the end.
return body
@@ 627,7 663,7 @@ func getDeviceList(runtimeConfiguration *RuntimeConfiguration, logger *zap.Sugar
*/
func verifyRecipients(notificationToSend *Notification, runtimeConfiguration *RuntimeConfiguration, logger *zap.SugaredLogger) {
responseFromPushover := getDeviceList(runtimeConfiguration, logger)
-
+
logger.Debugf("Device list query returned as follows: %s", responseFromPushover)
}
@@ 740,7 776,7 @@ func main() {
* We know that the config is sane, because it's hardcoded. We're using built-in
* defaults at that point.
*/
- // TODO: Verify given log level is sane before this line. Otherwise things go bang.
+ // TODO: Verify given log level is sane before this line. Otherwise things go bang.
zapDefaultAtomicLevel, err := zap.ParseAtomicLevel(runtimeConfiguration.logLevel)
if err != nil {