promptato: prompt yourself to think of things

It is important to remember or reflect on certain things now and then. With how busy life gets it's possible to forget and only think about certain things if they occur again or if something triggers the thought. I have certain things that I'd like to think about often but don't want to commit them on my TODO list because of the clutter and task maintenance burden that would come from that.

Enter promptato a simple command-line tool that periodically prompts you without cluttering your to-do list app.

./promptato -every 15m -f prompts-1.txt -reload

How it works

The program prompts you to think about something every interval and sends a Desktop Notification with the prompt as the text. The prompt items are chosen at random. The source of the prompts is a text file, where each prompt or question is on a separate line. You can also specify how often you want the prompts, the default is every 42 minutes.

For example, my prompts text file looks like this:

Prompts file example

And I get desktop notifications every few minutes like this:

Example notification

After running it like so

./promptato -every 15m -f prompts-1.txt -reload

Pro-tip: If you are feeling adventurous, you can trigger your own anxiety by having a prompt file with just one line "Where are you with that task?" and run it every 5 minutes - which could simulate the behavior of some micro-managing managers.

Building and Using

To use this you will need to have Go installed and use the following steps to create your build of the program. I may create a GitHub repository and pre-built some binaries for it if people are interested, but the following instructions can help you get it up and running for yourself.

Step 1: Create a go project and create an empty file named main.go

$ mkdir promptato
$ cd promptato
$ go mod init promptato
$ touch main.go

Step 2: Copy the full code below into main.go

Step 3: Get dependencies and compile, if you are on windows this will create a promptato.exe and on other OS it will create a binary (in this case one named promptato)

$ go mod tidy
$ go build

The full code

The full program is below, please note it's a very simple no frills, offline program - it could be made better (see below for ideas), below is the Minimum Useable Thing:

package main

import (
    "flag"
    "io/ioutil"
    "log"
    "math/rand"
    "strings"
    "time"

    "github.com/gen2brain/beeep"
    "github.com/go-co-op/gocron"
    "github.com/mroth/weightedrand"
)

var every string
var promptFile string
var reloadFile bool

func init() {
    flag.StringVar(&every, "every", "42m", "Every specifies the duration and supports formats like 5s, 42m, 1h ")
    flag.StringVar(&promptFile, "f", "./prompts.txt", "Which file to load prompts from")
    flag.BoolVar(&reloadFile, "reload", false, "Whether to reload prompts file on each run...")
}

func main() {
    rand.Seed(time.Now().UTC().UnixNano()) // always seed random!
    flag.Parse()

    preloadedChoices := loadChoices(promptFile)
    s := gocron.NewScheduler(time.UTC)

    s.Every(every).Do(func() {
        var chooser *weightedrand.Chooser
        if reloadFile {
            choices := loadChoices(promptFile)
            chooser, _ = weightedrand.NewChooser(choices...)
        } else {
            chooser, _ = weightedrand.NewChooser(preloadedChoices...)
        }

        result := chooser.Pick()
        err := beeep.Notify("Promptato", result.(string), "question.png")
        if err != nil {
            log.Fatalf("failed to notify user, got %v", err)
        }
    })

    // starts the scheduler and blocks current execution path
    s.StartBlocking()
}

func loadChoices(sourceFile string) []weightedrand.Choice {
    data, err := ioutil.ReadFile(sourceFile)
    if err != nil {
        log.Fatalf("failed to load choices file, got %v", err)
        return nil
    }

    lines := strings.Split(string(data), "\n")
    choices := make([]weightedrand.Choice, len(lines))
    for _, line := range lines {
        trimmed := strings.TrimSpace(strings.TrimPrefix(line, "- "))
        choices = append(choices, weightedrand.NewChoice(trimmed, 1))
    }

    return choices
}

Ideas for improvement

This code represents a small useable thing but could be improved further with:

  • making it a background service

  • having a way to weight the prompts (already using weightedrand library, just needs a way to define weights)

  • Having some kind of jitter to randomize the schedule of the prompts

  • Allowing users to interact with the prompts to indicate if they don't want it to come up again (this would necessitate re-implementing this with maybe a GUI toolkit)

Anyways, I may or may not implement these ideas. For now, I just needed this for reminding me of some things as the year comes to an end as I meditate on the next year.

Conclusion

In this post, we have seen how a simple command-line tool built with Go can help you remember to think of things without having to add that as a task to your to-do app. I hope you found this at least mildly interesting.