···11+package handler
22+33+import (
44+ "errors"
55+ "fmt"
66+ "os"
77+ "runtime"
88+ "strings"
99+1010+ "github.com/spf13/cobra"
1111+ "github.com/taciturnaxolotl/akami/styles"
1212+ "github.com/taciturnaxolotl/akami/wakatime"
1313+ "gopkg.in/ini.v1"
1414+)
1515+1616+func Doctor() *cobra.Command {
1717+ return &cobra.Command{
1818+ Use: "doc",
1919+ Short: "diagnose potential hackatime issues",
2020+ RunE: func(c *cobra.Command, _ []string) error {
2121+ // check our os
2222+ os_name := runtime.GOOS
2323+2424+ user_dir, err := os.UserHomeDir()
2525+ if err != nil {
2626+ 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")
2727+ }
2828+ hackatime_path := user_dir + "/.wakatime.cfg"
2929+3030+ switch os_name {
3131+ case "linux":
3232+ case "darwin":
3333+ case "windows":
3434+ default:
3535+ 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?")
3636+ }
3737+3838+ 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")
3939+4040+ rawCfg, err := os.ReadFile(hackatime_path)
4141+ if errors.Is(err, os.ErrNotExist) {
4242+ return errors.New("you don't have a wakatime config file! go check https://hackatime.hackclub.com/my/wakatime_setup for the instructions and then try this again")
4343+ }
4444+4545+ cfg, err := ini.Load(rawCfg)
4646+ if err != nil {
4747+ return errors.New(err.Error())
4848+ }
4949+5050+ settings, err := cfg.GetSection("settings")
5151+ if err != nil {
5252+ 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 https://hackatime.hackclub.com/my/wakatime_setup to regenerate it?\n\nThe raw error we got was: " + err.Error())
5353+ }
5454+5555+ api_key := settings.Key("api_key").String()
5656+ api_url := settings.Key("api_url").String()
5757+ if api_key == "" {
5858+ 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 https://hackatime.hackclub.com/my/wakatime_setup correctly?")
5959+ }
6060+ if api_url == "" {
6161+ 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 https://hackatime.hackclub.com/my/wakatime_setup correctly?")
6262+ }
6363+6464+ if api_url != "https://hackatime.hackclub.com/api/hackatime/v1" {
6565+ if api_url == "https://api.wakatime.com/api/v1" {
6666+ client := wakatime.NewClient(api_key)
6767+ _, err := client.GetStatusBar()
6868+6969+ if !errors.Is(err, wakatime.ErrUnauthorized) {
7070+ 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 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 https://waka.hackclub.com) and then click the import from hackatime v1 button at 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")
7171+ } else {
7272+ 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 https://hackatime.hackclub.com/my/wakatime_setup to run the setup script and fix your config file")
7373+ }
7474+ }
7575+ c.Println("\nYour api url", styles.Muted.Render(api_url), "doesn't match the expected url of", styles.Muted.Render("https://hackatime.hackclub.com/api/hackatime/v1"), "however if you are using a custom forwarder or are sure you know what you are doing then you are probably fine")
7676+ }
7777+7878+ client := wakatime.NewClientWithOptions(api_key, api_url)
7979+ duration, err := client.GetStatusBar()
8080+ if err != nil {
8181+ if errors.Is(err, wakatime.ErrUnauthorized) {
8282+ 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 https://hackatime.hackclub.com/my/wakatime_setup?")
8383+ }
8484+8585+ 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()))
8686+ }
8787+8888+ // Convert seconds to a formatted time string (hours, minutes, seconds)
8989+ totalSeconds := duration.Data.GrandTotal.TotalSeconds
9090+ hours := totalSeconds / 3600
9191+ minutes := (totalSeconds % 3600) / 60
9292+ seconds := totalSeconds % 60
9393+9494+ formattedTime := ""
9595+ if hours > 0 {
9696+ formattedTime += fmt.Sprintf("%d hours, ", hours)
9797+ }
9898+ if minutes > 0 || hours > 0 {
9999+ formattedTime += fmt.Sprintf("%d minutes, ", minutes)
100100+ }
101101+ formattedTime += fmt.Sprintf("%d seconds", seconds)
102102+103103+ c.Println("\nSweet!!! Looks like your hackatime is configured properly! Looks like you have coded today for", styles.Fancy.Render(formattedTime))
104104+105105+ c.Println("\nSending one quick heartbeat to make sure everything is ship shape and then you should be good to go!")
106106+107107+ err = client.SendHeartbeat(wakatime.Heartbeat{
108108+ Entity: "/home/kierank/Projects/akami/wakatime/main.go",
109109+ Type: "file",
110110+ Project: "akami",
111111+ Language: "Go",
112112+ Branch: "main",
113113+ Category: "coding",
114114+ IsWrite: true,
115115+ LineCount: 197,
116116+ ProjectRootCount: 5,
117117+ Dependencies: []string{"bytes", "encoding/base64", "encoding/json", "net/http", "runtime", "time"},
118118+ Time: 1750643351,
119119+ })
120120+ if err != nil {
121121+ return errors.New("oh dear; looks like something went wrong when sending that heartbeat. " + styles.Bad.Render("Full error: \""+strings.TrimSpace(err.Error())+"\""))
122122+ }
123123+124124+ c.Println("\n🥳 it worked! you are good to go! Happy coding 👋")
125125+126126+ return nil
127127+ },
128128+ }
129129+130130+}
+3-125
main.go
···2233import (
44 "context"
55- "errors"
66- "fmt"
75 "os"
88- "runtime"
99- "strings"
106117 "github.com/charmbracelet/fang"
1212- "github.com/charmbracelet/lipgloss/v2"
138 "github.com/spf13/cobra"
1414- "github.com/taciturnaxolotl/akami/wakatime"
1515- "gopkg.in/ini.v1"
99+ "github.com/taciturnaxolotl/akami/handler"
1610)
17111812func main() {
···2216 Short: "🌷 the cutsie hackatime helper",
2317 }
24182525- // add our lipgloss styles
2626- fancy := lipgloss.NewStyle().Foreground(lipgloss.Magenta).Bold(true).Italic(true)
2727- muted := lipgloss.NewStyle().Foreground(lipgloss.BrightBlue).Italic(true)
2828- bad := lipgloss.NewStyle().Foreground(lipgloss.BrightRed).Bold(true)
2929-3030- // root diagnose command
3131- cmd.AddCommand(&cobra.Command{
3232- Use: "doc",
3333- Short: "diagnose potential hackatime issues",
3434- RunE: func(c *cobra.Command, _ []string) error {
3535- // check our os
3636- os_name := runtime.GOOS
3737-3838- user_dir, err := os.UserHomeDir()
3939- if err != nil {
4040- 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")
4141- }
4242- hackatime_path := user_dir + "/.wakatime.cfg"
4343-4444- switch os_name {
4545- case "linux":
4646- case "darwin":
4747- case "windows":
4848- default:
4949- return errors.New("hmm you don't seem to be running a recognized os? you are listed as running " + fancy.Render(os_name) + "; can you plz report this to @krn on slack or via email at me@dunkirk.sh?")
5050- }
5151-5252- c.Println("Looks like you are running", fancy.Render(os_name), "so lets take a look at", muted.Render(hackatime_path), "for your config")
5353-5454- rawCfg, err := os.ReadFile(hackatime_path)
5555- if errors.Is(err, os.ErrNotExist) {
5656- return errors.New("you don't have a wakatime config file! go check https://hackatime.hackclub.com/my/wakatime_setup for the instructions and then try this again")
5757- }
5858-5959- cfg, err := ini.Load(rawCfg)
6060- if err != nil {
6161- return errors.New(err.Error())
6262- }
6363-6464- settings, err := cfg.GetSection("settings")
6565- if err != nil {
6666- 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 https://hackatime.hackclub.com/my/wakatime_setup to regenerate it?\n\nThe raw error we got was: " + err.Error())
6767- }
6868-6969- api_key := settings.Key("api_key").String()
7070- api_url := settings.Key("api_url").String()
7171- if api_key == "" {
7272- 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 https://hackatime.hackclub.com/my/wakatime_setup correctly?")
7373- }
7474- if api_url == "" {
7575- 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 https://hackatime.hackclub.com/my/wakatime_setup correctly?")
7676- }
7777-7878- if api_url != "https://hackatime.hackclub.com/api/hackatime/v1" {
7979- if api_url == "https://api.wakatime.com/api/v1" {
8080- client := wakatime.NewClient(api_key)
8181- _, err := client.GetStatusBar()
8282-8383- if !errors.Is(err, wakatime.ErrUnauthorized) {
8484- 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 " + muted.Render("https://github.com/JasonLovesDoggo/multitime") + " or you can wait for 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 https://waka.hackclub.com) and then click the import from hackatime v1 button at 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")
8585- } else {
8686- 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 https://hackatime.hackclub.com/my/wakatime_setup to run the setup script and fix your config file")
8787- }
8888- }
8989- c.Println("\nYour api url", muted.Render(api_url), "doesn't match the expected url of", muted.Render("https://hackatime.hackclub.com/api/hackatime/v1"), "however if you are using a custom forwarder or are sure you know what you are doing then you are probably fine")
9090- }
9191-9292- client := wakatime.NewClientWithOptions(api_key, api_url)
9393- duration, err := client.GetStatusBar()
9494- if err != nil {
9595- if errors.Is(err, wakatime.ErrUnauthorized) {
9696- 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 https://hackatime.hackclub.com/my/wakatime_setup?")
9797- }
9898-9999- 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" + bad.Render("Full error: "+err.Error()))
100100- }
101101-102102- // Convert seconds to a formatted time string (hours, minutes, seconds)
103103- totalSeconds := duration.Data.GrandTotal.TotalSeconds
104104- hours := totalSeconds / 3600
105105- minutes := (totalSeconds % 3600) / 60
106106- seconds := totalSeconds % 60
107107-108108- formattedTime := ""
109109- if hours > 0 {
110110- formattedTime += fmt.Sprintf("%d hours, ", hours)
111111- }
112112- if minutes > 0 || hours > 0 {
113113- formattedTime += fmt.Sprintf("%d minutes, ", minutes)
114114- }
115115- formattedTime += fmt.Sprintf("%d seconds", seconds)
116116-117117- c.Println("\nSweet!!! Looks like your hackatime is configured properly! Looks like you have coded today for", fancy.Render(formattedTime))
118118-119119- c.Println("\nSending one quick heartbeat to make sure everything is ship shape and then you should be good to go!")
120120-121121- err = client.SendHeartbeat(wakatime.Heartbeat{
122122- Entity: "/home/kierank/Projects/akami/wakatime/main.go",
123123- Type: "file",
124124- Project: "akami",
125125- Language: "Go",
126126- Branch: "main",
127127- Category: "coding",
128128- IsWrite: true,
129129- LineCount: 197,
130130- ProjectRootCount: 5,
131131- Dependencies: []string{"bytes", "encoding/base64", "encoding/json", "net/http", "runtime", "time"},
132132- Time: 1750643351,
133133- })
134134- if err != nil {
135135- return errors.New("oh dear; looks like something went wrong when sending that heartbeat. " + bad.Render("Full error: \""+strings.TrimSpace(err.Error())+"\""))
136136- }
137137-138138- c.Println("\n🥳 it worked! you are good to go! Happy coding 👋")
139139-140140- return nil
141141- },
142142- })
1919+ // diagnose command
2020+ cmd.AddCommand(handler.Doctor())
1432114422 // this is where we get the fancy fang magic ✨
14523 if err := fang.Execute(