krz/skunky-art

Alternative privacy frontend for DeviantArt.

clone: git clone https://gitbay.org/krz/skunky-art.git

v1.3.4: app/cli.go · raw

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