func urlScan(ctx *bot.Context) { words := strings.Split(ctx.Text(), " ") n, c := ctx.Storable() for _, w := range words { if util.LooksURLish(w) { if u := uc.GetByUrl(w); u != nil { if u.Nick != bot.Nick(ctx.Nick) && time.Since(u.Timestamp) > 2*time.Hour { ctx.Reply("that URL first mentioned by %s %s ago", u.Nick, util.TimeSince(u.Timestamp)) } continue } u := urls.NewUrl(w, n, c) if len(w) > autoShortenLimit && ctx.Public() { u.Shortened = Encode(w) } if err := uc.Insert(u); err != nil { ctx.ReplyN("Couldn't insert url '%s': %s", w, err) continue } if u.Shortened != "" { ctx.Reply("%s's URL shortened as %s%s%s", ctx.Nick, bot.HttpHost(), shortenPath, u.Shortened) } lastseen[ctx.Target()] = u.Id } } }
func recordKick(ctx *bot.Context) { n, c := ctx.Storable() kn := bot.Nick(ctx.Text()) // seenNickFromLine doesn't work with the hacks for KICKING and KICKED // First, handle KICKING kr := sc.LastSeenDoing(ctx.Nick, "KICKING") if kr == nil { kr = seen.SawNick(n, c, "KICKING", ctx.Args[2]) } else { kr.Nick, kr.Chan = n, c kr.Timestamp, kr.Text = time.Now(), ctx.Args[2] } kr.OtherNick = kn _, err := sc.Upsert(kr.Id(), kr) if err != nil { ctx.Reply("Failed to store seen data: %v", err) } // Now, handle KICKED ke := sc.LastSeenDoing(ctx.Text(), "KICKED") if ke == nil { ke = seen.SawNick(kn, c, "KICKED", ctx.Args[2]) } else { ke.Nick, ke.Chan = kn, c ke.Timestamp, ke.Text = time.Now(), ctx.Args[2] } ke.OtherNick = n _, err = sc.Upsert(ke.Id(), ke) if err != nil { ctx.Reply("Failed to store seen data: %v", err) } }
// remind func set(ctx *bot.Context) { // s == <target> <reminder> in|at|on <time> s := strings.Fields(ctx.Text()) if len(s) < 4 { ctx.ReplyN("You asked me to remind %s.", ctx.Text()) return } at, ok, reminder, timestr := time.Now(), false, "", "" for i := 1; i+1 < len(s); i++ { lc := strings.ToLower(s[i]) if lc == "in" || lc == "at" || lc == "on" { timestr = strings.Join(s[i+1:], " ") } else if i+2 == len(s) { // Hack to test the last word for e.g. "tomorrow" i++ timestr = strings.ToLower(s[i]) } else { continue } // TODO(fluffle): surface better errors from datetime.Parse at, ok = datetime.Parse(timestr) if ok { reminder = strings.Join(s[1:i], " ") break } } if reminder == "" { ctx.ReplyN("You asked me to remind %s.", ctx.Text()) return } if !ok { ctx.ReplyN("Couldn't parse time string '%s'", timestr) return } now := time.Now() start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) if at.Before(now) && at.After(start) { at = at.Add(24 * time.Hour) } if at.Before(now) { ctx.ReplyN("Time '%s' is in the past.", timestr) return } n, c := ctx.Storable() // TODO(fluffle): Use state tracking! And do this better. t := bot.Nick(s[0]) if t.Lower() == strings.ToLower(ctx.Nick) || t.Lower() == "me" { t = n } r := reminders.NewReminder(reminder, at, t, n, c) if err := rc.Insert(r); err != nil { ctx.ReplyN("Error saving reminder: %v", err) return } // Any previously-generated list of reminders is now obsolete. delete(listed, ctx.Nick) ctx.ReplyN("%s", r.Acknowledge()) Remind(r, ctx) }
// tell func tell(ctx *bot.Context) { // s == <target> <stuff> txt := ctx.Text() idx := strings.Index(txt, " ") if idx == -1 { ctx.ReplyN("Tell who what?") return } tell := txt[idx+1:] n, c := ctx.Storable() t := bot.Nick(txt[:idx]) if t.Lower() == strings.ToLower(ctx.Nick) || t.Lower() == "me" { ctx.ReplyN("You're a dick. Oh, wait, that wasn't *quite* it...") return } r := reminders.NewTell(tell, t, n, c) if err := rc.Insert(r); err != nil { ctx.ReplyN("Error saving tell: %v", err) return } if s := pc.GetByNick(txt[:idx]); s.CanPush() { push.Push(s, fmt.Sprintf("%s in %s asked me to tell you:", ctx.Nick, ctx.Target()), tell) } // Any previously-generated list of reminders is now obsolete. delete(listed, ctx.Nick) ctx.ReplyN("%s", r.Acknowledge()) }
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 lookup(line *base.Line) { // Only perform extra prefix removal if we weren't addressed directly key := ToKey(line.Args[1], !line.Addressed) var fact *factoids.Factoid if fact = fc.GetPseudoRand(key); fact == nil && line.Cmd == "ACTION" { // Support sp0rkle's habit of stripping off it's own nick // but only for actions, not privmsgs. if strings.HasSuffix(key, bot.Nick()) { key = strings.TrimSpace(key[:len(key)-len(bot.Nick())]) fact = fc.GetPseudoRand(key) } } if fact == nil { return } // Chance is used to limit the rate of factoid replies for things // people say a lot, like smilies, or 'lol', or 'i love the peen'. chance := fact.Chance if key == "" { // This is doing a "random" lookup, triggered by someone typing in // something entirely composed of the chars stripped by ToKey(). // To avoid making this too spammy, forcibly limit the chance to 40%. chance = 0.4 } if rand.Float64() < chance { // Store this as the last seen factoid LastSeen(line.Args[0], fact.Id) // Update the Accessed field // TODO(fluffle): fd should take care of updating Accessed internally fact.Access(line.Storable()) // And store the new factoid data if err := fc.Update(bson.M{"_id": fact.Id}, fact); err != nil { bot.ReplyN(line, "I failed to update '%s' (%s): %s ", fact.Key, fact.Id, err) } keys := map[string]bool{key: true} val := recurse(fact.Value, keys) switch fact.Type { case factoids.F_ACTION: bot.Do(line, "%s", val) default: bot.Reply(line, "%s", val) } } }