commit 0af6ada3ade33fdae0c2d81c0217de00286fb9f4 Author: Xevion Date: Wed Sep 18 01:33:22 2024 -0500 project init diff --git a/README.md b/README.md new file mode 100644 index 0000000..c021671 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# todoist-late-reset + +A basic Go program to reset certain tasks in Todoist if they're completed 'late'. + +## Story + +I need tasks that I complete after midnight to be automatically rescheduled for the same day so that I can still complete them the next day (without having to manually reschedule each one I complete late). + +This is effective for people like me who are still active (or may review their tasks) after midnight. diff --git a/api.go b/api.go new file mode 100644 index 0000000..9d12b60 --- /dev/null +++ b/api.go @@ -0,0 +1,35 @@ +package main + +import ( + "io" + "net/http" + "net/url" +) + +func getRecentlyCompleted() (string, error) { + baseURL := "https://api.todoist.com/sync/v9/activity/get" + params := url.Values{} + params.Add("event_type", "completed") + + fullURL := baseURL + "?" + params.Encode() + + req, err := http.NewRequest("GET", fullURL, nil) + if err != nil { + return "", err + } + + req.Header.Set("Authorization", "Bearer "+todoistApiToken) + + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(body), nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6d90d19 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/Xevion/todoist-late-reset + +go 1.22.3 + +require ( + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/go-co-op/gocron/v2 v2.12.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/redis/go-redis/v9 v9.6.1 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..04e2dc3 --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/go-co-op/gocron/v2 v2.12.0 h1:JuvX4J//Mdw18tN82ZwD9VYwOaL1aSCIu7HQyIhhrAE= +github.com/go-co-op/gocron/v2 v2.12.0/go.mod h1:xY7bJxGazKam1cz04EebrlP4S9q4iWdiAylMGP3jY9w= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..55e59b6 --- /dev/null +++ b/main.go @@ -0,0 +1,77 @@ +// Uses go cron to schedule a job to run every 3 minutes from 1AM to 3AM. +// Queries the todoist API for task completion +// Tasks that are completed between 12AM and 3AM are considered 'not too late' and will be rescheduled one day earlier (if they are recurring tasks). +// Only tasks with the included label (e.g. well-being) are considered. +// Most tasks are scheduled daily, so this means they'll be rescheduled for the current day. But not all. +package main + +import ( + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/go-co-op/gocron/v2" + "github.com/joho/godotenv" + "github.com/redis/go-redis/v9" +) + +var ( + todoistApiToken = os.Getenv("TODOIST_API_TOKEN") + redisURL = os.Getenv("REDIS_URL") + cronSchedule = os.Getenv("CRON_SCHEDULE") + client = &http.Client{} +) + +func init() { + // Load .env file if available + err := godotenv.Load() + if err != nil { + fmt.Println(".env file not loaded") + } +} + +func main() { + opt, _ := redis.ParseURL(redisURL) + client := redis.NewClient(opt) + + // create a scheduler + s, err := gocron.NewScheduler() + if err != nil { + // TODO: handle error + } + + // add a job to the scheduler + _, err = s.NewJob( + gocron.CronJob(cronSchedule, false), + gocron.NewTask( + func(a string, b int) { + fmt.Println("Current time:", time.Now().Format(time.RFC3339)) + }, + "hello", + 1, + ), + ) + if err != nil { + // handle error + } + + // Start the scheduler + s.Start() + + // Setup signal handler channel + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt) // Ctrl+C signal + signal.Notify(stop, syscall.SIGTERM) // Container stop signal + + // Wait for signal (indefinite) + closingSignal := <-stop + + fmt.Println("Gracefully shutting down, received signal:", closingSignal.String()) + err = s.Shutdown() + if err != nil { + // handle error + } +}