graw
The graw package uses low level account handles from the graw/reddit package to provide more abstract access to Reddit in the form of "event streams".
Requesting Event Streams
We must prepare a config to tell graw what event streams we want. For testing, let's subscribe to new posts in a subreddit dedicated to testing bots such as /r/bottesting:
cfg := graw.Config{Subreddits: []string{"bottesting"}}
For a complete list of available event streams see the config struct.
Handling Events
We have a config that requests new posts, so we should have a way to handle them when they come. Each event stream has an associated interface for handling the events. The graw/botfaces package defines these interfaces. To handle new posts in a subreddit, we need to implement PostHandler:
type reminderBot struct {
bot reddit.Bot
}
func (r *reminderBot) Post(p *reddit.Post) error {
if strings.Contains(p.SelfText, "remind me of this post") {
<-time.After(10 * time.Second)
return r.bot.SendMessage(
p.Author,
fmt.Sprintf("Reminder: %s", p.Title),
"You've been reminded!",
)
}
return nil
}
We can take as much time as we want in event handler methods because graw calls them as goroutines; pausing here will not block the handling of other events. Returning an error will bring the bot down.
Running The Bot
The graw package exports only two functions, one for running logged out scripts, and one for running bots. We will use the latter, and pass it our handler, account handle, and config:
cfg := graw.Config{Subreddits: []string{"roxven"}}
handler := &reminderBot{bot: bot}
stop, wait, err := graw.Run(handler, bot, cfg)
Run launches a goroutine to send our handler new events in our requested streams. It returns two functions: stop, and wait. Calling stop()
would stop the graw run. Calling wait()
would block until the run stops, returning an error if the run ended for any other reason than a call to stop()
.
Demo
This is the full program text:
package main
import (
"fmt"
"strings"
"time"
"github.com/turnage/graw"
"github.com/turnage/graw/reddit"
)
type reminderBot struct {
bot reddit.Bot
}
func (r *reminderBot) Post(p *reddit.Post) error {
if strings.Contains(p.SelfText, "remind me of this post") {
<-time.After(10 * time.Second)
return r.bot.SendMessage(
p.Author,
fmt.Sprintf("Reminder: %s", p.Title),
"You've been reminded!",
)
}
return nil
}
func main() {
if bot, err := reddit.NewBotFromAgentFile("reminderbot.agent", 0); err != nil {
fmt.Println("Failed to create bot handle: ", err)
} else {
cfg := graw.Config{Subreddits: []string{"bottesting"}}
handler := &reminderBot{bot: bot}
if _, wait, err := graw.Run(handler, bot, cfg); err != nil {
fmt.Println("Failed to start graw run: ", err)
} else {
fmt.Println("graw run failed: ", wait())
}
}
}
I ran this, changing only "bottesting" to my personal testing subreddit /r/roxven:
And made a post with the string our bot looks for:
Shortly I received a message from the bot (also running on my account):