slacker

by shomali11

shomali11 /slacker

Slack Bot Framework

442 Stars 53 Forks Last release: Not found MIT License 143 Commits 0 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

slacker Build Status Go Report Card GoDoc License: MIT Mentioned in Awesome Go

Built on top of the Slack API github.com/slack-go/slack with the idea to simplify the Real-Time Messaging feature to easily create Slack Bots, assign commands to them and extract parameters.

Features

  • Easy definitions of commands and their input
  • Available bot initialization, errors and default handlers
  • Simple parsing of String, Integer, Float and Boolean parameters
  • Contains support for
    context.Context
  • Built-in
    help
    command
  • Replies can be new messages or in threads
  • Supports authorization
  • Bot responds to mentions and direct messages
  • Handlers run concurrently via goroutines
  • Produces events for executed commands
  • Full access to the Slack API github.com/slack-go/slack

Dependencies

Install

go get github.com/shomali11/slacker

Examples

Example 1

Defining a command using slacker

package main

import ( "context" "log"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

definition := &slacker.CommandDefinition{
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("pong")
    },
}

bot.Command("ping", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 2

Defining a command with an optional description and example. The handler replies to a thread.

package main

import ( "context" "log"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

definition := &slacker.CommandDefinition{
    Description: "Ping!",
    Example:     "ping",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("pong", slacker.WithThreadReply(true))
    },
}

bot.Command("ping", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 3

Defining a command with a parameter

package main

import ( "context" "log"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

definition := &slacker.CommandDefinition{
    Description: "Echo a word!",
    Example:     "echo hello",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        word := request.Param("word")
        response.Reply(word)
    },
}

bot.Command("echo <word>", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 4

Defining a command with two parameters. Parsing one as a string and the other as an integer. (The second parameter is the default value in case no parameter was passed or could not parse the value)

package main

import ( "context" "log"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

definition := &amp;slacker.CommandDefinition{
    Description: "Repeat a word a number of times!",
    Example:     "repeat hello 10",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        word := request.StringParam("word", "Hello!")
        number := request.IntegerParam("number", 1)
        for i := 0; i &lt; number; i++ {
            response.Reply(word)
        }
    },
}

bot.Command("repeat <word> <number>", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 5

Defines two commands that display sending errors to the Slack channel. One that replies as a new message. The other replies to the thread.

package main

import ( "context" "errors" "log"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

messageReplyDefinition := &amp;slacker.CommandDefinition{
    Description: "Tests errors in new messages",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.ReportError(errors.New("Oops!"))
    },
}

threadReplyDefinition := &amp;slacker.CommandDefinition{
    Description: "Tests errors in threads",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.ReportError(errors.New("Oops!"), slacker.WithThreadError(true))
    },
}

bot.Command("message", messageReplyDefinition)
bot.Command("thread", threadReplyDefinition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 6

Send a "Typing" indicator

package main

import ( "context" "log" "time"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

definition := &amp;slacker.CommandDefinition{
    Description: "Server time!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Typing()

        time.Sleep(time.Second)

        response.Reply(time.Now().Format(time.RFC1123))
    },
}

bot.Command("time", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 7

Showcasing the ability to access the github.com/slack-go/slack API and the Real-Time Messaging Protocol. In this example, we are sending a message using RTM and uploading a file using the Slack API.

package main

import ( "context" "log"

"github.com/shomali11/slacker"
"github.com/slack-go/slack"

)

func main() { bot := slacker.NewClient("")

definition := &amp;slacker.CommandDefinition{
    Description: "Upload a word!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        word := request.Param("word")

        channel := botCtx.Event().Channel
        rtm := botCtx.RTM()
        client := botCtx.Client()

        rtm.SendMessage(rtm.NewOutgoingMessage("Uploading file ...", channel))
        client.UploadFile(slack.FileUploadParameters{Content: word, Channels: []string{channel}})
    },
}

bot.Command("upload <word>", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 8

Showcasing the ability to leverage

context.Context
to add a timeout
package main

import ( "context" "errors" "log" "time"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

definition := &amp;slacker.CommandDefinition{
    Description: "Process!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        timedContext, cancel := context.WithTimeout(botCtx.Context(), time.Second)
        defer cancel()

        select {
        case 

Example 9

Showcasing the ability to add attachments to a

Reply
package main

import ( "context" "log"

"github.com/shomali11/slacker"
"github.com/slack-go/slack"

)

func main() { bot := slacker.NewClient("")

definition := &amp;slacker.CommandDefinition{
    Description: "Echo a word!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        word := request.Param("word")

        attachments := []slack.Attachment{}
        attachments = append(attachments, slack.Attachment{
            Color:      "red",
            AuthorName: "Raed Shomali",
            Title:      "Attachment Title",
            Text:       "Attachment Text",
        })

        response.Reply(word, slacker.WithAttachments(attachments))
    },
}

bot.Command("echo <word>", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 10

Showcasing the ability to add blocks to a

Reply
package main

import ( "context" "log"

"github.com/shomali11/slacker"
"github.com/slack-go/slack"

)

func main() { bot := slacker.NewClient("")

definition := &amp;slacker.CommandDefinition{
    Description: "Echo a word!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        word := request.Param("word")

        attachments := []slack.Block{}
        attachments = append(attachments, slack.NewContextBlock("1",
            slack.NewTextBlockObject("mrkdwn", "Hi!", false, false)),
        )

        response.Reply(word, slacker.WithBlocks(attachments))
    },
}

bot.Command("echo <word>", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 11

Showcasing the ability to create custom responses via

CustomResponse
package main

import ( "log"

"context"
"errors"
"fmt"

"github.com/shomali11/slacker"

)

const ( errorFormat = "> Custom Error: %s" )

func main() { bot := slacker.NewClient("")

bot.CustomResponse(NewCustomResponseWriter)

definition := &amp;slacker.CommandDefinition{
    Description: "Custom!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("custom")
        response.ReportError(errors.New("oops"))
    },
}

bot.Command("custom", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

// NewCustomResponseWriter creates a new ResponseWriter structure func NewCustomResponseWriter(botCtx slacker.BotContext) slacker.ResponseWriter { return &MyCustomResponseWriter{botCtx: botCtx} }

// MyCustomResponseWriter a custom response writer type MyCustomResponseWriter struct { botCtx slacker.BotContext }

// ReportError sends back a formatted error message to the channel where we received the event from func (r *MyCustomResponseWriter) ReportError(err error, options ...slacker.ReportErrorOption) { rtm := r.botCtx.RTM() event := r.botCtx.Event() rtm.SendMessage(rtm.NewOutgoingMessage(fmt.Sprintf(errorFormat, err.Error()), event.Channel)) }

// Typing send a typing indicator func (r *MyCustomResponseWriter) Typing() { rtm := r.botCtx.RTM() event := r.botCtx.Event() rtm.SendMessage(rtm.NewTypingMessage(event.Channel)) }

// Reply send a attachments to the current channel with a message func (r *MyCustomResponseWriter) Reply(message string, options ...slacker.ReplyOption) { rtm := r.botCtx.RTM() event := r.botCtx.Event() rtm.SendMessage(rtm.NewOutgoingMessage(message, event.Channel)) }

Example 12

Showcasing the ability to toggle the slack Debug option via

WithDebug
package main

import ( "context" "github.com/shomali11/slacker" "log" )

func main() { bot := slacker.NewClient("", slacker.WithDebug(true))

definition := &amp;slacker.CommandDefinition{
    Description: "Ping!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("pong")
    },
}

bot.Command("ping", definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 13

Defining a command that can only be executed by authorized users

package main

import ( "context" "log"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

authorizedUsers := []string{"<user id>"}

authorizedDefinition := &amp;slacker.CommandDefinition{
    Description: "Very secret stuff",
    AuthorizationFunc: func(botCtx slacker.BotContext, request slacker.Request) bool {
        return contains(authorizedUsers, botCtx.Event().User)
    },
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("You are authorized!")
    },
}

bot.Command("secret", authorizedDefinition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

func contains(list []string, element string) bool { for _, value := range list { if value == element { return true } } return false }

Example 14

Adding handlers to when the bot is connected, encounters an error and a default for when none of the commands match

package main

import ( "log"

"context"
"fmt"

"github.com/shomali11/slacker"

)

func main() { bot := slacker.NewClient("")

bot.Init(func() {
    log.Println("Connected!")
})

bot.Err(func(err string) {
    log.Println(err)
})

bot.DefaultCommand(func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
    response.Reply("Say what?")
})

bot.DefaultEvent(func(event interface{}) {
    fmt.Println(event)
})

definition := &amp;slacker.CommandDefinition{
    Description: "help!",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("Your own help function...")
    },
}

bot.Help(definition)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

Example 15

Listening to the Commands Events being produced

package main

import ( "fmt" "log"

"context"

"github.com/shomali11/slacker"

)

func printCommandEvents(analyticsChannel ")

go printCommandEvents(bot.CommandEvents())

bot.Command("ping", &amp;slacker.CommandDefinition{
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        response.Reply("pong")
    },
})

bot.Command("echo <word>", &amp;slacker.CommandDefinition{
    Description: "Echo a word!",
    Example:     "echo hello",
    Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
        word := request.Param("word")
        response.Reply(word)
    },
})

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
    log.Fatal(err)
}

}

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.