diff --git a/cmd/jia.go b/cmd/jia.go index 912906a..ea4a131 100644 --- a/cmd/jia.go +++ b/cmd/jia.go @@ -16,6 +16,6 @@ func main() { config := jia.NewConfig() // Start receiving messages - fmt.Println(fmt.Sprintf("Listening on port %d", config.Port)) + fmt.Printf("Listening on port %d\n", config.Port) jia.StartServer(config) } diff --git a/events.yaml b/events.yaml new file mode 100644 index 0000000..226ca6f --- /dev/null +++ b/events.yaml @@ -0,0 +1,6 @@ +- name: Test Event + start: 8:00 May 2, 2021 EDT + end: 7:59 May 3, 2021 EDT +- name: May Counting + start: 8:00 May 3, 2021 EDT + end: 20:00 May 31, 2021 EDT diff --git a/go.mod b/go.mod index b03288d..992dba2 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,5 @@ require ( github.com/joho/godotenv v1.3.0 github.com/pkg/errors v0.9.1 // indirect github.com/slack-go/slack v0.6.5 + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 451eb3b..4276b5e 100644 --- a/go.sum +++ b/go.sum @@ -18,3 +18,6 @@ github.com/slack-go/slack v0.6.4/go.mod h1:sGRjv3w+ERAUMMMbldHObQPBcNSyVB7KLKYfn github.com/slack-go/slack v0.6.5 h1:IkDKtJ2IROJNoe3d6mW870/NRKvq2fhLB/Q5XmzWk00= github.com/slack-go/slack v0.6.5/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/pkg/jia/config.go b/pkg/jia/config.go index f4dd833..15d0055 100644 --- a/pkg/jia/config.go +++ b/pkg/jia/config.go @@ -3,23 +3,67 @@ package jia import ( "os" "strconv" + "time" + + "gopkg.in/yaml.v2" ) +type Event struct { + Name string `yaml:"name"` + StartTime time.Time + EndTime time.Time + + StartString string `yaml:"start"` + EndString string `yaml:"end"` +} + type Config struct { BotToken string ChannelID string Port int RedisURL string VerificationToken string + + Events []Event +} + +func (c Config) GetRunningEvents() []Event { + var running_events []Event + + for _, event := range c.Events { + if time.Since(event.StartTime) > 0 && time.Until(event.EndTime) > 0 { + running_events = append(running_events, event) + } + } + + return running_events } func NewConfig() *Config { + events_file, err := os.ReadFile("events.yaml") + if err != nil { + panic(err) + } + + var events []Event + + err = yaml.Unmarshal(events_file, &events) + if err != nil { + panic(err) + } + + for i, event := range events { + events[i].StartTime, _ = time.Parse("15:04 Jan 2, 2006 MST", event.StartString) + events[i].EndTime, _ = time.Parse("15:04 Jan 2, 2006 MST", event.EndString) + } + return &Config{ BotToken: getEnv("SLACK_BOT_TOKEN", ""), ChannelID: getEnv("SLACK_CHANNEL_ID", ""), Port: getEnvAsInt("PORT", 3000), RedisURL: getEnv("REDIS_URL", "redis://localhost:6379/0"), VerificationToken: getEnv("SLACK_VERIFICATION_TOKEN", ""), + Events: events, } } diff --git a/pkg/jia/handlers.go b/pkg/jia/handlers.go index 788e4d2..458117e 100644 --- a/pkg/jia/handlers.go +++ b/pkg/jia/handlers.go @@ -94,6 +94,11 @@ func onMessage(slackClient *slack.Client, event *slackevents.MessageEvent) { // Increment the person's monthly count redisClient.Incr(fmt.Sprintf("leaderboard:%d-%d:%s", year, month, event.User)) + + // Increment the person's count for any running events + for _, counting_event := range jiaConfig.GetRunningEvents() { + redisClient.Incr(fmt.Sprintf("event:%s:%s", counting_event.Name, event.User)) + } } func HandleLeaderboardSlashCommand(w http.ResponseWriter, r *http.Request) { @@ -171,6 +176,76 @@ func HandleLeaderboardSlashCommand(w http.ResponseWriter, r *http.Request) { w.Write(resp) } +func HandleEventsSlashCommand(w http.ResponseWriter, r *http.Request) { + events := jiaConfig.GetRunningEvents() + + var blocks []slack.Block + + if len(events) == 0 { + blocks = append(blocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", "There aren't any counting events running right now.", false, false), nil, nil)) + } else if len(events) == 1 { + blocks = append(blocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", fmt.Sprintf(":calendar: Counting stats for event *%s*:", events[0].Name), false, false), nil, nil)) + + scan := redisClient.Scan(0, fmt.Sprintf("event:%s:*", events[0].Name), 10) + scanIterator := scan.Iterator() + + type Entry struct { + Number int + User string + } + + entries := []Entry{} + + for scanIterator.Next() { + entry := redisClient.Get(scanIterator.Val()) + entryInt, err := entry.Int() + if err != nil { + return + } + + if _, user, ok := parseEventEntry(scanIterator.Val()); ok { + entries = append(entries, Entry{ + Number: entryInt, + User: user, + }) + } + } + + // Sort entries + sort.Slice(entries, func(i, j int) bool { + return entries[i].Number > entries[j].Number + }) + + for i, v := range entries { + emoji := "" + if i == 0 { + emoji = ":first_place_medal:" + } else if i == 1 { + emoji = ":second_place_medal:" + } else if i == 2 { + emoji = ":third_place_medal:" + } + + blocks = append(blocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("%s <@%s> has counted *%d* so far", emoji, v.User, v.Number), false, false), nil, nil)) + } + + if len(entries) > 10 { + entries = entries[:10] + } + blocks = append(blocks, slack.NewContextBlock("", slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("Event will end at **, your time", events[0].EndTime.Unix()), false, false))) + } else { + blocks = append(blocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", "Something went wrong fetching the events leaderboard :cry:", false, false), nil, nil)) + } + + resp, _ := json.Marshal(map[string]interface{}{ + "blocks": blocks, + "response_type": "ephemeral", + }) + + w.Header().Add("Content-Type", "application/json") + w.Write(resp) +} + func parseLeaderboardEntry(key string) (string, bool) { re := regexp.MustCompile(`leaderboard:\d+-\d+:(\w+)`) @@ -180,3 +255,19 @@ func parseLeaderboardEntry(key string) (string, bool) { } return match[1], true } + +func parseEventEntry(key string) (event_name string, user_id string, ok bool) { + re := regexp.MustCompile(`event:(.+):(\w+)`) + + match := re.FindStringSubmatch(key) + if match == nil { + ok = false + return + } + + ok = true + event_name = match[1] + user_id = match[2] + + return +} diff --git a/pkg/jia/server.go b/pkg/jia/server.go index 64ecb8c..390bf8a 100644 --- a/pkg/jia/server.go +++ b/pkg/jia/server.go @@ -36,6 +36,7 @@ func StartServer(config *Config) { // Start receiving events http.HandleFunc("/slack/events", handleSlackEvents) http.HandleFunc("/slack/leaderboard", HandleLeaderboardSlashCommand) + http.HandleFunc("/slack/eventsCommand", HandleEventsSlashCommand) http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", config.Port), nil) }