// TODO(fluffle): thisisn't quite PseudoRand but still ... func (uc *Collection) GetRand(regex string) *Url { lookup := bson.M{} if regex != "" { // Perform a regex lookup if we have one lookup["url"] = bson.M{"$regex": regex, "$options": "i"} } query := uc.Find(lookup) count, err := query.Count() if err != nil { logging.Warn("Count for URL lookup '%s' failed: %s", regex, err) return nil } if count == 0 { return nil } var res Url if count > 1 { query.Skip(rand.Intn(count)) } if err = query.One(&res); err != nil { logging.Warn("Fetch for URL lookup '%s' failed: %s", regex, err) return nil } return &res }
// Removes a Nick from being tracked. func (st *stateTracker) DelNick(n string) { if nk, ok := st.nicks[n]; ok { if nk != st.me { st.delNick(nk) } else { logging.Warn("Tracker.DelNick(): won't delete myself.") } } else { logging.Warn("Tracker.DelNick(): %s not tracked.", n) } }
// TODO(fluffle): reduce duplication with lib/factoids? func (qc *Collection) GetPseudoRand(regex string) *Quote { lookup := bson.M{} if regex != "" { // Only perform a regex lookup if there's a regex to match against, // otherwise this just fetches a quote at pseudo-random. lookup["quote"] = bson.M{"$regex": regex, "$options": "i"} } ids, ok := qc.seen[regex] if ok && len(ids) > 0 { logging.Debug("Looked for quotes matching '%s' before, %d stored id's", regex, len(ids)) lookup["_id"] = bson.M{"$nin": ids} } query := qc.Find(lookup) count, err := query.Count() if err != nil { logging.Warn("Count for quote lookup '%s' failed: %s", regex, err) return nil } if count == 0 { if ok { // Looked for this regex before, but nothing matches now delete(qc.seen, regex) } return nil } var res Quote if count > 1 { query = query.Skip(rand.Intn(count)) } if err = query.One(&res); err != nil { logging.Warn("Fetch for quote lookup '%s' failed: %s", regex, err) return nil } if count != 1 { if !ok { // only store seen for regex that match more than one quote logging.Debug("Creating seen data for regex '%s'.", regex) qc.seen[regex] = make([]bson.ObjectId, 0, count) } logging.Debug("Storing id %v for regex '%s'.", res.Id, regex) qc.seen[regex] = append(qc.seen[regex], res.Id) } else if ok { // if the count of results is 1 and we're storing seen data for regex // then we've exhausted the possible results and should wipe it logging.Debug("Zeroing seen data for regex '%s'.", regex) delete(qc.seen, regex) } return &res }
// 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) } } }
// 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 }
// Removes a Channel from being tracked. func (st *stateTracker) DelChannel(c string) { if ch, ok := st.chans[c]; ok { st.delChannel(ch) } else { logging.Warn("Tracker.DelChannel(): %s not tracked.", c) } }
func (fc *Collection) InfoMR(key string) *FactoidInfo { mr := &mgo.MapReduce{ Map: `function() { emit("count", { accessed: this.accessed.count, modified: this.modified.count, created: this.created.count, })}`, Reduce: `function(k,l) { var sum = { accessed: 0, modified: 0, created: 0 }; for each (var v in l) { sum.accessed += v.accessed; sum.modified += v.modified; sum.created += v.created; } return sum; }`, } var res []struct { Id int `bson:"_id"` Value FactoidInfo } info, err := fc.Find(lookup(key)).MapReduce(mr, &res) if err != nil || len(res) == 0 { logging.Warn("Info MR for '%s' failed: %v", key, err) return nil } else { logging.Debug("Info MR mapped %d, emitted %d, produced %d in %d ms.", info.InputCount, info.EmitCount, info.OutputCount, info.Time/1e6) } return &res[0].Value }
// Handle JOINs to channels to maintain state func (conn *Conn) h_JOIN(line *Line) { ch := conn.ST.GetChannel(line.Args[0]) nk := conn.ST.GetNick(line.Nick) if ch == nil { // first we've seen of this channel, so should be us joining it // NOTE this will also take care of nk == nil && ch == nil if nk != conn.Me { logging.Warn("irc.JOIN(): JOIN to unknown channel %s received "+ "from (non-me) nick %s", line.Args[0], line.Nick) return } ch = conn.ST.NewChannel(line.Args[0]) // since we don't know much about this channel, ask server for info // we get the channel users automatically in 353 and the channel // topic in 332 on join, so we just need to get the modes conn.Mode(ch.Name) // sending a WHO for the channel is MUCH more efficient than // triggering a WHOIS on every nick from the 353 handler conn.Who(ch.Name) } if nk == nil { // this is the first we've seen of this nick nk = conn.ST.NewNick(line.Nick) nk.Ident = line.Ident nk.Host = line.Host // since we don't know much about this nick, ask server for info conn.Who(nk.Nick) } // this takes care of both nick and channel linking \o/ conn.ST.Associate(ch, nk) }
func _console_log(args ...interface{}) interface{} { for _, arg := range args { log.Warn("> %s", arg) } return "" }
// Creates a new Channel, initialises it, and stores it so it // can be properly tracked for state management purposes. func (st *stateTracker) NewChannel(c string) *Channel { if _, ok := st.chans[c]; ok { logging.Warn("Tracker.NewChannel(): %s already tracked.", c) return nil } st.chans[c] = NewChannel(c) return st.chans[c] }
// Creates a new Nick, initialises it, and stores it so it // can be properly tracked for state management purposes. func (st *stateTracker) NewNick(n string) *Nick { if _, ok := st.nicks[n]; ok { logging.Warn("Tracker.NewNick(): %s already tracked.", n) return nil } st.nicks[n] = NewNick(n) return st.nicks[n] }
func (sc *Collection) TopTen(ch string) []Nick { var res []Nick q := sc.Find(bson.M{"action": "LINES", "chan": ch}).Sort("-lines").Limit(10) if err := q.All(&res); err != nil { logging.Warn("TopTen Find error: %v", err) } return res }
// Handle 671 whois reply (nick connected via SSL) func (conn *Conn) h_671(line *Line) { if nk := conn.ST.GetNick(line.Args[1]); nk != nil { nk.Modes.SSL = true } else { logging.Warn("irc.671(): received WHOIS SSL info for unknown nick %s", line.Args[1]) } }
// Handle TOPIC changes for channels func (conn *Conn) h_TOPIC(line *Line) { if ch := conn.ST.GetChannel(line.Args[0]); ch != nil { ch.Topic = line.Args[1] } else { logging.Warn("irc.TOPIC(): topic change on unknown channel %s", line.Args[0]) } }
// Handle 332 topic reply on join to channel func (conn *Conn) h_332(line *Line) { if ch := conn.ST.GetChannel(line.Args[1]); ch != nil { ch.Topic = line.Args[2] } else { logging.Warn("irc.332(): received TOPIC value for unknown channel %s", line.Args[1]) } }
// Disassociates a Channel from a Nick. func (nk *Nick) delChannel(ch *Channel) { if _, ok := nk.chans[ch]; ok { delete(nk.chans, ch) delete(nk.lookup, ch.Name) } else { logging.Warn("Nick.delChannel(): %s not on %s.", nk.Nick, ch.Name) } }
// Handle MODE changes for channels we know about (and our nick personally) func (conn *Conn) h_MODE(line *Line) { if ch := conn.ST.GetChannel(line.Args[0]); ch != nil { // channel modes first ch.ParseModes(line.Args[1], line.Args[2:]...) } else if nk := conn.ST.GetNick(line.Args[0]); nk != nil { // nick mode change, should be us if nk != conn.Me { logging.Warn("irc.MODE(): recieved MODE %s for (non-me) nick %s", line.Args[1], line.Args[0]) return } nk.ParseModes(line.Args[1]) } else { logging.Warn("irc.MODE(): not sure what to do with MODE %s", strings.Join(line.Args, " ")) } }
// Associates a Channel with a Nick. func (nk *Nick) addChannel(ch *Channel, cp *ChanPrivs) { if _, ok := nk.chans[ch]; !ok { nk.chans[ch] = cp nk.lookup[ch.Name] = ch } else { logging.Warn("Nick.addChannel(): %s already on %s.", nk.Nick, ch.Name) } }
// Disassociates a Nick from a Channel. func (ch *Channel) delNick(nk *Nick) { if _, ok := ch.nicks[nk]; ok { delete(ch.nicks, nk) delete(ch.lookup, nk.Nick) } else { logging.Warn("Channel.delNick(): %s not on %s.", nk.Nick, ch.Name) } }
// Associates a Nick with a Channel func (ch *Channel) addNick(nk *Nick, cp *ChanPrivs) { if _, ok := ch.nicks[nk]; !ok { ch.nicks[nk] = cp ch.lookup[nk.Nick] = nk } else { logging.Warn("Channel.addNick(): %s already on %s.", nk.Nick, ch.Name) } }
// Handle 324 mode reply func (conn *Conn) h_324(line *Line) { if ch := conn.ST.GetChannel(line.Args[1]); ch != nil { ch.ParseModes(line.Args[2], line.Args[3:]...) } else { logging.Warn("irc.324(): received MODE settings for unknown channel %s", line.Args[1]) } }
func recordNick(ctx *bot.Context) { sn := seenNickFromLine(ctx) sn.Chan = "" sn.Text = ctx.Target() if _, err := sc.Upsert(sn.Id(), sn); err != nil { // We don't have anyone to reply to in this case, so log instead. logging.Warn("Failed to store seen data: %v", err) } }
func recordNick(line *base.Line) { sn := seenNickFromLine(line) sn.Chan = "" sn.Text = line.Args[0] if _, err := sc.Upsert(sn.Id(), sn); err != nil { // We don't have anyone to reply to in this case, so log instead. logging.Warn("Failed to store seen data: %v", err) } }
func (fc *Collection) GetKeysMatching(regex string) []string { var res []string query := fc.Find(bson.M{"key": bson.M{"$regex": regex}}) if err := query.Distinct("key", &res); err != nil { logging.Warn("Distinct regex query for '%s' failed: %v\n", regex, err) return nil } return res }
// Handle 311 whois reply func (conn *Conn) h_311(line *Line) { if nk := conn.ST.GetNick(line.Args[1]); nk != nil && nk != conn.Me { nk.Ident = line.Args[2] nk.Host = line.Args[3] nk.Name = line.Args[5] } else { logging.Warn("irc.311(): received WHOIS info for unknown nick %s", line.Args[1]) } }
// Signals to the tracker that a Nick should be tracked // under a "neu" nick rather than the old one. func (st *stateTracker) ReNick(old, neu string) { if nk, ok := st.nicks[old]; ok { if _, ok := st.nicks[neu]; !ok { nk.Nick = neu delete(st.nicks, old) st.nicks[neu] = nk for ch, _ := range nk.chans { // We also need to update the lookup maps of all the channels // the nick is on, to keep things in sync. delete(ch.lookup, old) ch.lookup[neu] = nk } } else { logging.Warn("Tracker.ReNick(): %s already exists.", neu) } } else { logging.Warn("Tracker.ReNick(): %s not tracked.", old) } }
func (sc *Collection) SeenAnyMatching(rx string) []string { var res []string q := sc.Find(bson.M{"key": bson.M{"$regex": rx, "$options": "i"}}).Sort("-timestamp") if err := q.Distinct("key", &res); err != nil { logging.Warn("SeenAnyMatching Find error: %v", err) return []string{} } logging.Debug("Looked for matches, found %#v", res) return res }
func load(line *base.Line) { // We're connected to IRC, so load saved reminders r := rc.LoadAndPrune() for i := range r { if r[i] == nil { logging.Warn("Nil reminder %d from LoadAndPrune", i) continue } Remind(r[i]) } }
func load(ctx *bot.Context) { // We're connected to IRC, so load saved reminders r := rc.LoadAndPrune() for i := range r { if r[i] == nil { logging.Warn("Nil reminder %d from LoadAndPrune", i) continue } Remind(r[i], ctx) } }
func Shutdown() { lock.Lock() defer lock.Unlock() if bot == nil { logging.Fatal("Called Shutdown() before Init().") } if !bot.connected { logging.Warn("Not connected to servers.") } bot.connected = false bot.servers.Shutdown(false) }