A hubot clone in Go! botopolis is extendable with plugins and works with different chat services.
See example_test.go for usage details.
Here's an example of a program you can write with botopolis
. If you've used
Hubot before, you'll see a familiar API.
package main
import (
"github.com/botopolis/bot"
"github.com/botopolis/slack"
)
func main() {
// Create a bot with a Slack adapter
robot := bot.New(
slack.New(os.Getenv("SLACK_TOKEN")),
)
// Create a Listener for messages to the bot with the word "hi"
r.Respond(bot.Regexp("hi", func(r bot.Responder) error {
// Respond to the sender of the message
r.Reply("hello to you too!")
})
// Run the bot
r.Run()
}
You have the following listeners at your disposal to interact with incoming messages:
// Listens for any message
robot.Hear(bot.Matcher, func(bot.Responder) error)
// Listens for messages addressed to the bot
robot.Respond(bot.Matcher, func(bot.Responder) error)
// Listens for topic changes
robot.Topic(func(bot.Responder) error)
// Listens for people entering a channel
robot.Enter(func(bot.Responder) error)
// Listens for people leaving a channel
robot.Leave(func(bot.Responder) error)
You'll notice that some listeners take a
bot.Matcher
. Matchers
will run before the callback to determine whether the callback should be fired.
There are a few matchers provided by botopolis
and you can write your own as
long as it matches the function signature.
// Match the text of the message against a regular expression
bot.Regexp("^hi")
// Match a subset of the text
bot.Contains("hello")
// Match the user of a message
bot.User("jean")
// Match the room of a message
bot.Room("general")
Callbacks are given a
bot.Responder
.
The Responder holds a reference to the incoming bot.Message
and exposes a few
methods which simplify interacting with the chat service.
type Responder struct {
Name string
User string
Room string
Text string
// abridged
}
// Send message
func (r Responder) Send(Message) error
// DM message
func (r Responder) Direct(string) error
// Reply to message
func (r Responder) Reply(string) error
// Change topic
func (r Responder) Topic(string) error
bot.Brain
is a store with
customizable backends. By default it will only save things in memory, but you
can add or create your own stores such as
botopolis/redis
for data persistence.
r := bot.New(mock.NewChat())
r.Brain.Set("foo", "bar")
var out string
r.Brain.Get("foo", &out)
fmt.Println(out)
// Output: "bar"
botopolis
also has an HTTP server built in. One great usecase for this is
webhooks. It exposes gorilla/mux
for routing
requests.
r := bot.New(mock.NewChat())
r.Router.HandleFunc("/webhook/name", func(w http.ResponseWriter, r *http.Request) {
r.Send(bot.Message{Room: "general", Text: "Webhook incoming!"})
w.Write([]byte("OK"))
},
).Methods("POST")
botopolis
allows for injection of plugins via bot.New(chat, ...bot.Plugin)
.
It implements a Plugin
interface
that allows plugin writers to make use of the full bot.Robot
runtime on load.
type ExamplePlugin struct { PathName string }
// Load conforms to Plugin interface
func (p ExamplePlugin) Load(r *bot.Robot) {
r.Router.HandleFunc(e.PathName, func(w http.ResponseWriter, r *http.Request) {
// special sauce
}).Methods("GET")
}
// Unload can optionally be implemented for graceful shutdown
func (p ExamplePlugin) Unload(r *bot.Robot) {
// shutting down
}
Here are a couple examples of working plugins:
- https://github.com/botopolis/oauth2
- https://github.com/botopolis/redis (implements
Unload
)
There are very few configuration options in botopolis
itself, as it relies on
the introduction of plugins.
You can set the server port with an environment variable PORT
:
PORT=4567 ./botopolis
You can set up your own logger as long as it satisfies the Logger interface.
robot := bot.New(mychat.Plugin{})
robot.Logger = MyCustomLogger{}
robot.Run()