mirror of https://github.com/fluffle/goirc
reformat source with gofmt to nuke all of those unneeded semicolons
This commit is contained in:
parent
1c31d5fb47
commit
ae8e34ff0e
|
@ -13,17 +13,17 @@ You can build the test client also with:
|
||||||
make
|
make
|
||||||
./gobot
|
./gobot
|
||||||
|
|
||||||
This will connect to freenode and join #go-lang by default, so be careful ;-)
|
This will connect to freenode and join `#go-lang` by default, so be careful ;-)
|
||||||
|
|
||||||
### Using the framework
|
### Using the framework
|
||||||
|
|
||||||
The test client provides a good (if basic) example of how to use the framework.
|
The test client provides a good (if basic) example of how to use the framework.
|
||||||
Reading irc/handlers.go gives a more in-depth look at how handlers can be
|
Reading `irc/handlers.go` gives a more in-depth look at how handlers can be
|
||||||
written. Commands to be sent to the server (e.g. PRIVMSG) are methods of the
|
written. Commands to be sent to the server (e.g. PRIVMSG) are methods of the
|
||||||
main \*irc.Conn object, and can be found in irc/commands.go (not all of the
|
main `*irc.Conn` object, and can be found in `irc/commands.go` (not all of the
|
||||||
possible IRC commands are implemented yet). Events are produced directly from
|
possible IRC commands are implemented yet). Events are produced directly from
|
||||||
the messages from the IRC server, so you have to handle e.g. "332" for
|
the messages from the IRC server, so you have to handle e.g. "332" for
|
||||||
RPL\_TOPIC to get the topic for a channel.
|
`RPL_TOPIC` to get the topic for a channel.
|
||||||
|
|
||||||
The vast majority of handlers implemented within the framework implement state
|
The vast majority of handlers implemented within the framework implement state
|
||||||
tracking of all nicks in channels that the client is also present in. It's
|
tracking of all nicks in channels that the client is also present in. It's
|
||||||
|
|
65
client.go
65
client.go
|
@ -1,73 +1,70 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"./irc/_obj/irc";
|
"./irc/_obj/irc"
|
||||||
"fmt";
|
"fmt"
|
||||||
"os";
|
"os"
|
||||||
"bufio";
|
"bufio"
|
||||||
"strings";
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// create new IRC connection
|
// create new IRC connection
|
||||||
c := irc.New("GoTest", "gotest", "GoBot");
|
c := irc.New("GoTest", "gotest", "GoBot")
|
||||||
c.AddHandler("connected",
|
c.AddHandler("connected",
|
||||||
func(conn *irc.Conn, line *irc.Line) {
|
func(conn *irc.Conn, line *irc.Line) { conn.Join("#go-nuts") })
|
||||||
conn.Join("#go-lang");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// connect to server
|
// connect to server
|
||||||
if err := c.Connect("irc.freenode.net", ""); err != nil {
|
if err := c.Connect("irc.freenode.net", ""); err != nil {
|
||||||
fmt.Printf("Connection error: %s\n", err);
|
fmt.Printf("Connection error: %s\n", err)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up a goroutine to read commands from stdin
|
// set up a goroutine to read commands from stdin
|
||||||
in := make(chan string, 4);
|
in := make(chan string, 4)
|
||||||
reallyquit := false;
|
reallyquit := false
|
||||||
go func() {
|
go func() {
|
||||||
con := bufio.NewReader(os.Stdin);
|
con := bufio.NewReader(os.Stdin)
|
||||||
for {
|
for {
|
||||||
s, err := con.ReadString('\n');
|
s, err := con.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// wha?, maybe ctrl-D...
|
// wha?, maybe ctrl-D...
|
||||||
close(in);
|
close(in)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
// no point in sending empty lines down the channel
|
// no point in sending empty lines down the channel
|
||||||
if len(s) > 2 {
|
if len(s) > 2 {
|
||||||
in <- s[0:len(s)-1]
|
in <- s[0:len(s)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}();
|
}()
|
||||||
|
|
||||||
// set up a goroutine to do parsey things with the stuff from stdin
|
// set up a goroutine to do parsey things with the stuff from stdin
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if closed(in) {
|
if closed(in) {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
cmd := <-in;
|
cmd := <-in
|
||||||
if cmd[0] == ':' {
|
if cmd[0] == ':' {
|
||||||
switch idx := strings.Index(cmd, " "); {
|
switch idx := strings.Index(cmd, " "); {
|
||||||
case idx == -1:
|
case idx == -1:
|
||||||
continue;
|
continue
|
||||||
case cmd[1] == 'q':
|
case cmd[1] == 'q':
|
||||||
reallyquit = true;
|
reallyquit = true
|
||||||
c.Quit(cmd[idx+1:len(cmd)]);
|
c.Quit(cmd[idx+1 : len(cmd)])
|
||||||
case cmd[1] == 'j':
|
case cmd[1] == 'j':
|
||||||
c.Join(cmd[idx+1:len(cmd)]);
|
c.Join(cmd[idx+1 : len(cmd)])
|
||||||
case cmd[1] == 'p':
|
case cmd[1] == 'p':
|
||||||
c.Part(cmd[idx+1:len(cmd)]);
|
c.Part(cmd[idx+1 : len(cmd)])
|
||||||
case cmd[1] == 'd':
|
case cmd[1] == 'd':
|
||||||
fmt.Printf(c.String());
|
fmt.Printf(c.String())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Raw(cmd)
|
c.Raw(cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}();
|
}()
|
||||||
|
|
||||||
// stall here waiting for asplode on error channel
|
// stall here waiting for asplode on error channel
|
||||||
for {
|
for {
|
||||||
|
@ -76,17 +73,17 @@ func main() {
|
||||||
// server for some reason (e.g. quit, kill or ping timeout)
|
// server for some reason (e.g. quit, kill or ping timeout)
|
||||||
// if we don't really want to quit, reconnect!
|
// if we don't really want to quit, reconnect!
|
||||||
if !reallyquit {
|
if !reallyquit {
|
||||||
fmt.Println("Reconnecting...");
|
fmt.Println("Reconnecting...")
|
||||||
if err := c.Connect("irc.freenode.net", ""); err != nil {
|
if err := c.Connect("irc.freenode.net", ""); err != nil {
|
||||||
fmt.Printf("Connection error: %s\n", err);
|
fmt.Printf("Connection error: %s\n", err)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
if err := <-c.Err; err != nil {
|
if err := <-c.Err; err != nil {
|
||||||
fmt.Printf("goirc error: %s\n", err);
|
fmt.Printf("goirc error: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ package irc
|
||||||
// send to the server using an Conn connection
|
// send to the server using an Conn connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "fmt";
|
// "fmt";
|
||||||
"reflect";
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This could be a lot less ugly with the ability to manipulate
|
// This could be a lot less ugly with the ability to manipulate
|
||||||
|
@ -13,114 +13,92 @@ import (
|
||||||
// [ CMD, FMT, FMTARGS ] etc.
|
// [ CMD, FMT, FMTARGS ] etc.
|
||||||
|
|
||||||
// send a raw line to the server for debugging etc
|
// send a raw line to the server for debugging etc
|
||||||
func (conn *Conn) Raw(s string) {
|
func (conn *Conn) Raw(s string) { conn.out <- s }
|
||||||
conn.out <- s
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a PASS command to the server
|
// send a PASS command to the server
|
||||||
func (conn *Conn) Pass(p string) {
|
func (conn *Conn) Pass(p string) { conn.out <- "PASS "+p }
|
||||||
conn.out <- "PASS " + p
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a NICK command to the server
|
// send a NICK command to the server
|
||||||
func (conn *Conn) Nick(n string) {
|
func (conn *Conn) Nick(n string) { conn.out <- "NICK "+n }
|
||||||
conn.out <- "NICK " + n
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a USER command to the server
|
// send a USER command to the server
|
||||||
func (conn *Conn) User(u, n string) {
|
func (conn *Conn) User(u, n string) { conn.out <- "USER "+u+" 12 * :"+n }
|
||||||
conn.out <- "USER " + u + " 12 * :" + n
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a JOIN command to the server
|
// send a JOIN command to the server
|
||||||
func (conn *Conn) Join(c string) {
|
func (conn *Conn) Join(c string) { conn.out <- "JOIN "+c }
|
||||||
conn.out <- "JOIN " + c
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a PART command to the server
|
// send a PART command to the server
|
||||||
func (conn *Conn) Part(c string, a ...) {
|
func (conn *Conn) Part(c string, a ...) {
|
||||||
msg := getStringMsg(a);
|
msg := getStringMsg(a)
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
msg = " :" + msg
|
msg = " :" + msg
|
||||||
}
|
}
|
||||||
conn.out <- "PART " + c + msg
|
conn.out <- "PART "+c+msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a QUIT command to the server
|
// send a QUIT command to the server
|
||||||
func (conn *Conn) Quit(a ...) {
|
func (conn *Conn) Quit(a ...) {
|
||||||
msg := getStringMsg(a);
|
msg := getStringMsg(a)
|
||||||
if msg == "" {
|
if msg == "" {
|
||||||
msg = "GoBye!"
|
msg = "GoBye!"
|
||||||
}
|
}
|
||||||
conn.out <- "QUIT :" + msg
|
conn.out <- "QUIT :"+msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a WHOIS command to the server
|
// send a WHOIS command to the server
|
||||||
func (conn *Conn) Whois(t string) {
|
func (conn *Conn) Whois(t string) { conn.out <- "WHOIS "+t }
|
||||||
conn.out <- "WHOIS " + t
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a WHO command to the server
|
// send a WHO command to the server
|
||||||
func (conn *Conn) Who(t string) {
|
func (conn *Conn) Who(t string) { conn.out <- "WHO "+t }
|
||||||
conn.out <- "WHO " + t
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a PRIVMSG to the target t
|
// send a PRIVMSG to the target t
|
||||||
func (conn *Conn) Privmsg(t, msg string) {
|
func (conn *Conn) Privmsg(t, msg string) { conn.out <- "PRIVMSG "+t+" :"+msg }
|
||||||
conn.out <- "PRIVMSG " + t + " :" + msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a NOTICE to the target t
|
// send a NOTICE to the target t
|
||||||
func (conn *Conn) Notice(t, msg string) {
|
func (conn *Conn) Notice(t, msg string) { conn.out <- "NOTICE "+t+" :"+msg }
|
||||||
conn.out <- "NOTICE " + t + " :" +msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a (generic) CTCP to the target t
|
// send a (generic) CTCP to the target t
|
||||||
func (conn *Conn) Ctcp(t, ctcp string, a ...) {
|
func (conn *Conn) Ctcp(t, ctcp string, a ...) {
|
||||||
msg := getStringMsg(a);
|
msg := getStringMsg(a)
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
msg = " " + msg
|
msg = " " + msg
|
||||||
}
|
}
|
||||||
conn.Privmsg(t, "\001" + ctcp + msg + "\001")
|
conn.Privmsg(t, "\001"+ctcp+msg+"\001")
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a generic CTCP reply to the target t
|
// send a generic CTCP reply to the target t
|
||||||
func (conn *Conn) CtcpReply(t, ctcp string, a ...) {
|
func (conn *Conn) CtcpReply(t, ctcp string, a ...) {
|
||||||
msg := getStringMsg(a);
|
msg := getStringMsg(a)
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
msg = " " + msg
|
msg = " " + msg
|
||||||
}
|
}
|
||||||
conn.Notice(t, "\001" + ctcp + msg + "\001")
|
conn.Notice(t, "\001"+ctcp+msg+"\001")
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a CTCP "VERSION" to the target t
|
// send a CTCP "VERSION" to the target t
|
||||||
func (conn *Conn) Version(t string) {
|
func (conn *Conn) Version(t string) { conn.Ctcp(t, "VERSION") }
|
||||||
conn.Ctcp(t, "VERSION")
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a CTCP "ACTION" to the target t -- /me does stuff!
|
// send a CTCP "ACTION" to the target t -- /me does stuff!
|
||||||
func (conn *Conn) Action(t, msg string) {
|
func (conn *Conn) Action(t, msg string) { conn.Ctcp(t, "ACTION", msg) }
|
||||||
conn.Ctcp(t, "ACTION", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a TOPIC command to the channel c
|
// send a TOPIC command to the channel c
|
||||||
func (conn *Conn) Topic(c string, a ...) {
|
func (conn *Conn) Topic(c string, a ...) {
|
||||||
topic := getStringMsg(a);
|
topic := getStringMsg(a)
|
||||||
if topic != "" {
|
if topic != "" {
|
||||||
topic = " :" + topic
|
topic = " :" + topic
|
||||||
}
|
}
|
||||||
conn.out <- "TOPIC " + c + topic
|
conn.out <- "TOPIC "+c+topic
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a MODE command (this one gets complicated)
|
// send a MODE command (this one gets complicated)
|
||||||
// Mode(t) retrieves the user or channel modes for target t
|
// Mode(t) retrieves the user or channel modes for target t
|
||||||
// Mode(t, "string"
|
// Mode(t, "string"
|
||||||
func (conn *Conn) Mode(t string, a ...) {
|
func (conn *Conn) Mode(t string, a ...) {
|
||||||
mode := getStringMsg(a);
|
mode := getStringMsg(a)
|
||||||
if mode != "" {
|
if mode != "" {
|
||||||
mode = " " + mode
|
mode = " " + mode
|
||||||
}
|
}
|
||||||
conn.out <- "MODE " + t + mode
|
conn.out <- "MODE "+t+mode
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStringMsg(a ...) (msg string) {
|
func getStringMsg(a ...) (msg string) {
|
||||||
|
@ -128,7 +106,7 @@ func getStringMsg(a ...) (msg string) {
|
||||||
// the below stolen and munged from fmt/print.go func getString()
|
// the below stolen and munged from fmt/print.go func getString()
|
||||||
if v := reflect.NewValue(a).(*reflect.StructValue); v.NumField() > 0 {
|
if v := reflect.NewValue(a).(*reflect.StructValue); v.NumField() > 0 {
|
||||||
if s, ok := v.Field(0).(*reflect.StringValue); ok {
|
if s, ok := v.Field(0).(*reflect.StringValue); ok {
|
||||||
return s.Get();
|
return s.Get()
|
||||||
}
|
}
|
||||||
if b, ok := v.Interface().([]byte); ok {
|
if b, ok := v.Interface().([]byte); ok {
|
||||||
return string(b)
|
return string(b)
|
||||||
|
|
|
@ -1,76 +1,76 @@
|
||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio";
|
"bufio"
|
||||||
"os";
|
"os"
|
||||||
"net";
|
"net"
|
||||||
"fmt";
|
"fmt"
|
||||||
"strings";
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// the IRC connection object
|
// the IRC connection object
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
// Hostname, Nickname, etc.
|
// Hostname, Nickname, etc.
|
||||||
Host string;
|
Host string
|
||||||
Me *Nick;
|
Me *Nick
|
||||||
|
|
||||||
// I/O stuff to server
|
// I/O stuff to server
|
||||||
sock *net.TCPConn;
|
sock *net.TCPConn
|
||||||
io *bufio.ReadWriter;
|
io *bufio.ReadWriter
|
||||||
in chan *Line;
|
in chan *Line
|
||||||
out chan string;
|
out chan string
|
||||||
connected bool;
|
connected bool
|
||||||
|
|
||||||
// Error channel to transmit any fail back to the user
|
// Error channel to transmit any fail back to the user
|
||||||
Err chan os.Error;
|
Err chan os.Error
|
||||||
|
|
||||||
// Event handler mapping
|
// Event handler mapping
|
||||||
events map[string] []func (*Conn, *Line);
|
events map[string][]func(*Conn, *Line)
|
||||||
|
|
||||||
// Map of channels we're on
|
// Map of channels we're on
|
||||||
chans map[string] *Channel;
|
chans map[string]*Channel
|
||||||
|
|
||||||
// Map of nicks we know about
|
// Map of nicks we know about
|
||||||
nicks map[string] *Nick;
|
nicks map[string]*Nick
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll parse an incoming line into this struct
|
// We'll parse an incoming line into this struct
|
||||||
// raw =~ ":nick!user@host cmd args[] :text"
|
// raw =~ ":nick!user@host cmd args[] :text"
|
||||||
// src == "nick!user@host"
|
// src == "nick!user@host"
|
||||||
type Line struct {
|
type Line struct {
|
||||||
Nick, Ident, Host, Src string;
|
Nick, Ident, Host, Src string
|
||||||
Cmd, Text, Raw string;
|
Cmd, Text, Raw string
|
||||||
Args []string;
|
Args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct a new IRC Connection object
|
// construct a new IRC Connection object
|
||||||
func New(nick, user, name string) *Conn {
|
func New(nick, user, name string) *Conn {
|
||||||
conn := new(Conn);
|
conn := new(Conn)
|
||||||
conn.initialise();
|
conn.initialise()
|
||||||
conn.Me = conn.NewNick(nick, user, name, "");
|
conn.Me = conn.NewNick(nick, user, name, "")
|
||||||
conn.setupEvents();
|
conn.setupEvents()
|
||||||
return conn;
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) initialise() {
|
func (conn *Conn) initialise() {
|
||||||
// allocate meh some memoraaaahh
|
// allocate meh some memoraaaahh
|
||||||
fmt.Println("irc.initialise(): initialising...");
|
fmt.Println("irc.initialise(): initialising...")
|
||||||
conn.nicks = make(map[string] *Nick);
|
conn.nicks = make(map[string]*Nick)
|
||||||
conn.chans = make(map[string] *Channel);
|
conn.chans = make(map[string]*Channel)
|
||||||
conn.in = make(chan *Line, 32);
|
conn.in = make(chan *Line, 32)
|
||||||
conn.out = make(chan string, 32);
|
conn.out = make(chan string, 32)
|
||||||
conn.Err = make(chan os.Error, 4);
|
conn.Err = make(chan os.Error, 4)
|
||||||
conn.io = nil;
|
conn.io = nil
|
||||||
conn.sock = nil;
|
conn.sock = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect the IRC connection object to a host
|
// connect the IRC connection object to a host
|
||||||
func (conn *Conn) Connect(host, pass string) os.Error {
|
func (conn *Conn) Connect(host, pass string) os.Error {
|
||||||
if conn.connected {
|
if conn.connected {
|
||||||
return os.NewError(fmt.Sprintf("irc.Connect(): already connected to %s, cannot connect to %s", conn.Host, host));
|
return os.NewError(fmt.Sprintf("irc.Connect(): already connected to %s, cannot connect to %s", conn.Host, host))
|
||||||
}
|
}
|
||||||
if !hasPort(host) {
|
if !hasPort(host) {
|
||||||
host += ":6667";
|
host += ":6667"
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr, err := net.ResolveTCPAddr(host); err != nil {
|
if addr, err := net.ResolveTCPAddr(host); err != nil {
|
||||||
|
@ -78,99 +78,94 @@ func (conn *Conn) Connect(host, pass string) os.Error {
|
||||||
} else if conn.sock, err = net.DialTCP("tcp", nil, addr); err != nil {
|
} else if conn.sock, err = net.DialTCP("tcp", nil, addr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("irc.Connect(): connected happily...");
|
fmt.Println("irc.Connect(): connected happily...")
|
||||||
conn.Host = host;
|
conn.Host = host
|
||||||
|
|
||||||
conn.io = bufio.NewReadWriter(
|
conn.io = bufio.NewReadWriter(
|
||||||
bufio.NewReader(conn.sock),
|
bufio.NewReader(conn.sock),
|
||||||
bufio.NewWriter(conn.sock),
|
bufio.NewWriter(conn.sock))
|
||||||
);
|
go conn.send()
|
||||||
go conn.send();
|
go conn.recv()
|
||||||
go conn.recv();
|
|
||||||
|
|
||||||
if pass != "" {
|
if pass != "" {
|
||||||
conn.Pass(pass)
|
conn.Pass(pass)
|
||||||
}
|
}
|
||||||
conn.Nick(conn.Me.Nick);
|
conn.Nick(conn.Me.Nick)
|
||||||
conn.User(conn.Me.Ident, conn.Me.Name);
|
conn.User(conn.Me.Ident, conn.Me.Name)
|
||||||
|
|
||||||
go conn.runLoop();
|
go conn.runLoop()
|
||||||
fmt.Println("irc.Connect(): launched runLoop() goroutine.");
|
fmt.Println("irc.Connect(): launched runLoop() goroutine.")
|
||||||
return nil;
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch a nicely formatted os.Error to the error channel
|
// dispatch a nicely formatted os.Error to the error channel
|
||||||
func (conn *Conn) error(s string, a ...) {
|
func (conn *Conn) error(s string, a ...) { conn.Err <- os.NewError(fmt.Sprintf(s, a)) }
|
||||||
conn.Err <- os.NewError(fmt.Sprintf(s, a));
|
|
||||||
}
|
|
||||||
|
|
||||||
// copied from http.client for great justice
|
// copied from http.client for great justice
|
||||||
func hasPort(s string) bool {
|
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
|
||||||
return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
|
|
||||||
}
|
|
||||||
|
|
||||||
// dispatch input from channel as \r\n terminated line to peer
|
// dispatch input from channel as \r\n terminated line to peer
|
||||||
func (conn *Conn) send() {
|
func (conn *Conn) send() {
|
||||||
for {
|
for {
|
||||||
line := <-conn.out;
|
line := <-conn.out
|
||||||
if closed(conn.out) {
|
if closed(conn.out) {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
if err := conn.io.WriteString(line + "\r\n"); err != nil {
|
if err := conn.io.WriteString(line + "\r\n"); err != nil {
|
||||||
conn.error("irc.send(): %s", err.String());
|
conn.error("irc.send(): %s", err.String())
|
||||||
conn.shutdown();
|
conn.shutdown()
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
conn.io.Flush();
|
conn.io.Flush()
|
||||||
fmt.Println("-> " + line);
|
fmt.Println("-> " + line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// receive one \r\n terminated line from peer, parse and dispatch it
|
// receive one \r\n terminated line from peer, parse and dispatch it
|
||||||
func (conn *Conn) recv() {
|
func (conn *Conn) recv() {
|
||||||
for {
|
for {
|
||||||
s, err := conn.io.ReadString('\n');
|
s, err := conn.io.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.error("irc.recv(): %s", err.String());
|
conn.error("irc.recv(): %s", err.String())
|
||||||
conn.shutdown();
|
conn.shutdown()
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
// chop off \r\n
|
// chop off \r\n
|
||||||
s = s[0:len(s)-2];
|
s = s[0 : len(s)-2]
|
||||||
fmt.Println("<- " + s);
|
fmt.Println("<- " + s)
|
||||||
|
|
||||||
line := &Line{Raw: s};
|
line := &Line{Raw: s}
|
||||||
if s[0] == ':' {
|
if s[0] == ':' {
|
||||||
// remove a source and parse it
|
// remove a source and parse it
|
||||||
if idx := strings.Index(s, " "); idx != -1 {
|
if idx := strings.Index(s, " "); idx != -1 {
|
||||||
line.Src, s = s[1:idx], s[idx+1:len(s)];
|
line.Src, s = s[1:idx], s[idx+1:len(s)]
|
||||||
} else {
|
} else {
|
||||||
// pretty sure we shouldn't get here ...
|
// pretty sure we shouldn't get here ...
|
||||||
line.Src = s[1:len(s)];
|
line.Src = s[1:len(s)]
|
||||||
conn.in <- line;
|
conn.in <- line
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// src can be the hostname of the irc server or a nick!user@host
|
// src can be the hostname of the irc server or a nick!user@host
|
||||||
line.Host = line.Src;
|
line.Host = line.Src
|
||||||
nidx, uidx := strings.Index(line.Src, "!"), strings.Index(line.Src, "@");
|
nidx, uidx := strings.Index(line.Src, "!"), strings.Index(line.Src, "@")
|
||||||
if uidx != -1 && nidx != -1 {
|
if uidx != -1 && nidx != -1 {
|
||||||
line.Nick = line.Src[0:nidx];
|
line.Nick = line.Src[0:nidx]
|
||||||
line.Ident = line.Src[nidx+1:uidx];
|
line.Ident = line.Src[nidx+1 : uidx]
|
||||||
line.Host = line.Src[uidx+1:len(line.Src)];
|
line.Host = line.Src[uidx+1 : len(line.Src)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we're here, we've parsed a :nick!user@host or :server off
|
// now we're here, we've parsed a :nick!user@host or :server off
|
||||||
// s should contain "cmd args[] :text"
|
// s should contain "cmd args[] :text"
|
||||||
args := strings.Split(s, " :", 2);
|
args := strings.Split(s, " :", 2)
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
line.Text = args[1];
|
line.Text = args[1]
|
||||||
}
|
}
|
||||||
args = strings.Split(args[0], " ", 0);
|
args = strings.Split(args[0], " ", 0)
|
||||||
line.Cmd = strings.ToUpper(args[0]);
|
line.Cmd = strings.ToUpper(args[0])
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
line.Args = args[1:len(args)];
|
line.Args = args[1:len(args)]
|
||||||
}
|
}
|
||||||
conn.in <- line
|
conn.in <- line
|
||||||
}
|
}
|
||||||
|
@ -179,50 +174,49 @@ func (conn *Conn) recv() {
|
||||||
func (conn *Conn) runLoop() {
|
func (conn *Conn) runLoop() {
|
||||||
for {
|
for {
|
||||||
if closed(conn.in) {
|
if closed(conn.in) {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case line := <-conn.in:
|
case line := <-conn.in:
|
||||||
conn.dispatchEvent(line);
|
conn.dispatchEvent(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("irc.runLoop(): Exited runloop...");
|
fmt.Println("irc.runLoop(): Exited runloop...")
|
||||||
// if we fall off the end here due to shutdown,
|
// if we fall off the end here due to shutdown,
|
||||||
// reinit everything once the runloop is done
|
// reinit everything once the runloop is done
|
||||||
// so that Connect() can be called again.
|
// so that Connect() can be called again.
|
||||||
conn.initialise();
|
conn.initialise()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) shutdown() {
|
func (conn *Conn) shutdown() {
|
||||||
close(conn.in);
|
close(conn.in)
|
||||||
close(conn.out);
|
close(conn.out)
|
||||||
close(conn.Err);
|
close(conn.Err)
|
||||||
conn.connected = false;
|
conn.connected = false
|
||||||
conn.sock.Close();
|
conn.sock.Close()
|
||||||
fmt.Println("irc.shutdown(): shut down sockets and channels!");
|
fmt.Println("irc.shutdown(): shut down sockets and channels!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) String() string {
|
func (conn *Conn) String() string {
|
||||||
str := "GoIRC Connection\n";
|
str := "GoIRC Connection\n"
|
||||||
str += "----------------\n\n";
|
str += "----------------\n\n"
|
||||||
if conn.connected {
|
if conn.connected {
|
||||||
str += "Connected to " + conn.Host + "\n\n"
|
str += "Connected to " + conn.Host + "\n\n"
|
||||||
} else {
|
} else {
|
||||||
str += "Not currently connected!\n\n";
|
str += "Not currently connected!\n\n"
|
||||||
}
|
}
|
||||||
str += conn.Me.String() + "\n";
|
str += conn.Me.String() + "\n"
|
||||||
str += "GoIRC Channels\n";
|
str += "GoIRC Channels\n"
|
||||||
str += "--------------\n\n";
|
str += "--------------\n\n"
|
||||||
for _, ch := range conn.chans {
|
for _, ch := range conn.chans {
|
||||||
str += ch.String() + "\n"
|
str += ch.String() + "\n"
|
||||||
}
|
}
|
||||||
str += "GoIRC NickNames\n";
|
str += "GoIRC NickNames\n"
|
||||||
str += "---------------\n\n";
|
str += "---------------\n\n"
|
||||||
for _, n := range conn.nicks {
|
for _, n := range conn.nicks {
|
||||||
if n != conn.Me {
|
if n != conn.Me {
|
||||||
str += n.String() + "\n"
|
str += n.String() + "\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
333
irc/handlers.go
333
irc/handlers.go
|
@ -4,28 +4,28 @@ package irc
|
||||||
// to manage tracking an irc connection etc.
|
// to manage tracking an irc connection etc.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings";
|
"strings"
|
||||||
"strconv";
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add an event handler for a specific IRC command
|
// Add an event handler for a specific IRC command
|
||||||
func (conn *Conn) AddHandler(name string, f func (*Conn, *Line)) {
|
func (conn *Conn) AddHandler(name string, f func(*Conn, *Line)) {
|
||||||
n := strings.ToUpper(name);
|
n := strings.ToUpper(name)
|
||||||
if e, ok := conn.events[n]; ok {
|
if e, ok := conn.events[n]; ok {
|
||||||
if len(e) == cap(e) {
|
if len(e) == cap(e) {
|
||||||
// crap, we're full. expand e by another 10 handler slots
|
// crap, we're full. expand e by another 10 handler slots
|
||||||
ne := make([]func (*Conn, *Line), len(e), len(e)+10);
|
ne := make([]func(*Conn, *Line), len(e), len(e)+10)
|
||||||
for i := 0; i<len(e); i++ {
|
for i := 0; i < len(e); i++ {
|
||||||
ne[i] = e[i];
|
ne[i] = e[i]
|
||||||
}
|
}
|
||||||
e = ne;
|
e = ne
|
||||||
}
|
}
|
||||||
e = e[0:len(e)+1];
|
e = e[0 : len(e)+1]
|
||||||
e[len(e)-1] = f;
|
e[len(e)-1] = f
|
||||||
} else {
|
} else {
|
||||||
e := make([]func (*Conn, *Line), 1, 10);
|
e := make([]func(*Conn, *Line), 1, 10)
|
||||||
e[0] = f;
|
e[0] = f
|
||||||
conn.events[n] = e;
|
conn.events[n] = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ func (conn *Conn) dispatchEvent(line *Line) {
|
||||||
// seems that we end up dispatching an event with a nil line when receiving
|
// seems that we end up dispatching an event with a nil line when receiving
|
||||||
// EOF from the server. Until i've tracked down why....
|
// EOF from the server. Until i've tracked down why....
|
||||||
if line == nil {
|
if line == nil {
|
||||||
conn.error("irc.dispatchEvent(): buh? line == nil :-(");
|
conn.error("irc.dispatchEvent(): buh? line == nil :-(")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,25 +44,25 @@ func (conn *Conn) dispatchEvent(line *Line) {
|
||||||
if line.Cmd == "PRIVMSG" && len(line.Text) > 2 &&
|
if line.Cmd == "PRIVMSG" && len(line.Text) > 2 &&
|
||||||
line.Text[0] == '\001' && line.Text[len(line.Text)-1] == '\001' {
|
line.Text[0] == '\001' && line.Text[len(line.Text)-1] == '\001' {
|
||||||
// WOO, it's a CTCP message
|
// WOO, it's a CTCP message
|
||||||
t := strings.Split(line.Text[1:len(line.Text)-1], " ", 2);
|
t := strings.Split(line.Text[1:len(line.Text)-1], " ", 2)
|
||||||
if c := strings.ToUpper(t[0]); c == "ACTION" {
|
if c := strings.ToUpper(t[0]); c == "ACTION" {
|
||||||
// make a CTCP ACTION it's own event a-la PRIVMSG
|
// make a CTCP ACTION it's own event a-la PRIVMSG
|
||||||
line.Cmd = c;
|
line.Cmd = c
|
||||||
} else {
|
} else {
|
||||||
// otherwise, dispatch a generic CTCP event that
|
// otherwise, dispatch a generic CTCP event that
|
||||||
// contains the type of CTCP in line.Args[0]
|
// contains the type of CTCP in line.Args[0]
|
||||||
line.Cmd = "CTCP";
|
line.Cmd = "CTCP"
|
||||||
a := make([]string, len(line.Args)+1);
|
a := make([]string, len(line.Args)+1)
|
||||||
a[0] = c;
|
a[0] = c
|
||||||
for i:=0; i<len(line.Args); i++ {
|
for i := 0; i < len(line.Args); i++ {
|
||||||
a[i+1] = line.Args[i];
|
a[i+1] = line.Args[i]
|
||||||
}
|
}
|
||||||
line.Args = a;
|
line.Args = a
|
||||||
}
|
}
|
||||||
if len(t) > 1 {
|
if len(t) > 1 {
|
||||||
// for some CTCP messages this could make more sense
|
// for some CTCP messages this could make more sense
|
||||||
// in line.Args[], but meh. MEH, I say.
|
// in line.Args[], but meh. MEH, I say.
|
||||||
line.Text = t[1];
|
line.Text = t[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if funcs, ok := conn.events[line.Cmd]; ok {
|
if funcs, ok := conn.events[line.Cmd]; ok {
|
||||||
|
@ -83,144 +83,150 @@ func (conn *Conn) dispatchEvent(line *Line) {
|
||||||
// func (conn *Conn) h_handler(line *Line) {}
|
// func (conn *Conn) h_handler(line *Line) {}
|
||||||
// in the future, but for now the compiler throws a hissy fit.
|
// in the future, but for now the compiler throws a hissy fit.
|
||||||
func (conn *Conn) setupEvents() {
|
func (conn *Conn) setupEvents() {
|
||||||
conn.events = make(map[string] []func(*Conn, *Line));
|
conn.events = make(map[string][]func(*Conn, *Line))
|
||||||
|
|
||||||
// Basic ping/pong handler
|
// Basic ping/pong handler
|
||||||
conn.AddHandler("PING", func(conn *Conn, line *Line) {
|
conn.AddHandler("PING", func(conn *Conn, line *Line) { conn.Raw("PONG :" + line.Text) })
|
||||||
conn.Raw("PONG :" + line.Text);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handler to trigger a "CONNECTED" event on receipt of numeric 001
|
// Handler to trigger a "CONNECTED" event on receipt of numeric 001
|
||||||
conn.AddHandler("001", func(conn *Conn, line *Line) {
|
conn.AddHandler("001", func(conn *Conn, line *Line) {
|
||||||
// we're connected!
|
// we're connected!
|
||||||
conn.connected = true;
|
conn.connected = true
|
||||||
conn.dispatchEvent(&Line{Cmd: "CONNECTED"});
|
conn.dispatchEvent(&Line{Cmd: "CONNECTED"})
|
||||||
// and we're being given our hostname (from the server's perspective)
|
// and we're being given our hostname (from the server's perspective)
|
||||||
if ridx := strings.LastIndex(line.Text, " "); ridx != -1 {
|
if ridx := strings.LastIndex(line.Text, " "); ridx != -1 {
|
||||||
h := line.Text[ridx+1:len(line.Text)];
|
h := line.Text[ridx+1 : len(line.Text)]
|
||||||
if idx := strings.Index(h, "@"); idx != -1 {
|
if idx := strings.Index(h, "@"); idx != -1 {
|
||||||
conn.Me.Host = h[idx+1:len(h)];
|
conn.Me.Host = h[idx+1 : len(h)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// XXX: do we need 005 protocol support message parsing here?
|
// XXX: do we need 005 protocol support message parsing here?
|
||||||
// probably in the future, but I can't quite be arsed yet.
|
// probably in the future, but I can't quite be arsed yet.
|
||||||
/*
|
/*
|
||||||
:irc.pl0rt.org 005 GoTest CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=20 CHANLIMIT=#:20 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server
|
:irc.pl0rt.org 005 GoTest CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=20 CHANLIMIT=#:20 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server
|
||||||
:irc.pl0rt.org 005 GoTest MAXTARGETS=20 WALLCHOPS WATCH=128 WATCHOPTS=A SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMT NETWORK=bb101.net CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT :are supported by this server
|
:irc.pl0rt.org 005 GoTest MAXTARGETS=20 WALLCHOPS WATCH=128 WATCHOPTS=A SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMT NETWORK=bb101.net CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT :are supported by this server
|
||||||
:irc.pl0rt.org 005 GoTest STATUSMSG=~&@%+ EXCEPTS INVEX :are supported by this server
|
:irc.pl0rt.org 005 GoTest STATUSMSG=~&@%+ EXCEPTS INVEX :are supported by this server
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Handler to deal with "433 :Nickname already in use"
|
// Handler to deal with "433 :Nickname already in use"
|
||||||
conn.AddHandler("433", func(conn *Conn, line *Line) {
|
conn.AddHandler("433", func(conn *Conn, line *Line) {
|
||||||
// Args[1] is the new nick we were attempting to acquire
|
// Args[1] is the new nick we were attempting to acquire
|
||||||
conn.Nick(line.Args[1] + "_");
|
conn.Nick(line.Args[1] + "_")
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handler NICK messages to inform us about nick changes
|
// Handler NICK messages to inform us about nick changes
|
||||||
conn.AddHandler("NICK", func(conn *Conn, line *Line) {
|
conn.AddHandler("NICK", func(conn *Conn, line *Line) {
|
||||||
// all nicks should be handled the same way, our own included
|
// all nicks should be handled the same way, our own included
|
||||||
if n := conn.GetNick(line.Nick); n != nil {
|
if n := conn.GetNick(line.Nick); n != nil {
|
||||||
n.ReNick(line.Text);
|
n.ReNick(line.Text)
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.NICK(): buh? unknown nick %s.", line.Nick);
|
conn.error("irc.NICK(): buh? unknown nick %s.", line.Nick)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle VERSION requests and CTCP PING
|
// Handle VERSION requests and CTCP PING
|
||||||
conn.AddHandler("CTCP", func(conn *Conn, line *Line) {
|
conn.AddHandler("CTCP", func(conn *Conn, line *Line) {
|
||||||
if line.Args[0] == "VERSION" {
|
if line.Args[0] == "VERSION" {
|
||||||
conn.CtcpReply(line.Nick, "VERSION", "powered by goirc...");
|
conn.CtcpReply(line.Nick, "VERSION", "powered by goirc...")
|
||||||
} else if line.Args[0] == "PING" {
|
} else if line.Args[0] == "PING" {
|
||||||
conn.CtcpReply(line.Nick, "PING", line.Text);
|
conn.CtcpReply(line.Nick, "PING", line.Text)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle JOINs to channels to maintain state
|
// Handle JOINs to channels to maintain state
|
||||||
conn.AddHandler("JOIN", func(conn *Conn, line *Line) {
|
conn.AddHandler("JOIN", func(conn *Conn, line *Line) {
|
||||||
ch := conn.GetChannel(line.Text);
|
ch := conn.GetChannel(line.Text)
|
||||||
n := conn.GetNick(line.Nick);
|
n := conn.GetNick(line.Nick)
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
// first we've seen of this channel, so should be us joining it
|
// first we've seen of this channel, so should be us joining it
|
||||||
// NOTE this will also take care of n == nil && ch == nil
|
// NOTE this will also take care of n == nil && ch == nil
|
||||||
if n != conn.Me {
|
if n != conn.Me {
|
||||||
conn.error("irc.JOIN(): buh? JOIN to unknown channel %s recieved from (non-me) nick %s", line.Text, line.Nick);
|
conn.error("irc.JOIN(): buh? JOIN to unknown channel %s recieved from (non-me) nick %s", line.Text, line.Nick)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
ch = conn.NewChannel(line.Text);
|
ch = conn.NewChannel(line.Text)
|
||||||
// since we don't know much about this channel, ask server for info
|
// since we don't know much about this channel, ask server for info
|
||||||
// we get the channel users automatically in 353 and the channel
|
// we get the channel users automatically in 353 and the channel
|
||||||
// topic in 332 on join, so we just need to get the modes
|
// topic in 332 on join, so we just need to get the modes
|
||||||
conn.Mode(ch.Name);
|
conn.Mode(ch.Name)
|
||||||
}
|
}
|
||||||
if n == nil {
|
if n == nil {
|
||||||
// this is the first we've seen of this nick
|
// this is the first we've seen of this nick
|
||||||
n = conn.NewNick(line.Nick, line.Ident, "", line.Host);
|
n = conn.NewNick(line.Nick, line.Ident, "", line.Host)
|
||||||
// since we don't know much about this nick, ask server for info
|
// since we don't know much about this nick, ask server for info
|
||||||
conn.Whois(n.Nick);
|
conn.Whois(n.Nick)
|
||||||
}
|
}
|
||||||
// this takes care of both nick and channel linking \o/
|
// this takes care of both nick and channel linking \o/
|
||||||
ch.AddNick(n);
|
ch.AddNick(n)
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle PARTs from channels to maintain state
|
// Handle PARTs from channels to maintain state
|
||||||
conn.AddHandler("PART", func(conn *Conn, line *Line) {
|
conn.AddHandler("PART", func(conn *Conn, line *Line) {
|
||||||
ch := conn.GetChannel(line.Args[0]);
|
ch := conn.GetChannel(line.Args[0])
|
||||||
n := conn.GetNick(line.Nick);
|
n := conn.GetNick(line.Nick)
|
||||||
if ch != nil && n != nil {
|
if ch != nil && n != nil {
|
||||||
ch.DelNick(n);
|
ch.DelNick(n)
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.PART(): buh? PART of channel %s by nick %s", line.Args[0], line.Nick);
|
conn.error("irc.PART(): buh? PART of channel %s by nick %s", line.Args[0], line.Nick)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle KICKs from channels to maintain state
|
// Handle KICKs from channels to maintain state
|
||||||
conn.AddHandler("KICK", func(conn *Conn, line *Line) {
|
conn.AddHandler("KICK", func(conn *Conn, line *Line) {
|
||||||
// XXX: this won't handle autorejoining channels on KICK
|
// XXX: this won't handle autorejoining channels on KICK
|
||||||
// it's trivial to do this in a seperate handler...
|
// it's trivial to do this in a seperate handler...
|
||||||
ch := conn.GetChannel(line.Args[0]);
|
ch := conn.GetChannel(line.Args[0])
|
||||||
n := conn.GetNick(line.Args[1]);
|
n := conn.GetNick(line.Args[1])
|
||||||
if ch != nil && n != nil {
|
if ch != nil && n != nil {
|
||||||
ch.DelNick(n);
|
ch.DelNick(n)
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.KICK(): buh? KICK from channel %s of nick %s", line.Args[0], line.Args[1]);
|
conn.error("irc.KICK(): buh? KICK from channel %s of nick %s", line.Args[0], line.Args[1])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle other people's QUITs
|
// Handle other people's QUITs
|
||||||
conn.AddHandler("QUIT", func(conn *Conn, line *Line) {
|
conn.AddHandler("QUIT", func(conn *Conn, line *Line) {
|
||||||
if n := conn.GetNick(line.Nick); n != nil {
|
if n := conn.GetNick(line.Nick); n != nil {
|
||||||
n.Delete();
|
n.Delete()
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.QUIT(): buh? QUIT from unknown nick %s", line.Nick);
|
conn.error("irc.QUIT(): buh? QUIT from unknown nick %s", line.Nick)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle MODE changes for channels we know about (and our nick personally)
|
// Handle MODE changes for channels we know about (and our nick personally)
|
||||||
// this is moderately ugly. suggestions for improvement welcome
|
// this is moderately ugly. suggestions for improvement welcome
|
||||||
conn.AddHandler("MODE", func(conn *Conn, line *Line) {
|
conn.AddHandler("MODE", func(conn *Conn, line *Line) {
|
||||||
// channel modes first
|
// channel modes first
|
||||||
if ch := conn.GetChannel(line.Args[0]); ch != nil {
|
if ch := conn.GetChannel(line.Args[0]); ch != nil {
|
||||||
modeargs := line.Args[2:len(line.Args)];
|
modeargs := line.Args[2:len(line.Args)]
|
||||||
var modeop bool; // true => add mode, false => remove mode
|
var modeop bool // true => add mode, false => remove mode
|
||||||
var modestr string;
|
var modestr string
|
||||||
for i := 0; i < len(line.Args[1]); i++ {
|
for i := 0; i < len(line.Args[1]); i++ {
|
||||||
switch m := line.Args[1][i]; m {
|
switch m := line.Args[1][i]; m {
|
||||||
case '+':
|
case '+':
|
||||||
modeop = true;
|
modeop = true
|
||||||
modestr = string(m);
|
modestr = string(m)
|
||||||
case '-':
|
case '-':
|
||||||
modeop = false;
|
modeop = false
|
||||||
modestr = string(m);
|
modestr = string(m)
|
||||||
case 'i': ch.Modes.InviteOnly = modeop;
|
case 'i':
|
||||||
case 'm': ch.Modes.Moderated = modeop;
|
ch.Modes.InviteOnly = modeop
|
||||||
case 'n': ch.Modes.NoExternalMsg = modeop;
|
case 'm':
|
||||||
case 'p': ch.Modes.Private = modeop;
|
ch.Modes.Moderated = modeop
|
||||||
case 's': ch.Modes.Secret = modeop;
|
case 'n':
|
||||||
case 't': ch.Modes.ProtectedTopic = modeop;
|
ch.Modes.NoExternalMsg = modeop
|
||||||
case 'z': ch.Modes.SSLOnly = modeop;
|
case 'p':
|
||||||
case 'O': ch.Modes.OperOnly = modeop;
|
ch.Modes.Private = modeop
|
||||||
|
case 's':
|
||||||
|
ch.Modes.Secret = modeop
|
||||||
|
case 't':
|
||||||
|
ch.Modes.ProtectedTopic = modeop
|
||||||
|
case 'z':
|
||||||
|
ch.Modes.SSLOnly = modeop
|
||||||
|
case 'O':
|
||||||
|
ch.Modes.OperOnly = modeop
|
||||||
case 'k':
|
case 'k':
|
||||||
if len(modeargs) != 0 {
|
if len(modeargs) != 0 {
|
||||||
ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
|
ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
|
||||||
|
@ -229,99 +235,117 @@ func (conn *Conn) setupEvents() {
|
||||||
}
|
}
|
||||||
case 'l':
|
case 'l':
|
||||||
if len(modeargs) != 0 {
|
if len(modeargs) != 0 {
|
||||||
ch.Modes.Limit, _ = strconv.Atoi(modeargs[0]);
|
ch.Modes.Limit, _ = strconv.Atoi(modeargs[0])
|
||||||
modeargs = modeargs[1:len(modeargs)];
|
modeargs = modeargs[1:len(modeargs)]
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
|
conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
|
||||||
}
|
}
|
||||||
case 'q', 'a', 'o', 'h', 'v':
|
case 'q', 'a', 'o', 'h', 'v':
|
||||||
if len(modeargs) != 0 {
|
if len(modeargs) != 0 {
|
||||||
n := conn.GetNick(modeargs[0]);
|
n := conn.GetNick(modeargs[0])
|
||||||
if p, ok := ch.Nicks[n]; ok && n != nil {
|
if p, ok := ch.Nicks[n]; ok && n != nil {
|
||||||
switch m {
|
switch m {
|
||||||
case 'q': p.Owner = modeop;
|
case 'q':
|
||||||
case 'a': p.Admin = modeop;
|
p.Owner = modeop
|
||||||
case 'o': p.Op = modeop;
|
case 'a':
|
||||||
case 'h': p.HalfOp = modeop;
|
p.Admin = modeop
|
||||||
case 'v': p.Voice = modeop;
|
case 'o':
|
||||||
|
p.Op = modeop
|
||||||
|
case 'h':
|
||||||
|
p.HalfOp = modeop
|
||||||
|
case 'v':
|
||||||
|
p.Voice = modeop
|
||||||
}
|
}
|
||||||
modeargs = modeargs[1:len(modeargs)];
|
modeargs = modeargs[1:len(modeargs)]
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.MODE(): MODE %s %s%s %s: buh? state tracking failure.", ch.Name, modestr, m, modeargs[0]);
|
conn.error("irc.MODE(): MODE %s %s%s %s: buh? state tracking failure.", ch.Name, modestr, m, modeargs[0])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m);
|
conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if n := conn.GetNick(line.Args[0]); n != nil {
|
} else if n := conn.GetNick(line.Args[0]); n != nil {
|
||||||
// nick mode change, should be us
|
// nick mode change, should be us
|
||||||
if n != conn.Me {
|
if n != conn.Me {
|
||||||
conn.error("irc.MODE(): buh? recieved MODE %s for (non-me) nick %s", line.Args[1], n.Nick);
|
conn.error("irc.MODE(): buh? recieved MODE %s for (non-me) nick %s", line.Args[1], n.Nick)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
var modeop bool; // true => add mode, false => remove mode
|
var modeop bool // true => add mode, false => remove mode
|
||||||
for i := 0; i < len(line.Text); i++ {
|
for i := 0; i < len(line.Text); i++ {
|
||||||
switch m := line.Text[i]; m {
|
switch m := line.Text[i]; m {
|
||||||
case '+':
|
case '+':
|
||||||
modeop = true;
|
modeop = true
|
||||||
case '-':
|
case '-':
|
||||||
modeop = false;
|
modeop = false
|
||||||
case 'i': n.Modes.Invisible = modeop;
|
case 'i':
|
||||||
case 'o': n.Modes.Oper = modeop;
|
n.Modes.Invisible = modeop
|
||||||
case 'w': n.Modes.WallOps = modeop;
|
case 'o':
|
||||||
case 'x': n.Modes.HiddenHost = modeop;
|
n.Modes.Oper = modeop
|
||||||
case 'z': n.Modes.SSL = modeop;
|
case 'w':
|
||||||
|
n.Modes.WallOps = modeop
|
||||||
|
case 'x':
|
||||||
|
n.Modes.HiddenHost = modeop
|
||||||
|
case 'z':
|
||||||
|
n.Modes.SSL = modeop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.MODE(): buh? not sure what to do with MODE %s %s", line.Args[0], line.Args[1]);
|
conn.error("irc.MODE(): buh? not sure what to do with MODE %s %s", line.Args[0], line.Args[1])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle TOPIC changes for channels
|
// Handle TOPIC changes for channels
|
||||||
conn.AddHandler("TOPIC", func(conn *Conn, line *Line) {
|
conn.AddHandler("TOPIC", func(conn *Conn, line *Line) {
|
||||||
if ch := conn.GetChannel(line.Args[0]); ch != nil {
|
if ch := conn.GetChannel(line.Args[0]); ch != nil {
|
||||||
ch.Topic = line.Text;
|
ch.Topic = line.Text
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.TOPIC(): buh? topic change on unknown channel %s", line.Args[0]);
|
conn.error("irc.TOPIC(): buh? topic change on unknown channel %s", line.Args[0])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle 311 whois reply
|
// Handle 311 whois reply
|
||||||
conn.AddHandler("311", func(conn *Conn, line *Line) {
|
conn.AddHandler("311", func(conn *Conn, line *Line) {
|
||||||
if n := conn.GetNick(line.Args[1]); n != nil {
|
if n := conn.GetNick(line.Args[1]); n != nil {
|
||||||
n.Ident = line.Args[2];
|
n.Ident = line.Args[2]
|
||||||
n.Host = line.Args[3];
|
n.Host = line.Args[3]
|
||||||
n.Name = line.Text;
|
n.Name = line.Text
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.311(): buh? received WHOIS info for unknown nick %s", line.Args[1]);
|
conn.error("irc.311(): buh? received WHOIS info for unknown nick %s", line.Args[1])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle 324 mode reply
|
// Handle 324 mode reply
|
||||||
conn.AddHandler("324", func(conn *Conn, line *Line) {
|
conn.AddHandler("324", func(conn *Conn, line *Line) {
|
||||||
// XXX: copypasta from MODE, needs tidying.
|
// XXX: copypasta from MODE, needs tidying.
|
||||||
if ch := conn.GetChannel(line.Args[1]); ch != nil {
|
if ch := conn.GetChannel(line.Args[1]); ch != nil {
|
||||||
modeargs := line.Args[3:len(line.Args)];
|
modeargs := line.Args[3:len(line.Args)]
|
||||||
var modeop bool; // true => add mode, false => remove mode
|
var modeop bool // true => add mode, false => remove mode
|
||||||
var modestr string;
|
var modestr string
|
||||||
for i := 0; i < len(line.Args[2]); i++ {
|
for i := 0; i < len(line.Args[2]); i++ {
|
||||||
switch m := line.Args[2][i]; m {
|
switch m := line.Args[2][i]; m {
|
||||||
case '+':
|
case '+':
|
||||||
modeop = true;
|
modeop = true
|
||||||
modestr = string(m);
|
modestr = string(m)
|
||||||
case '-':
|
case '-':
|
||||||
modeop = false;
|
modeop = false
|
||||||
modestr = string(m);
|
modestr = string(m)
|
||||||
case 'i': ch.Modes.InviteOnly = modeop;
|
case 'i':
|
||||||
case 'm': ch.Modes.Moderated = modeop;
|
ch.Modes.InviteOnly = modeop
|
||||||
case 'n': ch.Modes.NoExternalMsg = modeop;
|
case 'm':
|
||||||
case 'p': ch.Modes.Private = modeop;
|
ch.Modes.Moderated = modeop
|
||||||
case 's': ch.Modes.Secret = modeop;
|
case 'n':
|
||||||
case 't': ch.Modes.ProtectedTopic = modeop;
|
ch.Modes.NoExternalMsg = modeop
|
||||||
case 'z': ch.Modes.SSLOnly = modeop;
|
case 'p':
|
||||||
case 'O': ch.Modes.OperOnly = modeop;
|
ch.Modes.Private = modeop
|
||||||
|
case 's':
|
||||||
|
ch.Modes.Secret = modeop
|
||||||
|
case 't':
|
||||||
|
ch.Modes.ProtectedTopic = modeop
|
||||||
|
case 'z':
|
||||||
|
ch.Modes.SSLOnly = modeop
|
||||||
|
case 'O':
|
||||||
|
ch.Modes.OperOnly = modeop
|
||||||
case 'k':
|
case 'k':
|
||||||
if len(modeargs) != 0 {
|
if len(modeargs) != 0 {
|
||||||
ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
|
ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
|
||||||
|
@ -330,31 +354,31 @@ func (conn *Conn) setupEvents() {
|
||||||
}
|
}
|
||||||
case 'l':
|
case 'l':
|
||||||
if len(modeargs) != 0 {
|
if len(modeargs) != 0 {
|
||||||
ch.Modes.Limit, _ = strconv.Atoi(modeargs[0]);
|
ch.Modes.Limit, _ = strconv.Atoi(modeargs[0])
|
||||||
modeargs = modeargs[1:len(modeargs)];
|
modeargs = modeargs[1:len(modeargs)]
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.324(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
|
conn.error("irc.324(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.324(): buh? received MODE settings for unknown channel %s", line.Args[1]);
|
conn.error("irc.324(): buh? received MODE settings for unknown channel %s", line.Args[1])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle 332 topic reply on join to channel
|
// Handle 332 topic reply on join to channel
|
||||||
conn.AddHandler("332", func(conn *Conn, line *Line) {
|
conn.AddHandler("332", func(conn *Conn, line *Line) {
|
||||||
if ch := conn.GetChannel(line.Args[1]); ch != nil {
|
if ch := conn.GetChannel(line.Args[1]); ch != nil {
|
||||||
ch.Topic = line.Text;
|
ch.Topic = line.Text
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.332(): buh? received TOPIC value for unknown channel %s", line.Args[1]);
|
conn.error("irc.332(): buh? received TOPIC value for unknown channel %s", line.Args[1])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle 353 names reply
|
// Handle 353 names reply
|
||||||
conn.AddHandler("353", func(conn *Conn, line *Line) {
|
conn.AddHandler("353", func(conn *Conn, line *Line) {
|
||||||
if ch := conn.GetChannel(line.Args[2]); ch != nil {
|
if ch := conn.GetChannel(line.Args[2]); ch != nil {
|
||||||
nicks := strings.Split(line.Text, " ", 0);
|
nicks := strings.Split(line.Text, " ", 0)
|
||||||
for _, nick := range nicks {
|
for _, nick := range nicks {
|
||||||
// UnrealIRCd's coders are lazy and leave a trailing space
|
// UnrealIRCd's coders are lazy and leave a trailing space
|
||||||
if nick == "" {
|
if nick == "" {
|
||||||
|
@ -362,41 +386,46 @@ func (conn *Conn) setupEvents() {
|
||||||
}
|
}
|
||||||
switch c := nick[0]; c {
|
switch c := nick[0]; c {
|
||||||
case '~', '&', '@', '%', '+':
|
case '~', '&', '@', '%', '+':
|
||||||
nick = nick[1:len(nick)];
|
nick = nick[1:len(nick)]
|
||||||
fallthrough;
|
fallthrough
|
||||||
default:
|
default:
|
||||||
n := conn.GetNick(nick);
|
n := conn.GetNick(nick)
|
||||||
if n == nil {
|
if n == nil {
|
||||||
// we don't know this nick yet!
|
// we don't know this nick yet!
|
||||||
n = conn.NewNick(nick, "", "", "");
|
n = conn.NewNick(nick, "", "", "")
|
||||||
conn.Whois(nick);
|
conn.Whois(nick)
|
||||||
}
|
}
|
||||||
if n != conn.Me {
|
if n != conn.Me {
|
||||||
// we will be in the names list, but should also be in
|
// we will be in the names list, but should also be in
|
||||||
// the channel's nick list from the JOIN handler above
|
// the channel's nick list from the JOIN handler above
|
||||||
ch.AddNick(n);
|
ch.AddNick(n)
|
||||||
}
|
}
|
||||||
p := ch.Nicks[n];
|
p := ch.Nicks[n]
|
||||||
switch c {
|
switch c {
|
||||||
case '~': p.Owner = true;
|
case '~':
|
||||||
case '&': p.Admin = true;
|
p.Owner = true
|
||||||
case '@': p.Op = true;
|
case '&':
|
||||||
case '%': p.HalfOp = true;
|
p.Admin = true
|
||||||
case '+': p.Voice = true;
|
case '@':
|
||||||
|
p.Op = true
|
||||||
|
case '%':
|
||||||
|
p.HalfOp = true
|
||||||
|
case '+':
|
||||||
|
p.Voice = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.353(): buh? received NAMES list for unknown channel %s", line.Args[2]);
|
conn.error("irc.353(): buh? received NAMES list for unknown channel %s", line.Args[2])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Handle 671 whois reply (nick connected via SSL)
|
// Handle 671 whois reply (nick connected via SSL)
|
||||||
conn.AddHandler("671", func(conn *Conn, line *Line) {
|
conn.AddHandler("671", func(conn *Conn, line *Line) {
|
||||||
if n := conn.GetNick(line.Args[1]); n != nil {
|
if n := conn.GetNick(line.Args[1]); n != nil {
|
||||||
n.Modes.SSL = true;
|
n.Modes.SSL = true
|
||||||
} else {
|
} else {
|
||||||
conn.error("irc.671(): buh? received WHOIS SSL info for unknown nick %s", line.Args[1]);
|
conn.error("irc.671(): buh? received WHOIS SSL info for unknown nick %s", line.Args[1])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
261
irc/nickchan.go
261
irc/nickchan.go
|
@ -4,25 +4,24 @@ package irc
|
||||||
// as well as the internal state maintenance code for the handlers
|
// as well as the internal state maintenance code for the handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt";
|
"fmt"
|
||||||
"reflect";
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A struct representing an IRC channel
|
// A struct representing an IRC channel
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
Name, Topic string;
|
Name, Topic string
|
||||||
Modes *ChanMode;
|
Modes *ChanMode
|
||||||
// MODE +q, +a, +o, +h, +v
|
Nicks map[*Nick]*ChanPrivs
|
||||||
Nicks map[*Nick] *ChanPrivs;
|
conn *Conn
|
||||||
conn *Conn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct representing an IRC nick
|
// A struct representing an IRC nick
|
||||||
type Nick struct {
|
type Nick struct {
|
||||||
Nick, Ident, Host, Name string;
|
Nick, Ident, Host, Name string
|
||||||
Modes *NickMode;
|
Modes *NickMode
|
||||||
Channels map[*Channel] *ChanPrivs;
|
Channels map[*Channel]*ChanPrivs
|
||||||
conn *Conn;
|
conn *Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct representing the modes of an IRC Channel
|
// A struct representing the modes of an IRC Channel
|
||||||
|
@ -30,13 +29,13 @@ type Nick struct {
|
||||||
// see the MODE handler in setupEvents() for details
|
// see the MODE handler in setupEvents() for details
|
||||||
type ChanMode struct {
|
type ChanMode struct {
|
||||||
// MODE +p, +s, +t, +n, +m
|
// MODE +p, +s, +t, +n, +m
|
||||||
Private, Secret, ProtectedTopic, NoExternalMsg, Moderated bool;
|
Private, Secret, ProtectedTopic, NoExternalMsg, Moderated bool
|
||||||
// MODE +i, +O, +z
|
// MODE +i, +O, +z
|
||||||
InviteOnly, OperOnly, SSLOnly bool;
|
InviteOnly, OperOnly, SSLOnly bool
|
||||||
// MODE +k
|
// MODE +k
|
||||||
Key string;
|
Key string
|
||||||
// MODE +l
|
// MODE +l
|
||||||
Limit int;
|
Limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct representing the modes of an IRC Nick (User Modes)
|
// A struct representing the modes of an IRC Nick (User Modes)
|
||||||
|
@ -45,23 +44,23 @@ type ChanMode struct {
|
||||||
// without IRC operator privileges (and even then only on some IRCd's).
|
// without IRC operator privileges (and even then only on some IRCd's).
|
||||||
type NickMode struct {
|
type NickMode struct {
|
||||||
// MODE +i, +o, +w, +x, +z
|
// MODE +i, +o, +w, +x, +z
|
||||||
Invisible, Oper, WallOps, HiddenHost, SSL bool;
|
Invisible, Oper, WallOps, HiddenHost, SSL bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct representing the modes a Nick can have on a Channel
|
// A struct representing the modes a Nick can have on a Channel
|
||||||
type ChanPrivs struct {
|
type ChanPrivs struct {
|
||||||
// MODE +q, +a, +o, +h, +v
|
// MODE +q, +a, +o, +h, +v
|
||||||
Owner, Admin, Op, HalfOp, Voice bool;
|
Owner, Admin, Op, HalfOp, Voice bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
* Conn methods to create/look up nicks/channels
|
* Conn methods to create/look up nicks/channels
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
func (conn *Conn) NewNick(nick, ident, name, host string) *Nick {
|
func (conn *Conn) NewNick(nick, ident, name, host string) *Nick {
|
||||||
n := &Nick{Nick: nick, Ident: ident, Name: name, Host: host, conn: conn};
|
n := &Nick{Nick: nick, Ident: ident, Name: name, Host: host, conn: conn}
|
||||||
n.initialise();
|
n.initialise()
|
||||||
conn.nicks[n.Nick] = n;
|
conn.nicks[n.Nick] = n
|
||||||
return n;
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) GetNick(n string) *Nick {
|
func (conn *Conn) GetNick(n string) *Nick {
|
||||||
|
@ -72,9 +71,9 @@ func (conn *Conn) GetNick(n string) *Nick {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) NewChannel(c string) *Channel {
|
func (conn *Conn) NewChannel(c string) *Channel {
|
||||||
ch := &Channel{Name: c, conn: conn};
|
ch := &Channel{Name: c, conn: conn}
|
||||||
ch.initialise();
|
ch.initialise()
|
||||||
conn.chans[ch.Name] = ch;
|
conn.chans[ch.Name] = ch
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,28 +88,28 @@ func (conn *Conn) GetChannel(c string) *Channel {
|
||||||
* Channel methods for state management
|
* Channel methods for state management
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
func (ch *Channel) initialise() {
|
func (ch *Channel) initialise() {
|
||||||
ch.Modes = new(ChanMode);
|
ch.Modes = new(ChanMode)
|
||||||
ch.Nicks = make(map[*Nick] *ChanPrivs);
|
ch.Nicks = make(map[*Nick]*ChanPrivs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) AddNick(n *Nick) {
|
func (ch *Channel) AddNick(n *Nick) {
|
||||||
if _, ok := ch.Nicks[n]; !ok {
|
if _, ok := ch.Nicks[n]; !ok {
|
||||||
ch.Nicks[n] = new(ChanPrivs);
|
ch.Nicks[n] = new(ChanPrivs)
|
||||||
n.Channels[ch] = ch.Nicks[n];
|
n.Channels[ch] = ch.Nicks[n]
|
||||||
} else {
|
} else {
|
||||||
ch.conn.error("irc.Channel.AddNick() warning: trying to add already-present nick %s to channel %s", n.Nick, ch.Name);
|
ch.conn.error("irc.Channel.AddNick() warning: trying to add already-present nick %s to channel %s", n.Nick, ch.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) DelNick(n *Nick) {
|
func (ch *Channel) DelNick(n *Nick) {
|
||||||
if _, ok := ch.Nicks[n]; ok {
|
if _, ok := ch.Nicks[n]; ok {
|
||||||
fmt.Printf("irc.Channel.DelNick(): deleting %s from %s\n", n.Nick, ch.Name);
|
fmt.Printf("irc.Channel.DelNick(): deleting %s from %s\n", n.Nick, ch.Name)
|
||||||
if n == n.conn.Me {
|
if n == n.conn.Me {
|
||||||
// we're leaving the channel, so remove all state we have about it
|
// we're leaving the channel, so remove all state we have about it
|
||||||
ch.Delete();
|
ch.Delete()
|
||||||
} else {
|
} else {
|
||||||
ch.Nicks[n] = nil, false;
|
ch.Nicks[n] = nil, false
|
||||||
n.DelChannel(ch);
|
n.DelChannel(ch)
|
||||||
}
|
}
|
||||||
} // no else here ...
|
} // no else here ...
|
||||||
// we call Channel.DelNick() and Nick.DelChan() from each other to ensure
|
// we call Channel.DelNick() and Nick.DelChan() from each other to ensure
|
||||||
|
@ -118,57 +117,57 @@ func (ch *Channel) DelNick(n *Nick) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) Delete() {
|
func (ch *Channel) Delete() {
|
||||||
fmt.Printf("irc.Channel.Delete(): deleting %s\n", ch.Name);
|
fmt.Printf("irc.Channel.Delete(): deleting %s\n", ch.Name)
|
||||||
for n, _ := range ch.Nicks {
|
for n, _ := range ch.Nicks {
|
||||||
n.DelChannel(ch);
|
n.DelChannel(ch)
|
||||||
}
|
}
|
||||||
ch.conn.chans[ch.Name] = nil, false;
|
ch.conn.chans[ch.Name] = nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
* Nick methods for state management
|
* Nick methods for state management
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
func (n *Nick) initialise() {
|
func (n *Nick) initialise() {
|
||||||
n.Modes = new(NickMode);
|
n.Modes = new(NickMode)
|
||||||
n.Channels = make(map[*Channel] *ChanPrivs);
|
n.Channels = make(map[*Channel]*ChanPrivs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// very slightly different to Channel.AddNick() ...
|
// very slightly different to Channel.AddNick() ...
|
||||||
func (n *Nick) AddChannel(ch *Channel) {
|
func (n *Nick) AddChannel(ch *Channel) {
|
||||||
if _, ok := n.Channels[ch]; !ok {
|
if _, ok := n.Channels[ch]; !ok {
|
||||||
ch.Nicks[n] = new(ChanPrivs);
|
ch.Nicks[n] = new(ChanPrivs)
|
||||||
n.Channels[ch] = ch.Nicks[n];
|
n.Channels[ch] = ch.Nicks[n]
|
||||||
} else {
|
} else {
|
||||||
n.conn.error("irc.Nick.AddChannel() warning: trying to add already-present channel %s to nick %s", ch.Name, n.Nick);
|
n.conn.error("irc.Nick.AddChannel() warning: trying to add already-present channel %s to nick %s", ch.Name, n.Nick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nick) DelChannel(ch *Channel) {
|
func (n *Nick) DelChannel(ch *Channel) {
|
||||||
if _, ok := n.Channels[ch]; ok {
|
if _, ok := n.Channels[ch]; ok {
|
||||||
fmt.Printf("irc.Nick.DelChannel(): deleting %s from %s\n", n.Nick, ch.Name);
|
fmt.Printf("irc.Nick.DelChannel(): deleting %s from %s\n", n.Nick, ch.Name)
|
||||||
n.Channels[ch] = nil, false;
|
n.Channels[ch] = nil, false
|
||||||
ch.DelNick(n);
|
ch.DelNick(n)
|
||||||
if len(n.Channels) == 0 {
|
if len(n.Channels) == 0 {
|
||||||
// nick is no longer in any channels we inhabit, stop tracking it
|
// nick is no longer in any channels we inhabit, stop tracking it
|
||||||
n.Delete();
|
n.Delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nick) ReNick(neu string) {
|
func (n *Nick) ReNick(neu string) {
|
||||||
n.conn.nicks[n.Nick] = nil, false;
|
n.conn.nicks[n.Nick] = nil, false
|
||||||
n.Nick = neu;
|
n.Nick = neu
|
||||||
n.conn.nicks[n.Nick] = n;
|
n.conn.nicks[n.Nick] = n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nick) Delete() {
|
func (n *Nick) Delete() {
|
||||||
// we don't ever want to remove *our* nick from conn.nicks...
|
// we don't ever want to remove *our* nick from conn.nicks...
|
||||||
if n != n.conn.Me {
|
if n != n.conn.Me {
|
||||||
fmt.Printf("irc.Nick.Delete(): deleting %s\n", n.Nick);
|
fmt.Printf("irc.Nick.Delete(): deleting %s\n", n.Nick)
|
||||||
for ch, _ := range n.Channels {
|
for ch, _ := range n.Channels {
|
||||||
ch.DelNick(n);
|
ch.DelNick(n)
|
||||||
}
|
}
|
||||||
n.conn.nicks[n.Nick] = nil, false;
|
n.conn.nicks[n.Nick] = nil, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,152 +175,152 @@ func (n *Nick) Delete() {
|
||||||
* String() methods for all structs in this file for ease of debugging.
|
* String() methods for all structs in this file for ease of debugging.
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
var ChanModeToString = map[string]string{
|
var ChanModeToString = map[string]string{
|
||||||
"Private":"p",
|
"Private": "p",
|
||||||
"Secret":"s",
|
"Secret": "s",
|
||||||
"ProtectedTopic":"t",
|
"ProtectedTopic": "t",
|
||||||
"NoExternalMsg":"n",
|
"NoExternalMsg": "n",
|
||||||
"Moderated":"m",
|
"Moderated": "m",
|
||||||
"InviteOnly":"i",
|
"InviteOnly": "i",
|
||||||
"OperOnly":"O",
|
"OperOnly": "O",
|
||||||
"SSLOnly":"z",
|
"SSLOnly": "z",
|
||||||
"Key":"k",
|
"Key": "k",
|
||||||
"Limit":"l",
|
"Limit": "l",
|
||||||
};
|
}
|
||||||
var NickModeToString = map[string]string{
|
var NickModeToString = map[string]string{
|
||||||
"Invisible":"i",
|
"Invisible": "i",
|
||||||
"Oper":"o",
|
"Oper": "o",
|
||||||
"WallOps":"w",
|
"WallOps": "w",
|
||||||
"HiddenHost":"x",
|
"HiddenHost": "x",
|
||||||
"SSL":"z",
|
"SSL": "z",
|
||||||
};
|
}
|
||||||
var ChanPrivToString = map[string]string{
|
var ChanPrivToString = map[string]string{
|
||||||
"Owner":"q",
|
"Owner": "q",
|
||||||
"Admin":"a",
|
"Admin": "a",
|
||||||
"Op":"o",
|
"Op": "o",
|
||||||
"HalfOp":"h",
|
"HalfOp": "h",
|
||||||
"Voice":"v",
|
"Voice": "v",
|
||||||
};
|
}
|
||||||
var ChanPrivToModeChar = map[string]byte{
|
var ChanPrivToModeChar = map[string]byte{
|
||||||
"Owner":'~',
|
"Owner": '~',
|
||||||
"Admin":'&',
|
"Admin": '&',
|
||||||
"Op":'@',
|
"Op": '@',
|
||||||
"HalfOp":'%',
|
"HalfOp": '%',
|
||||||
"Voice":'+',
|
"Voice": '+',
|
||||||
};
|
}
|
||||||
var StringToChanMode, StringToNickMode, StringToChanPriv map[string]string;
|
var StringToChanMode, StringToNickMode, StringToChanPriv map[string]string
|
||||||
var ModeCharToChanPriv map[byte]string;
|
var ModeCharToChanPriv map[byte]string
|
||||||
|
|
||||||
// Init function to fill in reverse mappings for *toString constants etc.
|
// Init function to fill in reverse mappings for *toString constants etc.
|
||||||
func init() {
|
func init() {
|
||||||
StringToChanMode = make(map[string]string);
|
StringToChanMode = make(map[string]string)
|
||||||
for k,v := range ChanModeToString {
|
for k, v := range ChanModeToString {
|
||||||
StringToChanMode[v] = k;
|
StringToChanMode[v] = k
|
||||||
}
|
}
|
||||||
StringToNickMode = make(map[string]string);
|
StringToNickMode = make(map[string]string)
|
||||||
for k,v := range NickModeToString {
|
for k, v := range NickModeToString {
|
||||||
StringToNickMode[v] = k;
|
StringToNickMode[v] = k
|
||||||
}
|
}
|
||||||
StringToChanPriv = make(map[string]string);
|
StringToChanPriv = make(map[string]string)
|
||||||
for k,v := range ChanPrivToString {
|
for k, v := range ChanPrivToString {
|
||||||
StringToChanPriv[v] = k;
|
StringToChanPriv[v] = k
|
||||||
}
|
}
|
||||||
ModeCharToChanPriv = make(map[byte]string);
|
ModeCharToChanPriv = make(map[byte]string)
|
||||||
for k,v := range ChanPrivToModeChar {
|
for k, v := range ChanPrivToModeChar {
|
||||||
ModeCharToChanPriv[v] = k;
|
ModeCharToChanPriv[v] = k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) String() string {
|
func (ch *Channel) String() string {
|
||||||
str := "Channel: " + ch.Name + "\n\t";
|
str := "Channel: " + ch.Name + "\n\t"
|
||||||
str += "Topic: " + ch.Topic + "\n\t";
|
str += "Topic: " + ch.Topic + "\n\t"
|
||||||
str += "Modes: " + ch.Modes.String() + "\n\t";
|
str += "Modes: " + ch.Modes.String() + "\n\t"
|
||||||
str += "Nicks: \n";
|
str += "Nicks: \n"
|
||||||
for n, p := range ch.Nicks {
|
for n, p := range ch.Nicks {
|
||||||
str += "\t\t" + n.Nick + ": " + p.String() + "\n";
|
str += "\t\t" + n.Nick + ": " + p.String() + "\n"
|
||||||
}
|
}
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nick) String() string {
|
func (n *Nick) String() string {
|
||||||
str := "Nick: "+ n.Nick + "\n\t";
|
str := "Nick: " + n.Nick + "\n\t"
|
||||||
str += "Hostmask: " + n.Ident + "@" + n.Host + "\n\t";
|
str += "Hostmask: " + n.Ident + "@" + n.Host + "\n\t"
|
||||||
str += "Real Name: " + n.Name + "\n\t";
|
str += "Real Name: " + n.Name + "\n\t"
|
||||||
str += "Modes: " + n.Modes.String() + "\n\t";
|
str += "Modes: " + n.Modes.String() + "\n\t"
|
||||||
str += "Channels: \n";
|
str += "Channels: \n"
|
||||||
for ch, p := range n.Channels {
|
for ch, p := range n.Channels {
|
||||||
str += "\t\t" + ch.Name + ": " + p.String() + "\n";
|
str += "\t\t" + ch.Name + ": " + p.String() + "\n"
|
||||||
}
|
}
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ChanMode) String() string {
|
func (cm *ChanMode) String() string {
|
||||||
str := "+";
|
str := "+"
|
||||||
a := make([]string, 2);
|
a := make([]string, 2)
|
||||||
v := reflect.Indirect(reflect.NewValue(cm)).(*reflect.StructValue);
|
v := reflect.Indirect(reflect.NewValue(cm)).(*reflect.StructValue)
|
||||||
t := v.Type().(*reflect.StructType);
|
t := v.Type().(*reflect.StructType)
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
switch f := v.Field(i).(type) {
|
switch f := v.Field(i).(type) {
|
||||||
case *reflect.BoolValue:
|
case *reflect.BoolValue:
|
||||||
if f.Get() {
|
if f.Get() {
|
||||||
str += ChanModeToString[t.Field(i).Name];
|
str += ChanModeToString[t.Field(i).Name]
|
||||||
}
|
}
|
||||||
case *reflect.StringValue:
|
case *reflect.StringValue:
|
||||||
if f.Get() != "" {
|
if f.Get() != "" {
|
||||||
str += ChanModeToString[t.Field(i).Name];
|
str += ChanModeToString[t.Field(i).Name]
|
||||||
a[0] = f.Get();
|
a[0] = f.Get()
|
||||||
}
|
}
|
||||||
case *reflect.IntValue:
|
case *reflect.IntValue:
|
||||||
if f.Get() != 0 {
|
if f.Get() != 0 {
|
||||||
str += ChanModeToString[t.Field(i).Name];
|
str += ChanModeToString[t.Field(i).Name]
|
||||||
a[1] = fmt.Sprintf("%d", cm.Limit);
|
a[1] = fmt.Sprintf("%d", cm.Limit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, s := range a {
|
for _, s := range a {
|
||||||
if s != "" {
|
if s != "" {
|
||||||
str += " " + s;
|
str += " " + s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if str == "+" {
|
if str == "+" {
|
||||||
str = "No modes set";
|
str = "No modes set"
|
||||||
}
|
}
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *NickMode) String() string {
|
func (nm *NickMode) String() string {
|
||||||
str := "+";
|
str := "+"
|
||||||
v := reflect.Indirect(reflect.NewValue(nm)).(*reflect.StructValue);
|
v := reflect.Indirect(reflect.NewValue(nm)).(*reflect.StructValue)
|
||||||
t := v.Type().(*reflect.StructType);
|
t := v.Type().(*reflect.StructType)
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
switch f := v.Field(i).(type) {
|
switch f := v.Field(i).(type) {
|
||||||
// only bools here at the mo!
|
// only bools here at the mo!
|
||||||
case *reflect.BoolValue:
|
case *reflect.BoolValue:
|
||||||
if f.Get() {
|
if f.Get() {
|
||||||
str += NickModeToString[t.Field(i).Name];
|
str += NickModeToString[t.Field(i).Name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if str == "+" {
|
if str == "+" {
|
||||||
str = "No modes set";
|
str = "No modes set"
|
||||||
}
|
}
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ChanPrivs) String() string {
|
func (p *ChanPrivs) String() string {
|
||||||
str := "+";
|
str := "+"
|
||||||
v := reflect.Indirect(reflect.NewValue(p)).(*reflect.StructValue);
|
v := reflect.Indirect(reflect.NewValue(p)).(*reflect.StructValue)
|
||||||
t := v.Type().(*reflect.StructType);
|
t := v.Type().(*reflect.StructType)
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
switch f := v.Field(i).(type) {
|
switch f := v.Field(i).(type) {
|
||||||
// only bools here at the mo too!
|
// only bools here at the mo too!
|
||||||
case *reflect.BoolValue:
|
case *reflect.BoolValue:
|
||||||
if f.Get() {
|
if f.Get() {
|
||||||
str += ChanPrivToString[t.Field(i).Name];
|
str += ChanPrivToString[t.Field(i).Name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if str == "+" {
|
if str == "+" {
|
||||||
str = "No modes set";
|
str = "No modes set"
|
||||||
}
|
}
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue