package main import ( "bufio" "database/sql" "flag" "fmt" _ "github.com/mattn/go-sqlite3" "net" "strings" "time" ) func sendmsg(conn net.Conn, channel string, msg string) { mesg := fmt.Sprintf("PRIVMSG %s :%s\r\n", channel, msg) fmt.Printf(mesg) conn.Write([]byte(mesg)) } func scanline_privmsg(msg string, irc *irc_msg) { LOG_ERR.Printf(msg) msg = msg[1:] t := strings.Split(msg, "!") (*irc).author, (*irc).channel = t[0], t[1] t = strings.Split((*irc).channel, "PRIVMSG ") (*irc).channel = t[1] t = strings.Split((*irc).channel, " :") (*irc).channel, (*irc).msg = t[0], t[1] t = strings.Split((*irc).msg, "\r\n") (*irc).msg = strings.TrimSpace(t[0]) if !strings.HasPrefix((*irc).channel, "#") { (*irc).channel = (*irc).author } (*irc).retmsg = "" } func bot_connect(server string) net.Conn { conn, err := net.Dial("tcp", server) if err != nil { LOG_ERR.Printf("Error while dialing server: %s.", server) return nil } return conn } func bot_register(conn net.Conn, nickname string, channels []string) { /* connect to irc */ fmt.Fprintf(conn, "USER %s 1 1 1:%s\r\n", nickname, nickname) fmt.Fprintf(conn, "NICK %s\r\n", nickname) for _, channel := range channels { fmt.Fprintf(conn, "JOIN %s\r\n", channel) } } func bot_listen(conn net.Conn, db *sql.DB, nickname string, channels []string) <-chan string { c := make(chan string) bot_register(conn, nickname, channels) go func(timeoutNormal time.Duration, timeoutVersion time.Duration) { bufReader := bufio.NewReader(conn) timeoutDuration := timeoutNormal waitversion := false defer func() { fmt.Println("Closing connection...") close(c) conn.Close() }() for { conn.SetReadDeadline(time.Now().Add(timeoutDuration)) line, err := bufReader.ReadString('\n') if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { fmt.Printf("Timeout!\n") if waitversion { fmt.Println("VERSION command did not return.") return } n, err := conn.Write([]byte("VERSION\n")) fmt.Printf("n: %d, err: %s\n", n, err) if err != nil { fmt.Println("Writing to channel failed.") return } // wait for return value of VERSION waitversion = true timeoutDuration = timeoutVersion } else { fmt.Println("Some more serious error occured while reading.") return } continue } // not waiting for version answer right now timeoutDuration = timeoutNormal waitversion = false c <- line if strings.Contains(line, "PING") { conn.Write([]byte(strings.Replace(line, "PING", "PONG", 1))) } else if strings.Contains(line, "You have not registered") { // set nickname time.Sleep(10 * time.Second) bot_register(conn, nickname, channels) } else if strings.Contains(line, "PRIVMSG") { var msg irc_msg scanline_privmsg(line, &msg) LOG_ERR.Printf(msg.author) parsemsg(&msg, conn, db, nickname) if msg.retmsg != "" { sendmsg(conn, msg.channel, msg.retmsg) } } } }(4*time.Minute, 1*time.Minute) return c } func db_open(info string) *sql.DB { LOG_INFO.Printf("Opening database: %s", info) var db *sql.DB = nil var err error parts := strings.SplitN(info, ":", 2) if len(parts) == 2 { db, err = sql.Open(parts[0], parts[1]) } else { db, err = sql.Open("sqlite3", info) } if err != nil { return nil } return db } func bot_run(server string, nickname string, database string, channels []string) { LOG_INFO.Println("Bot ready to run.") db := db_open(database) if db == nil { LOG_ERR.Println("Opening database failed.") return } for { conn := bot_connect(server) if conn == nil { LOG_INFO.Printf("Bot is nil. Try to connect again.") time.Sleep(10 * time.Second) continue } for val := range bot_listen(conn, db, nickname, channels) { LOG_INFO.Printf("%s", val) } } } type stringList []string func (i *stringList) String() string { return fmt.Sprintf("%v", *i) } func (i *stringList) Set(value string) error { *i = append(*i, value) return nil } func main() { LOG_init() var channels stringList flag.Var(&channels, "channel", "channels to connect") server := flag.String("server", "irc.ircnet.com:6667", "domain:port for irc-server") nickname := flag.String("nick", "boddle", "nickname to use for bot") database := flag.String("database", "./boddle.db", "database to use") flag.Parse() LOG_INFO.Printf("server: %s", *server) LOG_INFO.Printf("nick: %s", *nickname) bot_run(*server, *nickname, *database, channels) }