spongy

A Unixy IRC client
git clone https://git.woozle.org/neale/spongy.git

commit
9a1eafa
parent
b3a222b
author
Neale Pickett
date
2016-01-18 22:01:29 -0700 MST
New non-cgi client
10 files changed,  +245, -15
A spongycli/network.go
+171, -0
  1@@ -0,0 +1,171 @@
  2+package main
  3+
  4+import (
  5+	"bufio"
  6+	"fmt"
  7+	"github.com/go-fsnotify/fsnotify"
  8+	"io/ioutil"
  9+	"os"
 10+	"path"
 11+	"strconv"
 12+	"strings"
 13+	"time"
 14+)
 15+
 16+const eventIdSep = "/"
 17+
 18+type Network struct {
 19+	running bool
 20+
 21+	Name string
 22+	currentLog string
 23+	lineno int64
 24+
 25+	basePath string
 26+	seq int
 27+}
 28+
 29+func NewNetwork(basePath string) (*Network) {
 30+	return &Network{
 31+		running: true,
 32+		Name: path.Base(basePath),
 33+		basePath: basePath,
 34+	}
 35+}
 36+
 37+func (nw *Network) Close() {
 38+	nw.running = false
 39+}
 40+
 41+func (nw *Network) ReadLastEventId(lastEventId string) {
 42+	for _, eventId := range strings.Split(lastEventId, " ") {
 43+		parts := strings.Split(eventId, eventIdSep)
 44+		if len(parts) != 3 {
 45+			continue
 46+		}
 47+		
 48+		if parts[0] != nw.Name {
 49+			continue
 50+		}
 51+		nw.currentLog = parts[1]
 52+		nw.lineno, _ = strconv.ParseInt(parts[2], 10, 64)
 53+		return
 54+	}
 55+}
 56+
 57+func (nw *Network) LastEventId() string {
 58+	parts := []string{nw.Name, nw.currentLog, strconv.FormatInt(nw.lineno, 10)}
 59+	return strings.Join(parts, eventIdSep)
 60+}
 61+
 62+func (nw *Network) errmsg(err error) string {
 63+	return fmt.Sprintf("ERROR: %s", err.Error())
 64+}
 65+
 66+func (nw *Network) Tail(out chan<- string) {
 67+	if nw.currentLog == "" {
 68+		var err error
 69+		
 70+		currentfn := path.Join(nw.basePath, "log", "current")
 71+		nw.currentLog, err = os.Readlink(currentfn)
 72+		if err != nil {
 73+			out <- nw.errmsg(err)
 74+			return
 75+		}
 76+	}
 77+	
 78+	filepath := path.Join(nw.basePath, "log", nw.currentLog)
 79+	f, err := os.Open(filepath)
 80+	if err != nil {
 81+		out <- nw.errmsg(err)
 82+		return
 83+	}
 84+	defer f.Close()
 85+	
 86+	watcher, err := fsnotify.NewWatcher()
 87+	if err != nil {
 88+		out <- nw.errmsg(err)
 89+		return
 90+	}
 91+	defer watcher.Close()
 92+	
 93+	watcher.Add(filepath)
 94+	lineno := int64(0)
 95+	
 96+	// XXX: some way to stop this?
 97+	for nw.running {
 98+		bf := bufio.NewScanner(f)
 99+		for bf.Scan() {
100+			lineno += 1
101+			if lineno <= nw.lineno {
102+				continue
103+			} else {
104+				nw.lineno = lineno
105+			}
106+			
107+			t := bf.Text()
108+			
109+			parts := strings.Split(t, " ")
110+			if (len(parts) >= 3) && (parts[1] == "NEXTLOG") {
111+				watcher.Remove(filepath)
112+				filename := parts[2]
113+				filepath = path.Join(nw.basePath, "log", filename)
114+				f.Close()
115+				f, err = os.Open(filepath)
116+				if err != nil {
117+					out <- nw.errmsg(err)
118+					return
119+				}
120+				watcher.Add(filepath)
121+				lineno = 0
122+				nw.lineno = 0
123+			}
124+			out <- t
125+		}
126+		
127+		select {
128+		case _ = <-watcher.Events:
129+			// Somethin' happened!
130+		case err := <-watcher.Errors:
131+			out <- nw.errmsg(err)
132+			return
133+		}
134+	}
135+}
136+
137+func (nw *Network) Write(data []byte) {
138+	epoch := time.Now().Unix()
139+	pid := os.Getpid()
140+	filename := fmt.Sprintf("%d-%d-%d.txt", epoch, pid, nw.seq)
141+	
142+	filepath := path.Join(nw.basePath, "outq", filename)
143+	ioutil.WriteFile(filepath, data, 0750)
144+	nw.seq += 1
145+}
146+
147+
148+func Networks(basePath string) (found []*Network) {
149+
150+	dir, err := os.Open(basePath)
151+	if err != nil {
152+		return
153+	}
154+	defer dir.Close()
155+	
156+	
157+	entities, _ := dir.Readdirnames(0)
158+	for _, fn := range entities {
159+		netdir := path.Join(basePath, fn)
160+		
161+		_, err = os.Stat(path.Join(netdir, "nick"))
162+		if err != nil {
163+			continue
164+		}
165+		
166+		nw := NewNetwork(netdir)
167+		found = append(found, nw)
168+	}
169+	
170+	return
171+}
172+	
A spongycli/spongycli.go
+55, -0
 1@@ -0,0 +1,55 @@
 2+package main
 3+
 4+import (
 5+	"bufio"
 6+	"fmt"
 7+	"flag"
 8+	"log"
 9+	"os"
10+	"path/filepath"
11+)
12+
13+var playback int
14+var running bool = true
15+
16+func inputLoop(nw *Network) {
17+	bf := bufio.NewScanner(os.Stdin)
18+	for bf.Scan() {
19+		line := bf.Bytes()
20+		nw.Write(line)
21+	}
22+}
23+
24+func usage() {
25+	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] NETDIR\n", os.Args[0])
26+	fmt.Fprintf(os.Stderr, "\n")
27+	fmt.Fprintf(os.Stderr, "NETDIR is the path to your IRC directory (see README)\n")
28+	fmt.Fprintf(os.Stderr, "\n")
29+	fmt.Fprintf(os.Stderr, "OPTIONS:\n")
30+	flag.PrintDefaults()
31+}
32+
33+func main() {
34+	flag.Usage = usage
35+	flag.IntVar(&playback, "playback", 0, "Number of lines to play back on startup")
36+	
37+	flag.Parse()
38+	if flag.NArg() != 1 {
39+		usage()
40+		os.Exit(2)
41+	}
42+	netDir, err := filepath.Abs(flag.Arg(0))
43+	if err != nil {
44+		log.Fatal(err)
45+	}
46+
47+	nw := NewNetwork(netDir)
48+	defer nw.Close()
49+	go inputLoop(nw)
50+
51+ 	outq := make(chan string, 50) // to stdout
52+	go nw.Tail(outq)
53+	for line := range outq {
54+		fmt.Println(line)
55+	}
56+}
R spongy/irc.go => spongyd/irc.go
+0, -0
R spongy/logfile.go => spongyd/logfile.go
+4, -12
 1@@ -64,14 +64,6 @@ func (lf *Logfile) processQueue() {
 2 	lf.file.Close()
 3 }
 4 
 5-func (lf *Logfile) writeln(s string) error {
 6-	_, err := fmt.Fprintf(lf.file, "%d %s\n", time.Now().Unix(), s)
 7-	if err == nil {
 8-		lf.nlines += 1
 9-	}
10-	return err
11-}
12-
13 func (lf *Logfile) rotate() error {
14 	fn := fmt.Sprintf("%s.log", time.Now().UTC().Format(time.RFC3339))
15 	pathn := path.Join(lf.baseDir, "log", fn)
16@@ -83,15 +75,15 @@ func (lf *Logfile) rotate() error {
17 	currentPath := path.Join(lf.baseDir, "log", "current")
18 	
19 	if lf.file == nil {
20-		// Set lf.file just so we can write out NEXTLOG.
21-		// If this fails, that's okay
22+		// Open "current" to append a NEXTLOG line.
23+		// If there's no "current", that's okay
24 		lf.file, _ = os.OpenFile(currentPath, os.O_WRONLY|os.O_APPEND, 0666)
25 	}
26 	
27 	if lf.file != nil {
28 		// Note location of new log
29 		logmsg := fmt.Sprintf("NEXTLOG %s", fn)
30-		lf.writeln(timestamp(logmsg))
31+		fmt.Fprintln(lf.file, timestamp(logmsg))
32 		
33 		// All done with the current log
34 		lf.file.Close()
35@@ -105,7 +97,7 @@ func (lf *Logfile) rotate() error {
36 	os.Symlink(fn, currentPath)
37 	
38 	logmsg := fmt.Sprintf("PREVLOG %s", lf.name)
39-	lf.writeln(timestamp(logmsg))
40+	fmt.Fprintln(lf.file, timestamp(logmsg))
41 	
42 	lf.name = fn
43 	
R spongy/network.go => spongyd/network.go
+15, -3
 1@@ -200,7 +200,9 @@ func (nw *Network) messageDispatchLoop() {
 2 		if len(out) > 0 {
 3 			outlines := strings.Split(string(out), "\n")
 4 			for _, line := range outlines {
 5-				nw.outq <- line
 6+				if len(line) > 0 {
 7+					nw.outq <- line
 8+				}
 9 			}
10 		}
11 	}
12@@ -244,6 +246,14 @@ func (nw *Network) ConnectToNextServer() bool {
13 
14 func (nw *Network) login() {
15 	var name string
16+	var username string
17+
18+	usernames, err := ReadLines(path.Join(nw.basePath, "username"))
19+	if err == nil {
20+		username = usernames[0]
21+	} else {
22+		username = "sponge"
23+	}
24 
25 	names, err := ReadLines(path.Join(nw.basePath, "name"))
26 	if err == nil {
27@@ -258,10 +268,12 @@ func (nw *Network) login() {
28 	}
29 	
30 	if name == "" {
31-		name = "Charlie"
32+		// Rogue used "Rodney" if you didn't give it a name.
33+		// This one works for the ladies, too.
34+		name = "Ronnie"
35 	}
36 
37-	nw.outq <- "USER g g g :" + name
38+	nw.outq <- "USER " + username + " g g :" + name
39 	nw.NextNick()
40 }
41 
R spongy/network_test.go => spongyd/network_test.go
+0, -0
R spongy/readwritecloserwrapper.go => spongyd/readwritecloserwrapper.go
+0, -0
R spongy/readwritecloserwrapper_test.go => spongyd/readwritecloserwrapper_test.go
+0, -0
R spongy/spongy_test.go => spongyd/spongy_test.go
+0, -0
R spongy/spongy.go => spongyd/spongyd.go
+0, -0