144 行
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			144 行
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 |