commit d51641ee3654d5576daa8c107af5d90f4056d035 Author: Horscchtey Date: Thu Jan 21 00:42:01 2021 +0000 add new version of bot to raspi \o/ diff --git a/boddle.db b/boddle.db new file mode 100644 index 0000000..25ec74e Binary files /dev/null and b/boddle.db differ diff --git a/boddle.go b/boddle.go new file mode 100644 index 0000000..9dc42ad --- /dev/null +++ b/boddle.go @@ -0,0 +1,315 @@ +package main + +import "strings" +import "fmt" +import "regexp" +import "database/sql" +import _ "github.com/mattn/go-sqlite3" + +type irc_msg struct { + channel string + msg string + author string +} + +type cmd struct { + valid bool + add bool + cmd string + groups []int + suffix string +} + +var begin_char = []byte{'<'} +var db *sql.DB +var err error + +func checkErr(err error) { + if err != nil { + panic(err) + } +} + +func in (a byte, arr []byte) bool { + for _, x := range arr { + if x == a { + return true + } + } + return false +} + +func unique(intSlice []int) []int { + keys := make(map[int]bool) + list := []int{} + for _, entry := range intSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +// function to split advise message correctly +func filter(msg string) cmd { + var c cmd + + if len(msg) <= 0 { + c.valid = false + return c + } + + c.cmd = "" + c.valid = false + + if strings.HasPrefix(msg, "add ") { + c.add = true + msg = msg[4:] + } else { + c.add = false + } + + if (strings.Contains(msg, "[") && strings.Contains(msg, "]")) { + re, _ := regexp.Compile(`\s*(\w*)\s*\[(.*)\]\s*(.*)\s*`) + res := re.FindStringSubmatch(msg) + if len(res) < 4 { + return c + } + + c.cmd = res[1] + if c.cmd == "" { + LOG_WARN.Printf("empty cmd\n") + return c + } + + // TODO: substitute group names with ids + groups := strings.Fields(res[2]) + for _, i := range groups { + task := fmt.Sprintf("select id from groups where name = '%s'", i) + + row, err := db.Query(task) + if err != nil { + LOG_WARN.Printf("no such column: %s\n", i) + return c + } + group_id := 0 + if row.Next() { row.Scan(&group_id) } + row.Close() + if (group_id != 0) { + c.groups = append(c.groups, group_id) + } + } + c.suffix = res[3] + c.valid = true + } else { + c.groups = nil + re, _ := regexp.Compile(`\s*(\w*)\s*(.*)\s*`) + res := re.FindStringSubmatch(msg) + if len(res) < 3 { + return c + } + c.cmd = res[1] + if c.cmd == "" { + return c + } + + c.suffix = res[2] + c.valid = true + } + c.cmd = strings.TrimSpace(c.cmd) + c.suffix = strings.TrimSpace(c.suffix) + + return c +} + +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" + if len(c.groups) > 0 { + task = fmt.Sprintf("%s, groups g", task) + } + task = fmt.Sprintf("%s where l.id = b.line_id and t.id = b.tag_id", task) + task = fmt.Sprintf("%s and t.id = %s", task, c.cmd) + if len(c.groups) > 0 { + task = fmt.Sprintf("%s and g.id = b.group_id and (", task) + for i, group := range c.groups { + task = fmt.Sprintf("%sg.id = %d", task, group) + if i < (len(c.groups) - 1) { + task = fmt.Sprintf("%s or ", task) + } else { + task = fmt.Sprintf("%s)", task) + } + } + } + return fmt.Sprintf("%s ORDER BY RANDOM() LIMIT 1", task) +} + +// line: +// >befehl <[groups]> +// befehl ist genau ein wort +func parsemsg(msg irc_msg, foo bot) bool { + if len(msg.msg) <= 0 { + return false + } + if !in(msg.msg[0], begin_char) { + return false + } + msg.msg = msg.msg[1:] + + c := filter(msg.msg) + if !c.valid { + LOG_WARN.Printf("non valid input found.\n") + return false + } + if c.cmd == "" { + LOG_WARN.Printf("no command provided.\n") + return false + } + + sorter_row, err := db.Query(fmt.Sprintf("select id, tag_id, groups_allow, name_allow, has_groups from sorter where name = '%s'", c.cmd)) + checkErr(err) + if sorter_row.Next() { + var sorter_id string + var tag_id string + var groups_allow int + var name_allow int + var has_groups int + sorter_row.Scan(&sorter_id, &tag_id, &groups_allow, &name_allow, &has_groups) + + if groups_allow != 0 { + // warning: not allowed + c.groups = nil // use c.groups[:0] to keep allocated space + } + if name_allow != 0 && !c.add { + // warning: not allowed + c.suffix = "" + } + + c.cmd = tag_id + + if has_groups == 0 { + 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 + sorter_groups_row.Scan(&group_id) + c.groups = append(c.groups, group_id) + } + sorter_groups_row.Close() + } + c.groups = unique(c.groups) + sorter_row.Close() + } else { + LOG_WARN.Printf("no sorter entry, no translation possible\n") + return false + } + + if c.add { + if (len(c.suffix) <= 0) { + return false; + } + // insert into database + statement, err := db.Prepare("insert into line (content, author) values (?,?)") + checkErr(err) + _, err = statement.Exec(c.suffix, msg.author) + checkErr(err) + defer statement.Close() + + // get ID of content... + task := fmt.Sprintf("select l.id from line l where l.content = '%s'", c.suffix) + row, err := db.Query(task) + checkErr(err) + line_id := 0 + if row.Next() { row.Scan(&line_id) } + row.Close() + LOG_INFO.Printf(c.cmd) + if line_id == 0 { + LOG_WARN.Printf("no entry for adding") + return false + } + + // for tag and all groups + if len(c.groups) == 0 { + stat, err := db.Prepare("insert into brain(line_id, tag_id) values (?,?)") + checkErr(err) + _, err = stat.Exec(line_id, c.cmd) + checkErr(err) + LOG_INFO.Printf("inserted foo! \n") + defer stat.Close() + } else { + stat, err := db.Prepare("insert into brain(line_id, tag_id, group_id) values (?,?,?)") + checkErr(err) + for _, group := range c.groups { + _, err = stat.Exec(line_id, c.cmd, group) + checkErr(err) + } + LOG_INFO.Printf("inserted foo! \n") + defer stat.Close() + } + return true + } + + task := getRandomEntry(c) + + LOG_INFO.Printf(task) + row, err := db.Query(task) + checkErr(err) + res := "" + if row.Next() { + row.Scan(&res) + } + row.Close() + + if len(res) <= 0 { + LOG_WARN.Printf("no entry found\n") + res = "No matching entry found :(" + } else if len(c.suffix) > 0 { + res = fmt.Sprintf("%s... %s", c.suffix, res) + } + sendmsg(foo.conn, msg.channel, res) + + return true +} + +/* +func test() { + db, err = sql.Open("sqlite3", "./boddle.db") + checkErr(err) + var msg irc_msg + msg.channel = "testchannel" + msg.author = "max mustermann" + + // == invalid commands == + // empty cmd + msg.msg = ">add [test me bro hahahaha] blob" + parsemsg(msg) + msg.msg = "> [test me bro hahahaha]" + parsemsg(msg) + msg.msg = ">[test me bro hahahaha] meow " + parsemsg(msg) + msg.msg = ">add [test me bro hahahaha] meow " + parsemsg(msg) + + // non-existent groups + msg.msg = ">ohai [test me bro hahahaha]" + parsemsg(msg) + + // add command...? not implemented yet! + msg.msg = ">add jokes blob" + parsemsg(msg) + msg.msg = ">add test [test me bro hahahaha] meow " + parsemsg(msg) + + // ok command \o/ + msg.msg = ">flirt [sweet] meow " + parsemsg(msg) + msg.msg = ">jokes [flach] meow " + parsemsg(msg) + msg.msg = ">hate [sweet] blobfisch " + parsemsg(msg) + msg.msg = ">radschlag [science]" + parsemsg(msg) +} +*/ diff --git a/bot.go b/bot.go new file mode 100644 index 0000000..2559d02 --- /dev/null +++ b/bot.go @@ -0,0 +1,53 @@ +package main + +import ( + "os" + "fmt" + "net" + "time" + _ "github.com/mattn/go-sqlite3" +) + +var dead = true + +type bot struct { + name string + channel []string + port int + server string + conn net.Conn +} + +func main() { + LOG_init() + + boddle := bot { + name: "boddle", + channel: []string{"#faui2k16", "#fau", "#sigbike", "#sigfreibad", "#faui2k17"}, + port: 6667, + server: "irc.fau.de", + } + + LOG_INFO.Println("bot started!") + + var botchan <-chan []byte + var line []byte + dead = true + + for { + if dead { + LOG_WARN.Println("died.") + for { + botchan = connect(boddle) + if botchan != nil { + break + } + time.Sleep(10 * time.Second) + } + LOG_INFO.Println("reconnected.") + dead = false + } + line = <-botchan + fmt.Fprintf(os.Stderr, string(line)) + } +} diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..d623236 --- /dev/null +++ b/bot.py @@ -0,0 +1,85 @@ +from telegram.ext import Updater +from telegram.ext import CommandHandler +from telegram.ext import MessageHandler +from telegram.ext import Filters +from telegram import InlineQueryResultArticle, InputTextMessageContent +import random +import logging + +import sqlite3 as sql + +# updater +updater = Updater(token='935673062:AAH4By1EMqAUaD9wgnV3lZQRRBX6e5Lve6g', use_context=True) + +# sqlite database +connection = sql.connect("/home/horscchtey/bots/boddle/src/v2/boddle.db", check_same_thread=False) +cursor = connection.cursor() + +dispatcher = updater.dispatcher + +# enable logging +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.INFO) + +########### routinen ########## +def start(update, context): + context.bot.send_message(chat_id=update.effective_chat.id, text="I am your new source of bad jokes") + +def kaffee(update, context): + cursor.execute("select distinct l.content from line l, brain b, tag t where l.id = b.line_id and t.id = b.tag_id and t.id = 8 ORDER BY RANDOM() LIMIT 1") + selection = [] + result = cursor.fetchall() + for r in result: + selection.append(r[0]) + context.bot.send_message(chat_id=update.effective_chat.id, text=str(selection[0])) + +def radschlag(update, context): + cursor.execute("select distinct l.content from line l, brain b, tag t where l.id = b.line_id and t.id = b.tag_id and t.id = 3 ORDER BY RANDOM() LIMIT 1") + selection = [] + result = cursor.fetchall() + for r in result: + selection.append(r[0]) + context.bot.send_message(chat_id=update.effective_chat.id, text=str(selection[0])) + +def flirt(update, context): + cursor.execute("select distinct l.content from line l, brain b, tag t where l.id = b.line_id and t.id = b.tag_id and t.id = 1 ORDER BY RANDOM() LIMIT 1") + selection = [] + result = cursor.fetchall() + for r in result: + selection.append(r[0]) + context.bot.send_message(chat_id=update.effective_chat.id, text=str(selection[0])) + +def jokes(update, context): + cursor.execute("select distinct l.content from line l, brain b, tag t where l.id = b.line_id and t.id = b.tag_id and t.id = 4 ORDER BY RANDOM() LIMIT 1") + selection = [] + result = cursor.fetchall() + for r in result: + selection.append(r[0]) + context.bot.send_message(chat_id=update.effective_chat.id, text=str(selection[0])) + +def unknown(update, context): + context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.") + +#inline + +start_handler = CommandHandler('start', start) +dispatcher.add_handler(start_handler) + +flirt_handler = CommandHandler('flirt', flirt) +dispatcher.add_handler(flirt_handler) + +radschlag_handler = CommandHandler('radschlag', radschlag) +dispatcher.add_handler(radschlag_handler) + +kaffee_handler = CommandHandler('kaffee', kaffee) +dispatcher.add_handler(kaffee_handler) +coffee_handler = CommandHandler('coffee', kaffee) +dispatcher.add_handler(coffee_handler) + +jokes_handler = CommandHandler('jokes', jokes) +dispatcher.add_handler(jokes_handler) + +unknown_handler = MessageHandler(Filters.command, unknown) +dispatcher.add_handler(unknown_handler) + +updater.start_polling() diff --git a/forraspidothis b/forraspidothis new file mode 100755 index 0000000..3c8aefb --- /dev/null +++ b/forraspidothis @@ -0,0 +1,9 @@ +#!/bin/bash + +export GOPATH=/home/horscchtey/go + +env CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ \ + CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 \ + go build bot.go boddle.go logging.go ircfoo.go + +mv bot boddle_executable diff --git a/ircfoo.go b/ircfoo.go new file mode 100644 index 0000000..6af8c30 --- /dev/null +++ b/ircfoo.go @@ -0,0 +1,98 @@ +package main + +import ( + "fmt" + "net" + "os" + "bufio" + "bytes" + "strings" + "database/sql" + _ "github.com/mattn/go-sqlite3" +) + +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_msg { + var irc irc_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 + } + return irc +} + +func listenToIRC (foo bot) <-chan []byte { + c := make(chan []byte) + reader := bufio.NewReader(foo.conn) + db, err = sql.Open("sqlite3", "./boddle.db") + if err != nil { + LOG_ERR.Printf("opening database failed") + return nil + } + + var line []byte + var err error + + go func() { + for { + line, err = reader.ReadBytes('\n') + if err != nil { + LOG_ERR.Printf("Error while reading bytes from connection.\n") + dead = true + c <- []byte("killed it") + return + } + + c <- line + + if bytes.Contains([]byte(line), []byte("PING :")) { + v := []byte(bytes.Replace([]byte(line), []byte("PING"), []byte("PONG"), 1)) + fmt.Printf(string(v[:])) + foo.conn.Write(v) + continue + } + + if !bytes.Contains([]byte(line), []byte("PRIVMSG")) { + continue + } + + parsemsg(scanline_privmsg(string(line)), foo) + } + } () + return c +} + +func connect(boddle bot) <-chan []byte { + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", boddle.server, boddle.port)) + + if err != nil { + fmt.Fprintf(os.Stderr, "Error while dialing server.") + LOG_ERR.Printf("Error while connecting to server.") + return nil + } + + boddle.conn = conn + + /* connect to irc */ + fmt.Fprintf(boddle.conn, "NICK %s\r\n", boddle.name) + fmt.Fprintf(boddle.conn, "USER %s 1 1 1:%s\r\n", boddle.name, boddle.name) + for _, channel := range(boddle.channel) { + fmt.Fprintf(boddle.conn, "JOIN %s\r\n", channel) + } + + return listenToIRC(boddle) +} diff --git a/logging.go b/logging.go new file mode 100644 index 0000000..3a905d1 --- /dev/null +++ b/logging.go @@ -0,0 +1,24 @@ +package main + +import ( + "log" + "os" +) + +var ( + LOG_WARN *log.Logger + LOG_INFO *log.Logger + LOG_ERR *log.Logger +) + +func LOG_init() { + file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + 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) +}