commit ee425b3

delthas  ·  2023-06-19 13:34:26 +0000 UTC
parent 1318e78
Add setup assistant

I can't be bothered to create configuration folders and files
manually :-)
3 files changed,  +91, -25
+12, -13
 1@@ -12,20 +12,21 @@ senpai is an IRC client that works best with bouncers:
 2 - history is fetched from the server via [CHATHISTORY],
 3 - networks are fetched from the server via [bouncer-networks].
 4 
 5-## How do I use this?
 6+## Installing
 7 
 8+From source (requires Go):
 9 ```shell
10-mkdir -p ~/.config/senpai
11-cat <<EOF >~/.config/senpai/senpai.scfg
12-address chat.sr.ht
13-nickname senpai
14-password "my password can't be this cute (2010)"
15-# alternatively, specify a command to fetch your password:
16-# password-cmd  gopass show irc/<username>
17-EOF
18-go run ./cmd/senpai
19+go install git.sr.ht/~taiite/senpai/cmd/senpai@master
20 ```
21 
22+## Running
23+
24+From your terminal:
25+```shell
26+senpai
27+```
28+Senpai will guide you through a configuration assistant on your first run.
29+
30 Then, type `/join #senpai` on [Libera.Chat] and have a... chat!
31 
32 See `doc/senpai.1.scd` for more information and `doc/senpai.5.scd` for more
33@@ -43,8 +44,7 @@ things out with an IRC server, then you have two options:
34 
35 ## Contributing and stuff
36 
37-Contributions are accepted as patches to [the mailing list][ml] and as pull
38-requests on [Github].
39+Contributions are accepted as patches to [the mailing list][ml].
40 
41 Browse tickets at <https://todo.sr.ht/~taiite/senpai>.
42 
43@@ -56,6 +56,5 @@ Copyright (C) 2021 The senpai Contributors
44 
45 [bouncer-networks]: https://git.sr.ht/~emersion/soju/tree/master/item/doc/ext/bouncer-networks.md
46 [CHATHISTORY]: https://ircv3.net/specs/extensions/chathistory
47-[Github]: https://github.com/hhirtz/senpai/pulls
48 [Libera.Chat]: https://libera.chat/
49 [ml]: https://lists.sr.ht/~delthas/senpai-dev
+74, -2
 1@@ -1,6 +1,7 @@
 2 package main
 3 
 4 import (
 5+	"errors"
 6 	"flag"
 7 	"fmt"
 8 	"io/ioutil"
 9@@ -37,8 +38,79 @@ func main() {
10 
11 	cfg, err := senpai.LoadConfigFile(configPath)
12 	if err != nil {
13-		fmt.Fprintf(os.Stderr, "failed to load the required configuration file at %q: %s\n", configPath, err)
14-		os.Exit(1)
15+		if !errors.Is(err, os.ErrNotExist) {
16+			fmt.Fprintf(os.Stderr, "failed to load the required configuration file at %q: %s\n", configPath, err)
17+			os.Exit(1)
18+			return
19+		}
20+		var host, port string
21+		tls := true
22+		var nick, password string
23+		fmt.Fprintf(os.Stderr, "The configuration file at %q was not found.\n", configPath)
24+		fmt.Fprintf(os.Stderr, "Configuration assistant: senpai will create a configuration file for you.\n")
25+		fmt.Fprintf(os.Stderr, "Configuration assistant: Enter your server host (examples: example.com, localhost, 1.2.3.4): ")
26+		for host == "" {
27+			fmt.Scanln(&host)
28+		}
29+		fmt.Fprintf(os.Stderr, "Configuration assistant: Enter your server port (examples: 6667, 6697) [optional]: ")
30+		fmt.Scanln(&port)
31+		fmt.Fprintf(os.Stderr, "Configuration assistant: Enter whether your server uses TLS (examples: yes, no) [optional, default: yes]: ")
32+		for {
33+			var tlsStr string
34+			fmt.Scanln(&tlsStr)
35+			if tlsStr == "" {
36+				break
37+			}
38+			if _, err := fmt.Fscan(strings.NewReader(tlsStr), &tls); err == nil {
39+				break
40+			}
41+		}
42+		fmt.Fprintf(os.Stderr, "Configuration assistant: Enter your nickname: ")
43+		for nick == "" {
44+			fmt.Scanln(&nick)
45+		}
46+		fmt.Fprintf(os.Stderr, "Configuration assistant: Enter your password (only enter if you already have an account) [optional]: ")
47+		fmt.Scanln(&password)
48+
49+		folderPath := path.Dir(configPath)
50+		if err := os.MkdirAll(folderPath, 0700); err != nil {
51+			fmt.Fprintf(os.Stderr, "failed to create the configuration file folder at %q: %s\n", folderPath, err)
52+			os.Exit(1)
53+			return
54+		}
55+		f, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
56+		if err != nil {
57+			fmt.Fprintf(os.Stderr, "failed to create the configuration file at %q: %s\n", configPath, err)
58+			os.Exit(1)
59+			return
60+		}
61+		var addr string
62+		if !tls {
63+			addr += "irc+insecure://"
64+		}
65+		addr += host
66+		if port != "" {
67+			addr += ":" + port
68+		}
69+		fmt.Fprintf(f, "address %q\n", addr)
70+		fmt.Fprintf(f, "nickname %q\n", nick)
71+		if password != "" {
72+			fmt.Fprintf(f, "password %q\n", password)
73+		}
74+		f.Close()
75+
76+		cfg, err = senpai.LoadConfigFile(configPath)
77+		if err != nil {
78+			fmt.Fprintf(os.Stderr, "failed to load the configuration file at %q: %s\n", configPath, err)
79+			os.Exit(1)
80+			return
81+		}
82+
83+		fmt.Fprintf(os.Stderr, "Configuration assistant: Configuration saved to %q. Now starting.", configPath)
84+		for i := 0; i < 6; i++ {
85+			time.Sleep(500 * time.Millisecond)
86+			fmt.Fprintf(os.Stderr, ".")
87+		}
88 	}
89 
90 	cfg.Debug = cfg.Debug || debug
+5, -10
 1@@ -88,8 +88,8 @@ func DefaultHighlightPath() (string, error) {
 2 	return path.Join(configDir, "senpai", "highlight"), nil
 3 }
 4 
 5-func Defaults() (cfg Config, err error) {
 6-	cfg = Config{
 7+func Defaults() Config {
 8+	return Config{
 9 		Addr:             "",
10 		Nick:             "",
11 		Real:             "",
12@@ -115,15 +115,10 @@ func Defaults() (cfg Config, err error) {
13 		},
14 		Debug: false,
15 	}
16-
17-	return
18 }
19 
20 func LoadConfigFile(filename string) (cfg Config, err error) {
21-	cfg, err = Defaults()
22-	if err != nil {
23-		return
24-	}
25+	cfg = Defaults()
26 
27 	err = unmarshal(filename, &cfg)
28 	if err != nil {
29@@ -164,7 +159,7 @@ func LoadConfigFile(filename string) (cfg Config, err error) {
30 func unmarshal(filename string, cfg *Config) (err error) {
31 	directives, err := scfg.Load(filename)
32 	if err != nil {
33-		return fmt.Errorf("error parsing scfg: %s", err)
34+		return fmt.Errorf("error parsing scfg: %w", err)
35 	}
36 
37 	for _, d := range directives {
38@@ -205,7 +200,7 @@ func unmarshal(filename string, cfg *Config) (err error) {
39 			cmd := exec.Command(cmdName, d.Params[1:]...)
40 			var stdout []byte
41 			if stdout, err = cmd.Output(); err != nil {
42-				return fmt.Errorf("error running password command: %s", err)
43+				return fmt.Errorf("error running password command: %w", err)
44 			}
45 
46 			passCmdOut := strings.Split(string(stdout), "\n")