mirror of https://github.com/nealey/spongy
New non-cgi client
This commit is contained in:
parent
b3a222ba88
commit
9a1eafac31
|
@ -0,0 +1,171 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-fsnotify/fsnotify"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const eventIdSep = "/"
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
running bool
|
||||||
|
|
||||||
|
Name string
|
||||||
|
currentLog string
|
||||||
|
lineno int64
|
||||||
|
|
||||||
|
basePath string
|
||||||
|
seq int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNetwork(basePath string) (*Network) {
|
||||||
|
return &Network{
|
||||||
|
running: true,
|
||||||
|
Name: path.Base(basePath),
|
||||||
|
basePath: basePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *Network) Close() {
|
||||||
|
nw.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *Network) ReadLastEventId(lastEventId string) {
|
||||||
|
for _, eventId := range strings.Split(lastEventId, " ") {
|
||||||
|
parts := strings.Split(eventId, eventIdSep)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts[0] != nw.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nw.currentLog = parts[1]
|
||||||
|
nw.lineno, _ = strconv.ParseInt(parts[2], 10, 64)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *Network) LastEventId() string {
|
||||||
|
parts := []string{nw.Name, nw.currentLog, strconv.FormatInt(nw.lineno, 10)}
|
||||||
|
return strings.Join(parts, eventIdSep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *Network) errmsg(err error) string {
|
||||||
|
return fmt.Sprintf("ERROR: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *Network) Tail(out chan<- string) {
|
||||||
|
if nw.currentLog == "" {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
currentfn := path.Join(nw.basePath, "log", "current")
|
||||||
|
nw.currentLog, err = os.Readlink(currentfn)
|
||||||
|
if err != nil {
|
||||||
|
out <- nw.errmsg(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath := path.Join(nw.basePath, "log", nw.currentLog)
|
||||||
|
f, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
out <- nw.errmsg(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
out <- nw.errmsg(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
watcher.Add(filepath)
|
||||||
|
lineno := int64(0)
|
||||||
|
|
||||||
|
// XXX: some way to stop this?
|
||||||
|
for nw.running {
|
||||||
|
bf := bufio.NewScanner(f)
|
||||||
|
for bf.Scan() {
|
||||||
|
lineno += 1
|
||||||
|
if lineno <= nw.lineno {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
nw.lineno = lineno
|
||||||
|
}
|
||||||
|
|
||||||
|
t := bf.Text()
|
||||||
|
|
||||||
|
parts := strings.Split(t, " ")
|
||||||
|
if (len(parts) >= 3) && (parts[1] == "NEXTLOG") {
|
||||||
|
watcher.Remove(filepath)
|
||||||
|
filename := parts[2]
|
||||||
|
filepath = path.Join(nw.basePath, "log", filename)
|
||||||
|
f.Close()
|
||||||
|
f, err = os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
out <- nw.errmsg(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
watcher.Add(filepath)
|
||||||
|
lineno = 0
|
||||||
|
nw.lineno = 0
|
||||||
|
}
|
||||||
|
out <- t
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case _ = <-watcher.Events:
|
||||||
|
// Somethin' happened!
|
||||||
|
case err := <-watcher.Errors:
|
||||||
|
out <- nw.errmsg(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *Network) Write(data []byte) {
|
||||||
|
epoch := time.Now().Unix()
|
||||||
|
pid := os.Getpid()
|
||||||
|
filename := fmt.Sprintf("%d-%d-%d.txt", epoch, pid, nw.seq)
|
||||||
|
|
||||||
|
filepath := path.Join(nw.basePath, "outq", filename)
|
||||||
|
ioutil.WriteFile(filepath, data, 0750)
|
||||||
|
nw.seq += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Networks(basePath string) (found []*Network) {
|
||||||
|
|
||||||
|
dir, err := os.Open(basePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
|
||||||
|
|
||||||
|
entities, _ := dir.Readdirnames(0)
|
||||||
|
for _, fn := range entities {
|
||||||
|
netdir := path.Join(basePath, fn)
|
||||||
|
|
||||||
|
_, err = os.Stat(path.Join(netdir, "nick"))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nw := NewNetwork(netdir)
|
||||||
|
found = append(found, nw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var playback int
|
||||||
|
var running bool = true
|
||||||
|
|
||||||
|
func inputLoop(nw *Network) {
|
||||||
|
bf := bufio.NewScanner(os.Stdin)
|
||||||
|
for bf.Scan() {
|
||||||
|
line := bf.Bytes()
|
||||||
|
nw.Write(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] NETDIR\n", os.Args[0])
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "NETDIR is the path to your IRC directory (see README)\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "OPTIONS:\n")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.IntVar(&playback, "playback", 0, "Number of lines to play back on startup")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
if flag.NArg() != 1 {
|
||||||
|
usage()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
netDir, err := filepath.Abs(flag.Arg(0))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nw := NewNetwork(netDir)
|
||||||
|
defer nw.Close()
|
||||||
|
go inputLoop(nw)
|
||||||
|
|
||||||
|
outq := make(chan string, 50) // to stdout
|
||||||
|
go nw.Tail(outq)
|
||||||
|
for line := range outq {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,14 +64,6 @@ func (lf *Logfile) processQueue() {
|
||||||
lf.file.Close()
|
lf.file.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lf *Logfile) writeln(s string) error {
|
|
||||||
_, err := fmt.Fprintf(lf.file, "%d %s\n", time.Now().Unix(), s)
|
|
||||||
if err == nil {
|
|
||||||
lf.nlines += 1
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lf *Logfile) rotate() error {
|
func (lf *Logfile) rotate() error {
|
||||||
fn := fmt.Sprintf("%s.log", time.Now().UTC().Format(time.RFC3339))
|
fn := fmt.Sprintf("%s.log", time.Now().UTC().Format(time.RFC3339))
|
||||||
pathn := path.Join(lf.baseDir, "log", fn)
|
pathn := path.Join(lf.baseDir, "log", fn)
|
||||||
|
@ -83,15 +75,15 @@ func (lf *Logfile) rotate() error {
|
||||||
currentPath := path.Join(lf.baseDir, "log", "current")
|
currentPath := path.Join(lf.baseDir, "log", "current")
|
||||||
|
|
||||||
if lf.file == nil {
|
if lf.file == nil {
|
||||||
// Set lf.file just so we can write out NEXTLOG.
|
// Open "current" to append a NEXTLOG line.
|
||||||
// If this fails, that's okay
|
// If there's no "current", that's okay
|
||||||
lf.file, _ = os.OpenFile(currentPath, os.O_WRONLY|os.O_APPEND, 0666)
|
lf.file, _ = os.OpenFile(currentPath, os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lf.file != nil {
|
if lf.file != nil {
|
||||||
// Note location of new log
|
// Note location of new log
|
||||||
logmsg := fmt.Sprintf("NEXTLOG %s", fn)
|
logmsg := fmt.Sprintf("NEXTLOG %s", fn)
|
||||||
lf.writeln(timestamp(logmsg))
|
fmt.Fprintln(lf.file, timestamp(logmsg))
|
||||||
|
|
||||||
// All done with the current log
|
// All done with the current log
|
||||||
lf.file.Close()
|
lf.file.Close()
|
||||||
|
@ -105,7 +97,7 @@ func (lf *Logfile) rotate() error {
|
||||||
os.Symlink(fn, currentPath)
|
os.Symlink(fn, currentPath)
|
||||||
|
|
||||||
logmsg := fmt.Sprintf("PREVLOG %s", lf.name)
|
logmsg := fmt.Sprintf("PREVLOG %s", lf.name)
|
||||||
lf.writeln(timestamp(logmsg))
|
fmt.Fprintln(lf.file, timestamp(logmsg))
|
||||||
|
|
||||||
lf.name = fn
|
lf.name = fn
|
||||||
|
|
|
@ -200,10 +200,12 @@ func (nw *Network) messageDispatchLoop() {
|
||||||
if len(out) > 0 {
|
if len(out) > 0 {
|
||||||
outlines := strings.Split(string(out), "\n")
|
outlines := strings.Split(string(out), "\n")
|
||||||
for _, line := range outlines {
|
for _, line := range outlines {
|
||||||
|
if len(line) > 0 {
|
||||||
nw.outq <- line
|
nw.outq <- line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nw *Network) ConnectToNextServer() bool {
|
func (nw *Network) ConnectToNextServer() bool {
|
||||||
|
@ -244,6 +246,14 @@ func (nw *Network) ConnectToNextServer() bool {
|
||||||
|
|
||||||
func (nw *Network) login() {
|
func (nw *Network) login() {
|
||||||
var name string
|
var name string
|
||||||
|
var username string
|
||||||
|
|
||||||
|
usernames, err := ReadLines(path.Join(nw.basePath, "username"))
|
||||||
|
if err == nil {
|
||||||
|
username = usernames[0]
|
||||||
|
} else {
|
||||||
|
username = "sponge"
|
||||||
|
}
|
||||||
|
|
||||||
names, err := ReadLines(path.Join(nw.basePath, "name"))
|
names, err := ReadLines(path.Join(nw.basePath, "name"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -258,10 +268,12 @@ func (nw *Network) login() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "Charlie"
|
// Rogue used "Rodney" if you didn't give it a name.
|
||||||
|
// This one works for the ladies, too.
|
||||||
|
name = "Ronnie"
|
||||||
}
|
}
|
||||||
|
|
||||||
nw.outq <- "USER g g g :" + name
|
nw.outq <- "USER " + username + " g g :" + name
|
||||||
nw.NextNick()
|
nw.NextNick()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue