diff --git a/cmd/jia.go b/cmd/jia.go index 222514a..912906a 100644 --- a/cmd/jia.go +++ b/cmd/jia.go @@ -2,13 +2,9 @@ package main import ( "fmt" - "log" - "regexp" - "strconv" "github.com/ifvictr/jia/pkg/jia" "github.com/joho/godotenv" - "github.com/slack-go/slack" ) func init() { @@ -19,60 +15,7 @@ func main() { fmt.Println("Starting Jia…") config := jia.NewConfig() - api := slack.New(config.BotToken) - rtm := api.NewRTM() - - lastValidNumber := 0 - var lastSender string - - go rtm.ManageConnection() - - for event := range rtm.IncomingEvents { - switch ev := event.Data.(type) { - case *slack.MessageEvent: - // Ignore messages that aren't in the target channel, or are non-user messages - if ev.Channel != config.ChannelId || ev.User == "USLACKBOT" || ev.User == "" { - continue - } - - // Attempt to extract a positive number from the beginning of a string - countPattern := regexp.MustCompile(`^\d+`) - matchedNumber, err := strconv.Atoi(countPattern.FindString(ev.Text)) - log.Println(matchedNumber) - - // Ignore messages that don't have numbers - if err != nil { - log.Println("Failed to retrieve number, skipping…") - continue - } - - // Reject if sender also sent the previous message - if ev.User == lastSender { - api.AddReaction("bangbang", slack.ItemRef{ - Channel: ev.Channel, - Timestamp: ev.Timestamp, - }) - api.PostEphemeral(ev.Channel, ev.User, slack.MsgOptionText("You counted consecutively! That’s not allowed.", false)) - continue - } - - // Ignore numbers that aren't in order - if matchedNumber != lastValidNumber+1 { - api.AddReaction("bangbang", slack.ItemRef{ - Channel: ev.Channel, - Timestamp: ev.Timestamp, - }) - api.PostEphemeral(ev.Channel, ev.User, slack.MsgOptionText("You counted incorrectly! The next valid number is "+strconv.Itoa(lastValidNumber+1), false)) - continue - } - - // Finally! - lastValidNumber = matchedNumber - lastSender = ev.User - api.AddReaction("+1", slack.ItemRef{ - Channel: ev.Channel, - Timestamp: ev.Timestamp, - }) - } - } + // Start receiving messages + fmt.Println(fmt.Sprintf("Listening on port %d", config.Port)) + jia.StartServer(config) } diff --git a/go.mod b/go.mod index a987b4c..b03288d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/ifvictr/jia go 1.14 require ( - github.com/gorilla/websocket v1.4.2 // indirect + github.com/go-redis/redis v6.15.8+incompatible github.com/joho/godotenv v1.3.0 github.com/pkg/errors v0.9.1 // indirect github.com/slack-go/slack v0.6.5 diff --git a/go.sum b/go.sum index 4ea4c9f..451eb3b 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o= +github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= diff --git a/pkg/jia/config.go b/pkg/jia/config.go index beb5c88..418694d 100644 --- a/pkg/jia/config.go +++ b/pkg/jia/config.go @@ -2,17 +2,24 @@ package jia import ( "os" + "strconv" ) type Config struct { - BotToken string - ChannelId string + BotToken string + ChannelID string + Port int + RedisURL string + VerificationToken string } func NewConfig() *Config { return &Config{ - BotToken: getEnv("SLACK_CLIENT_BOT_TOKEN", ""), - ChannelId: getEnv("SLACK_CHANNEL_ID", ""), + BotToken: getEnv("SLACK_CLIENT_BOT_TOKEN", ""), + ChannelID: getEnv("SLACK_CHANNEL_ID", ""), + Port: getEnvAsInt("PORT", 3000), + RedisURL: getEnv("REDIS_URL", "redis://localhost:6379/0"), + VerificationToken: getEnv("SLACK_VERIFICATION_TOKEN", ""), } } @@ -24,3 +31,12 @@ func getEnv(key string, defaultValue string) string { return defaultValue } + +func getEnvAsInt(key string, defaultValue int) int { + valueStr := getEnv(key, "") + if value, err := strconv.Atoi(valueStr); err == nil { + return value + } + + return defaultValue +} diff --git a/pkg/jia/handlers.go b/pkg/jia/handlers.go new file mode 100644 index 0000000..e786adb --- /dev/null +++ b/pkg/jia/handlers.go @@ -0,0 +1,17 @@ +package jia + +import ( + "github.com/slack-go/slack" + "github.com/slack-go/slack/slackevents" +) + +func HandleInnerEvent(slackClient *slack.Client, innerEvent *slackevents.EventsAPIInnerEvent) { + switch e := innerEvent.Data.(type) { + case *slackevents.MessageEvent: + onMessage(slackClient, e) + break + } +} + +func onMessage(slackClient *slack.Client, event *slackevents.MessageEvent) { +} diff --git a/pkg/jia/server.go b/pkg/jia/server.go new file mode 100644 index 0000000..a890b28 --- /dev/null +++ b/pkg/jia/server.go @@ -0,0 +1,66 @@ +package jia + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/go-redis/redis" + "github.com/slack-go/slack" + "github.com/slack-go/slack/slackevents" +) + +var ( + jiaConfig *Config + redisClient *redis.Client + slackClient *slack.Client +) + +func StartServer(config *Config) { + jiaConfig = config + // Set up Redis connection + options, err := redis.ParseURL(config.RedisURL) + if err != nil { + panic(err) + } + redisClient = redis.NewClient(options) + + // Initialize Slack app + slackClient = slack.New(config.BotToken) + + // Start receiving events + http.HandleFunc("/slack/events", handleSlackEvents) + http.ListenAndServe(fmt.Sprintf("127.0.0.1:%d", config.Port), nil) +} + +func handleSlackEvents(w http.ResponseWriter, r *http.Request) { + // Verify the payload was sent by Slack. + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + body := buf.String() + apiEvent, err := slackevents.ParseEvent(json.RawMessage(body), + slackevents.OptionVerifyToken( + &slackevents.TokenComparator{VerificationToken: jiaConfig.VerificationToken})) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + // Handle the event that came through + switch apiEvent.Type { + case slackevents.URLVerification: + var r *slackevents.ChallengeResponse + err := json.Unmarshal([]byte(body), &r) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + + w.Header().Set("Content-Type", "text") + w.Write([]byte(r.Challenge)) + break + case slackevents.CallbackEvent: + HandleInnerEvent(slackClient, &apiEvent.InnerEvent) + break + } +}