2. Basic Flow Definition

2.1. Flows are like plugins

They are defined by a .flow file, similar to the plugin ones:

[Core]
Name = MyFlows
Module = myflows

[Documentation]
Description = my documentation.

[Python]
Version = 3

Now in the myflows.py file you will have pretty familiar structure with a BotFlow as type and @botflow as flow decorator:

from errbot import botflow, FlowRoot, BotFlow

class MyFlows(BotFlow):
    """ Conversation flows for Errbot"""

    @botflow
    def example(self, flow: FlowRoot):
        """ Docs for the flow example comes here """
        # [...]

Errbot will pass the root of the flow as the only parameter to your flow definition so you can build your graph from there.

2.2. Making a simple graph

Within your flow, you can connect commands together. For example, to make a simple linear flow between !first, !second and !third:

@botflow
def example(self, flow: FlowRoot):
    first_step = flow.connect('first')           # first is a command name from any loaded plugin.
    second_step = first_step.connect('second')
    third_step = second_step.connect('third')

You can represent this flow like this:

../../_images/basics_1.svg

O is the state “not started” for the flow example.

You can start this flow manually by doing !flows start example.

The bot will tell you that it expects a !first command:

../../_images/basics_3.svg

Once you have executed !first, you will be in that state:

../../_images/basics_2.svg

The bot will tell you that it expects !second, etc.

../../_images/basics_4.svg

2.3. Making a flow start automatically

Now, usually flows are linked to a first action your users want to do. For example: !poll new, !vm create, !report init or first commands like that that suggests that you will have a follow-up.

To trigger a flow automatically on those first commands, you can use auto_trigger.

@botflow
def example(self, flow: FlowRoot):
    first_step = flow.connect('first', auto_trigger=True)
    second_step = first_step.connect('second')
    third_step = second_step.connect('third')

You can still represent this flow like this:

../../_images/basics_1.svg

BUT, when a user will execute a !first command, the bot will instantly instantiate a Flow in this state:

../../_images/basics_2.svg

And tell the user that !second is the follow-up.

2.4. Flow ending

If a node has no more children and a user passed it, it will automatically end the flow.

Sometimes, with loops etc., you might want to explicitly mark an END FlowNode with a predicate. You can do it like this, for example for a guessing game plugin:

../../_images/end.svg

In the flow code…

from errbot import botflow, FlowRoot, BotFlow, FLOW_END

class GuessFlows(BotFlow):
    """ Conversation flows related to polls"""

    @botflow
    def guess(self, flow: FlowRoot):
        """ This is a flow that can set a guessing game."""
        # setup Flow
        game_created = flow.connect('tryme', auto_trigger=True)
        one_guess = game_created.connect('guessing')
        one_guess.connect(one_guess)  # loop on itself
        one_guess.connect(FLOW_END, predicate=lambda ctx: ctx['ended'])