218 lines · 4.3 KB · Go
RawDownload
package main import (	"code.google.com/p/go.net/websocket"	"fmt"	"github.com/bitly/go-notify"	"github.com/kballard/goirc/irc"	"html/template"	"log"	"math/rand"	"net/http"	"strings"	"sync"	"sync/atomic"	"time") const (	channel = "#comicchat") var (	nusers int32 = 0) type WebClientMessage struct {	Type    string	Message string} const (	// fresh connection	ClientNew int32 = iota	// everything ok - ws and irc connected	ClientOk	// someone closed - kill everything	ClientClosed) // a websocket connection to irctype WebClient struct {	mu      sync.Mutex	remote  string	nick    string	status  int32	config  irc.Config	irc     irc.SafeConn	ircquit chan bool	ws      *websocket.Conn} func NewWebClient(remote string, ws *websocket.Conn) *WebClient {	nick := fmt.Sprintf("comicchat%5.5d", rand.Intn(99999))	wc := &WebClient{		remote: remote,		nick:   nick,		status: ClientNew,		config: irc.Config{			Host:     "chat.freenode.net",			Nick:     nick,			User:     "comicchat",			RealName: "Comic Chat",		},		ws: ws,	} 	wc.config.Init = func(hr irc.HandlerRegistry) {		hr.AddHandler(irc.CONNECTED, wc.loggedin)	} 	return wc} func (w WebClient) String() string {	return fmt.Sprintf("%s %s", w.remote, w.nick)} func (w *WebClient) GetStatus() int32 {	return atomic.LoadInt32(&w.status)} func (w *WebClient) SetStatus(s int32) int32 {	return atomic.SwapInt32(&w.status, s)} // called when irc client connectsfunc (w *WebClient) loggedin(conn *irc.Conn, line irc.Line) {	conn.Join([]string{"#comicchat"}, nil)} func (w *WebClient) Send(typ, msg string) error {	if w.GetStatus() != ClientClosed {		var m WebClientMessage 		w.mu.Lock()		defer w.mu.Unlock() 		m.Type = typ		m.Message = msg 		if err := websocket.JSON.Send(w.ws, &m); err != nil {			return err		}	} 	return nil} // websocket read handlerfunc (w *WebClient) reader() {	var err error 	w.mu.Lock()	w.irc, err = irc.Connect(w.config)	if err != nil {		log.Printf("%s %s", w, err)		w.ws.Close()		w.mu.Unlock()		return	}	w.mu.Unlock() 	w.Send("connected", fmt.Sprintf("%d", atomic.LoadInt32(&nusers))) 	defer func() {		w.irc.Quit("disconnected")	}() 	for w.GetStatus() != ClientClosed {		var m WebClientMessage		err = websocket.JSON.Receive(w.ws, &m)		if err != nil {			log.Printf("%s read: %s", w, err)			w.SetStatus(ClientClosed)			continue		} 		if w.irc == nil {			continue		} 		switch m.Type {		case "nick":			w.irc.Nick(m.Message)		case "privmsg":			w.irc.Privmsg("#comicchat", m.Message)		case "action":			w.irc.Action("#comicchat", m.Message)		case "connect":		}	}} // websocket write handlerfunc (w *WebClient) writer() {	imgurlchan := make(chan interface{})	notify.Start("newimage", imgurlchan)	defer notify.Stop("newimage", imgurlchan) 	for w.GetStatus() != ClientClosed {		select {		case url := <-imgurlchan:			if err := w.Send("newimage", url.(string)); err != nil {				log.Printf("%s closed: %s", w, err)				w.SetStatus(ClientClosed)			}		case <-time.After(30 * time.Second):			if err := w.Send("ping", fmt.Sprintf("%d", time.Now().UnixNano())); err != nil {				log.Printf("%s closed: %s", w, err)				w.SetStatus(ClientClosed)			}		}	}} func dohttp() {	http.HandleFunc("/", indexhandler)	http.HandleFunc("/new", websockethandler)	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))	http.Handle("/comic/", http.StripPrefix("/comic/", http.FileServer(http.Dir("comic/"))))	if err := http.ListenAndServe(":8899", nil); err != nil {		log.Fatal("ListenAndServe:", err)	}} type Page struct {	Faces []string} func indexhandler(w http.ResponseWriter, r *http.Request) {	//create a new template	indextmpl := template.Must(template.New("index.tpl").ParseFiles("template/index.tpl")) 	page := Page{		Faces: facekeys,	}	if err := indextmpl.Execute(w, page); err != nil {		log.Printf("%s", err)	}} func websockethandler(w http.ResponseWriter, r *http.Request) {	// get remote client's address. we may be behind a proxy (nginx)	rem := r.RemoteAddr	if strings.Split(rem, ":")[0] == "127.0.0.1" {		rem = r.Header.Get("X-Real-IP")	} 	websocket.Handler(func(ws *websocket.Conn) {		wc := NewWebClient(rem, ws)		log.Printf("%s connected", wc)		atomic.AddInt32(&nusers, 1)		defer atomic.AddInt32(&nusers, -1)		go wc.writer()		wc.reader()		log.Printf("%s disconnected", wc)	}).ServeHTTP(w, r)}