diff --git a/cmd/visit/main.go b/cmd/visit/main.go new file mode 100644 index 0000000..2779c5c --- /dev/null +++ b/cmd/visit/main.go @@ -0,0 +1,143 @@ +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 +}