remove old irc bindings
This commit is contained in:
parent
616e71825c
commit
d12499bcdd
|
@ -1,27 +0,0 @@
|
||||||
// Copyright (c) 2009 Thomas Jager. All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,65 +0,0 @@
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Event based irc client library.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
* Event based. Register Callbacks for the events you need to handle.
|
|
||||||
* Handles basic irc demands for you
|
|
||||||
* Standard CTCP
|
|
||||||
* Reconnections on errors
|
|
||||||
* Detect stoned servers
|
|
||||||
|
|
||||||
Install
|
|
||||||
-------
|
|
||||||
$ go get github.com/thoj/go-ircevent
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
See test/irc_test.go
|
|
||||||
|
|
||||||
Events for callbacks
|
|
||||||
--------------------
|
|
||||||
* 001 Welcome
|
|
||||||
* PING
|
|
||||||
* CTCP Unknown CTCP
|
|
||||||
* CTCP_VERSION Version request (Handled internaly)
|
|
||||||
* CTCP_USERINFO
|
|
||||||
* CTCP_CLIENTINFO
|
|
||||||
* CTCP_TIME
|
|
||||||
* CTCP_PING
|
|
||||||
* CTCP_ACTION (/me)
|
|
||||||
* PRIVMSG
|
|
||||||
* MODE
|
|
||||||
* JOIN
|
|
||||||
|
|
||||||
+Many more
|
|
||||||
|
|
||||||
|
|
||||||
AddCallback Example
|
|
||||||
-------------------
|
|
||||||
ircobj.AddCallback("PRIVMSG", func(event *irc.Event) {
|
|
||||||
//event.Message() contains the message
|
|
||||||
//event.Nick Contains the sender
|
|
||||||
//event.Arguments[0] Contains the channel
|
|
||||||
});
|
|
||||||
|
|
||||||
Commands
|
|
||||||
--------
|
|
||||||
ircobj := irc.IRC("<nick>", "<user>") //Create new ircobj
|
|
||||||
//Set options
|
|
||||||
ircobj.UseTLS = true //default is false
|
|
||||||
//ircobj.TLSOptions //set ssl options
|
|
||||||
ircobj.Password = "[server password]"
|
|
||||||
//Commands
|
|
||||||
ircobj.Connect("irc.someserver.com:6667") //Connect to server
|
|
||||||
ircobj.SendRaw("<string>") //sends string to server. Adds \r\n
|
|
||||||
ircobj.SendRawf("<formatstring>", ...) //sends formatted string to server.n
|
|
||||||
ircobj.Join("<#channel> [password]")
|
|
||||||
ircobj.Nick("newnick")
|
|
||||||
ircobj.Privmsg("<nickname | #channel>", "msg")
|
|
||||||
ircobj.Privmsgf(<nickname | #channel>, "<formatstring>", ...)
|
|
||||||
ircobj.Notice("<nickname | #channel>", "msg")
|
|
||||||
ircobj.Noticef("<nickname | #channel>", "<formatstring>", ...)
|
|
|
@ -1,469 +0,0 @@
|
||||||
// Copyright 2009 Thomas Jager <mail@jager.no> All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
This package provides an event based IRC client library. It allows to
|
|
||||||
register callbacks for the events you need to handle. Its features
|
|
||||||
include handling standard CTCP, reconnecting on errors and detecting
|
|
||||||
stones servers.
|
|
||||||
Details of the IRC protocol can be found in the following RFCs:
|
|
||||||
https://tools.ietf.org/html/rfc1459
|
|
||||||
https://tools.ietf.org/html/rfc2810
|
|
||||||
https://tools.ietf.org/html/rfc2811
|
|
||||||
https://tools.ietf.org/html/rfc2812
|
|
||||||
https://tools.ietf.org/html/rfc2813
|
|
||||||
The details of the client-to-client protocol (CTCP) can be found here: http://www.irchelp.org/irchelp/rfc/ctcpspec.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
package irc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
VERSION = "go-ircevent v2.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrDisconnected = errors.New("Disconnect Called")
|
|
||||||
|
|
||||||
// Read data from a connection. To be used as a goroutine.
|
|
||||||
func (irc *Connection) readLoop() {
|
|
||||||
defer irc.Done()
|
|
||||||
br := bufio.NewReaderSize(irc.socket, 512)
|
|
||||||
|
|
||||||
errChan := irc.ErrorChan()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-irc.end:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
// Set a read deadline based on the combined timeout and ping frequency
|
|
||||||
// We should ALWAYS have received a response from the server within the timeout
|
|
||||||
// after our own pings
|
|
||||||
if irc.socket != nil {
|
|
||||||
irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq))
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := br.ReadString('\n')
|
|
||||||
|
|
||||||
// We got past our blocking read, so bin timeout
|
|
||||||
if irc.socket != nil {
|
|
||||||
var zero time.Time
|
|
||||||
irc.socket.SetReadDeadline(zero)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if irc.Debug {
|
|
||||||
irc.Log.Printf("<-- %s\n", strings.TrimSpace(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.lastMessage = time.Now()
|
|
||||||
event, err := parseToEvent(msg)
|
|
||||||
event.Connection = irc
|
|
||||||
if err == nil {
|
|
||||||
/* XXX: len(args) == 0: args should be empty */
|
|
||||||
irc.RunCallbacks(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Parse raw irc messages
|
|
||||||
func parseToEvent(msg string) (*Event, error) {
|
|
||||||
msg = strings.TrimSuffix(msg, "\n") //Remove \r\n
|
|
||||||
msg = strings.TrimSuffix(msg, "\r")
|
|
||||||
event := &Event{Raw: msg}
|
|
||||||
if len(msg) < 5 {
|
|
||||||
return nil, errors.New("Malformed msg from server")
|
|
||||||
}
|
|
||||||
if msg[0] == ':' {
|
|
||||||
if i := strings.Index(msg, " "); i > -1 {
|
|
||||||
event.Source = msg[1:i]
|
|
||||||
msg = msg[i+1 : len(msg)]
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("Malformed msg from server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 && i < j {
|
|
||||||
event.Nick = event.Source[0:i]
|
|
||||||
event.User = event.Source[i+1 : j]
|
|
||||||
event.Host = event.Source[j+1 : len(event.Source)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
split := strings.SplitN(msg, " :", 2)
|
|
||||||
args := strings.Split(split[0], " ")
|
|
||||||
event.Code = strings.ToUpper(args[0])
|
|
||||||
event.Arguments = args[1:]
|
|
||||||
if len(split) > 1 {
|
|
||||||
event.Arguments = append(event.Arguments, split[1])
|
|
||||||
}
|
|
||||||
return event, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop to write to a connection. To be used as a goroutine.
|
|
||||||
func (irc *Connection) writeLoop() {
|
|
||||||
defer irc.Done()
|
|
||||||
errChan := irc.ErrorChan()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-irc.end:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
b, ok := <-irc.pwrite
|
|
||||||
if !ok || b == "" || irc.socket == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if irc.Debug {
|
|
||||||
irc.Log.Printf("--> %s\n", strings.TrimSpace(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a write deadline based on the time out
|
|
||||||
irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout))
|
|
||||||
|
|
||||||
_, err := irc.socket.Write([]byte(b))
|
|
||||||
|
|
||||||
// Past blocking write, bin timeout
|
|
||||||
var zero time.Time
|
|
||||||
irc.socket.SetWriteDeadline(zero)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pings the server if we have not received any messages for 5 minutes
|
|
||||||
// to keep the connection alive. To be used as a goroutine.
|
|
||||||
func (irc *Connection) pingLoop() {
|
|
||||||
defer irc.Done()
|
|
||||||
ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring
|
|
||||||
ticker2 := time.NewTicker(irc.PingFreq) // Tick at the ping frequency.
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
//Ping if we haven't received anything from the server within the keep alive period
|
|
||||||
if time.Since(irc.lastMessage) >= irc.KeepAlive {
|
|
||||||
irc.SendRawf("PING %d", time.Now().UnixNano())
|
|
||||||
}
|
|
||||||
case <-ticker2.C:
|
|
||||||
//Ping at the ping frequency
|
|
||||||
irc.SendRawf("PING %d", time.Now().UnixNano())
|
|
||||||
//Try to recapture nickname if it's not as configured.
|
|
||||||
if irc.nick != irc.nickcurrent {
|
|
||||||
irc.nickcurrent = irc.nick
|
|
||||||
irc.SendRawf("NICK %s", irc.nick)
|
|
||||||
}
|
|
||||||
case <-irc.end:
|
|
||||||
ticker.Stop()
|
|
||||||
ticker2.Stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main loop to control the connection.
|
|
||||||
func (irc *Connection) Loop() {
|
|
||||||
errChan := irc.ErrorChan()
|
|
||||||
for !irc.stopped {
|
|
||||||
err := <-errChan
|
|
||||||
if irc.stopped {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
irc.Log.Printf("Error, disconnected: %s\n", err)
|
|
||||||
for !irc.stopped {
|
|
||||||
if err = irc.Reconnect(); err != nil {
|
|
||||||
irc.Log.Printf("Error while reconnecting: %s\n", err)
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quit the current connection and disconnect from the server
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.6
|
|
||||||
func (irc *Connection) Quit() {
|
|
||||||
irc.SendRaw("QUIT")
|
|
||||||
irc.stopped = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the connection to join a given channel.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.1
|
|
||||||
func (irc *Connection) Join(channel string) {
|
|
||||||
irc.pwrite <- fmt.Sprintf("JOIN %s\r\n", channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leave a given channel.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.2
|
|
||||||
func (irc *Connection) Part(channel string) {
|
|
||||||
irc.pwrite <- fmt.Sprintf("PART %s\r\n", channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a notification to a nickname. This is similar to Privmsg but must not receive replies.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2
|
|
||||||
func (irc *Connection) Notice(target, message string) {
|
|
||||||
irc.pwrite <- fmt.Sprintf("NOTICE %s :%s\r\n", target, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a formated notification to a nickname.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2
|
|
||||||
func (irc *Connection) Noticef(target, format string, a ...interface{}) {
|
|
||||||
irc.Notice(target, fmt.Sprintf(format, a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send (action) message to a target (channel or nickname).
|
|
||||||
// No clear RFC on this one...
|
|
||||||
func (irc *Connection) Action(target, message string) {
|
|
||||||
irc.pwrite <- fmt.Sprintf("PRIVMSG %s :\001ACTION %s\001\r\n", target, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send formatted (action) message to a target (channel or nickname).
|
|
||||||
func (irc *Connection) Actionf(target, format string, a ...interface{}) {
|
|
||||||
irc.Action(target, fmt.Sprintf(format, a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send (private) message to a target (channel or nickname).
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.1
|
|
||||||
func (irc *Connection) Privmsg(target, message string) {
|
|
||||||
irc.pwrite <- fmt.Sprintf("PRIVMSG %s :%s\r\n", target, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send formated string to specified target (channel or nickname).
|
|
||||||
func (irc *Connection) Privmsgf(target, format string, a ...interface{}) {
|
|
||||||
irc.Privmsg(target, fmt.Sprintf(format, a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kick <user> from <channel> with <msg>. For no message, pass empty string ("")
|
|
||||||
func (irc *Connection) Kick(user, channel, msg string) {
|
|
||||||
var cmd bytes.Buffer
|
|
||||||
cmd.WriteString(fmt.Sprintf("KICK %s %s", channel, user))
|
|
||||||
if msg != "" {
|
|
||||||
cmd.WriteString(fmt.Sprintf(" :%s", msg))
|
|
||||||
}
|
|
||||||
cmd.WriteString("\r\n")
|
|
||||||
irc.pwrite <- cmd.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kick all <users> from <channel> with <msg>. For no message, pass
|
|
||||||
// empty string ("")
|
|
||||||
func (irc *Connection) MultiKick(users []string, channel string, msg string) {
|
|
||||||
var cmd bytes.Buffer
|
|
||||||
cmd.WriteString(fmt.Sprintf("KICK %s %s", channel, strings.Join(users, ",")))
|
|
||||||
if msg != "" {
|
|
||||||
cmd.WriteString(fmt.Sprintf(" :%s", msg))
|
|
||||||
}
|
|
||||||
cmd.WriteString("\r\n")
|
|
||||||
irc.pwrite <- cmd.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send raw string.
|
|
||||||
func (irc *Connection) SendRaw(message string) {
|
|
||||||
irc.pwrite <- message + "\r\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send raw formated string.
|
|
||||||
func (irc *Connection) SendRawf(format string, a ...interface{}) {
|
|
||||||
irc.SendRaw(fmt.Sprintf(format, a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set (new) nickname.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.2
|
|
||||||
func (irc *Connection) Nick(n string) {
|
|
||||||
irc.nick = n
|
|
||||||
irc.SendRawf("NICK %s", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine nick currently used with the connection.
|
|
||||||
func (irc *Connection) GetNick() string {
|
|
||||||
return irc.nickcurrent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query information about a particular nickname.
|
|
||||||
// RFC 1459: https://tools.ietf.org/html/rfc1459#section-4.5.2
|
|
||||||
func (irc *Connection) Whois(nick string) {
|
|
||||||
irc.SendRawf("WHOIS %s", nick)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query information about a given nickname in the server.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.5.1
|
|
||||||
func (irc *Connection) Who(nick string) {
|
|
||||||
irc.SendRawf("WHO %s", nick)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set different modes for a target (channel or nickname).
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.3
|
|
||||||
func (irc *Connection) Mode(target string, modestring ...string) {
|
|
||||||
if len(modestring) > 0 {
|
|
||||||
mode := strings.Join(modestring, " ")
|
|
||||||
irc.SendRawf("MODE %s %s", target, mode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
irc.SendRawf("MODE %s", target)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (irc *Connection) ErrorChan() chan error {
|
|
||||||
return irc.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the connection is connected to an IRC server.
|
|
||||||
func (irc *Connection) Connected() bool {
|
|
||||||
return !irc.stopped
|
|
||||||
}
|
|
||||||
|
|
||||||
// A disconnect sends all buffered messages (if possible),
|
|
||||||
// stops all goroutines and then closes the socket.
|
|
||||||
func (irc *Connection) Disconnect() {
|
|
||||||
for event := range irc.events {
|
|
||||||
irc.ClearCallback(event)
|
|
||||||
}
|
|
||||||
if irc.end != nil {
|
|
||||||
close(irc.end)
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.end = nil
|
|
||||||
|
|
||||||
if irc.pwrite != nil {
|
|
||||||
close(irc.pwrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.Wait()
|
|
||||||
if irc.socket != nil {
|
|
||||||
irc.socket.Close()
|
|
||||||
}
|
|
||||||
irc.socket = nil
|
|
||||||
irc.ErrorChan() <- ErrDisconnected
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconnect to a server using the current connection.
|
|
||||||
func (irc *Connection) Reconnect() error {
|
|
||||||
if irc.end != nil {
|
|
||||||
close(irc.end)
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.end = nil
|
|
||||||
|
|
||||||
irc.Wait() //make sure that wait group is cleared ensuring that all spawned goroutines have completed
|
|
||||||
|
|
||||||
irc.end = make(chan struct{})
|
|
||||||
return irc.Connect(irc.Server)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to a given server using the current connection configuration.
|
|
||||||
// This function also takes care of identification if a password is provided.
|
|
||||||
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1
|
|
||||||
func (irc *Connection) Connect(server string) error {
|
|
||||||
irc.Server = server
|
|
||||||
// mark Server as stopped since there can be an error during connect
|
|
||||||
irc.stopped = true
|
|
||||||
|
|
||||||
// make sure everything is ready for connection
|
|
||||||
if len(irc.Server) == 0 {
|
|
||||||
return errors.New("empty 'server'")
|
|
||||||
}
|
|
||||||
if strings.Count(irc.Server, ":") != 1 {
|
|
||||||
return errors.New("wrong number of ':' in address")
|
|
||||||
}
|
|
||||||
if strings.Index(irc.Server, ":") == 0 {
|
|
||||||
return errors.New("hostname is missing")
|
|
||||||
}
|
|
||||||
if strings.Index(irc.Server, ":") == len(irc.Server)-1 {
|
|
||||||
return errors.New("port missing")
|
|
||||||
}
|
|
||||||
// check for valid range
|
|
||||||
ports := strings.Split(irc.Server, ":")[1]
|
|
||||||
port, err := strconv.Atoi(ports)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("extracting port failed")
|
|
||||||
}
|
|
||||||
if !((port >= 0) && (port <= 65535)) {
|
|
||||||
return errors.New("port number outside valid range")
|
|
||||||
}
|
|
||||||
if irc.Log == nil {
|
|
||||||
return errors.New("'Log' points to nil")
|
|
||||||
}
|
|
||||||
if len(irc.nick) == 0 {
|
|
||||||
return errors.New("empty 'nick'")
|
|
||||||
}
|
|
||||||
if len(irc.user) == 0 {
|
|
||||||
return errors.New("empty 'user'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if irc.UseTLS {
|
|
||||||
dialer := &net.Dialer{Timeout: irc.Timeout}
|
|
||||||
irc.socket, err = tls.DialWithDialer(dialer, "tcp", irc.Server, irc.TLSConfig)
|
|
||||||
} else {
|
|
||||||
irc.socket, err = net.DialTimeout("tcp", irc.Server, irc.Timeout)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.stopped = false
|
|
||||||
irc.Log.Printf("Connected to %s (%s)\n", irc.Server, irc.socket.RemoteAddr())
|
|
||||||
|
|
||||||
irc.pwrite = make(chan string, 10)
|
|
||||||
irc.Error = make(chan error, 2)
|
|
||||||
irc.Add(3)
|
|
||||||
go irc.readLoop()
|
|
||||||
go irc.writeLoop()
|
|
||||||
go irc.pingLoop()
|
|
||||||
if len(irc.Password) > 0 {
|
|
||||||
irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password)
|
|
||||||
}
|
|
||||||
irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick)
|
|
||||||
irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a connection with the (publicly visible) nickname and username.
|
|
||||||
// The nickname is later used to address the user. Returns nil if nick
|
|
||||||
// or user are empty.
|
|
||||||
func IRC(nick, user string) *Connection {
|
|
||||||
// catch invalid values
|
|
||||||
if len(nick) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(user) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
irc := &Connection{
|
|
||||||
nick: nick,
|
|
||||||
nickcurrent: nick,
|
|
||||||
user: user,
|
|
||||||
Log: log.New(os.Stdout, "", log.LstdFlags),
|
|
||||||
end: make(chan struct{}),
|
|
||||||
Version: VERSION,
|
|
||||||
KeepAlive: 4 * time.Minute,
|
|
||||||
Timeout: 1 * time.Minute,
|
|
||||||
PingFreq: 15 * time.Minute,
|
|
||||||
}
|
|
||||||
irc.setupCallbacks()
|
|
||||||
return irc
|
|
||||||
}
|
|
|
@ -1,229 +0,0 @@
|
||||||
package irc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register a callback to a connection and event code. A callback is a function
|
|
||||||
// which takes only an Event pointer as parameter. Valid event codes are all
|
|
||||||
// IRC/CTCP commands and error/response codes. This function returns the ID of
|
|
||||||
// the registered callback for later management.
|
|
||||||
func (irc *Connection) AddCallback(eventcode string, callback func(*Event)) string {
|
|
||||||
eventcode = strings.ToUpper(eventcode)
|
|
||||||
|
|
||||||
if _, ok := irc.events[eventcode]; !ok {
|
|
||||||
irc.events[eventcode] = make(map[string]func(*Event))
|
|
||||||
}
|
|
||||||
h := sha1.New()
|
|
||||||
rawId := []byte(fmt.Sprintf("%v%d", reflect.ValueOf(callback).Pointer(), rand.Int63()))
|
|
||||||
h.Write(rawId)
|
|
||||||
id := fmt.Sprintf("%x", h.Sum(nil))
|
|
||||||
irc.events[eventcode][id] = callback
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove callback i (ID) from the given event code. This functions returns
|
|
||||||
// true upon success, false if any error occurs.
|
|
||||||
func (irc *Connection) RemoveCallback(eventcode string, i string) bool {
|
|
||||||
eventcode = strings.ToUpper(eventcode)
|
|
||||||
|
|
||||||
if event, ok := irc.events[eventcode]; ok {
|
|
||||||
if _, ok := event[i]; ok {
|
|
||||||
delete(irc.events[eventcode], i)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
irc.Log.Printf("Event found, but no callback found at id %s\n", i)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.Log.Println("Event not found")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all callbacks from a given event code. It returns true
|
|
||||||
// if given event code is found and cleared.
|
|
||||||
func (irc *Connection) ClearCallback(eventcode string) bool {
|
|
||||||
eventcode = strings.ToUpper(eventcode)
|
|
||||||
|
|
||||||
if _, ok := irc.events[eventcode]; ok {
|
|
||||||
irc.events[eventcode] = make(map[string]func(*Event))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
irc.Log.Println("Event not found")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace callback i (ID) associated with a given event code with a new callback function.
|
|
||||||
func (irc *Connection) ReplaceCallback(eventcode string, i string, callback func(*Event)) {
|
|
||||||
eventcode = strings.ToUpper(eventcode)
|
|
||||||
|
|
||||||
if event, ok := irc.events[eventcode]; ok {
|
|
||||||
if _, ok := event[i]; ok {
|
|
||||||
event[i] = callback
|
|
||||||
return
|
|
||||||
}
|
|
||||||
irc.Log.Printf("Event found, but no callback found at id %s\n", i)
|
|
||||||
}
|
|
||||||
irc.Log.Printf("Event not found. Use AddCallBack\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute all callbacks associated with a given event.
|
|
||||||
func (irc *Connection) RunCallbacks(event *Event) {
|
|
||||||
msg := event.Message()
|
|
||||||
if event.Code == "PRIVMSG" && len(msg) > 2 && msg[0] == '\x01' {
|
|
||||||
event.Code = "CTCP" //Unknown CTCP
|
|
||||||
|
|
||||||
if i := strings.LastIndex(msg, "\x01"); i > 0 {
|
|
||||||
msg = msg[1:i]
|
|
||||||
} else {
|
|
||||||
irc.Log.Printf("Invalid CTCP Message: %s\n", strconv.Quote(msg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg == "VERSION" {
|
|
||||||
event.Code = "CTCP_VERSION"
|
|
||||||
|
|
||||||
} else if msg == "TIME" {
|
|
||||||
event.Code = "CTCP_TIME"
|
|
||||||
|
|
||||||
} else if strings.HasPrefix(msg, "PING") {
|
|
||||||
event.Code = "CTCP_PING"
|
|
||||||
|
|
||||||
} else if msg == "USERINFO" {
|
|
||||||
event.Code = "CTCP_USERINFO"
|
|
||||||
|
|
||||||
} else if msg == "CLIENTINFO" {
|
|
||||||
event.Code = "CTCP_CLIENTINFO"
|
|
||||||
|
|
||||||
} else if strings.HasPrefix(msg, "ACTION") {
|
|
||||||
event.Code = "CTCP_ACTION"
|
|
||||||
if len(msg) > 6 {
|
|
||||||
msg = msg[7:]
|
|
||||||
} else {
|
|
||||||
msg = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event.Arguments[len(event.Arguments)-1] = msg
|
|
||||||
}
|
|
||||||
|
|
||||||
if callbacks, ok := irc.events[event.Code]; ok {
|
|
||||||
if irc.VerboseCallbackHandler {
|
|
||||||
irc.Log.Printf("%v (%v) >> %#v\n", event.Code, len(callbacks), event)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, callback := range callbacks {
|
|
||||||
go callback(event)
|
|
||||||
}
|
|
||||||
} else if irc.VerboseCallbackHandler {
|
|
||||||
irc.Log.Printf("%v (0) >> %#v\n", event.Code, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
if callbacks, ok := irc.events["*"]; ok {
|
|
||||||
if irc.VerboseCallbackHandler {
|
|
||||||
irc.Log.Printf("Wildcard %v (%v) >> %#v\n", event.Code, len(callbacks), event)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, callback := range callbacks {
|
|
||||||
go callback(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up some initial callbacks to handle the IRC/CTCP protocol.
|
|
||||||
func (irc *Connection) setupCallbacks() {
|
|
||||||
irc.events = make(map[string]map[string]func(*Event))
|
|
||||||
|
|
||||||
//Handle error events
|
|
||||||
irc.AddCallback("ERROR", func(e *Event) { irc.Disconnect() })
|
|
||||||
|
|
||||||
//Handle ping events
|
|
||||||
irc.AddCallback("PING", func(e *Event) { irc.SendRaw("PONG :" + e.Message()) })
|
|
||||||
|
|
||||||
//Version handler
|
|
||||||
irc.AddCallback("CTCP_VERSION", func(e *Event) {
|
|
||||||
irc.SendRawf("NOTICE %s :\x01VERSION %s\x01", e.Nick, irc.Version)
|
|
||||||
})
|
|
||||||
|
|
||||||
irc.AddCallback("CTCP_USERINFO", func(e *Event) {
|
|
||||||
irc.SendRawf("NOTICE %s :\x01USERINFO %s\x01", e.Nick, irc.user)
|
|
||||||
})
|
|
||||||
|
|
||||||
irc.AddCallback("CTCP_CLIENTINFO", func(e *Event) {
|
|
||||||
irc.SendRawf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", e.Nick)
|
|
||||||
})
|
|
||||||
|
|
||||||
irc.AddCallback("CTCP_TIME", func(e *Event) {
|
|
||||||
ltime := time.Now()
|
|
||||||
irc.SendRawf("NOTICE %s :\x01TIME %s\x01", e.Nick, ltime.String())
|
|
||||||
})
|
|
||||||
|
|
||||||
irc.AddCallback("CTCP_PING", func(e *Event) { irc.SendRawf("NOTICE %s :\x01%s\x01", e.Nick, e.Message()) })
|
|
||||||
|
|
||||||
// 437: ERR_UNAVAILRESOURCE "<nick/channel> :Nick/channel is temporarily unavailable"
|
|
||||||
// Add a _ to current nick. If irc.nickcurrent is empty this cannot
|
|
||||||
// work. It has to be set somewhere first in case the nick is already
|
|
||||||
// taken or unavailable from the beginning.
|
|
||||||
irc.AddCallback("437", func(e *Event) {
|
|
||||||
// If irc.nickcurrent hasn't been set yet, set to irc.nick
|
|
||||||
if irc.nickcurrent == "" {
|
|
||||||
irc.nickcurrent = irc.nick
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(irc.nickcurrent) > 8 {
|
|
||||||
irc.nickcurrent = "_" + irc.nickcurrent
|
|
||||||
} else {
|
|
||||||
irc.nickcurrent = irc.nickcurrent + "_"
|
|
||||||
}
|
|
||||||
irc.SendRawf("NICK %s", irc.nickcurrent)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 433: ERR_NICKNAMEINUSE "<nick> :Nickname is already in use"
|
|
||||||
// Add a _ to current nick.
|
|
||||||
irc.AddCallback("433", func(e *Event) {
|
|
||||||
// If irc.nickcurrent hasn't been set yet, set to irc.nick
|
|
||||||
if irc.nickcurrent == "" {
|
|
||||||
irc.nickcurrent = irc.nick
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(irc.nickcurrent) > 8 {
|
|
||||||
irc.nickcurrent = "_" + irc.nickcurrent
|
|
||||||
} else {
|
|
||||||
irc.nickcurrent = irc.nickcurrent + "_"
|
|
||||||
}
|
|
||||||
irc.SendRawf("NICK %s", irc.nickcurrent)
|
|
||||||
})
|
|
||||||
|
|
||||||
irc.AddCallback("PONG", func(e *Event) {
|
|
||||||
ns, _ := strconv.ParseInt(e.Message(), 10, 64)
|
|
||||||
delta := time.Duration(time.Now().UnixNano() - ns)
|
|
||||||
if irc.Debug {
|
|
||||||
irc.Log.Printf("Lag: %vs\n", delta)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// NICK Define a nickname.
|
|
||||||
// Set irc.nickcurrent to the new nick actually used in this connection.
|
|
||||||
irc.AddCallback("NICK", func(e *Event) {
|
|
||||||
if e.Nick == irc.nick {
|
|
||||||
irc.nickcurrent = e.Message()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 1: RPL_WELCOME "Welcome to the Internet Relay Network <nick>!<user>@<host>"
|
|
||||||
// Set irc.nickcurrent to the actually used nick in this connection.
|
|
||||||
irc.AddCallback("001", func(e *Event) {
|
|
||||||
irc.nickcurrent = e.Arguments[0]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
// Copyright 2009 Thomas Jager <mail@jager.no> All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package irc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Connection struct {
|
|
||||||
sync.WaitGroup
|
|
||||||
Debug bool
|
|
||||||
Error chan error
|
|
||||||
Password string
|
|
||||||
UseTLS bool
|
|
||||||
TLSConfig *tls.Config
|
|
||||||
Version string
|
|
||||||
Timeout time.Duration
|
|
||||||
PingFreq time.Duration
|
|
||||||
KeepAlive time.Duration
|
|
||||||
Server string
|
|
||||||
|
|
||||||
socket net.Conn
|
|
||||||
pwrite chan string
|
|
||||||
end chan struct{}
|
|
||||||
|
|
||||||
nick string //The nickname we want.
|
|
||||||
nickcurrent string //The nickname we currently have.
|
|
||||||
user string
|
|
||||||
registered bool
|
|
||||||
events map[string]map[string]func(*Event)
|
|
||||||
|
|
||||||
lastMessage time.Time
|
|
||||||
|
|
||||||
VerboseCallbackHandler bool
|
|
||||||
Log *log.Logger
|
|
||||||
|
|
||||||
stopped bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// A struct to represent an event.
|
|
||||||
type Event struct {
|
|
||||||
Code string
|
|
||||||
Raw string
|
|
||||||
Nick string //<nick>
|
|
||||||
Host string //<nick>!<usr>@<host>
|
|
||||||
Source string //<host>
|
|
||||||
User string //<usr>
|
|
||||||
Arguments []string
|
|
||||||
Connection *Connection
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the last message from Event arguments.
|
|
||||||
// This function leaves the arguments untouched and
|
|
||||||
// returns an empty string if there are none.
|
|
||||||
func (e *Event) Message() string {
|
|
||||||
if len(e.Arguments) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return e.Arguments[len(e.Arguments)-1]
|
|
||||||
}
|
|
|
@ -1,258 +0,0 @@
|
||||||
package irc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConnectionEmtpyServer(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
err := irccon.Connect("")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("emtpy server string not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionDoubleColon(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
err := irccon.Connect("::")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("wrong number of ':' not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionMissingHost(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
err := irccon.Connect(":6667")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("missing host not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionMissingPort(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
err := irccon.Connect("chat.freenode.net:")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("missing port not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionNegativePort(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
err := irccon.Connect("chat.freenode.net:-1")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("negative port number not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionTooLargePort(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
err := irccon.Connect("chat.freenode.net:65536")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("too large port number not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionMissingLog(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
irccon.Log = nil
|
|
||||||
err := irccon.Connect("chat.freenode.net:6667")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("missing 'Log' not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionEmptyUser(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
// user may be changed after creation
|
|
||||||
irccon.user = ""
|
|
||||||
err := irccon.Connect("chat.freenode.net:6667")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("empty 'user' not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionEmptyNick(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
// nick may be changed after creation
|
|
||||||
irccon.nick = ""
|
|
||||||
err := irccon.Connect("chat.freenode.net:6667")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("empty 'nick' not detected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveCallback(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
irccon.VerboseCallbackHandler = true
|
|
||||||
irccon.Debug = true
|
|
||||||
|
|
||||||
done := make(chan int, 10)
|
|
||||||
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
|
|
||||||
id := irccon.AddCallback("TEST", func(e *Event) { done <- 2 })
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 3 })
|
|
||||||
|
|
||||||
// Should remove callback at index 1
|
|
||||||
irccon.RemoveCallback("TEST", id)
|
|
||||||
|
|
||||||
irccon.RunCallbacks(&Event{
|
|
||||||
Code: "TEST",
|
|
||||||
})
|
|
||||||
|
|
||||||
var results []int
|
|
||||||
|
|
||||||
results = append(results, <-done)
|
|
||||||
results = append(results, <-done)
|
|
||||||
|
|
||||||
if len(results) != 2 || results[0] == 2 || results[1] == 2 {
|
|
||||||
t.Error("Callback 2 not removed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWildcardCallback(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
irccon.VerboseCallbackHandler = true
|
|
||||||
irccon.Debug = true
|
|
||||||
|
|
||||||
done := make(chan int, 10)
|
|
||||||
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
|
|
||||||
irccon.AddCallback("*", func(e *Event) { done <- 2 })
|
|
||||||
|
|
||||||
irccon.RunCallbacks(&Event{
|
|
||||||
Code: "TEST",
|
|
||||||
})
|
|
||||||
|
|
||||||
var results []int
|
|
||||||
|
|
||||||
results = append(results, <-done)
|
|
||||||
results = append(results, <-done)
|
|
||||||
|
|
||||||
if len(results) != 2 || !(results[0] == 1 && results[1] == 2) {
|
|
||||||
t.Error("Wildcard callback not called")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClearCallback(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
irccon.VerboseCallbackHandler = true
|
|
||||||
irccon.Debug = true
|
|
||||||
|
|
||||||
done := make(chan int, 10)
|
|
||||||
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 0 })
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
|
|
||||||
irccon.ClearCallback("TEST")
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 2 })
|
|
||||||
irccon.AddCallback("TEST", func(e *Event) { done <- 3 })
|
|
||||||
|
|
||||||
irccon.RunCallbacks(&Event{
|
|
||||||
Code: "TEST",
|
|
||||||
})
|
|
||||||
|
|
||||||
var results []int
|
|
||||||
|
|
||||||
results = append(results, <-done)
|
|
||||||
results = append(results, <-done)
|
|
||||||
|
|
||||||
if len(results) != 2 || !(results[0] == 2 && results[1] == 3) {
|
|
||||||
t.Error("Callbacks not cleared")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIRCemptyNick(t *testing.T) {
|
|
||||||
irccon := IRC("", "go-eventirc")
|
|
||||||
irccon = nil
|
|
||||||
if irccon != nil {
|
|
||||||
t.Error("empty nick didn't result in error")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIRCemptyUser(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "")
|
|
||||||
if irccon != nil {
|
|
||||||
t.Error("empty user didn't result in error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestConnection(t *testing.T) {
|
|
||||||
irccon1 := IRC("go-eventirc1", "go-eventirc1")
|
|
||||||
irccon1.VerboseCallbackHandler = true
|
|
||||||
irccon1.Debug = true
|
|
||||||
irccon2 := IRC("go-eventirc2", "go-eventirc2")
|
|
||||||
irccon2.VerboseCallbackHandler = true
|
|
||||||
irccon2.Debug = true
|
|
||||||
err := irccon1.Connect("irc.freenode.net:6667")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err.Error())
|
|
||||||
t.Fatal("Can't connect to freenode.")
|
|
||||||
}
|
|
||||||
err = irccon2.Connect("irc.freenode.net:6667")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err.Error())
|
|
||||||
t.Fatal("Can't connect to freenode.")
|
|
||||||
}
|
|
||||||
irccon1.AddCallback("001", func(e *Event) { irccon1.Join("#go-eventirc") })
|
|
||||||
irccon2.AddCallback("001", func(e *Event) { irccon2.Join("#go-eventirc") })
|
|
||||||
con2ok := false
|
|
||||||
irccon1.AddCallback("366", func(e *Event) {
|
|
||||||
t := time.NewTicker(1 * time.Second)
|
|
||||||
i := 10
|
|
||||||
for {
|
|
||||||
<-t.C
|
|
||||||
irccon1.Privmsgf("#go-eventirc", "Test Message%d\n", i)
|
|
||||||
if con2ok {
|
|
||||||
i -= 1
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
t.Stop()
|
|
||||||
irccon1.Quit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
irccon2.AddCallback("366", func(e *Event) {
|
|
||||||
irccon2.Privmsg("#go-eventirc", "Test Message\n")
|
|
||||||
con2ok = true
|
|
||||||
irccon2.Nick("go-eventnewnick")
|
|
||||||
})
|
|
||||||
|
|
||||||
irccon2.AddCallback("PRIVMSG", func(e *Event) {
|
|
||||||
t.Log(e.Message())
|
|
||||||
if e.Message() == "Test Message5" {
|
|
||||||
irccon2.Quit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
irccon2.AddCallback("NICK", func(e *Event) {
|
|
||||||
if irccon2.nickcurrent == "go-eventnewnick" {
|
|
||||||
t.Fatal("Nick change did not work!")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
go irccon2.Loop()
|
|
||||||
irccon1.Loop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectionSSL(t *testing.T) {
|
|
||||||
irccon := IRC("go-eventirc", "go-eventirc")
|
|
||||||
irccon.VerboseCallbackHandler = true
|
|
||||||
irccon.Debug = true
|
|
||||||
irccon.UseTLS = true
|
|
||||||
irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
err := irccon.Connect("irc.freenode.net:7000")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err.Error())
|
|
||||||
t.Fatal("Can't connect to freenode.")
|
|
||||||
}
|
|
||||||
irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") })
|
|
||||||
|
|
||||||
irccon.AddCallback("366", func(e *Event) {
|
|
||||||
irccon.Privmsg("#go-eventirc", "Test Message\n")
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
irccon.Quit()
|
|
||||||
})
|
|
||||||
|
|
||||||
irccon.Loop()
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package irc
|
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
|
||||||
b := bytes.NewBuffer(data)
|
|
||||||
event, err := parseToEvent(b.String())
|
|
||||||
if err == nil {
|
|
||||||
irc := IRC("go-eventirc", "go-eventirc")
|
|
||||||
irc.RunCallbacks(event)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
Loading…
Reference in New Issue