Skip to content

Listening for things

Slack Machine allows you to listen for various different things and respond to that. By decorating functions in your plugin using the decorators Slack Machine provides, you can tell Slack Machine to run those functions when something specific happens.

Listen for a mention

The respond_to decorator tells Slack Machine to listen for messages mentioning your bot and matching a specific pattern. Slack Machine will hear messages sent in any channel or private group it is a member of. For a message to trigger a function decorated by @respond_to(...), the message has to start with a mention of your bot or with any alias the user configured using the ALIASES setting. The exception is direct messages sent to the bot, they don't have to include a mention to trigger @respond_to.

@respond_to takes 3 parameters:

regex (required): the regular expression Slack Machine should listen for. The regex pattern should not account for the mention of your bot, as Slack Machine will remove the mention before looking for a match. Slack Machine listens for any occurrence of the pattern in the message, so if you want to specifically match the whole message, you can anchor your pattern using the ^ and $ symbols.

flags (optional): can be used to pass flags for the regex matching as defined in the re module. By default, re.IGNORECASE is applied.

handle_message_changed (optional): is used to configure if Slack Machine should trigger this function for messages that have been changed. By default, only new messages will trigger the decorated function.

How your function will be called

Your function will be called with a Message object that represents the message that triggered the function. It not only contains the message text itself, but also has many convenient methods for replying.

Example:

@respond_to(r"^I love you")
async def spread_love(self, msg):
    await msg.reply("I love you too!")

The regex pattern can optionally contain named groups that will be captured and passed to your function as keyword arguments.

Example:

@respond_to(r"You deserve (?P<num_stars>\d+) stars!")
async def award(self, msg, num_stars):
    stars_back = int(num_stars) + 1
    await msg.reply("Well, you deserve {}!".format(stars_back))

Hear any message

The @listen_to decorator works similar as the @respond_to decorator, but it will hear any message matching a pattern, without the bot being explicitly mentioned. @listen_to takes the same parameters as @respond_to.

Example:

@listen_to(r"go for it")
@listen_to(r"go 4 it")
async def go_for_it(self, msg):
    await msg.say("https://a-z-animals.com/media/animals/images/original/gopher_2.jpg")

As you can see, you can also apply the same decorator multiple times to a function, each time with different arguments. Of course, you can also combine different decorators on one function.

More flexibility with Slack events

If you want your bot to respond to other things than messages, you can do so using the @process decorator. @process requires an event_type as parameter and will trigger the decorated function any time an event of the specified type happens. It can listen to any Slack event that is supported by the Events API.

The received event will be passed to your function.

The following example will listen for the reaction_added event to know if a reaction was added to a message, and will match that reaction:

@process("reaction_added")
async def match_reaction(self, event):
    emoji = event["reaction"]
    channel = event["item"]["channel"]
    ts = event["item"]["ts"]
    await self.react(channel, ts, emoji)

As you can see, @process gives you a lot of flexibility by allowing you to process any event Slack Machine does not provide a specific decorator for.

Take action on a Schedule

Slack Machine can also run functions on a schedule, using the @schedule decorator. @schedule behaves like Linux/Unix Crontab, and receives similar parameters. You can specify on what schedule your function should be called. When your function is called, it will not receive any arguments except self, but you can of course call any MachineBasePlugin methods to send message and do other things.

Example:

@schedule(hour="9-17", minute="*/30")
async def movement_reminder(self):
    await self.say("general", "<!here> maybe now is a good time to take a short walk!")

Slack Machine events

Slack Machine can respond to events that are emitted by your plugin(s) or plugins of others, or events generated by parts of Slack Machine itself. You can use the @on decorator on a function to run that function whenever a certain event is emitted somewhere.

Example:

@on("bathroom_used")
async def call_cleaning_department(self, **kwargs):
    await self.say("cleaning-department", "<!here> Somebody used the toilet!")

This function will be called whenever the bathroom_used event is emitted somewhere.

Some things to be aware of

Event names are global, every plugin can emit and listen for the same events. This is by design, because this way, you can use events to exchange data between plugins. Events can be a way to expose a "public API" for plugins. But this can also mean your functions are unexpectedly triggered by events sent by other plugins, especially if the event names you choose are very generic.

When emitting events, plugins can attach whatever variables they want to the event, and when listening for an event, your function will be called with whatever arguments were attached to the event when the event was emitted. It's therefor a good idea to always include **kwargs as a catch-all, otherwise your function could return an error when it's called with arguments that have not been explicitly defined.

So what is this event system useful for? As mentioned in the above note, events can be used to communicate between plugins and/or for plugins to respond to events that happen within the core of Slack Machine. A good example of this, is the unauthorized-access event. This event will be emitted whenever someone tries to use a bot command protected by the require_any_role or require_all_roles decorators without having the right roles to issue that command. By listening to this event, your plugins can take action when this happens. The built-in RBAC plugin also listens for this event.

You can read emitting events to learn how to emit events from your own plugins.