func (p *Plugin) detailTask(msg *slick.Message, id string) { todo := p.store.Get(msg.Channel) index, err := getTaskIndex(id, todo) if err != nil { msg.ReplyMention("Task not found...") return } task := todo[index] msg.Reply(printTaskDetails(task)) }
func (p *Plugin) deleteTask(msg *slick.Message, id string) { todo := p.store.Get(msg.Channel) index, err := getTaskIndex(id, todo) if err != nil { msg.ReplyMention("Task not found...") return } todo = append(todo[:index], todo[index+1:]...) p.store.Put(msg.Channel, todo) msg.Reply(fmt.Sprintf("Deleted task `%s`", id)) }
func (p *Plugin) handleRecognize(listen *slick.Listener, msg *slick.Message) { users := msg.Match[1] feat := msg.Match[5] channel := p.bot.GetChannelByName(p.config.Channel) if channel == nil { fmt.Println("Didn't find the recognitions, can't handle `!recognition` requests. Searched for:", p.config.Channel) return } recipients := parseRecipients(users) if userIsInRecipients(msg.FromUser.ID, recipients) { msg.ReplyMention("you can't recognize yourself, can you ?!") return } announcement := p.bot.SendOutgoingMessage(fmt.Sprintf("<@%s|%s> would like to recognize %s\n>>> For %s", msg.FromUser.ID, msg.FromUser.Name, users, feat), channel.ID) announcement.AddReaction("+1") announcement.AddReaction("dart") announcement.AddReaction("tada") announcement.AddReaction("100") announcement.AddReaction("clap") announcement.AddReaction("muscle") announcement.AddReaction("nerd_face") announcement.AddReaction("joy") announcement.OnAck(func(ack *slack.AckMessage) { ts := ack.Timestamp domain := p.bot.Config.TeamDomain url := fmt.Sprintf("https://%s.slack.com/archives/%s/p%s", domain, channel.Name, strings.Replace(ts, ".", "", 1)) msg.ReplyMention("Great! Everyone can upvote this recognition here %s", url) recog := &Recognition{ MsgTimestamp: ts, CreatedAt: time.Now(), Sender: msg.FromUser.ID, Recipients: recipients, Categories: []string{}, Reactions: map[string]int{ msg.FromUser.ID: 1, }, } p.store.Put(recog) p.bot.PubSub.Pub(recog, "recognition:recognized") //fmt.Println("Timestamp for the message:", ts) }) }
func (p *Plugin) handleTodo(listen *slick.Listener, msg *slick.Message) { idFormat := regexp.MustCompile(`^[a-z]{2}$`) match := msg.Match parts := strings.Split(match[0], " ") if len(parts) == 1 { p.listTasks(msg, false) return } act := parts[1] allCommands := map[string]bool{ "all": true, "-a": true, "--all": true, ":allthethings:": true, } switch act { case "add": p.createTask(msg) case "close", "fix", "scratch", "done", "strike", "ship", ":boom:", "remove": if len(parts) < 3 || !idFormat.MatchString(parts[2]) { msg.ReplyMention(fmt.Sprintf("Please %s a task with `!todo %s ID`", act, act)) return } if act == "remove" { p.deleteTask(msg, parts[2]) } else { p.closeTask(msg, parts[2]) } case "list": includeClosed := len(parts) > 2 && allCommands[parts[2]] p.listTasks(msg, includeClosed) case "help": p.replyHelp(msg, "") default: if idFormat.MatchString(act) { p.detailTask(msg, act) } else { p.replyHelp(msg, "Wooops, not sure what you wanted.\n") } } }
func (p *Plugin) closeTask(msg *slick.Message, id string) { todo := p.store.Get(msg.Channel) index, err := getTaskIndex(id, todo) if err != nil { msg.ReplyMention("Task not found...") return } parts := strings.Split(msg.Match[0], " ") task := todo[index] task.Closed = true task.ClosedAt = time.Now() if len(parts) > 3 { task.ClosingNote = strings.Join(parts[3:], " ") } p.store.Put(msg.Channel, todo) msg.Reply("`" + task.ID + "` ~" + strings.Join(task.Text, " // ") + "~ " + task.ClosingNote) }
func (p *Plugin) createTask(msg *slick.Message) { var text []string text = append(text, strings.TrimPrefix(msg.Match[0], "!todo add ")) todo := p.store.Get(msg.Channel) if len(todo) > 600 { msg.ReplyMention("Gosh you have over 600 tasks!!! Clean some up first.") return } id := p.generateRandomID(todo) task := &Task{ ID: id, CreatedAt: time.Now(), User: msg.FromUser.ID, Text: text, Closed: false, } todo = append(todo, task) p.store.Put(msg.Channel, todo) msg.Reply("`" + task.ID + "` added to the todo") }
func (wicked *Wicked) ChatHandler(listen *slick.Listener, msg *slick.Message) { bot := listen.Bot uuidNow := time.Now() if strings.HasPrefix(msg.Text, "!wicked ") { fromRoom := "" if msg.FromChannel != nil { fromRoom = msg.FromChannel.ID } availableRoom := wicked.FindAvailableRoom(fromRoom) if availableRoom == nil { msg.Reply("No available Wicked Confroom for a meeting! Seems you'll need to create new Wicked Confrooms !") goto continueLogging } id := wicked.NextMeetingID() meeting := NewMeeting(id, msg.FromUser, msg.Text[7:], bot, availableRoom, uuidNow) wicked.pastMeetings = append(wicked.pastMeetings, meeting) wicked.meetings[availableRoom.ID] = meeting if availableRoom.ID == fromRoom { meeting.sendToRoom(fmt.Sprintf(`*** Starting wicked meeting W%s in here.`, meeting.ID)) } else { msg.Reply(fmt.Sprintf(`*** Starting wicked meeting W%s in room "%s". Join with !join W%s`, meeting.ID, availableRoom.Name, meeting.ID)) initiatedFrom := "" if fromRoom != "" { initiatedFrom = fmt.Sprintf(` in "%s"`, msg.FromChannel.Name) } meeting.sendToRoom(fmt.Sprintf(`*** Wicked meeting initiated by @%s%s. Goal: %s`, msg.FromUser.Name, initiatedFrom, meeting.Goal)) } meeting.sendToRoom(fmt.Sprintf(`*** Access report at %s/wicked/%s.html`, wicked.bot.Config.WebBaseURL, meeting.ID)) meeting.setTopic(fmt.Sprintf(`[Running] W%s goal: %s`, meeting.ID, meeting.Goal)) } else if strings.HasPrefix(msg.Text, "!join") { match := joinMatcher.FindStringSubmatch(msg.Text) if match == nil { msg.ReplyMention(`invalid !join syntax. Use something like "!join W123"`) } else { for _, meeting := range wicked.meetings { if match[1] == meeting.ID { meeting.sendToRoom(fmt.Sprintf(`*** @%s asked to join`, msg.FromUser.Name)) } } } } continueLogging: // // Public commands and messages // if msg.FromChannel == nil { return } room := msg.FromChannel.ID meeting, meetingExists := wicked.meetings[room] if !meetingExists { return } user := meeting.ImportUser(msg.FromUser) if strings.HasPrefix(msg.Text, "!proposition ") { decision := meeting.AddDecision(user, msg.Text[12:], uuidNow) if decision == nil { msg.Reply("Whoops, wrong syntax for !proposition") } else { msg.Reply(fmt.Sprintf("Proposition added, ref: D%s", decision.ID)) } } else if strings.HasPrefix(msg.Text, "!ref ") { meeting.AddReference(user, msg.Text[4:], uuidNow) msg.Reply("Ref. added") } else if strings.HasPrefix(msg.Text, "!conclude") { meeting.Conclude() // TODO: kill all waiting goroutines dealing with messaging delete(wicked.meetings, room) meeting.sendToRoom("Concluding Wicked meeting, that's all folks!") meeting.setTopic(fmt.Sprintf(`[Concluded] W%s goal: %s`, meeting.ID, meeting.Goal)) } else if match := decisionMatcher.FindStringSubmatch(msg.Text); match != nil { decision := meeting.GetDecisionByID(match[1]) if decision != nil { decision.RecordPlusplus(user) msg.ReplyMention("noted") } } // Log message newMessage := &Message{ From: user, Timestamp: uuidNow, Text: msg.Text, } meeting.Logs = append(meeting.Logs, newMessage) }
func (funny *Funny) ChatHandler(listen *slick.Listener, msg *slick.Message) { bot := listen.Bot if msg.Contains("mama") { listen.Bot.Listen(&slick.Listener{ ListenDuration: time.Duration(10 * time.Second), MessageHandlerFunc: func(listen *slick.Listener, msg *slick.Message) { if strings.Contains(msg.Text, "papa") { msg.Reply("3s", "yo rocker").DeleteAfter("3s") msg.AddReaction("wink") go func() { time.Sleep(3 * time.Second) msg.AddReaction("beer") time.Sleep(1 * time.Second) msg.RemoveReaction("wink") }() } }, }) } if msg.MentionsMe { if msg.Contains("you're funny") { if bot.Mood == slick.Happy { msg.Reply("/me blushes") } else { msg.Reply("here's another one") msg.Reply(slick.RandomString("robot jokes")) } } else if msg.ContainsAny([]string{"dumb ass", "dumbass"}) { msg.Reply("don't say such things") } else if msg.ContainsAny([]string{"thanks", "thank you", "thx", "thnks"}) { msg.Reply(bot.WithMood("my pleasure", "any time, just ask, I'm here for you, ffiieeewww!get a life")) } else if msg.Contains("how are you") && msg.MentionsMe { msg.ReplyMention(bot.WithMood("good, and you ?", "I'm wild today!! wadabout you ?")) bot.Listen(&slick.Listener{ ListenDuration: 60 * time.Second, FromUser: msg.FromUser, FromChannel: msg.FromChannel, MentionsMeOnly: true, MessageHandlerFunc: func(listen *slick.Listener, msg *slick.Message) { msg.ReplyMention(bot.WithMood("glad to hear it!", "zwweeeeeeeeet !")) listen.Close() }, TimeoutFunc: func(listen *slick.Listener) { msg.ReplyMention("well, we can catch up later") listen.Close() }, }) } } if msg.ContainsAny([]string{"lot of excitement", "that's exciting", "how exciting", "much excitement"}) { msg.Reply("http://static.fjcdn.com/gifs/Japanese+kids+spongebob+toys_0ad21b_3186721.gif") } else if msg.ContainsAny([]string{"what is your problem", "what's your problem", "is there a problem", "which problem"}) { msg.Reply("http://media4.giphy.com/media/19hU0m3TJe6I/200w.gif") } else if msg.Contains("force push") { url := slick.RandomString("forcePush") msg.Reply(url) } else if msg.ContainsAny([]string{"there is a bug", "there's a bug"}) { msg.Reply("https://s3.amazonaws.com/pushbullet-uploads/ujy7DF0U8wm-9YYvLZkmSM8pMYcxCXXig8LjJORE9Xzt/The-life-of-a-coder.jpg") } else if msg.ContainsAny([]string{"oh yeah", "approved"}) { msg.Reply("https://i.chzbgr.com/maxW250/4496881920/h9C58F860.gif") } else if msg.Contains("ice cream") { msg.Reply("http://i.giphy.com/IGyLuFXIGSJj2.gif") msg.Reply("I love ice cream too") } else if msg.ContainsAny([]string{"lot of tension", "some tension", " tensed"}) { msg.Reply("http://thumbpress.com/wp-content/uploads/2014/01/funny-gif-meeting-strangers-girl-scared1.gif") msg.Reply("tensed, like that ?") } else if msg.Contains("quick fix") { msg.Reply("http://blog.pgi.com/wp-content/uploads/2013/02/jim-carey.gif") msg.Reply("make it real quick") } else if msg.ContainsAny([]string{"crack an egg", "crack something", "to crack"}) { msg.Reply("http://s3-ec.buzzfed.com/static/enhanced/webdr02/2012/11/8/18/anigif_enhanced-buzz-31656-1352415875-9.gif") msg.Reply("crack an egg, yeah") } else if msg.ContainsAny([]string{"i'm stuck", "I'm stuck", "we're stuck"}) { msg.Reply("http://media.giphy.com/media/RVlWx1msxnf7W/giphy.gif") msg.Reply("I'm stuck too!") } else if msg.ContainsAny([]string{"watching tv", "watch tv"}) { msg.Reply("http://i0.kym-cdn.com/photos/images/newsfeed/000/495/040/9ab.gif") msg.Reply("like that ?") } else if msg.ContainsAny([]string{"spider", "pee on", "inappropriate"}) { msg.Reply("https://i.chzbgr.com/maxW500/5626597120/hB2E11E61.gif") } else if msg.ContainsAny([]string{"a meeting", "an interview"}) { msg.Reply("like this one") msg.Reply("https://i.chzbgr.com/maxW500/6696664320/hFC69678C.gif") } else if msg.ContainsAny([]string{"it's odd", "it is odd", "that's odd", "that is odd", "it's awkward", "it is awkward", "that's awkward", "that is awkward"}) { term := "awkward" if msg.Contains("odd") { term = "odd" } msg.Reply(fmt.Sprintf("THAT's %s", term)) msg.Reply("https://i.chzbgr.com/maxW500/8296294144/h7AC1001C.gif") } else if msg.Text == "ls" { msg.Reply("/code deploy/ Contributors-Guide/ image_server/ sheep_porn/ streambed/\nstreamhead/ README.md") } else if msg.ContainsAny([]string{"that's really cool", "that is really cool", "really happy"}) { msg.Reply("http://media.giphy.com/media/BlVnrxJgTGsUw/giphy.gif") } else if msg.ContainsAny([]string{"difficult problem", "hard problem"}) { msg.Reply("naming things, cache invalidation and off-by-1 errors are the two most difficult computer science problems") } else if msg.Contains("in theory") { msg.Reply("yeah, theory and practice perfectly match... in theory.") } else if msg.Contains("dishes") { msg.Reply(slick.RandomString("dishes")) } else if msg.Contains(" bean") { msg.Reply("http://media3.giphy.com/media/c35RMDO6luMaQ/500w.gif") } else if msg.Contains("steak") { msg.Reply("http://media.tumblr.com/tumblr_me6r52h1md1r6nno1.gif") } else if msg.ContainsAny([]string{"booze", "alcohol", "martini", " dog "}) { msg.Reply("http://media2.giphy.com/media/ZmJBjPdd44gXS/200w.gif") } else if msg.ContainsAny([]string{"internet", " tube "}) { msg.Reply("https://pbs.twimg.com/media/By0J3YHCcAA4UBo.jpg:large") } }
func (totw *Totw) ChatHandler(conv *slick.Listener, msg *slick.Message) { if strings.HasPrefix(msg.Text, "!totw") || strings.HasPrefix(msg.Text, "!techoftheweek") { msg.ReplyMention(slick.RandomString("tech adept")) } }
func (v *Vote) voteHandler(listen *slick.Listener, msg *slick.Message) { v.mutex.Lock() defer v.mutex.Unlock() bot := v.bot // TODO: ok, @kat wants to survey what's for lunch, use "!vote The Food Place http://food-place.url" .. you can vote for the same place with a substring: "!vote food place" // TODO: match "!vote Mucha Dogs http://bigdogs.com" // TODO: match "!vote mucha dogs" // TODO: match "!vote Other place if msg.Text == "!what-for-lunch" || msg.Text == "!vote-for-lunch" { msg.ReplyMention("you can say `!what-for-lunch 5m` to get a vote that will last 5 minutes. `!vote-for-lunch` is an alias") return } if msg.HasPrefix("!what-for-lunch ") || msg.HasPrefix("!vote-for-lunch ") { if v.runningVotes[msg.FromChannel.ID] != nil { msg.ReplyMention("vote is already running!").DeleteAfter("3s") return } timing := strings.TrimSpace(strings.SplitN(msg.Text, " ", 2)[1]) dur, err := time.ParseDuration(timing) if err != nil { msg.ReplyMention(fmt.Sprintf("couldn't parse duration: %s", err)) return } v.runningVotes[msg.FromChannel.ID] = make([]vote, 0) go func() { time.Sleep(dur) v.mutex.Lock() defer v.mutex.Unlock() res := make(map[string]int) for _, oneVote := range v.runningVotes[msg.FromChannel.ID] { res[oneVote.vote] = res[oneVote.vote] + 1 } // TODO: print report, clear up if len(res) == 0 { msg.ReplyMention("polls closed, but no one voted") } else { out := []string{"polls closed, here are the results:"} for theVote, count := range res { plural := "" if count > 1 { plural = "s" } out = append(out, fmt.Sprintf("* %s: %d vote%s", theVote, count, plural)) } msg.ReplyMention(strings.Join(out, "\n")) } delete(v.runningVotes, msg.FromChannel.ID) }() msg.Reply("<!channel> okay, what do we eat ? Votes are open. Use `!vote The Food Place http://food-place.url` .. you can vote for the same place with a substring, ex: `!vote food place`") } if msg.HasPrefix("!vote ") { running := v.runningVotes[msg.FromChannel.ID] if running == nil { msg.Reply(bot.WithMood("what vote ?!", "oh you're so cute! voting while there's no vote going on !")) return } voteCast := strings.TrimSpace(strings.SplitN(msg.Text, " ", 2)[1]) if len(voteCast) == 0 { return } // TODO: check for dupe for _, prevVote := range running { if msg.FromUser.ID == prevVote.user { // buzz off if you voted already msg.ReplyMention(bot.WithMood("you voted already", "trying to double vote ! how charming :)")) return } } for _, prevVote := range running { if strings.Contains(strings.ToLower(prevVote.vote), strings.ToLower(voteCast)) { running = append(running, vote{msg.FromUser.ID, prevVote.vote}) v.runningVotes[msg.FromChannel.ID] = running msg.ReplyMention(bot.WithMood("okay", "hmmm kaay")).DeleteAfter("2s") return } } running = append(running, vote{msg.FromUser.ID, voteCast}) v.runningVotes[msg.FromChannel.ID] = running msg.ReplyMention(bot.WithMood("taking note", "taking note! what a creative mind...")).DeleteAfter("2s") // TODO: match "!what-for-lunch 1h|5m|50s" } }