boddle/bot.go
2025-01-25 00:23:42 +01:00

195 lines
4.5 KiB
Go

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)
}