package main import ( "bufio" "fmt" "io/ioutil" "log" "os" "os/exec" "os/signal" "os/user" "runtime" "strings" "time" "github.com/akamensky/argparse" ) const ( redirectTo = "127.0.0.1" marker = "## MANAGED BY HYPERFOCUS ##\n" runningOS = runtime.GOOS ) var distractorsFile string = os.Getenv("HOME") + ".hyperfocus_distractors" func getHostFile() string { var hostFile string switch runningOS { case "windows": hostFile = "/Windows/System32/drivers/etc/hosts" default: hostFile = "/etc/hosts" } return hostFile } func resetNetwork(message string) { switch runningOS { case "windows": cmd := exec.Command("ipconfig /flushdns") if err := cmd.Run(); err != nil { log.Fatalln(err) } fmt.Println(message) case "darwin": cmd := exec.Command("dscacheutil", "-flushcache") if err := cmd.Run(); err != nil { log.Fatalln(err) } fmt.Println(message) case "linux": var ( usingDnsmasq bool = false usingResolved bool = false ) // Detect if using dnsmasq out, err := exec.Command("systemctl", "is-active", "dnsmasq").Output() if err != nil { log.Fatalln(err) } if string(out) == "active" { usingDnsmasq = true } // Detect if using systemd-resolved out, err = exec.Command("systemctl", "is-active", "systemd-resolved").Output() if err != nil { log.Fatalln(err) } if string(out) == "active" { usingResolved = true } // Restart networking service if usingDnsmasq { cmd := exec.Command("systemctl", "restart", "dnsmasq") if err := cmd.Run(); err != nil { log.Fatalln(err) } } if usingResolved { cmd := exec.Command("resolvectl", "flush-caches") if err := cmd.Run(); err != nil { log.Fatalln(err) } } fmt.Println(message) } } func isRoot() bool { currUser, err := user.Current() if err != nil { log.Fatalf("[Super user check] Unable to get current user: %s\n", err) } return currUser.Username == "root" } // Disables access to websites that are defined as 'distractors' func improveFocus() { hostFile, err := os.OpenFile(getHostFile(), os.O_APPEND|os.O_WRONLY, 0666) if err != nil { log.Fatalln(err) } defer hostFile.Close() userDistractorsFile, err := os.Open(distractorsFile) if err != nil { log.Fatalln(err) } defer userDistractorsFile.Close() if _, err := hostFile.WriteString(marker); err != nil { log.Fatalln(err) } scanner := bufio.NewScanner(userDistractorsFile) for scanner.Scan() { var hostLine string = fmt.Sprintf("127.0.0.1\t%s\n", scanner.Text()) if _, err := hostFile.WriteString(hostLine); err != nil { log.Fatalln(err) } } if _, err := hostFile.WriteString(marker); err != nil { log.Fatalln(err) } resetNetwork("Focus is now improved 😊") } // Enables access to websites that are defined as 'distractors' func loseFocus() { hostFileContent, err := ioutil.ReadFile(getHostFile()) if err != nil { log.Fatalln(err) } hostFileSlice := strings.Split(string(hostFileContent), marker) hostFileSlice = append(hostFileSlice[:1], hostFileSlice[2:]...) hostFileBytes := []byte(strings.Join(hostFileSlice, "")) if err := ioutil.WriteFile(getHostFile(), hostFileBytes, 0666); err != nil { log.Fatalln(err) } resetNetwork("Focus is now lost ðŸĪŠ") } // Enables temporarily breaking concentration func takeBreak(minutes int) { fmt.Println() fmt.Println("Your (probably) well-deserved break is commencing...") loseFocus() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) s := <-c t := minutes * 60 for i := t; i > 0; i-- { fmt.Printf("%d seconds remaining", i) time.Sleep(time.Second) if s == os.Interrupt { break } } improveFocus() fmt.Println("Welcome back to hyperfocus mode 😊 ") } // Prints the current list of distractors to be blocked func listDistractors() { userDistractorsFile, err := os.Open(distractorsFile) if err != nil { log.Fatalln(err) } defer userDistractorsFile.Close() scanner := bufio.NewScanner(userDistractorsFile) for scanner.Scan() { fmt.Println(scanner.Text()) } } // Adds predefined distractors to .hyperfocus_distractors file func addPredefinedDistractors() { } func main() { parser := argparse.NewParser("hf", "Block distracting websites and hyperfocus on your work") improveCmd := parser.NewCommand("improve", "Improve focus, block the distractors") loseCmd := parser.NewCommand("lose", "Lose focus, unblock the distractors") breakCmd := parser.NewCommand("break", "Take a break for a number of minutes") minutesForBreak := breakCmd.Int("", "minutes", &argparse.Options{Help: "Number of minutes to break for."}) listCmd := parser.NewCommand("list", "List the distractors defined in the block file (~/.hyperfocus_distractors)") predefinedCmd := parser.NewCommand("predefined", "Add predefined set of distractors to block file") err := parser.Parse(os.Args) if err != nil { fmt.Print(parser.Usage(err)) } if improveCmd.Happened() { improveFocus() } else if loseCmd.Happened() { loseFocus() } else if breakCmd.Happened() { takeBreak(*minutesForBreak) } else if listCmd.Happened() { listDistractors() } else if predefinedCmd.Happened() { addPredefinedDistractors() } else { log.Fatalln("Unknown command") } }