diff --git a/boddle.go b/boddle.go index 899e030..11c733a 100644 --- a/boddle.go +++ b/boddle.go @@ -1,19 +1,21 @@ package main import ( - "database/sql" - "fmt" - _ "github.com/mattn/go-sqlite3" - "math/rand" - "net" - "regexp" "strings" + "fmt" + "regexp" + "database/sql" + "math/rand" + _ "github.com/mattn/go-sqlite3" ) var begin_char = []byte{'-'} +var db *sql.DB +var err error + // function to split advise message correctly -func filter(msg string, conn net.Conn, db *sql.DB, nickname string) cmd { +func filter(msg string, foo bot) cmd { var c cmd if len(msg) <= 0 { @@ -32,7 +34,7 @@ func filter(msg string, conn net.Conn, db *sql.DB, nickname string) cmd { c.add = false } - if strings.Contains(msg, "[") && strings.Contains(msg, "]") { + if (strings.Contains(msg, "[") && strings.Contains(msg, "]")) { re, _ := regexp.Compile(`\s*(\w*)\s*\[(.+)\]\s*(.*)\s*`) res := re.FindStringSubmatch(msg) if len(res) < 4 { @@ -64,7 +66,7 @@ func filter(msg string, conn net.Conn, db *sql.DB, nickname string) cmd { c.groups = append(c.groups, fmt.Sprintf("%d", group_id)) } else { if c.add { - c.groups = append(c.groups, addgroups(db, []string{group}, nil)...) + c.groups = append(c.groups, addgroups([]string{group}, nil)...) } } rows.Close() @@ -99,16 +101,16 @@ func filter(msg string, conn net.Conn, db *sql.DB, nickname string) cmd { func getRandomEntry(c cmd) string { // TODO: increment counter // if database entry exists: set tag / group and run query - task := "select distinct l.content from line l, brain b, tag t" + task := "select distinct l.content from line l, brain b, tag t" task += " where l.id = b.line_id and t.id = b.tag_id and t.id = " + c.cmd if len(c.groups) > 0 { - task += " and l.id in (select line_id from brain where group_id = " + strings.Join(c.groups, " intersect select line_id from brain where group_id = ") + ")" + task += " and l.id in (select line_id from brain where group_id = " + strings.Join(c.groups," intersect select line_id from brain where group_id = ") + ")" } task += " ORDER BY RANDOM() LIMIT 1" return task } -func addgroups(db *sql.DB, new_groups []string, msg *irc_msg) []string { +func addgroups(new_groups []string, msg *irc_msg) []string { stat_sel, err := db.Prepare("select id from groups where name = ?") checkErr(err) stat_ins, err := db.Prepare("insert into groups(name) values (?)") @@ -139,7 +141,7 @@ func addgroups(db *sql.DB, new_groups []string, msg *irc_msg) []string { return groupids_added } -func checkSorter(db *sql.DB, msg *irc_msg, c *cmd) bool { +func checkSorter(msg *irc_msg, c *cmd) bool { sorter_row_sel, err := db.Prepare("select id, tag_id, groups_allow, name_allow, has_groups from sorter where name = ?") checkErr(err) sorter_row, err := sorter_row_sel.Query((*c).cmd) @@ -168,7 +170,7 @@ func checkSorter(db *sql.DB, msg *irc_msg, c *cmd) bool { group_sel, err := db.Prepare("select group_id from sorter_groups where sorter_id = ?") checkErr(err) sorter_groups_row, err := group_sel.Query(sorter_id) - //sorter_groups_row, err := foo.db.Query(fmt.Sprintf("select group_id from sorter_groups where sorter_id = '%s'", sorter_id)) + //sorter_groups_row, err := db.Query(fmt.Sprintf("select group_id from sorter_groups where sorter_id = '%s'", sorter_id)) checkErr(err) if sorter_groups_row.Next() { var group_id int @@ -197,7 +199,7 @@ func checkSorter(db *sql.DB, msg *irc_msg, c *cmd) bool { return true } -func chooseEntry(db *sql.DB, msg *irc_msg, c *cmd) { +func chooseEntry(msg *irc_msg, c *cmd) { task := getRandomEntry(*c) LOG_INFO.Printf(task) @@ -216,7 +218,7 @@ func chooseEntry(db *sql.DB, msg *irc_msg, c *cmd) { } } -func addLine(db *sql.DB, msg *irc_msg, line string, groups []string, cmd string) string { +func addLine(msg *irc_msg, line string, groups []string, cmd string) string { LOG_WARN.Printf("adding new stuff: %s from %s.\n", line, (*msg).author) res, err := db.Exec("insert into line (content, author) values (?,?)", line, (*msg).author) @@ -224,7 +226,7 @@ func addLine(db *sql.DB, msg *irc_msg, line string, groups []string, cmd string) LOG_WARN.Printf("added line to table.\n") // get ID of content... - line_id, err := res.LastInsertId() + line_id, err := res.LastInsertId(); checkErr(err) LOG_WARN.Printf("line_id: %d, tag_id: %s, groups: %s\n", line_id, cmd, strings.Join(groups, "-")) @@ -245,13 +247,12 @@ func addLine(db *sql.DB, msg *irc_msg, line string, groups []string, cmd string) defer stat.Close() } (*msg).retmsg = fmt.Sprintf("success adding new super funny enjoyable line with ID %d!", line_id) - return fmt.Sprintf("new line: > %s < (ID %d), into groups %s", line, line_id, strings.Join(groups, ",")) + return fmt.Sprintf("new line: > %s < (ID %d), into groups %s", line, line_id, strings.Join(groups,",")) } - // line: // >befehl <[groups]> // befehl ist genau ein wort -func parsemsg(msg *irc_msg, conn net.Conn, db *sql.DB, nickname string) bool { +func parsemsg(msg *irc_msg, foo bot) bool { if len((*msg).msg) <= 0 { return false } @@ -264,7 +265,7 @@ func parsemsg(msg *irc_msg, conn net.Conn, db *sql.DB, nickname string) bool { } (*msg).msg = (*msg).msg[1:] - c := filter((*msg).msg, conn, db, nickname) + c := filter((*msg).msg, foo) if !c.valid { LOG_WARN.Printf("non valid input found.\n") if c.error != "" { @@ -283,7 +284,7 @@ func parsemsg(msg *irc_msg, conn net.Conn, db *sql.DB, nickname string) bool { } if c.cmd == "slap" { who := []string{(*msg).author, "\x01ACTION"} - whom := []string{(*msg).author, nickname} + whom := []string{(*msg).author, foo.Conf.Name} LOG_WARN.Printf("suffix: " + c.suffix) if c.suffix != "" { who = append(who, strings.Split(c.suffix, " ")...) @@ -294,28 +295,28 @@ func parsemsg(msg *irc_msg, conn net.Conn, db *sql.DB, nickname string) bool { } // hard code 'groups' to add new groups to the groups-table if c.add && (c.cmd == "groups" || c.cmd == "group") { - if c.groups != nil { + if (c.groups != nil) { (*msg).retmsg = "Ignoring groups for the groups-adding. Well, what were you expecting...?" } - addgroups(db, strings.Fields((&c).suffix), msg) + addgroups(strings.Fields((&c).suffix), msg) return false } // have a look at sorter table if command is valid - if !checkSorter(db, msg, &c) { + if !checkSorter(msg, &c) { return false } // new line for database if c.add { // if there is nothing to add, just return. :-) - if len(c.suffix) <= 0 { - return false + if (len(c.suffix) <= 0) { + return false; } - c.suffix = strings.Replace(c.suffix, "\"", "'", -1) - sendmsg(conn, "horscchtey", addLine(db, msg, c.suffix, c.groups, c.cmd)) + c.suffix = strings.Replace(c.suffix,"\"","'", -1) + sendmsg(foo.conn, "horscchtey", addLine(msg, c.suffix, c.groups, c.cmd)) } else { - chooseEntry(db, msg, &c) + chooseEntry(msg, &c) } return true } diff --git a/bot.go b/bot.go index 57047c2..c2598df 100644 --- a/bot.go +++ b/bot.go @@ -1,23 +1,24 @@ package main import ( - "bufio" - "database/sql" - "flag" + "log" "fmt" - _ "github.com/mattn/go-sqlite3" "net" - "strings" "time" + "bufio" + "strings" + "database/sql" + configo "github.com/distributedio/configo" + _ "github.com/mattn/go-sqlite3" ) -func sendmsg(conn net.Conn, channel string, msg string) { +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) { +func scanline_privmsg (msg string, irc *irc_msg) { LOG_ERR.Printf(msg) msg = msg[1:] @@ -29,36 +30,38 @@ func scanline_privmsg(msg string, irc *irc_msg) { (*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, "#") { + if ! strings.HasPrefix((*irc).channel, "#") { (*irc).channel = (*irc).author } (*irc).retmsg = "" } -func bot_connect(server string) net.Conn { - conn, err := net.Dial("tcp", server) +func bot_connect(bot *bot) { + conn, err := net.Dial("tcp", (*bot).Conf.Server) if err != nil { - LOG_ERR.Printf("Error while dialing server: %s.", server) - return nil + LOG_ERR.Printf("Error while dialing server.") + (*bot).conn = nil + return } - return conn + + (*bot).conn = conn + fmt.Fprintf((*bot).conn, "USER %s 1 1 1:%s\r\n", (*bot).Conf.Name, (*bot).Conf.Name) } -func bot_register(conn net.Conn, nickname string, channels []string) { +func bot_register(bot *bot) { /* 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) + fmt.Fprintf((*bot).conn, "NICK %s\r\n", (*bot).Conf.Name) + for _, channel := range((*bot).Conf.Channels) { + fmt.Fprintf((*bot).conn, "JOIN %s\r\n", channel) } } -func bot_listen(conn net.Conn, db *sql.DB, nickname string, channels []string) <-chan string { +func bot_listen(bot *bot) <-chan string { c := make(chan string) - bot_register(conn, nickname, channels) + bot_register(bot) go func(timeoutNormal time.Duration, timeoutVersion time.Duration) { - bufReader := bufio.NewReader(conn) + bufReader := bufio.NewReader((*bot).conn) timeoutDuration := timeoutNormal waitversion := false @@ -66,11 +69,11 @@ func bot_listen(conn net.Conn, db *sql.DB, nickname string, channels []string) < defer func() { fmt.Println("Closing connection...") close(c) - conn.Close() + (*bot).conn.Close() }() for { - conn.SetReadDeadline(time.Now().Add(timeoutDuration)) + (*bot).conn.SetReadDeadline(time.Now().Add(timeoutDuration)) line, err := bufReader.ReadString('\n') if err != nil { @@ -81,7 +84,7 @@ func bot_listen(conn net.Conn, db *sql.DB, nickname string, channels []string) < return } - n, err := conn.Write([]byte("VERSION\n")) + n, err := (*bot).conn.Write([]byte("VERSION\n")) fmt.Printf("n: %d, err: %s\n", n, err) if err != nil { fmt.Println("Writing to channel failed.") @@ -103,93 +106,56 @@ func bot_listen(conn net.Conn, db *sql.DB, nickname string, channels []string) < c <- line if strings.Contains(line, "PING") { - conn.Write([]byte(strings.Replace(line, "PING", "PONG", 1))) + (*bot).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) - + bot_register(bot) } else if strings.Contains(line, "PRIVMSG") { var msg irc_msg scanline_privmsg(line, &msg) LOG_ERR.Printf(msg.author) - parsemsg(&msg, conn, db, nickname) + parsemsg(&msg, (*bot)) if msg.retmsg != "" { - sendmsg(conn, msg.channel, msg.retmsg) + sendmsg((*bot).conn, msg.channel, msg.retmsg) } } } - }(4*time.Minute, 1*time.Minute) + } (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) { +func bot_run(bot *bot) { LOG_INFO.Println("Bot ready to run.") - db := db_open(database) - if db == nil { + LOG_INFO.Println("Opening database.") + db, err = sql.Open("sqlite3", (*bot).Conf.Database) + if err != nil { LOG_ERR.Println("Opening database failed.") return } for { - conn := bot_connect(server) - if conn == nil { + bot_connect(bot) + if bot.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) { + for val := range bot_listen(bot) { 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 + var boddle bot + if err := configo.Load("./boddle.toml", &boddle.Conf); err != nil { + log.Fatal(err) + } - 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) + bot_run(&boddle) } diff --git a/conf.go b/conf.go new file mode 100644 index 0000000..ccccef9 --- /dev/null +++ b/conf.go @@ -0,0 +1,8 @@ +package main + +type Boddle struct { + name string `cfg:"name; boddle; printableascii; IRC nick of bot"` + channels []string `cfg:"channels; required; printableascii; Channel list to join to"` + server string `cfg:"server; required; netaddr; Server name to connect to"` + database string `cfg:"database; ./boddle.db; path; Path to database"` +} diff --git a/helpers.go b/helpers.go index ca0d985..f59c740 100644 --- a/helpers.go +++ b/helpers.go @@ -11,7 +11,7 @@ func checkErr(err error) { } } -func in(a byte, arr []byte) bool { +func in (a byte, arr []byte) bool { for _, x := range arr { if x == a { return true diff --git a/logging.go b/logging.go index 960763a..6c03744 100644 --- a/logging.go +++ b/logging.go @@ -6,9 +6,9 @@ import ( ) var ( - LOG_WARN *log.Logger - LOG_INFO *log.Logger - LOG_ERR *log.Logger + LOG_WARN *log.Logger + LOG_INFO *log.Logger + LOG_ERR *log.Logger ) func LOG_init() { @@ -16,5 +16,5 @@ func LOG_init() { LOG_INFO = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) LOG_WARN = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) - LOG_ERR = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + LOG_ERR = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) } diff --git a/types.go b/types.go index 3830a6e..b2968c4 100644 --- a/types.go +++ b/types.go @@ -1,17 +1,34 @@ package main +import ( + "net" +) + +type Config struct { + Name string `cfg:"name; boddle; printableascii; IRC nick of bot"` + Channels []string `cfg:"channels; required; printableascii; Channel list to join to"` + Server string `cfg:"server; required; netaddr; Server name to connect to"` + Database string `cfg:"database; ./boddle.db; path; Path to database"` +} + type irc_msg struct { - channel string - msg string - author string - retmsg string + channel string + msg string + author string + retmsg string } type cmd struct { - valid bool - add bool - cmd string - groups []string - suffix string - error string + valid bool + add bool + cmd string + groups []string + suffix string + error string } + +type bot struct { + Conf Config + conn net.Conn +} +