simple program for visiting other townies' home directories
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

144 wiersze
2.6 KiB

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "math/rand"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "time"
  12. "github.com/AlecAivazis/survey/v2"
  13. "github.com/charmbracelet/glamour"
  14. )
  15. func main() {
  16. var err error
  17. if len(os.Args) > 2 {
  18. fmt.Println("expected zero or one arguments")
  19. }
  20. if len(os.Args) > 1 {
  21. arg := os.Args[1]
  22. switch arg {
  23. case "-r", "--random":
  24. err = visitRandomUser()
  25. default:
  26. err = visitUsername(os.Args[1])
  27. }
  28. } else {
  29. err = visitPrompt()
  30. }
  31. if err != nil {
  32. fmt.Fprintf(os.Stderr, "%s\n", err)
  33. os.Exit(1)
  34. }
  35. }
  36. func visitRandomUser() error {
  37. rand.Seed(time.Now().UnixNano())
  38. usernames, err := getUsernames()
  39. if err != nil {
  40. return err
  41. }
  42. return visitUser(usernames[rand.Intn(len(usernames))])
  43. }
  44. func visitUsername(username string) error {
  45. usernames, err := getUsernames()
  46. if err != nil {
  47. return err
  48. }
  49. for _, name := range usernames {
  50. if name == username {
  51. return visitUser(username)
  52. }
  53. }
  54. return fmt.Errorf("no such user: %s", username)
  55. }
  56. func visitPrompt() error {
  57. usernames, err := getUsernames()
  58. if err != nil {
  59. return err
  60. }
  61. username := ""
  62. prompt := &survey.Select{
  63. Message: "Choose a townie to visit!",
  64. Options: usernames,
  65. }
  66. survey.AskOne(prompt, &username)
  67. return visitUser(username)
  68. }
  69. func visitUser(username string) error {
  70. files, err := ioutil.ReadDir(filepath.Join("/home", username))
  71. if err != nil {
  72. return fmt.Errorf("user is not accepting visitors (could not read user's home directory: %w)", err)
  73. }
  74. for _, file := range files {
  75. if !isReadme(file.Name()) {
  76. continue
  77. }
  78. path := filepath.Join("/home", username, file.Name())
  79. data, err := ioutil.ReadFile(path)
  80. if err != nil {
  81. break
  82. }
  83. if isMarkdown(file.Name()) {
  84. r, _ := glamour.NewTermRenderer(glamour.WithAutoStyle())
  85. out, err := r.RenderBytes(data)
  86. if err == nil {
  87. fmt.Println(string(out))
  88. return nil
  89. }
  90. }
  91. fmt.Println(string(data))
  92. return nil
  93. }
  94. // TODO handle .plan and .project files
  95. fmt.Println("TODO non-readme fallback")
  96. return nil
  97. }
  98. func isReadme(filename string) bool {
  99. fn := strings.ToLower(filename)
  100. return strings.HasPrefix(fn, "readme") || strings.HasPrefix(fn, "welcome")
  101. }
  102. func isMarkdown(filename string) bool {
  103. fn := strings.ToLower(filename)
  104. return strings.HasSuffix(fn, ".md") || strings.HasSuffix(fn, ".markdown")
  105. }
  106. func getUsernames() ([]string, error) {
  107. cmd := exec.Command("/bin/bash", "-c", "town stats | jq -r '.users|map(.username)'")
  108. output, err := cmd.Output()
  109. if err != nil {
  110. return nil, err
  111. }
  112. usernames := []string{}
  113. err = json.Unmarshal(output, &usernames)
  114. if err != nil {
  115. return nil, err
  116. }
  117. return usernames, nil
  118. }