1

I have just recently started faffing with golang. I taught myself php & jquery pretty easily & by creating a project to do so.

I am trying the same atm to teach myself golang, but I have gotten to a point now which either im missing the point or just searching incorrectly.

....

Ok so what I am trying to do is make a IRC bot. The one function listens to channel chatter & if certain commands are picked up it then will add relevant information to a sqlite3 database.

The problem is that one of the commands queries the database & will return multiple rows, which then need to be passed back to the original function & outputted into the IRC channel.

Where I am getting stuck is returning the queries output to the original function since it is multiple rows of data

Im importing the below libraries fmt net/textproto regexp strings os database/sql _ github.com/mattn/go-sqlite3

func getLineup() {
    // Open Database
    db, err := sql.Open("sqlite3", "./database.db")
    if err != nil {
            fmt.Println(err)
            os.Exit(1)
    }
    defer db.Close()

    // Prepare Query
    statement, err := db.Prepare("SELECT team, player FROM lineup ORDER BY team DESC;")
    if err != nil {
            fmt.Println(err)
            os.Exit(1)
    }

    // Execute Statement
    rows, err := statement.Query()
    defer rows.Close()


    fmt.Println("Lineup:")

    for rows.Next() {
            var team string
            var player string
            rows.Scan(&team, &player)
            fmt.Printf("%v %v\n", team, player)
    }

}

So I can print it, but I need to pass it to another function which is where im lost

================================UPDATE====================================

Ok so here is my full code... @evanmcdonnal With the updated code you gave above I now get the error bot.go:70: cannot use p (type Player) as type *Player in append

package main

import (
    "fmt"
    "net/textproto"
    "regexp"
    "log"
    "strings"
    "database/sql"
    // SQLite3
    _ "github.com/mattn/go-sqlite3"
)


type PrivMsg struct {
    nick, channel, text string
}

var (
    conn *textproto.Conn
    err  error

    ping    = regexp.MustCompile("^PING :([a-zA-Z0-9\\.]+)$")
    motd    = regexp.MustCompile(":End of /MOTD command\\.$")
    privmsg = regexp.MustCompile("^:([a-zA-Z0-9`_\\-]+)![a-zA-Z0-9/\\\\\\.\\-]+@[a-zA-Z0-9/\\\\\\.\\-]+ PRIVMSG (#[a-zA-Z0-9]+) :(.*)$")
)


func talk(channel, msg string) {
    conn.Cmd("PRIVMSG " + channel + " :" + msg)
}


func handlePing(auth string) {
    conn.Cmd("PONG :" + auth)
    fmt.Printf("PONG :%s\n", auth)
}

type Player struct {
    TeamName string
    PlayerName string
}

func getLineup() {
    // Open Database
    db, err := sql.Open("sqlite3", "./database.db")
    if err != nil {
        log.Fatal(err)
    }

    // Prepare Query
    statement, err := db.Prepare("SELECT team, player FROM lineup ORDER BY team DESC;")
    if err != nil {
        log.Fatal(err)
    }

    // Execute Statement
    rows, err := statement.Query()
    defer rows.Close()


// Output Code
    var Players []*Player
    for rows.Next() {
        p := &Player{}
        if err := rows.Scan(p.TeamName, p.PlayerName); err != nil{
            log.Fatal(err)
        }
        //Players = append(Players, p)
        return p.TeamName, p.PlayerName
    }
    // pass Players to next function/return it whatever

    fmt.Println(Players)
}




func handlePrivmsg(pm *PrivMsg) {
    if strings.Contains(pm.text, "!add t") {
        talk(pm.channel, pm.nick + " added to Terrorists")
        saveLineup("T", pm.nick)
    }
    if strings.Contains(pm.text, "!add ct") {
        talk(pm.channel, pm.nick + " added to Counter-Terrorists")
        saveLineup("CT", pm.nick)
    }
    if strings.Contains(pm.text, "!rem") {
        talk(pm.channel, pm.nick + " has been removed from the current lineup")
    }
    if strings.Contains(pm.text, "!votemap") {
        talk(pm.channel, pm.nick + " map vote code")
    }
    if strings.Contains(pm.text, "!moveme") {
        talk(pm.channel, pm.nick + " has been moved to Counter-Terrorists")
    }
    if strings.Contains(pm.text, "!teams") {
        getLineup()
        //fmt.Println(*tpList)
        talk(pm.channel, pm.nick + " will show the current teams")
    }
    if strings.Contains(pm.text, "!add ct") {
        talk(pm.channel, pm.nick + " added to Counter-Terrorists")
    }
    if strings.Contains(pm.text, "pug-bot") {
        talk(pm.channel, "Hello, " + pm.nick + "!")
    }
}


func saveLineup(Team, Player string) {

    // Open Database
    db, err := sql.Open("sqlite3", "./database.db")
    if err != nil {
        //log.Fatal(err)
        fmt.Printf("%s", err)
    }

    // Get Current Lineup
    rows, err := db.Query("SELECT team, player FROM lineup WHERE player = ?;", Player)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    if Player == Player {
        fmt.Println("You have already added yourself")
    } else {
        // Insert new Player
        db.Exec(
            "INSERT INTO lineup (team, player) VALUES (?, ?);",
            Team,
            Player,
        )
    }

}


func handleMotd() {
    conn.Cmd("JOIN #ircchannel")
    fmt.Println("JOIN #ircchannel")
}


func parseLine(line string) {
    // Channel activity
    if match := privmsg.FindStringSubmatch(line); match != nil {
        pm := new(PrivMsg)
        pm.nick, pm.channel, pm.text = match[1], match[2], match[3]
        handlePrivmsg(pm)
        return
    }

    // Server PING
    if match := ping.FindStringSubmatch(line); match != nil {
        handlePing(match[1])
        return
    }

    // End of MOTD (successful login to IRC server)
    if match := motd.FindString(line); match != "" {
        handleMotd()
        return
    }
}

func main() {
    conn, err = textproto.Dial("tcp", "irc.server.org:6667")
    if err != nil {
        fmt.Printf("%s", err)
        return
    }

    conn.Cmd("NICK pug-bot\n\rUSER pug-bot 8 * :pAsSwOrD")

    for {
        text, err := conn.ReadLine()
        if err != nil {
            fmt.Printf("%s", err)
            return
        }

        go parseLine(text)

        fmt.Println(text)
    }


}

Essentially I would like to pass the result of the sql query back into the talk(pm.channel, pm.nick + " SQL QUERY RESULT") irc talk section

Sayajin
  • 33
  • 2
  • 9
  • 1
    Have you studied the example in [`DB.Query(...)`](http://golang.org/pkg/database/sql/#example_DB_Query)? – maerics Apr 23 '15 at 18:32
  • Side note/nit: move the `sql.Open` elsewhere and re-use the returned `db` value. Similarly you can re-use your prepared statement. [The docs](https://golang.org/pkg/database/sql/#Open): "The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB." – Dave C Apr 23 '15 at 19:58
  • @evanmcdonnal ok posted my full script above as an edit – Sayajin Apr 29 '15 at 23:01

2 Answers2

0

I think what you're failing to understand is how to model the data in your Go app (after it's returned from the db). The variables you're using to store the values you read are only scoped for the loop and are singletons, how would you return a collection? Well by using a collection of course!

So here are the two options I would consider;

1) just concat everything into one big string. If you're going to dump the output to the console soon after his and you don't have any real processing to do, it's probably simplest to just declare a string before the loop, keep your scan as it is, append the results into the string in some common format like comma delimited then pass that to the function/write it to IRC or whatever.

2) use an actual collection. In any serious program you're probably going to do more with data coming from a db than print just print it. A more realistic implementation would be to create Player struct which has teamName and playerName fields. Before the loop you'd initialize a slice of Players or an array if you know how many results will be coming back. In the loop you would create a new instance and add it to the slice with the append function. After the loop you pass the slice/array to whatever needs to use the data next.

Here are couple samples. Note that these are both untested and I am not considering performance. For example if performance were a concern you should probably be using something like this for string concatenation How to efficiently concatenate strings in Go?

type Player struct {
    TeamName string
    PlayerName string
}

var Players []*Player
for rows.Next() {
        p := &Player{}
        if err := rows.Scan(p.TeamName, p.PlayerName); err != nil{
             // handle error
        }
        Players = append(Players, p)
}
// pass Players to next function/return it whatever


// simpler less robust option 1

lineUp := ""
for rows.Next() {
            var team string
            var player string
            rows.Scan(&team, &player)
            lineUp += team + ":" + player "\n"
    }
return lineUp
Community
  • 1
  • 1
evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115
  • If I try the simpler less robust option 1 I get the error: too many arguments to return – Sayajin Apr 26 '15 at 06:27
  • If I use the 2nd option I get error: cannot use p (type *Player) as type Player in append – Sayajin Apr 26 '15 at 06:27
  • @Sayajin the first error is because your method has no return type, if you're using option one the method containing that code should return at least a string (convention would probably be to return `(string, error)`). In the second case it's because the collection needs to be `[]*Player`, I'll edit with that. You could also change the code in the loop to use a `Player` value type but I prefer the pointer implementation. – evanmcdonnal Apr 27 '15 at 15:37
0

try putting your team and player in a struct

type teamplayer struct {
   team string
   player string

}

Now in your function where you will query the db, create a list of teamplayer

var tpList []teamplayer
for rows.Next() {
        var tp teamplayer
        if err := rows.Scan(&tp.team, &tp.player); err != nil{
             //process what to do in case of error
        }
        tplist = append (tplist, tp)
}//end of for loop
return tplist
user3203010
  • 261
  • 1
  • 8
  • If i try this way I get the errors: 121: undefined: tplist 123: undefined: tplist 123: too many arguments to return – Sayajin Apr 26 '15 at 06:32
  • Can you post your new code and function, have you declared you are going to return type tpList? cannot correlate the error lines you have posted without the code – user3203010 Apr 27 '15 at 16:07
  • I will paste my script here soon, then at least then some1 can point me in the correct direction, as I said I am a total golang newb but i learn best by doing, and starting a project is the best way for me. – Sayajin Apr 28 '15 at 08:10