spongy

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

commit
b3a222b
parent
53d8d00
author
Neale Pickett
date
2016-01-18 18:09:29 -0700 MST
Add the handler program call
4 files changed,  +111, -57
M spongy/irc.go
+3, -5
 1@@ -3,10 +3,10 @@ package main
 2 import (
 3 	"strconv"
 4 	"strings"
 5-	"fmt"
 6 )
 7 
 8 type Message struct {
 9+	Unparsed   string
10 	Command    string
11 	FullSender string
12 	Sender     string
13@@ -20,6 +20,7 @@ func NewMessage(v string) (Message, error) {
14 	var parts []string
15 	var lhs string
16 
17+	m.Unparsed = v
18 	parts = strings.SplitN(v, " :", 2)
19 	if len(parts) == 2 {
20 		lhs = parts[0]
21@@ -98,11 +99,9 @@ func NewMessage(v string) (Message, error) {
22 }
23 
24 func (m Message) String() string {
25-	args := strings.Join(m.Args, " ")
26-	return fmt.Sprintf("%s %s %s %s %s :%s", m.FullSender, m.Command, m.Sender, m.Forum, args, m.Text)
27+	return m.Unparsed
28 }
29 
30-
31 func SplitTarget(s string) (string, string, string) {
32 	var parts []string
33 
34@@ -130,4 +129,3 @@ func IsChannel(s string) bool {
35 		return false
36 	}
37 }
38-
M spongy/logfile.go
+45, -24
  1@@ -3,6 +3,7 @@ package main
  2 import (
  3 	"fmt"
  4 	"os"
  5+	"log"
  6 	"path"
  7 	"time"
  8 )
  9@@ -13,17 +14,54 @@ type Logfile struct {
 10 	name string
 11 	nlines int
 12 	maxlines int
 13+	outq chan string
 14+}
 15+
 16+func timestamp(s string) string {
 17+	ret := fmt.Sprintf("%d %s", time.Now().Unix(), s)
 18+	return ret
 19 }
 20 
 21 func NewLogfile(baseDir string, maxlines int) (*Logfile) {
 22-	return &Logfile{baseDir, nil, "", 0, maxlines}
 23+	lf := Logfile{baseDir, nil, "", 0, maxlines, make(chan string, 50)}
 24+	go lf.processQueue();
 25+	return &lf
 26 }
 27 
 28 func (lf *Logfile) Close() {
 29 	if lf.file != nil {
 30-		lf.writeln("EXIT")
 31-		lf.file.Close()
 32+		lf.Log("EXIT")
 33+		close(lf.outq)
 34+	}
 35+}
 36+
 37+func (lf *Logfile) Log(s string) error {
 38+	lf.outq <- timestamp(s)
 39+	return nil
 40+}
 41+
 42+//
 43+//
 44+
 45+func (lf *Logfile) processQueue() {
 46+	for line := range lf.outq {
 47+		if (lf.file == nil) || (lf.nlines >= lf.maxlines) {
 48+			if err := lf.rotate(); err != nil {
 49+				// Just keep trying, I guess.
 50+				log.Print(err)
 51+				continue
 52+			}
 53+			lf.nlines = 0
 54+		}
 55+
 56+		if _, err := fmt.Fprintln(lf.file, line); err != nil {
 57+			log.Print(err)
 58+			continue
 59+		}
 60+		lf.nlines += 1
 61 	}
 62+
 63+	lf.file.Close()
 64 }
 65 
 66 func (lf *Logfile) writeln(s string) error {
 67@@ -52,8 +90,8 @@ func (lf *Logfile) rotate() error {
 68 	
 69 	if lf.file != nil {
 70 		// Note location of new log
 71-		logmsg := fmt.Sprintf(". NEXTLOG %s", fn)
 72-		lf.writeln(logmsg)
 73+		logmsg := fmt.Sprintf("NEXTLOG %s", fn)
 74+		lf.writeln(timestamp(logmsg))
 75 		
 76 		// All done with the current log
 77 		lf.file.Close()
 78@@ -66,27 +104,10 @@ func (lf *Logfile) rotate() error {
 79 	os.Remove(currentPath)
 80 	os.Symlink(fn, currentPath)
 81 	
 82-	logmsg := fmt.Sprintf(". PREVLOG %s", lf.name)
 83-	lf.writeln(logmsg)
 84+	logmsg := fmt.Sprintf("PREVLOG %s", lf.name)
 85+	lf.writeln(timestamp(logmsg))
 86 	
 87 	lf.name = fn
 88 	
 89 	return nil
 90 }
 91-
 92-func (lf *Logfile) Log(s string) error {
 93-	if lf.file == nil {
 94-		lf.rotate()
 95-	}
 96-	
 97-	err := lf.writeln(s)
 98-	if err == nil {
 99-		return err
100-	}
101-
102-	if lf.nlines >= lf.maxlines {
103-		return lf.rotate()
104-	}
105-	
106-	return nil
107-}
M spongy/network.go
+53, -26
  1@@ -9,6 +9,7 @@ import (
  2 	"net"
  3 	"os"
  4 	"os/user"
  5+	"os/exec"
  6 	"path"
  7 	"strings"
  8 	"time"
  9@@ -48,7 +49,9 @@ type Network struct {
 10 	serverIndex int
 11 
 12 	conn io.ReadWriteCloser
 13-	logq chan Message
 14+
 15+	logf *Logfile
 16+	
 17 	inq  chan string
 18 	outq chan string
 19 }
 20@@ -57,23 +60,21 @@ func NewNetwork(basePath string) *Network {
 21 	nw := Network{
 22 		running: true,
 23 		basePath: basePath,
 24-		logq: make(chan Message, 20),
 25 	}
 26-	
 27-	go nw.LogLoop()
 28+	nw.logf = NewLogfile(nw.basePath, int(maxlogsize))
 29 	
 30 	return &nw
 31 }
 32 
 33 func (nw *Network) Close() {
 34 	nw.running = false
 35-	close(nw.logq)
 36 	if nw.conn != nil {
 37 		nw.conn.Close()
 38 	}
 39+	nw.logf.Close()
 40 }
 41 
 42-func (nw *Network) WatchOutqDirectory() {
 43+func (nw *Network) watchOutqDirectory() {
 44 	outqDirname := path.Join(nw.basePath, "outq")
 45 
 46 	dir, err := os.Open(outqDirname)
 47@@ -113,20 +114,12 @@ func (nw *Network) HandleInfile(fn string) {
 48 	}
 49 }
 50 
 51-func (nw *Network) LogLoop() {
 52-	logf := NewLogfile(nw.basePath, int(maxlogsize))
 53-	defer logf.Close()
 54-	
 55-	for m := range nw.logq {
 56-		logf.Log(m.String())
 57-	}
 58-}
 59-
 60-func (nw *Network) ServerWriteLoop() {
 61+func (nw *Network) serverWriteLoop() {
 62 	for v := range nw.outq {
 63-		m, _ := NewMessage(v)
 64-		nw.logq <- m
 65+		debug("ยป %s", v)
 66+		nw.logf.Log(v)
 67 		fmt.Fprintln(nw.conn, v)
 68+		time.Sleep(500 * time.Millisecond)
 69 	}
 70 }
 71 
 72@@ -163,21 +156,21 @@ func (nw *Network) JoinChannels() {
 73 	}
 74 	
 75 	for _, ch := range chans {
 76+		debug("Joining %s", ch)
 77 		nw.outq <- "JOIN " + ch
 78 	}
 79 }
 80 
 81-func (nw *Network) MessageDispatch() {
 82+func (nw *Network) messageDispatchLoop() {
 83 	for line := range nw.inq {
 84+		nw.logf.Log(line)
 85+
 86 		m, err := NewMessage(line)
 87 		if err != nil {
 88 			log.Print(err)
 89 			continue
 90 		}
 91 		
 92-		nw.logq <- m
 93-		// XXX: Add in a handler subprocess call
 94-		
 95 		switch m.Command {
 96 		case "PING":
 97 			nw.outq <- "PONG :" + m.Text
 98@@ -186,6 +179,30 @@ func (nw *Network) MessageDispatch() {
 99 		case "433":
100 			nw.NextNick()
101 		}
102+
103+		handlerPath := path.Join(nw.basePath, "handler")
104+		cmd := exec.Command(handlerPath, m.Args...)
105+		cmd.Env = []string{
106+			"command=" + m.Command,
107+			"fullsender=" + m.FullSender,
108+			"sender=" + m.Sender,
109+			"forum=" + m.Forum,
110+			"text=" + m.Text,
111+			"raw=" + line,
112+		}
113+		cmd.Stderr = os.Stderr
114+		out, err := cmd.Output()
115+		if err != nil {
116+			log.Print(err)
117+			continue
118+		}
119+
120+		if len(out) > 0 {
121+			outlines := strings.Split(string(out), "\n")
122+			for _, line := range outlines {
123+				nw.outq <- line
124+			}
125+		}
126 	}
127 }
128 
129@@ -201,6 +218,7 @@ func (nw *Network) ConnectToNextServer() bool {
130 	}
131 	server := servers[nw.serverIndex]
132 
133+	debug("Connecting to %s", server)
134 	switch (server[0]) {
135 	case '|':
136 		parts := strings.Split(server[1:], " ")
137@@ -219,6 +237,7 @@ func (nw *Network) ConnectToNextServer() bool {
138 		log.Print(err)
139 		return false
140 	}
141+	debug("Connected")
142 	
143 	return true
144 }
145@@ -246,8 +265,15 @@ func (nw *Network) login() {
146 	nw.NextNick()
147 }
148 
149+func (nw *Network) keepaliveLoop() {
150+	for nw.running {
151+		time.Sleep(1 * time.Minute)
152+		nw.outq <- "PING :keepalive"
153+	}
154+}
155+
156 
157-func (nw *Network) Connect(){
158+func (nw *Network) Connect() {
159 	for nw.running {
160 		if ! nw.ConnectToNextServer() {
161 			time.Sleep(8 * time.Second)
162@@ -257,9 +283,10 @@ func (nw *Network) Connect(){
163 		nw.inq = make(chan string, 20)
164 		nw.outq = make(chan string, 20)
165 
166-		go nw.ServerWriteLoop()
167-		go nw.MessageDispatch()
168-		go nw.WatchOutqDirectory()
169+		go nw.serverWriteLoop()
170+		go nw.messageDispatchLoop()
171+		go nw.watchOutqDirectory()
172+		go nw.keepaliveLoop()
173 
174 		nw.login()
175 		
M spongy/spongy.go
+10, -2
 1@@ -11,8 +11,15 @@ import (
 2 )
 3 
 4 var running bool = true
 5+var verbose bool = false
 6 var maxlogsize uint
 7 
 8+func debug(format string, a ...interface{}) {
 9+	if verbose {
10+		log.Printf(format, a...)
11+	}
12+}
13+
14 func exists(filename string) bool {
15 	_, err := os.Stat(filename); if err != nil {
16 		return false
17@@ -78,8 +85,9 @@ func usage() {
18 
19 func main() {
20 	flag.Usage = usage
21-	flag.UintVar(&maxlogsize, "logsize", 1000, "Log entries before rotating")
22-	notime := flag.Bool("notime", false, "Don't timestamp log messages")
23+	flag.UintVar(&maxlogsize, "logsize", 6000, "Log entries before rotating")
24+	flag.BoolVar(&verbose, "verbose", false, "Verbose logging")
25+	notime := flag.Bool("notime", false, "Don't timestamp debugging messages")
26 	flag.Parse()
27 	if flag.NArg() != 1 {
28 		usage()