cute command to show a thank you to contributors

this commit is the result of April's hack day. it animates a thank you
for a project's contributors.

it only really works on linux/mac; animation woes led me to just make it
static and plain on windows.
This commit is contained in:
vilmibm 2020-04-27 10:14:58 -05:00
parent e172f31c80
commit 1aab2084f1

248
command/credits.go Normal file
View file

@ -0,0 +1,248 @@
package command
import (
"bytes"
"fmt"
"math"
"math/rand"
"os"
"os/exec"
"runtime"
"strings"
"time"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
"github.com/cli/cli/utils"
)
var isWindows bool
var thankYou = `
_ _
| | | |
_|_ | | __, _ _ | | __
| |/ \ / | / |/ | |/_) | | / \_| |
|_/| |_/\_/|_/ | |_/| \_/ \_/|/\__/ \_/|_/
/|
\|
_
o | | |
__ __ _ _ _|_ ,_ | | _|_ __ ,_ , |
/ / \_/ |/ | | / | | |/ \_| | | / \_/ | / \_|
\___/\__/ | |_/|_/ |_/|_/\_/ \_/|_/|_/\__/ |_/ \/ o
`
func init() {
rand.Seed(time.Now().UnixNano())
isWindows = runtime.GOOS == "windows"
RootCmd.AddCommand(creditsCmd)
creditsCmd.Flags().BoolP("static", "s", false, "Print a static version of the credits")
}
var creditsCmd = &cobra.Command{
Use: "credits [repository]",
Short: "View project's credits",
Long: `View animated credits for this or another project.
Examples:
gh credits # see a credits animation for this project
gh credits owner/repo # see a credits animation for owner/repo
gh credts -s # display a static thank you instead of animating
gh credits | cat # just print the contributors, one per line
`,
Args: cobra.MaximumNArgs(1),
RunE: credits,
}
func credits(cmd *cobra.Command, args []string) error {
ctx := contextForCommand(cmd)
client, err := apiClientForContext(ctx)
if err != nil {
return err
}
owner := "cli"
repo := "cli"
if len(args) > 0 {
parts := strings.SplitN(args[0], "/", 2)
owner = parts[0]
repo = parts[1]
}
type Contributor struct {
Login string
}
type Result []Contributor
result := Result{}
body := bytes.NewBufferString("")
path := fmt.Sprintf("repos/%s/%s/contributors", owner, repo)
err = client.REST("GET", path, body, &result)
if err != nil {
return err
}
out := cmd.OutOrStdout()
isTTY := false
outFile, isFile := out.(*os.File)
if isFile {
isTTY = utils.IsTerminal(outFile)
if isTTY {
// FIXME: duplicates colorableOut
out = utils.NewColorable(outFile)
}
}
static, err := cmd.Flags().GetBool("static")
if err != nil {
return err
}
static = static || isWindows
if isTTY && static {
fmt.Fprintln(out, "THANK YOU CONTRIBUTORS!!! <3")
fmt.Println()
}
logins := []string{}
for x, c := range result {
if isTTY && !static {
logins = append(logins, getColor(x)(c.Login))
} else {
fmt.Fprintf(out, "%s\n", c.Login)
}
}
if !isTTY || static {
return nil
}
lines := []string{}
thankLines := strings.Split(thankYou, "\n")
for x, tl := range thankLines {
lines = append(lines, getColor(x)(tl))
}
lines = append(lines, "")
lines = append(lines, logins...)
lines = append(lines, "( <3 press ctrl-c to quit <3 )")
termWidth, termHeight, err := terminal.GetSize(int(outFile.Fd()))
if err != nil {
return err
}
margin := termWidth / 3
starLinesLeft := []string{}
for x := 0; x < len(lines); x++ {
starLinesLeft = append(starLinesLeft, starLine(margin))
}
starLinesRight := []string{}
for x := 0; x < len(lines); x++ {
lineWidth := termWidth - (margin + len(lines[x]))
starLinesRight = append(starLinesRight, starLine(lineWidth))
}
loop := true
startx := termHeight - 1
li := 0
for loop {
clear()
for x := 0; x < termHeight; x++ {
if x == startx || startx < 0 {
starty := 0
if startx < 0 {
starty = int(math.Abs(float64(startx)))
}
for y := starty; y < li+1; y++ {
if y >= len(lines) {
continue
}
starLineLeft := starLinesLeft[y]
starLinesLeft[y] = twinkle(starLineLeft)
starLineRight := starLinesRight[y]
starLinesRight[y] = twinkle(starLineRight)
fmt.Fprintf(out, "%s %s %s\n", starLineLeft, lines[y], starLineRight)
}
li += 1
x += li
} else {
fmt.Fprintf(out, "\n")
}
}
if li < len(lines) {
startx -= 1
}
time.Sleep(300 * time.Millisecond)
}
return nil
}
func starLine(width int) string {
line := ""
starChance := 0.1
for y := 0; y < width; y++ {
chance := rand.Float64()
if chance <= starChance {
charRoll := rand.Float64()
switch {
case charRoll < 0.3:
line += "."
case charRoll > 0.3 && charRoll < 0.6:
line += "+"
default:
line += "*"
}
} else {
line += " "
}
}
return line
}
func twinkle(starLine string) string {
starLine = strings.ReplaceAll(starLine, ".", "P")
starLine = strings.ReplaceAll(starLine, "+", "A")
starLine = strings.ReplaceAll(starLine, "*", ".")
starLine = strings.ReplaceAll(starLine, "P", "+")
starLine = strings.ReplaceAll(starLine, "A", "*")
return starLine
}
func getColor(x int) func(string) string {
rainbow := []func(string) string{
utils.Magenta,
utils.Red,
utils.Yellow,
utils.Green,
utils.Cyan,
utils.Blue,
}
ix := x % len(rainbow)
return rainbow[ix]
}
func clear() {
// on windows we'd do cmd := exec.Command("cmd", "/c", "cls"); unfortunately the draw speed is so
// slow that the animation is very jerky, flashy, and painful to look at.
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
_ = cmd.Run()
}