Skip to main content

Maxml (Advanced)

This document explains the advanced use of Maxml (Markup Language). General information can be found in Maxml (Markup Language).

How to get plain text from text command?

Use the default renderer:

from maxbot import MaxBot

builder = MaxBot.builder()
builder.use_inline_resources("""
dialog:
- condition: true
response: |
<text>
Hello,
world! <br /> How are
you?
</text>
""")
command, = builder.build().process_message(message={"text": "test"})
command['text'].render()
# 'Hello, world!\nHow are you?'

Markup and Jinja

Whitespace normalization applied to computed Jinja expressions as well.

In the following example, a newline from the slot value will be normalized to a space:

from maxbot import MaxBot

builder = MaxBot.builder()
builder.use_inline_resources("""
dialog:
- condition: true
response: |
{% set slots.message = "Hello!\\nHow are you?" %}
{{ slots.message }}
""")
bot = builder.build()
dialog = {"channel_name": "cli", "user_id": "1"}
message = {"text": "Hello!"}
command, = bot.process_message(message, dialog)
command['text'].render()
# 'Hello! How are you?'

To leave a newline in the text sent to a user, it’s necessary to use the Jinja filter nl2br:

from maxbot import MaxBot

builder = MaxBot.builder()
builder.use_inline_resources("""
dialog:
- condition: true
response: |
{% set slots.message = "Hello!\\nHow are you?" %}
{{ slots.message|nl2br }}
""")
bot = builder.build()
dialog = {"channel_name": "cli", "user_id": "1"}
message = {"text": "Hello!"}
command, = bot.process_message(message, dialog)
command['text'].render()
# 'Hello!\nHow are you?'

Metadata

XML allows you to specify the content of values in different ways. Below is an example of adding the location command, which allows you to send an arbitrary point on the map to the user.

import json
from marshmallow import fields, Schema
from maxbot import MaxBot

builder = MaxBot.builder()

@builder.command("location")
class LocationCommand(Schema):
longitude = fields.Float(required=True)
latitude = fields.Float(required=True)

builder.use_inline_resources("""
dialog:
- condition: message.text == "Where are you?"
response: |
<location latitude="40.7580" longitude="-73.9855" />
""")
bot = builder.build()

dialog = {"channel_name": "cli", "user_id": "1"}
message = {"text": "Where are you?"}
print(json.dumps(bot.process_message(message, dialog), indent=2))
[
{
"location": {
"longitude": -73.9855,
"latitude": 40.758
}
}
]

For example, if we want to represent fields with simple data types in the form of nested XML elements, we need to specify it in the metadata explicitly: the key maxml must contain the value element.

We will rewrite the example above so that the fields longitude and latitude had to be specified as nested XML elements:

import json
from marshmallow import fields, Schema
from maxbot import MaxBot

builder = MaxBot.builder()

@builder.command("location")
class LocationCommand(Schema):
longitude = fields.Float(required=True, metadata={"maxml": "element"})
latitude = fields.Float(required=True, metadata={"maxml": "element"})

builder.use_inline_resources("""
dialog:
- condition: message.text == "Where are you?"
response: |
<location>
<latitude>40.7580</latitude>
<longitude>-73.9855</longitude>
</location>
""")
bot = builder.build()

message = {"text": "Where are you?"}
print(json.dumps(bot.process_message(message), indent=2))
[
{
"location": {
"longitude": -73.9855,
"latitude": 40.758
}
}
]

List

A command can contain a list. In order to organize a list of simple types, one can use the type fields.List. For example, a simple version of the command quick_reply can be described like this:

import json
from marshmallow import fields, Schema
from maxbot import MaxBot

builder = MaxBot.builder()

@builder.command("quick_reply")
class QuickReplyCommand(Schema):
button = fields.List(fields.String)

builder.use_inline_resources("""
dialog:
- condition: message.text == "What is on the menu?"
response: |
<quick_reply>
<button>Pizza</button>
<button>Desserts</button>
</quick_reply>
""")
bot = builder.build()

dialog = {"channel_name": "cli", "user_id": "1"}
message = {"text": "What is on the menu?"}
print(json.dumps(bot.process_message(message, dialog), indent=2))
[
{
"quick_reply": {
"button": [
"Pizza",
"Desserts"
]
}
}
]

For more complex versions when the lists of nested objects are used, one needs to use Nested with the argument many=True. For example, like this:

import json
from marshmallow import fields, Schema
from maxbot import MaxBot

builder = MaxBot.builder()

class CarouselItem(Schema):
title = fields.String(metadata={"maxml": "element"}, required=True)
image = fields.Url(required=True)


@builder.command("carousel")
class CarouselCommand(Schema):
item = fields.Nested(CarouselItem, many=True)


builder.use_inline_resources("""
dialog:
- condition: message.text == "What is on the menu?"
response: |
<carousel>
<item image="http://127.0.0.1/Pizza.jpeg">
<title>Pizza</title>
</item>
<item image="http://127.0.0.1/Desserts.jpeg">
<title>Desserts</title>
</item>
</carousel>
""")
bot = builder.build()

dialog = {"channel_name": "cli", "user_id": "1"}
message = {"text": "What is on the menu?"}
print(json.dumps(bot.process_message(message, dialog), indent=2))
[
{
"carousel": {
"item": [
{
"title": "Pizza",
"image": "http://127.0.0.1/Pizza.jpeg"
},
{
"title": "Desserts",
"image": "http://127.0.0.1/Desserts.jpeg"
}
]
}
}
]