🌷 the cutsie hackatime helper

feat: split into packages

dunkirk.sh 94211e3e 30461c77

verified
+140 -125
+130
handler/main.go
··· 1 + package handler 2 + 3 + import ( 4 + "errors" 5 + "fmt" 6 + "os" 7 + "runtime" 8 + "strings" 9 + 10 + "github.com/spf13/cobra" 11 + "github.com/taciturnaxolotl/akami/styles" 12 + "github.com/taciturnaxolotl/akami/wakatime" 13 + "gopkg.in/ini.v1" 14 + ) 15 + 16 + func Doctor() *cobra.Command { 17 + return &cobra.Command{ 18 + Use: "doc", 19 + Short: "diagnose potential hackatime issues", 20 + RunE: func(c *cobra.Command, _ []string) error { 21 + // check our os 22 + os_name := runtime.GOOS 23 + 24 + user_dir, err := os.UserHomeDir() 25 + if err != nil { 26 + 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") 27 + } 28 + hackatime_path := user_dir + "/.wakatime.cfg" 29 + 30 + switch os_name { 31 + case "linux": 32 + case "darwin": 33 + case "windows": 34 + default: 35 + 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?") 36 + } 37 + 38 + 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") 39 + 40 + rawCfg, err := os.ReadFile(hackatime_path) 41 + if errors.Is(err, os.ErrNotExist) { 42 + 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") 43 + } 44 + 45 + cfg, err := ini.Load(rawCfg) 46 + if err != nil { 47 + return errors.New(err.Error()) 48 + } 49 + 50 + settings, err := cfg.GetSection("settings") 51 + if err != nil { 52 + 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()) 53 + } 54 + 55 + api_key := settings.Key("api_key").String() 56 + api_url := settings.Key("api_url").String() 57 + if api_key == "" { 58 + 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?") 59 + } 60 + if api_url == "" { 61 + 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?") 62 + } 63 + 64 + if api_url != "https://hackatime.hackclub.com/api/hackatime/v1" { 65 + if api_url == "https://api.wakatime.com/api/v1" { 66 + client := wakatime.NewClient(api_key) 67 + _, err := client.GetStatusBar() 68 + 69 + if !errors.Is(err, wakatime.ErrUnauthorized) { 70 + 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") 71 + } else { 72 + 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") 73 + } 74 + } 75 + 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") 76 + } 77 + 78 + client := wakatime.NewClientWithOptions(api_key, api_url) 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 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 + Entity: "/home/kierank/Projects/akami/wakatime/main.go", 109 + Type: "file", 110 + Project: "akami", 111 + Language: "Go", 112 + Branch: "main", 113 + Category: "coding", 114 + IsWrite: true, 115 + LineCount: 197, 116 + ProjectRootCount: 5, 117 + Dependencies: []string{"bytes", "encoding/base64", "encoding/json", "net/http", "runtime", "time"}, 118 + Time: 1750643351, 119 + }) 120 + if err != nil { 121 + return errors.New("oh dear; looks like something went wrong when sending that heartbeat. " + styles.Bad.Render("Full error: \""+strings.TrimSpace(err.Error())+"\"")) 122 + } 123 + 124 + c.Println("\n🥳 it worked! you are good to go! Happy coding 👋") 125 + 126 + return nil 127 + }, 128 + } 129 + 130 + }
+3 -125
main.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "errors" 6 - "fmt" 7 5 "os" 8 - "runtime" 9 - "strings" 10 6 11 7 "github.com/charmbracelet/fang" 12 - "github.com/charmbracelet/lipgloss/v2" 13 8 "github.com/spf13/cobra" 14 - "github.com/taciturnaxolotl/akami/wakatime" 15 - "gopkg.in/ini.v1" 9 + "github.com/taciturnaxolotl/akami/handler" 16 10 ) 17 11 18 12 func main() { ··· 22 16 Short: "🌷 the cutsie hackatime helper", 23 17 } 24 18 25 - // add our lipgloss styles 26 - fancy := lipgloss.NewStyle().Foreground(lipgloss.Magenta).Bold(true).Italic(true) 27 - muted := lipgloss.NewStyle().Foreground(lipgloss.BrightBlue).Italic(true) 28 - bad := lipgloss.NewStyle().Foreground(lipgloss.BrightRed).Bold(true) 29 - 30 - // root diagnose command 31 - cmd.AddCommand(&cobra.Command{ 32 - Use: "doc", 33 - Short: "diagnose potential hackatime issues", 34 - RunE: func(c *cobra.Command, _ []string) error { 35 - // check our os 36 - os_name := runtime.GOOS 37 - 38 - user_dir, err := os.UserHomeDir() 39 - if err != nil { 40 - 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") 41 - } 42 - hackatime_path := user_dir + "/.wakatime.cfg" 43 - 44 - switch os_name { 45 - case "linux": 46 - case "darwin": 47 - case "windows": 48 - default: 49 - 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?") 50 - } 51 - 52 - c.Println("Looks like you are running", fancy.Render(os_name), "so lets take a look at", muted.Render(hackatime_path), "for your config") 53 - 54 - rawCfg, err := os.ReadFile(hackatime_path) 55 - if errors.Is(err, os.ErrNotExist) { 56 - 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") 57 - } 58 - 59 - cfg, err := ini.Load(rawCfg) 60 - if err != nil { 61 - return errors.New(err.Error()) 62 - } 63 - 64 - settings, err := cfg.GetSection("settings") 65 - if err != nil { 66 - 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()) 67 - } 68 - 69 - api_key := settings.Key("api_key").String() 70 - api_url := settings.Key("api_url").String() 71 - if api_key == "" { 72 - 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?") 73 - } 74 - if api_url == "" { 75 - 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?") 76 - } 77 - 78 - if api_url != "https://hackatime.hackclub.com/api/hackatime/v1" { 79 - if api_url == "https://api.wakatime.com/api/v1" { 80 - client := wakatime.NewClient(api_key) 81 - _, err := client.GetStatusBar() 82 - 83 - if !errors.Is(err, wakatime.ErrUnauthorized) { 84 - 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") 85 - } else { 86 - 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") 87 - } 88 - } 89 - 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") 90 - } 91 - 92 - client := wakatime.NewClientWithOptions(api_key, api_url) 93 - duration, err := client.GetStatusBar() 94 - if err != nil { 95 - if errors.Is(err, wakatime.ErrUnauthorized) { 96 - 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?") 97 - } 98 - 99 - 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())) 100 - } 101 - 102 - // Convert seconds to a formatted time string (hours, minutes, seconds) 103 - totalSeconds := duration.Data.GrandTotal.TotalSeconds 104 - hours := totalSeconds / 3600 105 - minutes := (totalSeconds % 3600) / 60 106 - seconds := totalSeconds % 60 107 - 108 - formattedTime := "" 109 - if hours > 0 { 110 - formattedTime += fmt.Sprintf("%d hours, ", hours) 111 - } 112 - if minutes > 0 || hours > 0 { 113 - formattedTime += fmt.Sprintf("%d minutes, ", minutes) 114 - } 115 - formattedTime += fmt.Sprintf("%d seconds", seconds) 116 - 117 - c.Println("\nSweet!!! Looks like your hackatime is configured properly! Looks like you have coded today for", fancy.Render(formattedTime)) 118 - 119 - c.Println("\nSending one quick heartbeat to make sure everything is ship shape and then you should be good to go!") 120 - 121 - err = client.SendHeartbeat(wakatime.Heartbeat{ 122 - Entity: "/home/kierank/Projects/akami/wakatime/main.go", 123 - Type: "file", 124 - Project: "akami", 125 - Language: "Go", 126 - Branch: "main", 127 - Category: "coding", 128 - IsWrite: true, 129 - LineCount: 197, 130 - ProjectRootCount: 5, 131 - Dependencies: []string{"bytes", "encoding/base64", "encoding/json", "net/http", "runtime", "time"}, 132 - Time: 1750643351, 133 - }) 134 - if err != nil { 135 - return errors.New("oh dear; looks like something went wrong when sending that heartbeat. " + bad.Render("Full error: \""+strings.TrimSpace(err.Error())+"\"")) 136 - } 137 - 138 - c.Println("\n🥳 it worked! you are good to go! Happy coding 👋") 139 - 140 - return nil 141 - }, 142 - }) 19 + // diagnose command 20 + cmd.AddCommand(handler.Doctor()) 143 21 144 22 // this is where we get the fancy fang magic ✨ 145 23 if err := fang.Execute(
+7
styles/main.go
··· 1 + package styles 2 + 3 + import "github.com/charmbracelet/lipgloss/v2" 4 + 5 + var Fancy = lipgloss.NewStyle().Foreground(lipgloss.Magenta).Bold(true).Italic(true) 6 + var Muted = lipgloss.NewStyle().Foreground(lipgloss.BrightBlue).Italic(true) 7 + var Bad = lipgloss.NewStyle().Foreground(lipgloss.BrightRed).Bold(true)