Skip to content

Instantly share code, notes, and snippets.

@philpennock
Created January 13, 2016 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philpennock/773ecabd5445de5c9aaf to your computer and use it in GitHub Desktop.
Save philpennock/773ecabd5445de5c9aaf to your computer and use it in GitHub Desktop.
A simple Slack responder to the /uuid command
package main
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"strconv"
"strings"
"github.com/apcera/util/uuid"
"github.com/brettbuddin/victor"
)
var programOptions struct {
listenSpec string
victorChatSystem string
}
func init() {
port := os.Getenv("PORT")
if port == "" {
port = ":8042"
} else if _, err := strconv.Atoi(port); err == nil {
port = ":" + port
}
flag.StringVar(&programOptions.listenSpec, "listen", port,
"webserver listen spec")
flag.StringVar(&programOptions.victorChatSystem, "chat-system", "slack",
"victor chat system; 'slack' for prod, 'shell' for console testing")
}
type slackBot struct {
// victor.New() returns a pointer to a private type, but that should
// satisfy this interface:
bot victor.Robot
stop chan struct{}
sigChan chan os.Signal
}
var variantMap map[string]func() uuid.UUID
func init() {
variantMap = map[string]func() uuid.UUID{
// 3 & 5 require parameters, TODO
"default": uuid.Generate,
"generate": uuid.Generate,
"1": uuid.Variant1,
"4": uuid.Variant4,
"variant1": uuid.Variant1,
"variant4": uuid.Variant4,
"v1": uuid.Variant1,
"v4": uuid.Variant4,
}
}
func handleUUID(s victor.State) {
var k string
if len(s.Params()) == 0 || s.Params()[0] == "" {
k = "default"
} else {
k = strings.ToLower(s.Params()[0])
}
if generator, ok := variantMap[k]; ok {
u := generator()
log.Printf("[user %q channel %q] issuing UUID/%q: %s",
s.Message().UserName(), s.Message().ChannelName(),
k, u)
s.Chat().Send(
s.Message().ChannelID(),
fmt.Sprintf("%s [requested by %q]", u.String(), s.Message().UserName()))
return
}
if k == "help" {
log.Printf("[user %q] help requested", s.Message().UserName())
s.Chat().Send(s.Message().UserID(), "No parameters, or `v1` or `v4` for those variants")
return
}
log.Printf("[user %q] unknown UUID type: %q", s.Message().UserName(), k)
s.Chat().Send(s.Message().UserID(), fmt.Sprintf("Sorry, don't know how to make a %q UUID", k))
}
func handleAnything(s victor.State) {
log.Printf("[user %q] saw 'anything': %v", s.Message().UserName(), s.Params())
s.Chat().Send(s.Message().UserID(), fmt.Sprintf("debug fallthru: matched on %v", s.Params()))
}
func makeSlackBot() *slackBot {
sb := &slackBot{
bot: victor.New(victor.Config{
Name: "uuidbot",
ChatAdapter: programOptions.victorChatSystem,
StoreAdapter: "memory",
HTTPAddr: programOptions.listenSpec,
}),
stop: make(chan struct{}),
}
// This one would be used for messages in some kinds of integrations
//sb.bot.HandleFunc(`(?i)uuid(?:\s+(\S+))?`, handleUUID)
// but for a slack command, we just get the parameters to the command
sb.bot.HandleFunc(`(\S+)?`, handleUUID)
// uncomment this one for debugging:
//sb.bot.HandleFunc(`(.+)`, handleAnything)
return sb
}
func (b *slackBot) Run() {
go b.bot.Run()
_ = <-b.stop
b.bot.Stop()
}
func (b *slackBot) StopOnSignals(sigList ...os.Signal) {
if b.sigChan == nil {
b.sigChan = make(chan os.Signal, 1)
go func(sigIn <-chan os.Signal, stopOut chan<- struct{}) {
for {
_, ok := <-sigIn
if !ok {
break
}
stopOut <- struct{}{}
}
}(b.sigChan, b.stop)
}
for _, s := range sigList {
signal.Notify(b.sigChan, s)
}
}
func main() {
flag.Parse()
bot := makeSlackBot()
bot.StopOnSignals(os.Interrupt)
log.Printf("Starting server listening at %q", programOptions.listenSpec)
bot.Run()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment