simple program for visiting other townies' home directories
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

144 lignes
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. }