Useful Example

Each bot consists of a chat Adapter (e.g. to integrate with Slack), a Memory implementation to remember key-value data (e.g. using Redis) and a Brain which routes new messages or custom events (e.g. receiving an HTTP call) to the corresponding registered handler functions.

By default joe.New(…) uses the CLI adapter which makes the bot read messages from stdin and respond on stdout. Additionally the bot will store key value data in-memory which means it will forget anything you told it when it is restarted. This default setup is useful for local development without any dependencies but you will quickly want to add other Modules to extend the bots capabilities.

For instance we can extend the previous example to connect the Bot with a Slack workspace and store key-value data in Redis. To allow the message handlers to access the memory we define them as functions on a custom ExampleBottype which embeds the joe.Bot.

package main

import (


type ExampleBot struct {

func main() {
	b := &ExampleBot{
		Bot: joe.New("example",

	b.Respond("remember (.+) is (.+)", b.Remember)
	b.Respond("what is (.+)", b.WhatIs)

	err := b.Run()
	if err != nil {

func (b *ExampleBot) Remember(msg joe.Message) error {
	key, value := msg.Matches[0], msg.Matches[1]
	msg.Respond("OK, I'll remember %s is %s", key, value)
	return b.Store.Set(key, value)

func (b *ExampleBot) WhatIs(msg joe.Message) error {
	key := msg.Matches[0]
	var value string
	ok, err := b.Store.Get(key, &value)
	if err != nil {
		return fmt.Errorf("failed to retrieve key %q from brain: %w", key, err)

	if ok {
		msg.Respond("%s is %s", key, value)
	} else {
		msg.Respond("I do not remember %q", key)

	return nil