3. Advanced Flow Definitions

3.1. Storing something in the flow context

Flows have a state the plugins can use to store some contextual information. Let’s take back out simple linear flow:

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

You can represent this flow like this:

../../_images/basics_1.svg

You can store something in the context, for example in !first and retrieve it in !second. Like this:

@botcmd
def first(self, msg, args):
    msg.ctx['mydata'] = 'Hello'
    return 'First done!'

@botcmd
def second(self, msg, args):
    return msg.ctx['mydata'] + ' World!'

msg.ctx is a dictionary created every time a flow starts.

3.2. Making a step execute automatically

In our previous example, if msg.ctx['mydata'] is populated, we can arguably expect that Errbot should not wait for the user to enter !second to execute it and just proceed by itself.

You can do that by defining a predicate, which is a simple function that returns True if the conditions to proceed to the next step are met. The function takes only one parameter, the context, the same one you get from msg.ctx.

@botflow
def example(self, flow: FlowRoot):
    first_step = flow.connect('first')
    second_step = first_step.connect('second',
                                     predicate=lambda ctx: 'mydata' in ctx)
    third_step = second_step.connect('third')

Now, after starting the flow with !flows start example, the state will be:

../../_images/basics_3.svg

Errbot will wait for !first. But then, once the user executes !first, it will see that the predicate between !first and !second is verified, so will go on and execute !second, displaying ‘Hello World’ and proceed to wait for !third:

../../_images/basics_4.svg

3.3. Branching in the graph

It is perfectly possible to branch out to several possibilities (possibly with different predicates).

@botflow
def example(self, flow: FlowRoot):
    first_step = flow.connect('first')
    second_step = first_step.connect('second',
                                     predicate=lambda ctx: 'mydata' in ctx)
    other = first_step.connect('other_second',
                               predicate= lambda ctx: 'otherdata' in ctx)

This will do something like that:

../../_images/advanced_1.svg

In manual mode, the bot will tell the user about his 2 possible options to continue.

3.4. Making a looping graph

You can also perfectly reexecute a part of a graph in a “loop”. You can branch directly the node object instead of the command name in that case.

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

You can represent this flow like this:

../../_images/advanced_2.svg

The typical use case is to repeatedly ask something to the user.