- Notifications
You must be signed in to change notification settings - Fork0
atcoder/slacker
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Built on top of the Slack APIgithub.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.
- Supports Slack Apps usingSocket Mode
- 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 APIgithub.com/slack-go/slack
commander
github.com/shomali11/commanderslack
github.com/slack-go/slack
go get github.com/shomali11/slacker
Slacker works by communicating with the SlackEvents API using theSocket Mode connection protocol.
To get started, you must have or create aSlack App and enableSocket Mode
, which will generate your app token that will be needed to authenticate.
Additionally, you need to subscribe to events for your bot to respond to under theEvent Subscriptions
section. Common event subscriptions for bots includeapp_mention
ormessage.im
.
After setting up your subscriptions, add additional scopes necessary to your bot in theOAuth & Permissions
and install your app into your workspace.
Once installed, navigate back to theOAuth & Permissions
section and retrieve yor bot token from the top of the page.
With both tokens in hand, you can now proceed with the examples below.
Defining a command using slacker
package mainimport ("context""log""os""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Defining a command with an optional description and example. The handler replies to a thread.
package mainimport ("context""log""os""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Defining a command with a parameter
package mainimport ("context""log""os""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
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 mainimport ("context""log""os""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))definition:=&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)fori:=0;i<number;i++ {response.Reply(word)}},}bot.Command("repeat <word> <number>",definition)ctx,cancel:=context.WithCancel(context.Background())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
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 mainimport ("context""errors""log""os""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))messageReplyDefinition:=&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:=&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Showcasing the ability to access thegithub.com/slack-go/slack API and upload a file
package mainimport ("context""fmt""log""os""github.com/shomali11/slacker""github.com/slack-go/slack")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))definition:=&slacker.CommandDefinition{Description:"Upload a word!",Handler:func(botCtx slacker.BotContext,request slacker.Request,response slacker.ResponseWriter) {word:=request.Param("word")client:=botCtx.Client()ev:=botCtx.Event()ifev.Channel!="" {client.PostMessage(ev.Channel,slack.MsgOptionText("Uploading file ...",false))_,err:=client.UploadFile(slack.FileUploadParameters{Content:word,Channels: []string{ev.Channel}})iferr!=nil {fmt.Printf("Error encountered when uploading file: %+v\n",err)}}},}bot.Command("upload <word>",definition)ctx,cancel:=context.WithCancel(context.Background())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Showcasing the ability to leveragecontext.Context
to add a timeout
package mainimport ("context""errors""log""os""time""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))definition:=&slacker.CommandDefinition{Description:"Process!",Handler:func(botCtx slacker.BotContext,request slacker.Request,response slacker.ResponseWriter) {timedContext,cancel:=context.WithTimeout(botCtx.Context(),time.Second)defercancel()select {case<-timedContext.Done():response.ReportError(errors.New("timed out"))case<-time.After(time.Minute):response.Reply("Processing done!")}},}bot.Command("process",definition)ctx,cancel:=context.WithCancel(context.Background())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Showcasing the ability to add attachments to aReply
package mainimport ("context""log""os""github.com/shomali11/slacker""github.com/slack-go/slack")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))definition:=&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Showcasing the ability to add blocks to aReply
package mainimport ("context""log""os""github.com/shomali11/slacker""github.com/slack-go/slack")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))definition:=&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Showcasing the ability to create custom responses viaCustomResponse
package mainimport ("context""errors""fmt""log""os""github.com/shomali11/slacker""github.com/slack-go/slack")const (errorFormat="> Custom Error: _%s_")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))bot.CustomResponse(NewCustomResponseWriter)definition:=&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}// NewCustomResponseWriter creates a new ResponseWriter structurefuncNewCustomResponseWriter(botCtx slacker.BotContext) slacker.ResponseWriter {return&MyCustomResponseWriter{botCtx:botCtx}}// MyCustomResponseWriter a custom response writertypeMyCustomResponseWriterstruct {botCtx slacker.BotContext}// ReportError sends back a formatted error message to the channel where we received the event fromfunc (r*MyCustomResponseWriter)ReportError(errerror,options...slacker.ReportErrorOption) {defaults:=slacker.NewReportErrorDefaults(options...)client:=r.botCtx.Client()event:=r.botCtx.Event()opts:= []slack.MsgOption{slack.MsgOptionText(fmt.Sprintf(errorFormat,err.Error()),false),}ifdefaults.ThreadResponse {opts=append(opts,slack.MsgOptionTS(event.TimeStamp))}_,_,err=client.PostMessage(event.Channel,opts...)iferr!=nil {fmt.Println("failed to report error: %v",err)}}// Reply send a attachments to the current channel with a messagefunc (r*MyCustomResponseWriter)Reply(messagestring,options...slacker.ReplyOption)error {defaults:=slacker.NewReplyDefaults(options...)client:=r.botCtx.Client()event:=r.botCtx.Event()ifevent==nil {returnfmt.Errorf("Unable to get message event details")}opts:= []slack.MsgOption{slack.MsgOptionText(message,false),slack.MsgOptionAttachments(defaults.Attachments...),slack.MsgOptionBlocks(defaults.Blocks...),}ifdefaults.ThreadResponse {opts=append(opts,slack.MsgOptionTS(event.TimeStamp))}_,_,err:=client.PostMessage(event.Channel,opts...,)returnerr}
Showcasing the ability to toggle the slack Debug option viaWithDebug
package mainimport ("context""github.com/shomali11/slacker""log""os")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))definition:=&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Defining a command that can only be executed by authorized users
package mainimport ("context""log""os""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))authorizedUsers:= []string{"<User ID>"}authorizedDefinition:=&slacker.CommandDefinition{Description:"Very secret stuff",AuthorizationFunc:func(botCtx slacker.BotContext,request slacker.Request)bool {returncontains(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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}funccontains(list []string,elementstring)bool {for_,value:=rangelist {ifvalue==element {returntrue}}returnfalse}
Adding handlers to when the bot is connected, encounters an error and a default for when none of the commands match
package mainimport ("log""os""context""fmt""github.com/shomali11/slacker")funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))bot.Init(func() {log.Println("Connected!")})bot.Err(func(errstring) {log.Println(err)})bot.DefaultCommand(func(botCtx slacker.BotContext,request slacker.Request,response slacker.ResponseWriter) {response.Reply("Say what?")})bot.DefaultEvent(func(eventinterface{}) {fmt.Println(event)})definition:=&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
Listening to the Commands Events being produced
package mainimport ("fmt""log""os""context""github.com/shomali11/slacker")funcprintCommandEvents(analyticsChannel<-chan*slacker.CommandEvent) {forevent:=rangeanalyticsChannel {fmt.Println("Command Events")fmt.Println(event.Timestamp)fmt.Println(event.Command)fmt.Println(event.Parameters)fmt.Println(event.Event)fmt.Println()}}funcmain() {bot:=slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"),os.Getenv("SLACK_APP_TOKEN"))goprintCommandEvents(bot.CommandEvents())bot.Command("ping",&slacker.CommandDefinition{Handler:func(botCtx slacker.BotContext,request slacker.Request,response slacker.ResponseWriter) {response.Reply("pong")},})bot.Command("echo <word>",&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())defercancel()err:=bot.Listen(ctx)iferr!=nil {log.Fatal(err)}}
About
Slack Bot Framework
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Languages
- Go100.0%