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:
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.-
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.
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.Notification Screenshot:
Once you run the bot, you should see a notification on your phone.
How It Works
-
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
Every day, the bot reads these goals and sends them as a push notification.
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.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.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
}
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 {}
}
⚠️ 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:
- Build the Binary: Compile your Go program:
go build -o resolution-bot main.go
-
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
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.
- 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
Alternatively, if you prefer a quick manual approach without systemd, run:
nohup ./resolution-bot &
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:
Feel free to fork it, submit issues, or contribute improvements!
References:
Happy coding!
Top comments (1)
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.