// Associates an already known nick with an already known channel. func (st *stateTracker) Associate(ch *Channel, nk *Nick) *ChanPrivs { if ch == nil || nk == nil { logging.Error("Tracker.Associate(): passed nil values :-(") return nil } else if _ch, ok := st.chans[ch.Name]; !ok || ch != _ch { // As we can implicitly delete both nicks and channels from being // tracked by dissociating one from the other, we should verify that // we're not being passed an old Nick or Channel. logging.Error("Tracker.Associate(): channel %s not found in "+ "(or differs from) internal state.", ch.Name) return nil } else if _nk, ok := st.nicks[nk.Nick]; !ok || nk != _nk { logging.Error("Tracker.Associate(): nick %s not found in "+ "(or differs from) internal state.", nk.Nick) return nil } else if _, ok := nk.IsOn(ch); ok { logging.Warn("Tracker.Associate(): %s already on %s.", nk.Nick, ch.Name) return nil } cp := new(ChanPrivs) ch.addNick(nk, cp) nk.addChannel(ch, cp) return cp }
// Dissociates an already known nick from an already known channel. // Does some tidying up to stop tracking nicks we're no longer on // any common channels with, and channels we're no longer on. func (st *stateTracker) Dissociate(ch *Channel, nk *Nick) { if ch == nil || nk == nil { logging.Error("Tracker.Dissociate(): passed nil values :-(") } else if _ch, ok := st.chans[ch.Name]; !ok || ch != _ch { // As we can implicitly delete both nicks and channels from being // tracked by dissociating one from the other, we should verify that // we're not being passed an old Nick or Channel. logging.Error("Tracker.Dissociate(): channel %s not found in "+ "(or differs from) internal state.", ch.Name) } else if _nk, ok := st.nicks[nk.Nick]; !ok || nk != _nk { logging.Error("Tracker.Dissociate(): nick %s not found in "+ "(or differs from) internal state.", nk.Nick) } else if _, ok := nk.IsOn(ch); !ok { logging.Warn("Tracker.Dissociate(): %s not on %s.", nk.Nick, ch.Name) } else if nk == st.me { // I'm leaving the channel for some reason, so it won't be tracked. st.delChannel(ch) } else { // Remove the nick from the channel and the channel from the nick. ch.delNick(nk) nk.delChannel(ch) if len(nk.chans) == 0 { // We're no longer in any channels with this nick. st.delNick(nk) } } }
func githubWatcher(ctx *bot.Context) { // Watch #sp0rklf for IRC messages about issues coming from github. if ctx.Nick != "fluffle\\sp0rkle" || ctx.Target() != "#sp0rklf" || !strings.Contains(ctx.Text(), "issue #") { return } text := util.RemoveColours(ctx.Text()) // srsly github why colours :( l := &util.Lexer{Input: text} l.Find(' ') text = text[l.Pos()+1:] l.Find('#') l.Next() issue := int(l.Number()) labels, _, err := gh.Issues.ListLabelsByIssue( githubUser, githubRepo, issue, &github.ListOptions{}) if err != nil { logging.Error("Error getting labels for issue %d: %v", issue, err) return } for _, l := range labels { kv := strings.Split(*l.Name, ":") if len(kv) == 2 && kv[0] == "nick" { logging.Debug("Recording tell for %s about issue %d.", kv[1], issue) r := reminders.NewTell("that "+text, bot.Nick(kv[1]), "github", "") if err := rc.Insert(r); err != nil { logging.Error("Error inserting github tell: %v", err) } } } }
func (cs *commandSet) Add(cmd Command, prefix string) { if cmd == nil || prefix == "" { logging.Error("Can't handle prefix '%s' with command.", prefix) return } cs.Lock() defer cs.Unlock() if _, ok := cs.set[prefix]; ok { logging.Error("Prefix '%s' already registered.", prefix) return } cs.set[prefix] = cmd }
func (cs *commandSet) Add(r Runner, prefix string) { if r == nil || prefix == "" { logging.Error("Prefix or runner empty when adding command.", prefix) return } cs.Lock() defer cs.Unlock() if _, ok := cs.set[prefix]; ok { logging.Error("Prefix '%s' already registered.", prefix) return } cs.set[prefix] = r }
func Init() *Collection { uc := &Collection{db.Init().C(collection)} err := uc.EnsureIndex(mgo.Index{Key: []string{"url"}, Unique: true}) if err != nil { logging.Error("Couldn't create url index on sp0rkle.urls: %s", err) } for _, idx := range []string{"cachedas", "shortened"} { err := uc.EnsureIndex(mgo.Index{Key: []string{idx}}) if err != nil { logging.Error("Couldn't create %s index on sp0rkle.urls: %s", idx, err) } } return uc }
func Init() *Collection { kc := &Collection{db.Init().C(COLLECTION)} if err := kc.EnsureIndex(mgo.Index{ Key: []string{"key"}, Unique: true, }); err != nil { logging.Error("Couldn't create index on karma.key: %s", err) } for _, key := range []string{"score", "votes"} { if err := kc.EnsureIndexKey(key); err != nil { logging.Error("Couldn't create index on karma.%s: %s", key, err) } } return kc }
func (m *Module) checkMatchers(regexes map[*regexp.Regexp]v8.V8Function, target string, line string, from string) (responded bool) { // Activate callback on any matches for regex, fn := range regexes { matches := regex.FindStringSubmatch(line) match, _ := json.Marshal(matches) if len(matches) > 0 { responded = true go func(match string, fn v8.V8Function) { // Clone a response object m.response++ m.Context.Eval(`var response` + strconv.Itoa(m.response) + ` = clone(response);`) // Set response parameters m.Context.Eval(`response` + strconv.Itoa(m.response) + `.match = ` + match) m.Context.Eval(`response` + strconv.Itoa(m.response) + `.target = "` + target + `";`) m.Context.Eval(`response` + strconv.Itoa(m.response) + `.nick = "` + m.Client.Me().Nick + `"; response` + strconv.Itoa(m.response) + `.message = {}; response` + strconv.Itoa(m.response) + `.message.nick = "` + from + `"; response` + strconv.Itoa(m.response) + `.message.text = "` + line + `"`) _, err := fn.Call(v8.V8Object{`response` + strconv.Itoa(m.response)}) if err != nil { log.Error("%s\n%s", err, fn) } }(string(match), fn) } } return }
func (hs *hSet) remove(hn *hNode) { hs.Lock() defer hs.Unlock() l, ok := hs.set[hn.event] if !ok { logging.Error("Removing node for unknown event '%s'", hn.event) return } if hn.next == nil { l.end = hn.prev } else { hn.next.prev = hn.prev } if hn.prev == nil { l.start = hn.next } else { hn.prev.next = hn.next } hn.next = nil hn.prev = nil hn.set = nil if l.start == nil || l.end == nil { delete(hs.set, hn.event) } }
func Init() { lock.Lock() defer lock.Unlock() if irc != nil { return } if *server == "" { // Don't call logging.Fatal as we don't want a backtrace in this case logging.Error("--server option required. \nOptions are:\n") flag.PrintDefaults() os.Exit(1) } // Configure IRC client irc = client.SimpleClient(*nick, "boing", "not really sp0rkle") irc.SSL = *ssl irc.Flood = true HandleFunc(bot_connected, "connected") HandleFunc(bot_disconnected, "disconnected") // This is a special handler that dispatches commands from the command set HandleFunc(bot_command, "privmsg") // This is a special handler that triggers a rebuild and re-exec HandleFunc(bot_rebuild, "notice") // This is a special handler that triggers a shutdown and disconnect HandleFunc(bot_shutdown, "notice") CommandFunc(bot_help, "help", "If you need to ask, you're beyond help.") }
func (sc *Collection) TopTen(ch string) []*NickStat { var res []*NickStat q := sc.Find(bson.M{"chan": ch}).Sort("-lines").Limit(10) if err := q.All(&res); err != nil { logging.Error("TopTen Find error for channel %s: %v", ch, err) } return res }
func Init() { bot.Command(urbanDictionary, "ud", "ud <term> -- "+ "Look up <term> on UrbanDictionary.") mcConf = conf.Ns("mc") srv := mcConf.String(mcServer) if srv != "" { if st, err := pollServer(srv); err == nil { logging.Info("Starting MC poller for '%s'", srv) bot.Poll(st) bot.Handle(func(ctx *bot.Context) { st.Topic(ctx) }, "332") } else { logging.Error("Not starting MC poller: %v", err) } } bot.Command(mcSet, "mc set", "mc set <key> <value> -- "+ "Set minecraft server polling config vars.") // TODO(fluffle): Polling can only be en/disabled at reconnect. // bot.Command(mcPoll, "mc poll", "mc poll start|stop -- "+ // "Enable or disable minecraft server polling.") if *githubToken != "" { rc = reminders.Init() gh = githubClient() bot.Handle(githubWatcher, client.PRIVMSG) bot.Command(githubCreateIssue, "file bug:", "file bug: <title>. "+ "<descriptive body> -- Files a bug on GitHub. Abusers will be hurt.") bot.Command(githubCreateIssue, "file bug", "file bug <title>. "+ "<descriptive body> -- Files a bug on GitHub. Abusers will be hurt.") bot.Command(githubCreateIssue, "report bug", "report bug <title>. "+ "<descriptive body> -- Files a bug on GitHub. Abusers will be hurt.") bot.Command(githubUpdateIssue, "update bug #", "update bug #<number> "+ "<comment> -- Adds a comment to bug <number>. Abusers will be hurt.") } if push.Enabled() { pc = pushes.Init() bot.Command(pushEnable, "push enable", "push enable -- "+ "Start the OAuth flow to enable pushbullet notifications.") bot.Command(pushDisable, "push disable", "push disable -- "+ "Disable pushbullet notifications and delete tokens.") bot.Command(pushConfirm, "push auth", "push auth <pin> -- "+ "Confirm pushed PIN to finish pushbullet auth dance.") bot.Command(pushAddAlias, "push add alias", "push add alias -- "+ "Add a push alias for your nick.") bot.Command(pushDelAlias, "push del alias", "push del alias -- "+ "Delete a push alias for your nick.") http.HandleFunc("/oauth/auth", pushAuthHTTP) http.HandleFunc("/oauth/device", pushDeviceHTTP) http.HandleFunc("/oauth/success", pushSuccessHTTP) http.HandleFunc("/oauth/failure", pushFailureHTTP) } }
func (st *stateTracker) delNick(nk *Nick) { if nk == st.me { // Shouldn't get here => internal state tracking code is fubar. logging.Error("Tracker.DelNick(): TRYING TO DELETE ME :-(") return } delete(st.nicks, nk.Nick) for ch, _ := range nk.chans { nk.delChannel(ch) ch.delNick(nk) if len(ch.nicks) == 0 { // Deleting a nick from tracking shouldn't empty any channels as // *we* should be on the channel with them to be tracking them. logging.Error("Tracker.delNick(): deleting nick %s emptied "+ "channel %s, this shouldn't happen!", nk.Nick, ch.Name) } } }
// Wrapper to get hold of a factoid collection handle func Init() *Collection { mc := &Collection{db.Mongo.C(COLLECTION).Mongo()} if err := mc.EnsureIndex(mgo.Index{ Key: []string{"tag", "source", "dest"}, }); err != nil { logging.Error("Couldn't create an index on markov: %s", err) } return mc }
func (rc *Collection) TellsFor(nick string) []*Reminder { nick = strings.ToLower(nick) q := rc.Find(bson.M{"$and": []bson.M{{"tell": true}, {"to": nick}}}) ret := make([]*Reminder, 0) if err := q.All(&ret); err != nil { logging.Error("Loading tells for %s returned error: %v", nick, err) return nil } return ret }
func (rc *Collection) RemindersFor(nick string) []*Reminder { nick = strings.ToLower(nick) q := rc.Find(bson.M{"$or": []bson.M{{"from": nick}, {"to": nick}}}) q.Sort("remindat") ret := make([]*Reminder, 0) if err := q.All(&ret); err != nil { logging.Error("Loading reminders for %s returned error: %v", nick, err) return nil } return ret }
// Wrapper to get hold of a factoid collection handle func Init() *Collection { fc := &Collection{ Collection: db.Mongo.C(COLLECTION).Mongo(), seen: make(map[string][]bson.ObjectId), } err := fc.EnsureIndex(mgo.Index{Key: []string{"key"}}) if err != nil { logging.Error("Couldn't create index on sp0rkle.factoids: %v", err) } return fc }
func Init() *Collection { rc := &Collection{ Collection: db.Init().C(COLLECTION), } for _, k := range []string{"remindat", "from", "to", "tell"} { if err := rc.EnsureIndexKey(k); err != nil { logging.Error("Couldn't create %s index on sp0rkle.reminders: %v", k, err) } } return rc }
func (b *boltDatabase) doBackup() { fn := path.Join(b.dir, fmt.Sprintf("sp0rkle.boltdb.%s.gz", time.Now().Format("2006-01-02.15:04"))) fh, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { logging.Error("Could not create backup file %q: %v", fn, err) return } fz := gzip.NewWriter(fh) defer fz.Close() err = b.db.View(func(tx *bolt.Tx) error { return tx.Copy(fz) }) if err != nil { logging.Error("Could not write backup file %q: %v", fn, err) os.Remove(fn) return } logging.Info("Wrote backup to %q.", fn) }
func _httpclient_get(args ...interface{}) interface{} { url := strings.Trim(args[0].(string), `"`) // Initialize client client := &http.Client{} req, err := http.NewRequest("GET", url, nil) if err != nil { log.Error(err.Error()) return "" } // Set request headers var headers map[string]string err = json.Unmarshal([]byte(args[1].(string)), &headers) if err != nil { log.Error(err.Error()) return "" } for header, value := range headers { req.Header.Add(header, value) } // Send request resp, err := client.Do(req) if err != nil { log.Error(err.Error()) return "" } // Get response defer resp.Body.Close() bytes, err := ioutil.ReadAll(resp.Body) if err != nil { log.Error(err.Error()) return "" } return string(bytes) }
func Ns(ns string) namespace { lock.Lock() defer lock.Unlock() if conf == nil { conf = db.Init().C(COLLECTION) err := conf.EnsureIndex(mgo.Index{Key: []string{"ns", "key"}, Unique: true}) if err != nil { logging.Error("Couldn't create index on sp0rkle.conf: %s", err) } } return namespace(ns) }
func connect(c *irc.Conn, server string, password string, exiting chan bool) { err := c.ConnectTo(server, password) if err != nil { log.Error(err.Error()) log.Info("Unable to connect to server") exiting <- true } else { log.Info(c.String()) } }
func (mc *Collection) incUses(source, dest, tag string) { link := mc.Get(source, dest, tag) if link == nil { link = New(source, dest, tag) } link.Uses++ if _, err := mc.UpsertId(link.Id, link); err != nil { logging.Error("Failed to insert MarkovLink %s(%s->%s): %s", tag, source, dest, err) } }
func (rc *Collection) LoadAndPrune() []*Reminder { // First, drop any reminders where RemindAt < time.Now() ci, err := rc.RemoveAll(bson.M{"$and": []bson.M{ {"remindat": bson.M{"$lt": time.Now()}}, {"tell": false}, }}) if err != nil { logging.Error("Pruning reminders returned error: %v", err) } if ci.Removed > 0 { logging.Info("Removed %d old reminders", ci.Removed) } // Now, load the remainder; the db is just used for persistence q := rc.Find(bson.M{"tell": false}) ret := make([]*Reminder, 0) if err := q.All(&ret); err != nil { logging.Error("Loading reminders returned error: %v", err) return nil } return ret }
func Init() *Collection { sc := &Collection{db.Init().C(COLLECTION)} indexes := [][]string{ {"key", "action"}, // For searching ... {"timestamp"}, // ... and ordering seen entries. } for _, key := range indexes { if err := sc.EnsureIndex(mgo.Index{Key: key}); err != nil { logging.Error("Couldn't create %v index on sp0rkle.seen: %v", key, err) } } return sc }
func Forget(id bson.ObjectId, stop bool) { c, ok := running[id] if ok { // If it's *not* in running, it's probably a Tell. delete(running, id) if stop { c <- struct{}{} } } if err := rc.RemoveId(id); err != nil { logging.Error("Failure removing reminder %s: %v", id, err) } }
func Init() *Collection { sc := &Collection{db.Init().C(COLLECTION)} indexes := [][]string{ {"chan", "key"}, {"lines"}, } for _, key := range indexes { if err := sc.EnsureIndex(mgo.Index{Key: key}); err != nil { logging.Error("Couldn't create %v index on sp0rkle.stats: %v", key, err) } } return sc }
// Write a \r\n terminated line of output to the connected server, // using Hybrid's algorithm to rate limit if conn.cfg.Flood is false. func (conn *Conn) write(line string) { if !conn.cfg.Flood { if t := conn.rateLimit(len(line)); t != 0 { // sleep for the current line's time value before sending it logging.Debug("irc.rateLimit(): Flood! Sleeping for %.2f secs.", t.Seconds()) <-time.After(t) } } if _, err := conn.io.WriteString(line + "\r\n"); err != nil { logging.Error("irc.send(): %s", err.Error()) conn.shutdown() return } if err := conn.io.Flush(); err != nil { logging.Error("irc.send(): %s", err.Error()) conn.shutdown() return } logging.Debug("-> %s", line) }
func Forget(id bson.ObjectId, stop bool) { c, ok := running[id] if !ok { return } delete(running, id) if stop { c <- true } if err := rc.RemoveId(id); err != nil { logging.Error("Failure removing reminder %s: %v", id, err) } }
func connectLoop() bool { var retries uint32 for { if err := irc.Connect(*server); err != nil { logging.Error("Connection error: %s", err) retries++ if retries > 10 { logging.Error("Giving up connection after 10 failed retries.") return false } <-time.After(time.Second * 1 << retries) } else { retries, shutdown, reexec = 0, false, false // Wait here for a signal from bot_disconnected <-disconnected if shutdown { return reexec } } } panic("unreachable") }