kunt

golang IRC bot
git clone git://git.2f30.org/kunt
Log | Files | Refs | LICENSE

commit e1967c5e1d907594d55e30010579bf997f72e734
parent 65e10c4665d32d325c0a174999bef69ac89e0ab4
Author: sin <sin@2f30.org>
Date:   Fri,  3 May 2013 14:27:57 +0100

update irc

Diffstat:
Msrc/irc/events.go | 4----
Msrc/irc/irc.go | 183+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/irc/message.go | 18+++++++++---------
Msrc/kunt/kunt.go | 16++++++++++------
4 files changed, 133 insertions(+), 88 deletions(-)

diff --git a/src/irc/events.go b/src/irc/events.go @@ -4,10 +4,6 @@ import ( "fmt" ) -const ( - MAX_EVENTS = 16 -) - type IrcEvent struct { Command string Fn func(IrcMessage) diff --git a/src/irc/irc.go b/src/irc/irc.go @@ -8,7 +8,7 @@ import ( ) type IrcContext struct { - conn net.Conn // Actual connection + conn net.Conn // actual connection outgoingMsg chan IrcMessage // TX messaging channel incomingMsg chan IrcMessage // RX messaging channel ev []IrcEvent // slice of callbacks @@ -18,14 +18,14 @@ type IrcContext struct { } type IrcConfig struct { - Network string // network name - Nick string // nickname - User string // username - Pass string // password - Serv string // server address - Port string // server port - Tls bool // enable/disable ssl - Ipv6 bool // enable/disable ipv6 (not implemented) + NetworkName string // network name + Nick string // nickname + User string // username + Pass string // password + Serv string // server address + Port string // server port + Tls bool // enable/disable ssl + Ipv6 bool // enable/disable ipv6 (not implemented) } type ircChan struct { @@ -34,45 +34,43 @@ type ircChan struct { } type ircNetwork struct { - network string // network name - nick string // nickname - user string // username - pass string // password - serv string // server address - port string // server port - tls bool // enable/disable ssl - ipv6 bool // enable/disable ipv6 (not implemented) - channels [32]ircChan // a maximum of 32 channels for now - channum int // number of channels + name string // network name + nick string // nickname + user string // username + pass string // password + serv string // server address + port string // server port + tls bool // enable/disable ssl + ipv6 bool // enable/disable ipv6 (not implemented) + channels []ircChan // slice of channels } // Create a new IrcContext func NewIrcContext(ircConfig IrcConfig) *IrcContext { network := ircNetwork{ - network: ircConfig.Network, - nick: ircConfig.Nick, - user: ircConfig.User, - pass: ircConfig.Pass, - serv: ircConfig.Serv, - port: ircConfig.Port, - tls: ircConfig.Tls, - ipv6: ircConfig.Ipv6, + name: ircConfig.NetworkName, + nick: ircConfig.Nick, + user: ircConfig.User, + pass: ircConfig.Pass, + serv: ircConfig.Serv, + port: ircConfig.Port, + tls: ircConfig.Tls, + ipv6: ircConfig.Ipv6, + channels: make([]ircChan, 0), } return &IrcContext{ outgoingMsg: make(chan IrcMessage), incomingMsg: make(chan IrcMessage), - ev: make([]IrcEvent, 0, MAX_EVENTS), + ev: make([]IrcEvent, 0), network: network, intercepts: make([]*chan IrcMessage, 0), } } -// Appends a new channel pointer to intercept rx func (i *IrcContext) AddIntercept(a *chan IrcMessage) { i.intercepts = append(i.intercepts, a) } -// Deletes based on pointer equality. caller must hold ref. func (i *IrcContext) DelIntercept(ap *chan IrcMessage) error { for j := 0; j < len(i.intercepts); j++ { if ap == i.intercepts[j] { @@ -87,40 +85,52 @@ func (i *IrcContext) DelIntercept(ap *chan IrcMessage) error { return fmt.Errorf("Intercept not found") } -// Add a channel to the set of channels func (i *IrcContext) AddChannel(s string) error { - for c := 0; c < i.network.channum; c++ { - if i.network.channels[c].name == s { + for _, v := range i.network.channels { + if v.name == s { return fmt.Errorf("Channel %s already added", s) } } - if i.network.channum+1 < len(i.network.channels) { - i.network.channels[i.network.channum] = ircChan{s, false} - i.network.channum++ - return nil + i.network.channels = append(i.network.channels, ircChan{s, false}) + return nil +} + +func (i *IrcContext) JoinChannel(s string) error { + for c, v := range i.network.channels { + if v.name == s { + if !v.joined { + i.Join(v.name) + i.network.channels[c].joined = true + return nil + } else { + return fmt.Errorf("Already joined on channel %s", s) + } + } } - return fmt.Errorf("Too many channels added") + return fmt.Errorf("Can't find channel %s", s) } -// Join all channels -func (i *IrcContext) JoinChannels() { - for c := 0; c < i.network.channum; c++ { - if !i.network.channels[c].joined { - i.Join(i.network.channels[c].name) +func (i *IrcContext) JoinChannels() error { + for c, v := range i.network.channels { + if !v.joined { + if err := i.JoinChannel(v.name); err != nil { + return err + } i.network.channels[c].joined = true } } + return nil } func (i *IrcContext) PartChannel(s string) error { - for c := 0; c < i.network.channum; c++ { - if i.network.channels[c].name == s { - if i.network.channels[c].joined { + for c, v := range i.network.channels { + if v.name == s { + if v.joined { i.Part(s) i.network.channels[c].joined = false return nil } else { - return fmt.Errorf("Channel %s is not joined\n", s) + return fmt.Errorf("Channel %s is not joined", s) } } } @@ -141,27 +151,27 @@ func (i *IrcContext) Connect() error { service := i.network.serv + ":" + i.network.port if i.network.tls { - conf, err := loadCerts() - if err != nil { - return err - } - conn, err := tls.Dial("tcp", service, conf) - if err != nil { + if conf, err := loadCerts(); err != nil { return err + } else { + if conn, err := tls.Dial("tcp", service, conf); err != nil { + return err + } else { + i.conn = conn + } } - i.conn = conn } else { - conn, err := net.Dial("tcp", service) - if err != nil { + if conn, err := net.Dial("tcp", service); err != nil { return err + } else { + i.conn = conn } - i.conn = conn } // Fire off the goroutines now! go i.incomingMsgLoop() go i.outgoingMsgLoop() - go i.readLoop() + go i.RxRawMessages() i.Nick() i.User() @@ -178,7 +188,7 @@ func (i *IrcContext) read(buf []byte) (int, error) { // This is the actual raw read loop. Here we parse the incoming bytes // and form messages. -func (i *IrcContext) readLoop() error { +func (i *IrcContext) RxRawMessages() error { for { var buf [512]byte n, err := i.read(buf[0:]) @@ -188,16 +198,6 @@ func (i *IrcContext) readLoop() error { fmt.Println(string(buf[0 : n-1])) s := string(buf[0:n]) - if strings.HasPrefix(s, "PING :") { - r := strings.Replace(s, "PING :", "PONG :", 6) - r = strings.TrimSpace(r) + "\r\n" - err := i.TxRawMessage([]byte(r)) - if err != nil { - return err - } - continue - } - msg := i.ParseRawMessage(s) i.incomingMsg <- msg for _, v := range i.intercepts { @@ -206,3 +206,48 @@ func (i *IrcContext) readLoop() error { } return nil } + +// Transmit a raw message +func (i *IrcContext) TxRawMessage(msg []byte) error { + _, err := i.conn.Write(msg[0:]) + if err != nil { + return err + } + return nil +} + +func (i *IrcContext) String() string { + s := fmt.Sprintf("IRC CONTEXT DUMP") + if len(i.ev) == 0 { + s += fmt.Sprintf("\n\tEvent handlers: [NONE]\n") + } else { + s += fmt.Sprintf("\n\tEvent handlers: [%d]\n", len(i.ev)) + for _, v := range i.ev { + s += fmt.Sprintf("\t\tCommand: %s\n", v.Command) + } + } + s += fmt.Sprintf("\tNetwork:\n") + s += fmt.Sprintf("\t\tName: %s\n", i.network.name) + s += fmt.Sprintf("\t\tNick: %s\n", i.network.nick) + s += fmt.Sprintf("\t\tUser: %s\n", i.network.user) + if i.network.pass != "" { + s += fmt.Sprintf("\t\tPass: %s\n", i.network.pass) + } else { + s += fmt.Sprintf("\t\tPass: [NONE]\n") + } + s += fmt.Sprintf("\t\tServer: %s\n", i.network.serv) + if i.network.tls { + s += fmt.Sprintf("\t\tTLS: on") + } else { + s += fmt.Sprintf("\t\tTLS: off") + } + if len(i.network.channels) == 0 { + s += fmt.Sprintf("\n\t\tChannels: [NONE]") + } else { + s += fmt.Sprintf("\n\t\tChannels: [%d]\n", len(i.network.channels)) + for _, v := range i.network.channels { + s += fmt.Sprintf("\t\t\tName: %s\n", v.name) + } + } + return strings.TrimSpace(s) +} diff --git a/src/irc/message.go b/src/irc/message.go @@ -17,6 +17,15 @@ const ( IRCTEXT ) +// Send PONG reply +func (i *IrcContext) Pong(host string) { + msg := IrcMessage{ + Command: "PONG", + Args: []string{":" + host}, + } + i.outgoingMsg <- msg +} + // Send the nickname func (i *IrcContext) Nick() { msg := IrcMessage{ @@ -109,15 +118,6 @@ func (i *IrcContext) UnpackMessage(msg IrcMessage) []byte { return []byte(rawMsg) } -// Transmit a raw message -func (i *IrcContext) TxRawMessage(msg []byte) error { - _, err := i.conn.Write(msg[0:]) - if err != nil { - return err - } - return nil -} - func (i *IrcContext) incomingMsgLoop() error { for { select { diff --git a/src/kunt/kunt.go b/src/kunt/kunt.go @@ -359,12 +359,12 @@ func main() { } cfg := irc.IrcConfig{ - Network: "grnet", - Nick: botname, - User: "z0mg", - Serv: hostport[0], - Port: hostport[1], - Tls: *sslon, + NetworkName: "grnet", + Nick: botname, + User: "z0mg", + Serv: hostport[0], + Port: hostport[1], + Tls: *sslon, } kunt.ircCtx = irc.NewIrcContext(cfg) @@ -377,6 +377,10 @@ func main() { } }}) + kunt.ircCtx.AddEventHandler(irc.IrcEvent{"PING", func(msg irc.IrcMessage) { + kunt.ircCtx.Pong(msg.Args[0]) + }}) + kunt.ircCtx.Connect() kunt.ircCtx.AddChannel("#2f30") kunt.ircCtx.JoinChannels()