app/cli.go

184 lines · 5106 bytes · executable

  1package app
  2
  3import (
  4	"bufio"
  5	"bytes"
  6	"encoding/json"
  7	"html/template"
  8	"os"
  9	"time"
 10)
 11
 12// ExecuteCommandLineArguments parses argv, applying the flags that override
 13// config and running one-shot commands such as --help and --add-instance. Some
 14// of those commands exit the process rather than return.
 15func ExecuteCommandLineArguments() {
 16	var helpmsg = `SkunkyArt v{{.Version}} [{{.Description}}]
 17Usage:
 18	- [-c|--config] 	| path to config
 19	- [-a|--add-instance]	| generates 'instances.json' and 'INSTANCES.md' files with ur instance
 20	- [-h|--help]		| returns this message
 21Example:
 22	./skunkyart -c config.json
 23Copyright lost+skunk and zerolabs, X11. https://gitbay.org/krz/skunky-art/releases`
 24
 25	a := os.Args[1:]
 26	for n, x := range a {
 27		switch x {
 28		case "-c", "--config":
 29			if n+1 < len(a) {
 30				CFG.cfg = a[n+1]
 31				cfgExplicit = true
 32			} else {
 33				exit("Not enought arguments", 1)
 34			}
 35		case "-h", "--help":
 36			var buf bytes.Buffer
 37			t := template.New("help")
 38			tryWithExitStatus(func() error {
 39				if _, err := t.Parse(helpmsg); err != nil {
 40					return err
 41				}
 42				return t.Execute(&buf, &Release)
 43			}(), 1)
 44			exit(buf.String(), 0)
 45		case "-a", "--add-instance":
 46			addInstance()
 47		}
 48	}
 49}
 50
 51type settingsUrls struct {
 52	I2P      string `json:"i2p,omitempty"`
 53	Ygg      string `json:"ygg,omitempty"`
 54	Tor      string `json:"tor,omitempty"`
 55	Clearnet string `json:"clearnet,omitempty"`
 56}
 57
 58type settingsParams struct {
 59	Nsfw   bool   `json:"nsfw"`
 60	Proxy  bool   `json:"proxy"`
 61	HideAI bool   `json:"hide-ai"`
 62	Theme  string `json:"theme"`
 63}
 64
 65type settings struct {
 66	Title       string         `json:"title"`
 67	Country     string         `json:"country"`
 68	ModifiedSrc string         `json:"modified-src,omitempty"`
 69	Urls        settingsUrls   `json:"urls"`
 70	Settings    settingsParams `json:"settings"`
 71}
 72
 73func addInstance() {
 74	prompt := func(txt string, necessary bool) string {
 75		input := bufio.NewScanner(os.Stdin)
 76		for {
 77			print(txt)
 78			print(": ")
 79			input.Scan()
 80
 81			if i := input.Text(); necessary && i == "" {
 82				println("Please specify the", txt)
 83			} else {
 84				return i
 85			}
 86		}
 87	}
 88
 89	var settingsVar struct {
 90		Instances []settings `json:"instances"`
 91	}
 92	// 0644: both files are committed to the repository and are meant to be
 93	// world-readable, so gosec's 0600 default does not apply.
 94	instancesJSON, err := os.OpenFile("instances.json", os.O_CREATE|os.O_WRONLY, 0644) //nolint:gosec // G302
 95	if err != nil {
 96		exit(err.Error(), 1)
 97	}
 98	defer func() { try(instancesJSON.Close()) }()
 99
100	instancesFile, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //nolint:gosec // G302
101	if err != nil {
102		exit(err.Error(), 1)
103	}
104	defer func() { try(instancesFile.Close()) }()
105
106	for {
107		if string(instances) == "" {
108			print("\rDownloading instance list...")
109		} else {
110			println("\r\033[2KDownloaded!")
111			try(json.Unmarshal(instances, &settingsVar))
112
113			settingsVar.Instances = append(settingsVar.Instances, settings{
114				Title:       prompt("Title", true),
115				Country:     prompt("Country", true),
116				ModifiedSrc: prompt("Link to modified sources", false),
117				Settings: settingsParams{
118					Nsfw:   CFG.Nsfw,
119					Proxy:  CFG.Proxy,
120					HideAI: CFG.HideAI,
121					Theme:  CFG.Theme,
122				},
123				Urls: settingsUrls{
124					Clearnet: prompt("Clearnet link", false),
125					Ygg:      prompt("Yggdrasil link", false),
126					Tor:      prompt("Onion link", false),
127					I2P:      prompt("I2P link", false),
128				},
129			})
130
131			j, err := json.MarshalIndent(&settingsVar, "", "    ")
132			try(err)
133
134			try(func() error { _, err := instancesJSON.Write(j); return err }())
135
136			settingsVar := &settingsVar.Instances[len(settingsVar.Instances)-1]
137			var mdstr bytes.Buffer
138
139			mdbuilder := func(yes bool, link string, title string) {
140				switch {
141				case yes && (title != "" && link != ""):
142					mdstr.WriteString("[")
143					mdstr.WriteString(title)
144					mdstr.WriteString("](")
145					mdstr.WriteString(link)
146					mdstr.WriteString(")")
147				case yes && link != "":
148					mdstr.WriteString("[Yes](")
149					mdstr.WriteString(link)
150					mdstr.WriteString(")")
151				case yes:
152					mdstr.WriteString("Yes")
153				default:
154					mdstr.WriteString("No")
155				}
156				mdstr.WriteString("|")
157			}
158
159			mdstr.WriteString("\n|")
160			mdbuilder(settingsVar.Urls.Clearnet != "", settingsVar.Urls.Clearnet, settingsVar.Title)
161
162			urls := []string{settingsVar.Urls.Ygg, settingsVar.Urls.I2P, settingsVar.Urls.Tor}
163			for i, l := 0, len(urls); i < l; i++ {
164				url := urls[i]
165				mdbuilder(url != "", url, "")
166			}
167
168			settings := []bool{settingsVar.Settings.Nsfw, settingsVar.Settings.Proxy}
169			for i, l := 0, len(settings); i < l; i++ {
170				mdbuilder(settings[i], "", "")
171			}
172
173			mdbuilder(settingsVar.ModifiedSrc != "", settingsVar.ModifiedSrc, "")
174
175			mdstr.WriteString(settingsVar.Country)
176			mdstr.WriteString("|")
177
178			try(func() error { _, err := instancesFile.Write(mdstr.Bytes()); return err }())
179			break
180		}
181		time.Sleep(500 * time.Millisecond)
182	}
183	exit("Done! Now add the files 'instances.json' and 'INSTANCES.md' to the 'main' branch by merge request to https://gitbay.org/krz/skunky-art", 0)
184}