bobo
is a modular Slack RTM bot kit for Golang.
To customize your bot, just add your own commands or written by other developers.
Link: https://github.com/eure/bobo
For Japanese developers: I already wrote the detailed article in Japanese here,
https://medium.com/eureka-engineering/slack-rtm-bot-bobo-6e7f60da744c
Overview
bobo runs as just a bot process. It does nothing as default.
bobo accepts optional data including an engine (so far, only supports Slack RTM) and commands.
You don’t have to write the special codes to maintain the Slack bot. It’s already in.
Let me show an example here.
If you want to have your slack bot and have it echo
command, write the EchoCommand
package main
import (
"github.com/eure/bobo/command"
)
// EchoCommand is an example command.
// This command says same text.
var EchoCommand = command.BasicCommandTemplate{
// If you set help text, bot shows this text on `help` command.
Help: "reply same text",
// If you set NoHelp=true, bot does not show this command on `help` command.
NoHelp: false,
// Bot can react and exec function by the `MentionCommand`
// e.g.) @bot echo foo // <- in a channel
// e.g.) echo foo // <- by DM
MentionCommand: "echo",
// write the reaction process
GenerateFn: func(d command.CommandData) command.Command {
c := command.Command{}
// d.RawText // "@bot <command> <other>" or "<command> <other>"
// d.Text // "@bot <command> <other>"
// d.TextMention // "@bot"
// d.TextCommand // "<command>"
// d.TextOther // "<other>"
if d.TextOther == "" {
return c
}
// '<@sender_id>' is a mention to you.
text := fmt.Sprintf("<@%s> %s", d.SenderID, d.TextOther)
// ReplyEngineTask is a task to send message in the same channel.
task := command.NewReplyEngineTask(d.Engine, d.Channel, text)
// you can add multiple tasks to the command.
c.Add(task)
return c
},
}
And add the EchoCommand
to entry point,
package main
import (
"github.com/eure/bobo"
"github.com/eure/bobo/command"
"github.com/eure/bobo/engine/slack"
"github.com/eure/bobo/log"
)
func main() {
bobo.Run(bobo.RunOption{
Engine: &slack.SlackEngine{},
Logger: &log.StdLogger{
IsDebug: bobo.IsDebug(),
},
CommandSet: command.NewCommandSet(
// defalt example commands
command.PingCommand,
command.HelpCommand,
// add your original commands
EchoCommand,
),
})
}
Then build and run the binary with Slack token from environmental variables, a Slack bot process will run.
$ go build -o bin/bobo .
$ SLACK_RTM_TOKEN=xoxb-0000... ./bin/bobo
The image of the command is like this,
echo
command is on the last two lines.
CommandData and Task
CommandData
is a parameter of the command. It has convenient fields.
type CommandData struct {
// You can use (Slack) engine's API inside the tasks
Engine engine.Engine
// This is what the user typed in slack.
RawText string // "@bot <command> <other>" or "<command> <other>"
// Same as RawText basically, but when in DM, the mentioned text is complemented.
Text string // "@bot <command> <other>" (always)
// 1st part of the text (mention)
TextMention string // @bot
// 2nd part of the text (command name)
TextCommand string // <command>
// 3rd and later part of the text (all of the text without 1st mention and 2nd command name parts)
TextOther string // <other>
// flag of Direct Message
IsDM bool // it's on DM or not
// (Slack) UserID of this bot
BotID string
// (Slack) UserID of the message sender
SenderID string
// (Slack) user name of the message sender
SenderName string
// The channel name that the message sent
Channel string
// Timestamp of the message thread
ThreadTimestamp string
// You don't need this in many cases, but you can access the other commands
// e.g.) `help` command
CommandSet *CommandSet
}
You can use these data and proceed and generate tasks.
Task
itself is a interface
so you just needs two methods.
type Task interface {
GetName() string // return the task name(used in the error logs)
Run() error // run the process (main routine)
}
For example, let’s see the replyEngineTask
that send message to the target channel.
type replyEngineTask struct {
engine engine.Engine
channel string
text string
}
func NewReplyEngineTask(e engine.Engine, channel, text string) *replyEngineTask {
return &replyEngineTask{
engine: e,
channel: channel,
text: text,
}
}
// (omitted...)
func (t replyEngineTask) Run() error {
return t.engine.Reply(t.channel, t.text)
}
NewReplyEngineTask
is a constructor function and required data is set there,
- engine: to use Slack API
- channel: target channel name
- text: message text
And this task just execute only engine.Reply(channel, text)
in the Run
method.
Other Commands
I wrote some trivial commands below,
- https://github.com/eure/bobo-googlehome
- For Google Home integration
- https://github.com/evalphobia/bobo-experiment
- AWS Cost, SQS, DynamoDB stats
- Google Calendar
- Face++
You can see the cmd/main.go
and each commands to find what’s going on.