package main import ( "encoding/json" "fmt" "io/ioutil" "math/rand" "os" "os/exec" "path/filepath" "strings" "time" "github.com/AlecAivazis/survey/v2" "github.com/charmbracelet/glamour" ) func main() { var err error if len(os.Args) > 2 { fmt.Println("expected zero or one arguments") } if len(os.Args) > 1 { arg := os.Args[1] switch arg { case "-r", "--random": err = visitRandomUser() default: err = visitUsername(os.Args[1]) } } else { err = visitPrompt() } if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } } func visitRandomUser() error { rand.Seed(time.Now().UnixNano()) usernames, err := getUsernames() if err != nil { return err } return visitUser(usernames[rand.Intn(len(usernames))]) } func visitUsername(username string) error { usernames, err := getUsernames() if err != nil { return err } for _, name := range usernames { if name == username { return visitUser(username) } } return fmt.Errorf("no such user: %s", username) } func visitPrompt() error { usernames, err := getUsernames() if err != nil { return err } username := "" prompt := &survey.Select{ Message: "Choose a townie to visit!", Options: usernames, } survey.AskOne(prompt, &username) return visitUser(username) } func visitUser(username string) error { files, err := ioutil.ReadDir(filepath.Join("/home", username)) if err != nil { return fmt.Errorf("user is not accepting visitors (could not read user's home directory: %w)", err) } for _, file := range files { if !isReadme(file.Name()) { continue } path := filepath.Join("/home", username, file.Name()) data, err := ioutil.ReadFile(path) if err != nil { break } if isMarkdown(file.Name()) { r, _ := glamour.NewTermRenderer(glamour.WithAutoStyle()) out, err := r.RenderBytes(data) if err == nil { fmt.Println(string(out)) return nil } } fmt.Println(string(data)) return nil } // TODO handle .plan and .project files fmt.Println("TODO non-readme fallback") return nil } func isReadme(filename string) bool { fn := strings.ToLower(filename) return strings.HasPrefix(fn, "readme") || strings.HasPrefix(fn, "welcome") } func isMarkdown(filename string) bool { fn := strings.ToLower(filename) return strings.HasSuffix(fn, ".md") || strings.HasSuffix(fn, ".markdown") } func getUsernames() ([]string, error) { cmd := exec.Command("/bin/bash", "-c", "town stats | jq -r '.users|map(.username)'") output, err := cmd.Output() if err != nil { return nil, err } usernames := []string{} err = json.Unmarshal(output, &usernames) if err != nil { return nil, err } return usernames, nil }