aboutsummaryrefslogtreecommitdiff
path: root/irc
diff options
context:
space:
mode:
authorChristine Dodrill <xena@yolo-swag.com>2015-06-21 20:12:03 -0700
committerChristine Dodrill <xena@yolo-swag.com>2015-06-21 20:12:03 -0700
commitfaaff96b8e76d84ca47cb37c2dd170946a303070 (patch)
tree565c38252329cf751e626e9404bc865f70817e10 /irc
parent88b0c6b5a6a151c9be8f49ef5b477c1d4bd0c7c8 (diff)
downloadx-faaff96b8e76d84ca47cb37c2dd170946a303070.tar.xz
x-faaff96b8e76d84ca47cb37c2dd170946a303070.zip
Add markov chain bot for @yofun
Diffstat (limited to 'irc')
-rw-r--r--irc/trolling/smartbot/.gitignore2
-rw-r--r--irc/trolling/smartbot/main.go85
-rw-r--r--irc/trolling/smartbot/markov.go137
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
+}