DEV Community

Cover image for Send Daily Push Notifications to Your Phone Using Golang
Bravian
Bravian

Posted on

Send Daily Push Notifications to Your Phone Using Golang

Build a New Year’s Resolution BOT

2024 was kind of a shitshow—we lost track of what we really wanted to achieve and got caught up in the chaos of everyday life. So this year, I decided to build a little bot that sends me a daily reminder of exactly what I set out to do. Not only does it keep me accountable, but it also lets me play with Golang, learn a scheduling package, and integrate with an awesome notification service. Let’s dive in!


Why I Built This Bot

  • Personal Accountability:

I needed a daily nudge to remind me of my New Year’s resolutions.

  • A Learning Opportunity:

Combining Golang’s efficient concurrency, a user-friendly scheduler (gocron ), and the straightforward Pushover API via its Go wrapper (gregdel/pushover) was too good an opportunity to pass up.

  • Keeping It Simple:

My resolutions are stored in a plain text file (resolutions.txt), one bullet point per line. Updating goals is as simple as editing a notepad file.


Setting Up Your Pushover Account

Before you can send notifications, you need to create a Pushover account and set up your application:

  1. Create a Pushover Account:

    Head over to Pushover and sign up for an account. Once you’re logged in, you’ll see your unique User Key on your dashboard.

  2. Register Your Application:

    Click on the “Apps & Plugins” section in your dashboard and create a new application. Give it a name (e.g., "Resolution Bot"). This process will generate an API token for your app.

    Note: Keep both your User Key and API token secure.

  3. Download the Pushover Mobile App:

    Install the Pushover application on your mobile device from the App Store (iOS) or Google Play (Android). This app will display the notifications sent by your bot.

  4. Notification Screenshot:

    Once you run the bot, you should see a notification on your phone.
    Notification Screenshot


How It Works

  1. Resolutions File: Your resolutions are stored in resolutions.txt. For example:
   - Exercise for 30 minutes
   - Read 20 pages of a book
   - Meditate for 10 minutes
   - Write blogs everyday
Enter fullscreen mode Exit fullscreen mode

Every day, the bot reads these goals and sends them as a push notification.

  1. Push Notifications with Pushover:

    Using the Pushover API, our bot sends notifications to your phone. The gregdel/pushover package wraps the API in a simple-to-use Go interface.

  2. Scheduling with gocron:

    We use the gocron package to schedule the job. With its human-friendly syntax, setting a daily reminder (e.g., at 8:00 AM) is a breeze.

  3. Running in the Background on Linux:

    Rather than keeping a terminal open all day, you can run this program as a background service on Linux using systemd (or nohup) so that it restarts automatically on boot.


Reading Resolutions from a File

Let’s keep our goals dynamic. Instead of hardcoding your resolutions, store them in a simple text file and read them at runtime:


func getResolutions() (string, error) {
    // Open file with defer to ensure it's closed
    file, err := os.Open("resolutions.txt")
    if err != nil {
        return "", fmt.Errorf("failed to open resolutions.txt: %w", err)
    }
    defer file.Close()

    // Read file contents
    data, err := io.ReadAll(file)
    if err != nil {
        return "", fmt.Errorf("failed to read resolutions.txt: %w", err)
    }

    // Handle empty file case
    if len(data) == 0 {
        return "", fmt.Errorf("resolutions.txt is empty")
    }

    return string(data), nil
}
Enter fullscreen mode Exit fullscreen mode

Complete Code Example

Below is the full Go code that puts everything together. The bot reads your resolutions, schedules a daily notification at 8:00 AM, and is designed to run in the background on Linux.

package main

import (
    "fmt"
    "log"
    "os"
    "time"

    "github.com/go-co-op/gocron"
    "github.com/gregdel/pushover"
)

const (
    apiToken     = "" // Replace with your Pushover API token
    userKey      = ""// Replace with your Pushover User Key
    reminderTime = "08:00"                           // Daily reminder time (24-hour format)
    logFile      = "resolution_reminder.log"         // Log file path
)

func initLogger() error {
    // Open log file with append mode, create if not exists
    file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return fmt.Errorf("failed to open log file: %w", err)
    }

    // Configure logger to write to file with timestamp and file location
    log.SetOutput(file)
    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
    return nil
}

func getResolutions() (string, error) {
    data, err := os.ReadFile("resolutions.txt")
    if err != nil {
        return "", fmt.Errorf("failed to read resolutions.txt: %w", err)
    }

    if len(data) == 0 {
        return "", fmt.Errorf("resolutions.txt is empty")
    }

    return string(data), nil
}

// sendDailyNotification constructs and sends the push notification.
// Handles error cases by sending a fallback message when resolutions are unavailable.
func sendDailyNotification() {
    app := pushover.New(apiToken)
    recipient := pushover.NewRecipient(userKey)
    message := pushover.NewMessage("")
    message.Title = "Daily Resolution Reminder"

    resolutionText, err := getResolutions()
    if err != nil {
        message.Message = "You have not set any resolutions for this year. Please add your resolutions to resolutions.txt."
        log.Printf("Error reading resolutions: %v", err)
    } else {
        message.Message = resolutionText
    }

    response, err := app.SendMessage(message, recipient)
    if err != nil {
        log.Printf("Error sending notification: %v", err)
        return
    }

    log.Printf("Notification sent at %v: %v", time.Now().Format(time.RFC1123), response)
}

func main() {
    // Initialize file logger
    if err := initLogger(); err != nil {
        // If we can't set up logging, print to stderr and exit
        fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err)
        os.Exit(1)
    }

    log.Printf("Resolution reminder service starting up")

    scheduler := gocron.NewScheduler(time.Local)

    _, err := scheduler.Every(1).Day().At(reminderTime).Do(sendDailyNotification)
    if err != nil {
        log.Fatalf("Error scheduling daily notification: %v", err)
    }

    log.Printf("Scheduled daily notification for %s", reminderTime)

    scheduler.StartAsync()

// Block indefinitely to keep the scheduler running.
    // Use select{} instead of time.Sleep for cleaner background execution.
    select {}
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Security Note:

Avoid hardcoding sensitive credentials like your API token and User Key directly in the code. Instead, use environment variables or a secure secrets manager.

Running as a Background Service on Linux

By default, the bot runs in the foreground. If your laptop restarts, the bot stops—unless you set it up to run in the background. Here’s how to do that on Linux:

  1. Build the Binary: Compile your Go program:
   go build -o resolution-bot main.go
Enter fullscreen mode Exit fullscreen mode
  1. Create a Systemd Service File: Create a file named /etc/systemd/system/resolution-bot.service with the following content:
   [Unit]
   Description=Daily New Year’s Resolution Notification Bot
   After=network.target

   [Service]
   ExecStart=/path/to/resolution-bot
   WorkingDirectory=/path/to/your/
   Restart=always
   User=yourusername
   Group=yourusergroup

   [Install]
   WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Replace /path/to/resolution-bot with the full path to your compiled binary (e.g., /home/username/projects/resolution-bot) and /path/to/your/ with the directory containing resolutions.txt. Find your username and group by running id -un and id -gn in the terminal.

  1. Enable and Start the Service:
   sudo systemctl daemon-reload
   sudo systemctl enable resolution-bot.service
   sudo systemctl start resolution-bot.service
   sudo systemctl status resolution-bot.service
Enter fullscreen mode Exit fullscreen mode

Alternatively, if you prefer a quick manual approach without systemd, run:

nohup ./resolution-bot &
Enter fullscreen mode Exit fullscreen mode

This ensures your bot runs continuously in the background, even after a reboot.


Final Thoughts

By leveraging the Pushover API beyond our daily reminder bot, you can unlock powerful possibilities for secure authentication, real-time alerts, and seamless cross-platform messaging. Whether you’re aiming to replace SMS-based OTPs with push notifications or centralize alerts from your IoT devices, Pushover’s simple REST interface makes it easy to send custom notifications to users.


Check Out the Code

For those who want to dive deeper or try it out themselves, you can check out the complete source code on my GitHub repository:

github

Feel free to fork it, submit issues, or contribute improvements!


References:

Happy coding!

Top comments (1)

Collapse
 
anoduor profile image
Antony Oduor

Interesting read. I can see the potential in this program. With a bit of a tweak on this function, getResolutions, I can literary add support for a database or a better file handling system and I am set for a daily reminder. I could even create an interface for adding my goals for the next day each time I get a notification. It would be more like an interactive Todo list. Overall, I loved the article.