馃尫 the cutsie hackatime helper
1package handler
2
3import (
4 "errors"
5 "fmt"
6 "os"
7 "path/filepath"
8 "runtime"
9 "strings"
10 "time"
11
12 "github.com/spf13/cobra"
13 "github.com/taciturnaxolotl/akami/styles"
14 "github.com/taciturnaxolotl/akami/wakatime"
15 "gopkg.in/ini.v1"
16)
17
18func Doctor() *cobra.Command {
19 return &cobra.Command{
20 Use: "doc",
21 Short: "diagnose potential hackatime issues",
22 RunE: func(c *cobra.Command, _ []string) error {
23 // check our os
24 os_name := runtime.GOOS
25
26 user_dir, err := os.UserHomeDir()
27 if err != nil {
28 return errors.New("somehow your user doesn't exist? fairly sure this should never happen; plz report this to @krn on slack or via email at me@dunkirk.sh")
29 }
30 hackatime_path := filepath.Join(user_dir, ".wakatime.cfg")
31
32 if os_name != "linux" && os_name != "darwin" && os_name != "windows" {
33 return errors.New("hmm you don't seem to be running a recognized os? you are listed as running " + styles.Fancy.Render(os_name) + "; can you plz report this to @krn on slack or via email at me@dunkirk.sh?")
34 }
35
36 c.Println("Looks like you are running", styles.Fancy.Render(os_name), "so lets take a look at", styles.Muted.Render(hackatime_path), "for your config")
37
38 rawCfg, err := os.ReadFile(hackatime_path)
39 if errors.Is(err, os.ErrNotExist) {
40 return errors.New("you don't have a wakatime config file! go check " + styles.Muted.Render("https://hackatime.hackclub.com/my/wakatime_setup") + " for the instructions and then try this again")
41 }
42
43 cfg, err := ini.Load(rawCfg)
44 if err != nil {
45 return errors.New(err.Error())
46 }
47
48 settings, err := cfg.GetSection("settings")
49 if err != nil {
50 return errors.New("wow! your config file seems to be messed up and doesn't have a settings heading; can you follow the instructions at " + styles.Muted.Render("https://hackatime.hackclub.com/my/wakatime_setup") + " to regenerate it?\n\nThe raw error we got was: " + err.Error())
51 }
52
53 api_key := settings.Key("api_key").String()
54 api_url := settings.Key("api_url").String()
55 if api_key == "" {
56 return errors.New("hmm 馃 looks like you don't have an api_key in your config file? are you sure you have followed the setup instructions at " + styles.Muted.Render("https://hackatime.hackclub.com/my/wakatime_setup") + " correctly?")
57 }
58 if api_url == "" {
59 return errors.New("hmm 馃 looks like you don't have an api_url in your config file? are you sure you have followed the setup instructions at " + styles.Muted.Render("https://hackatime.hackclub.com/my/wakatime_setup") + " correctly?")
60 }
61
62 correctApiUrl := "https://hackatime.hackclub.com/api/hackatime/v1"
63 if api_url != correctApiUrl {
64 if api_url == "https://api.wakatime.com/api/v1" {
65 client := wakatime.NewClient(api_key)
66 _, err := client.GetStatusBar()
67
68 if !errors.Is(err, wakatime.ErrUnauthorized) {
69 return errors.New("turns out you were connected to wakatime.com instead of hackatime; since your key seems to work if you would like to keep syncing data to wakatime.com as well as to hackatime you can either setup a realy serve like " + styles.Muted.Render("https://github.com/JasonLovesDoggo/multitime") + " or you can wait for " + styles.Muted.Render("https://github.com/hackclub/hackatime/issues/85") + " to get merged in hackatime and have it synced there :)\n\nIf you want to import your wakatime.com data into hackatime then you can use hackatime v1 temporarily to connect your wakatime account and import (in settings under integrations at " + styles.Muted.Render("https://waka.hackclub.com") + ") and then click the import from hackatime v1 button at " + styles.Muted.Render("https://hackatime.hackclub.com/my/settings") + ".\n\n If you have more questions feel free to reach out to me (hackatime v1 creator) on slack (at @krn) or via email at me@dunkirk.sh")
70 } else {
71 return errors.New("turns out your config is connected to the wrong api url and is trying to use wakatime.com to sync time but you don't have a working api key from them. Go to " + styles.Muted.Render("https://hackatime.hackclub.com/my/wakatime_setup") + " to run the setup script and fix your config file")
72 }
73 }
74 c.Println("\nYour api url", styles.Muted.Render(api_url), "doesn't match the expected url of", styles.Muted.Render(correctApiUrl), "however if you are using a custom forwarder or are sure you know what you are doing then you are probably fine")
75 }
76
77 client := wakatime.NewClientWithOptions(api_key, api_url)
78 c.Println("\nChecking your coding stats for today...")
79 duration, err := client.GetStatusBar()
80 if err != nil {
81 if errors.Is(err, wakatime.ErrUnauthorized) {
82 return errors.New("Your config file looks mostly correct and you have the correct api url but when we tested your api_key it looks like it is invalid? Can you double check if the key in your config file is the same as at " + styles.Muted.Render("https://hackatime.hackclub.com/my/wakatime_setup") + "?")
83 }
84
85 return errors.New("Something weird happened with the hackatime api; if the error doesn't make sense then please contact @krn on slack or via email at me@dunkirk.sh\n\n" + styles.Bad.Render("Full error: "+err.Error()))
86 }
87
88 // Convert seconds to a formatted time string (hours, minutes, seconds)
89 totalSeconds := duration.Data.GrandTotal.TotalSeconds
90 hours := totalSeconds / 3600
91 minutes := (totalSeconds % 3600) / 60
92 seconds := totalSeconds % 60
93
94 formattedTime := ""
95 if hours > 0 {
96 formattedTime += fmt.Sprintf("%d hours, ", hours)
97 }
98 if minutes > 0 || hours > 0 {
99 formattedTime += fmt.Sprintf("%d minutes, ", minutes)
100 }
101 formattedTime += fmt.Sprintf("%d seconds", seconds)
102
103 c.Println("\nSweet!!! Looks like your hackatime is configured properly! Looks like you have coded today for", styles.Fancy.Render(formattedTime))
104
105 c.Println("\nSending one quick heartbeat to make sure everything is ship shape and then you should be good to go!")
106
107 err = client.SendHeartbeat(wakatime.Heartbeat{
108 Branch: "main",
109 Category: "coding",
110 CursorPos: 1,
111 Entity: filepath.Join(user_dir, "akami.txt"),
112 Type: "file",
113 IsWrite: true,
114 Language: "Go",
115 LineNo: 1,
116 LineCount: 4,
117 Project: "example",
118 ProjectRootCount: 3,
119 Time: float64(time.Now().Unix()),
120 })
121 if err != nil {
122 return errors.New("oh dear; looks like something went wrong when sending that heartbeat. " + styles.Bad.Render("Full error: \""+strings.TrimSpace(err.Error())+"\""))
123 }
124
125 c.Println("\n馃コ it worked! you are good to go! Happy coding 馃憢")
126
127 return nil
128 },
129 }
130}