diff options
| author | Christine Dodrill <xena@yolo-swag.com> | 2015-06-21 20:12:03 -0700 |
|---|---|---|
| committer | Christine Dodrill <xena@yolo-swag.com> | 2015-06-21 20:12:03 -0700 |
| commit | faaff96b8e76d84ca47cb37c2dd170946a303070 (patch) | |
| tree | 565c38252329cf751e626e9404bc865f70817e10 /irc | |
| parent | 88b0c6b5a6a151c9be8f49ef5b477c1d4bd0c7c8 (diff) | |
| download | x-faaff96b8e76d84ca47cb37c2dd170946a303070.tar.xz x-faaff96b8e76d84ca47cb37c2dd170946a303070.zip | |
Add markov chain bot for @yofun
Diffstat (limited to 'irc')
| -rw-r--r-- | irc/trolling/smartbot/.gitignore | 2 | ||||
| -rw-r--r-- | irc/trolling/smartbot/main.go | 85 | ||||
| -rw-r--r-- | irc/trolling/smartbot/markov.go | 137 |
3 files changed, 224 insertions, 0 deletions
diff --git a/irc/trolling/smartbot/.gitignore b/irc/trolling/smartbot/.gitignore new file mode 100644 index 0000000..075d92a --- /dev/null +++ b/irc/trolling/smartbot/.gitignore @@ -0,0 +1,2 @@ +smartbot +mybrain.gob diff --git a/irc/trolling/smartbot/main.go b/irc/trolling/smartbot/main.go new file mode 100644 index 0000000..9d4c5b0 --- /dev/null +++ b/irc/trolling/smartbot/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "bufio" + "flag" + "log" + "math/rand" + "os" + "time" + + "github.com/thoj/go-ircevent" +) + +var ( + brainInput = flag.String("brain", "", "brain file") +) + +func main() { + flag.Parse() + + chain := NewChain(3) + + if *brainInput != "" { + log.Printf("Opening %s...", *brainInput) + + fin, err := os.Open(*brainInput) + if err != nil { + panic(err) + } + + s := bufio.NewScanner(fin) + for s.Scan() { + t := s.Text() + + log.Println(t) + + _, err := chain.Write(t) + if err != nil { + panic(err) + } + } + + err = chain.Save("mybrain.gob") + if err != nil { + panic(err) + } + } else { + err := chain.Load("mybrain.gob") + if err != nil { + panic(err) + } + } + + rand.Seed(time.Now().Unix()) + + conn := irc.IRC("sjj999sjj", "sjj") + + conn.Debug = true + + err := conn.Connect("irc.frenzic.net:6667") + if err != nil { + panic(err) + } + + conn.AddCallback("001", func(e *irc.Event) { + conn.Join("#chat") + }) + + conn.AddCallback("PRIVMSG", func(e *irc.Event) { + if e.Nick == "jjs999jjs" { + chain.Write(e.Arguments[1]) + chain.Save("mybrain.gob") + } + }) + + conn.AddCallback("PRIVMSG", func(e *irc.Event) { + if rand.Int()%4 == 2 { + log.Printf("About to say something...") + time.Sleep(time.Duration((rand.Int()%15)+4) * time.Second) + conn.Privmsg(e.Arguments[0], chain.Generate(rand.Int()%8)) + } + }) + + conn.Loop() +} diff --git a/irc/trolling/smartbot/markov.go b/irc/trolling/smartbot/markov.go new file mode 100644 index 0000000..f31c133 --- /dev/null +++ b/irc/trolling/smartbot/markov.go @@ -0,0 +1,137 @@ +package main + +// This Markov chain code is taken from the "Generating arbitrary text" +// codewalk: http://golang.org/doc/codewalk/markov/ +// +// Minor modifications have been made to make it easier to integrate +// with a webserver and to save/load state + +import ( + "encoding/gob" + "fmt" + "math/rand" + "os" + "strings" + "sync" +) + +// Prefix is a Markov chain prefix of one or more words. +type Prefix []string + +// String returns the Prefix as a string (for use as a map key). +func (p Prefix) String() string { + return strings.Join(p, " ") +} + +// Shift removes the first word from the Prefix and appends the given word. +func (p Prefix) Shift(word string) { + copy(p, p[1:]) + p[len(p)-1] = word +} + +// Chain contains a map ("chain") of prefixes to a list of suffixes. +// A prefix is a string of prefixLen words joined with spaces. +// A suffix is a single word. A prefix can have multiple suffixes. +type Chain struct { + Chain map[string][]string + prefixLen int + mu sync.Mutex +} + +// NewChain returns a new Chain with prefixes of prefixLen words. +func NewChain(prefixLen int) *Chain { + return &Chain{ + Chain: make(map[string][]string), + prefixLen: prefixLen, + } +} + +// Write parses the bytes into prefixes and suffixes that are stored in Chain. +func (c *Chain) Write(in string) (int, error) { + sr := strings.NewReader(in) + p := make(Prefix, c.prefixLen) + for { + var s string + if _, err := fmt.Fscan(sr, &s); err != nil { + break + } + key := p.String() + c.mu.Lock() + c.Chain[key] = append(c.Chain[key], s) + c.mu.Unlock() + p.Shift(s) + } + return len(in), nil +} + +// Generate returns a string of at most n words generated from Chain. +func (c *Chain) Generate(n int) string { + c.mu.Lock() + defer c.mu.Unlock() + p := make(Prefix, c.prefixLen) + var words []string + for i := 0; i < n; i++ { + choices := c.Chain[p.String()] + if len(choices) == 0 { + break + } + next := choices[rand.Intn(len(choices))] + words = append(words, next) + p.Shift(next) + } + return strings.Join(words, " ") +} + +// Save the chain to a file +func (c *Chain) Save(fileName string) error { + // Open the file for writing + fo, err := os.Create(fileName) + if err != nil { + return err + } + // close fo on exit and check for its returned error + defer func() { + if err := fo.Close(); err != nil { + panic(err) + } + }() + + // Create an encoder and dump to it + c.mu.Lock() + defer c.mu.Unlock() + + enc := gob.NewEncoder(fo) + err = enc.Encode(c) + if err != nil { + return err + } + + return nil +} + +// Load the chain from a file +func (c *Chain) Load(fileName string) error { + // Open the file for reading + fi, err := os.Open(fileName) + if err != nil { + return err + } + // close fi on exit and check for its returned error + defer func() { + if err := fi.Close(); err != nil { + panic(err) + } + }() + + // Create a decoder and read from it + c.mu.Lock() + defer c.mu.Unlock() + + dec := gob.NewDecoder(fi) + err = dec.Decode(c) + if err != nil { + return err + } + + return nil +} |
