package main import ( "bufio" "errors" "fmt" "io/ioutil" "log" "os" "os/signal" "os/user" "runtime" "strings" "time" "github.com/akamensky/argparse" ) var ( marker string = "## MANAGED BY HYPERFOCUS ##\n" distractorsFile string = "/etc/hyperfocus_distractors" predefDistractorsFile string = "/etc/hyperfocus_distractors_predefined" ) const resetDNSMsg string = ` Note that you may need to flush your system's DNS cache for changes to take effect. In most cases this could be done by restarting the resolver daemon. Windows systems: open PowerShell as administrator and run "ipconfig /flushdns" macOS: open terminal and run "sudo dscacheutil -flushcache" Linux with Systemd: open terminal and run "sudo systemctl restart systemd-resolved" Other init systems or *BSD: You probably already know how to do this.` func fileExists(path string) bool { _, err := os.Stat(path) return !errors.Is(err, os.ErrNotExist) } func getHostFile() string { var hostFile string switch runtime.GOOS { case "windows": hostFile = "/Windows/System32/drivers/etc/hosts" default: hostFile = "/etc/hosts" } return hostFile } 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() if _, err := hostFile.WriteString(marker); err != nil { log.Fatalln(err) } var ( distractorsFileWarn bool predefDistractorsFileWarn bool ) if fileExists(distractorsFile) { distractorsFileObj, err := os.Open(distractorsFile) if err != nil { log.Fatalln(err) } defer distractorsFileObj.Close() scanner := bufio.NewScanner(distractorsFileObj) 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) } } } else { distractorsFileWarn = true } if fileExists(predefDistractorsFile) { predefDistractorsFileObj, err := os.Open(predefDistractorsFile) if err != nil { log.Fatalln(err) } defer predefDistractorsFileObj.Close() scanner := bufio.NewScanner(predefDistractorsFileObj) 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) } } } else { predefDistractorsFileWarn = true } if distractorsFileWarn && predefDistractorsFileWarn { log.Fatalln("Error: Please define a set of distractors in your distractors file, one per line. Alternatively, you can use a predefined set by running `hf predefined`.") } if _, err := hostFile.WriteString(marker); err != nil { log.Fatalln(err) } fmt.Println("Focus is now improved 😊") fmt.Println(resetDNSMsg) } // 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) } fmt.Println("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) sig := <-c t := minutes * 60 for i := t; i > 0; i-- { fmt.Printf("%d seconds remaining", i) time.Sleep(time.Second) if sig == 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() { distractors := []string{ "anandtech.com", "answerbag.com", "arstechnica.com", "awkwardfamilyphotos.com", "bbc.com", "befunky.com", "break.com", "buzzfeed.com", "cheezburger.com", "cnet.com", "cnn.com", "cracked.com", "digg.com", "digitaltrends.com", "distrowatch.com", "ebay.com", "engadget.com", "everypoet.com", "facebook.com", "failblog.com", "fark.com", "firstpost.com", "flyertalk.com", "fmylife.com", "fox.com", "freerice.com", "gawker.com", "geek.com", "gizmag.com", "gizmodo.com", "gsmarena.com ", "homestarrunner.com", "hulu.com", "imgur.com", "imore.com", "instagram.com", "iwastesomuchtime.com", "lifehacker.com", "liveplasma.com", "lunchtimers.com", "mashable.com", "mcsweeneys.net", "medium.com", "meetup.com", "mono-1.com", "myparentsjoinedfacebook.com", "myspace.com", "netflix.com", "notalwaysright.com", "omegle.com", "opentable.com", "passiveaggressivenotes.com", "pcworld.com", "petsinclothes.com", "phoronix.com", "pinterest.com", "pogo.com", "popurls.com", "postsecret.com", "realclearpolitics.com", "realtor.com", "reddit.com", "redfin.com", "sciencemag.org", "sho.com", "slashdot.com", "slashdot.org", "slickdeals.net", "stripgenerator.com", "techcrunch.com", "technorati.com", "techradar.com", "techrepublic.com", "textsfromlastnight.com", "thenextweb.com", "thepointsguy.com", "theverge.com", "tomshardware.com", "trulia.com", "tumblr.com", "twitter.com", "twitterfall.com", "typepad.com", "wearables.com", "wired.com", "woot.com", "xkcd.com", "ycombinator.com", "yelp.com", "youtube.com", "zdnet.com", "zillow.com", } predefDistractorsFileObj, err := os.OpenFile(predefDistractorsFile, os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatalln(err) } defer predefDistractorsFileObj.Close() dataWriter := bufio.NewWriter(predefDistractorsFileObj) for _, v := range distractors { _, _ = dataWriter.WriteString(v + "\n") } dataWriter.Flush() } 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 (/etc/hyperfocus_distractors)") predefinedCmd := parser.NewCommand("predefined", "Add predefined set of distractors to block file") if isRoot() { 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 { fmt.Println("Enter a subcommand; use --help or -h for details.") } } else { fmt.Println("Super user not detected. This program requires root privileges; please re-run with sudo or become root.") } }