The task language (DSL)
Every Botream task can be read and written as text: one command per line, with --name:value parameters. The Code view of the editor shows this text, and the blocks view is the same task drawn as cards.
input --name:folder --type:string --default:"C:\Invoices"
set --name:count --type:number --value:0
loop --type:eachfile --folder:$folder$ --filter:*.pdf --variable:file
log --message:"Processing $file.Name$"
number --action:increment --name:count
endloop
log --message:"$count$ invoices processed"
Note: The text is a view of the task. Botream stores the task as structured commands and the agents run those; you never have to keep the text and the blocks in sync yourself.
Lines and commands
- Each line holds one command: the command name, then its parameters.
- Blank lines and indentation are ignored. The editor indents block contents by four spaces.
- Command names are not case-sensitive (
Logworks likelog). Parameter names are written in lowercase, as in the reference; onlyruntaskmappings use the input and output names of the called task (--invoiceNo:$no$,--totalTo:total). - A parameter may appear only once per line.
- There is no comment syntax:
#or//at the start of a line is an error. Use thecommentcommand, which does nothing when the task runs:
comment --text:"Log in with the service account"
See the command reference for every command and parameter.
Parameters
A parameter is written --name:value, with no space around the colon.
Unquoted values
An unquoted value runs to the end of the line or up to the next --name:; spaces at its end are dropped. Spaces inside are allowed, so conditions and JSON need no quotes:
if --condition:$count$ > 5 and $status$ = "ok"
log --message:Status ok
endif
set --name:items --type:list --value:["a", "b", 3]
--name: followed by a space is an empty value.
Quoted values
Put a value in double quotes when it:
- is empty (
--text:""), - starts with a space or a double quote,
- contains text that looks like another parameter (
--x:), - spans several lines or contains tabs.
The editor quotes values with spaces as well, which is always safe. After the closing quote there must be a space or the end of the line.
Inside quotes, the backtick (`) starts an escape:
| Write | To get |
|---|---|
`" |
a double quote |
`n |
a line break |
`t |
a tab |
`r |
a carriage return |
`` |
a backtick |
A backtick followed by any other character stays a backtick. Backslashes are always literal, so Windows paths and regular expressions are written as they are. Outside quotes nothing is escaped.
log --message:"Line one`nLine two"
messagebox --message:"Click `"Continue`" when you are ready"
file --action:write --path:C:\Out\report.txt --content:$text$
Switching a command off
Any command can carry --disabled:true. It stays in the task but does not run (a disabled block takes its contents along). This is what Ctrl+/ in the editor writes.
Variables in values
Variables are written between dollar signs. Anything else with a $ is plain text, so US$ 5 or a lone $ stay as they are.
| Reference | Reads |
|---|---|
$name$ |
The variable |
$order.Total$ |
A field of a record or dictionary (or a property of a window, file, response…) |
$items[0]$ |
An item of a list (0 is the first) |
$table[2].Name$ |
A cell: row 2, column Name of a data table |
$row.Items[1].Sku$ |
Any combination of fields and indexes |
$items.Count$, $text.Length$ |
Number of items (rows, keys) / number of characters |
- Variable names start with a letter or
_and contain letters, digits and_. They are not case-sensitive when the task runs. - A field after a dot must be a name of the same kind (no spaces); field names are matched without regard to case.
- When a value is exactly one reference (
--list:$items$), the command receives the variable itself, with its type: a list stays a list, a number a number. When a reference is part of a longer text ("Total: $sum$ EUR"), its value is put into the text. - Parameters that name a variable to write to (
--output,set --name, loop--variable) take the bare name:--output:total, not--output:$total$.
See Variables and data types for the types and their literals.
Formulas in values
A value that starts with = is a formula, calculated when the command runs, as in Excel: --value:=$price$ * 1.2, --message:="Order " & $id$ & " is ready", --value:=round($total$ * 0.18, 2). Formulas need no quotes around them, even with spaces. See Formulas for the operators and functions. To write text that starts with =, put an apostrophe in front: --message:'=== done === (the apostrophe is removed when the task runs).
Older $name syntax
Tasks written before this syntax used an open form ($name without the closing $). They are upgraded to $name$ automatically when they are opened; you do not need to change them. If you type $name for a variable the task has, the editor shows a warning, because without the closing $ it is plain text.
Declarations come first
input and output declare the task's parameters (see task inputs and outputs). They must be at the top of the task; only comment lines may come before them. A name can be declared only once.
comment --text:"Books one invoice"
input --name:invoiceNo --type:string
input --name:retries --type:number --default:3 --required:false
output --name:bookingNo --type:string
Blocks
Block commands enclose other commands and end with their own end line. End lines take no parameters.
| Block | Sections | End |
|---|---|---|
if |
elseif (any number), then else (at most one) |
endif |
loop |
– | endloop |
try |
catch and/or finally (at least one, in that order) |
endtry |
switch |
case (at least one), then default (at most one) |
endswitch |
retry |
– | endretry |
step |
– | endstep |
if / elseif / else
if --condition:$total$ > 1000
log --message:"Needs approval"
elseif --condition:$total$ = 0
throw --message:"Invoice $no$ has no total"
else
log --message:"Booking directly"
endif
Instead of an expression, if and elseif can take a typed condition with --type:
--type |
Parameters | True when |
|---|---|---|
expression (or no --type) |
--condition |
The expression is true (see below) |
string |
--left, --operator, --right, --ignorecase |
Text comparison: equals, notequals, contains, notcontains, startswith, endswith, matches (regex), isempty, isnotempty |
number |
--left, --operator, --right |
Number comparison: =, !=, >, <, >=, <= |
boolean |
--value, --is |
The value is true (or false with --is:false) |
fileexists, filenotexists |
--path |
A matching file exists / does not exist (* and ? allowed in the last part) |
folderexists, foldernotexists |
--path |
Same for folders |
windowexists, windownotexists |
--window, --timeout |
The window is open / closed, waiting up to --timeout seconds |
elementexists, elementnotexists |
--dompath (Capture), --frame, --browser, --timeout |
The web element is shown on the page / is hidden or missing |
desktopelementexists, desktopelementnotexists |
--window, --by, --target (Capture), --timeout |
The desktop element is in the window / is not |
imageexists, imagenotexists |
--image (Capture picture), --window, --similarity, --timeout |
The picture is on the screen (or in the window) / is not |
if --type:string --left:$subject$ --operator:contains --right:invoice --ignorecase:true
log --message:"Invoice mail"
endif
if --type:fileexists --path:C:\In\*.xlsx
log --message:"New workbooks are waiting"
endif
The element and picture conditions check what is on the screen in one block, instead of reading capture --action:exists into a variable and testing it with if --type:boolean. In the editor, Capture fills in the element (with its object properties table) and Capture picture marks the picture, just as for capture, desktop and image. --timeout waits up to that many seconds for the element or picture to appear (…exists) or to go away (…notexists) before deciding; without it the screen is checked once. A missing element, window or picture never fails the task: the condition is simply false (or true for the not forms).
if --type:elementexists --dompath:"//div[@class='error']" --timeout:3
capture --action:gettext --dompath:"//div[@class='error']" --output:problem
throw --type:business --message:"The portal refused the invoice: $problem$"
elseif --type:desktopelementexists --window:$erp$ --by:name --target:"Saved"
log --message:"Saved in the ERP"
elseif --type:imagenotexists --image:"C:\Pictures\spinner.png" --window:$citrix$ --timeout:30
log --message:"The remote screen is ready"
endif
Older agents (installed before these conditions existed) run them too: the server sends them as a wait into a hidden variable followed by if --type:boolean (the log then shows a "Gave up" warning when the answer is no). Only imagenotexists with a --timeout needs the current agent.
switch / case / default
switch runs one of several sections by a value: the first case whose --value equals the switch's --value runs; when none does, the default section runs (if there is one). Values are compared as text; with --ignorecase:true, Approved and APPROVED are the same. A number case of a switch on a variable compares as a number, so 1 also matches 1.0.
switch --value:$status$ --ignorecase:true
case --value:Approved
log --message:"Book the invoice"
case --value:Rejected
log --message:"Send it back"
default
log --message:"Unknown status $status$" --level:warning
endswitch
The commands go inside the sections: a switch needs at least one case, and default comes last. In the editor, add sections with Add case and Default in the block's properties or on its End line, and select a case to type its value. Every agent runs a switch: the server sends it as if / elseif / else, keeping the line numbers of the switch and its cases for the log and the debugger.
loop
--type chooses what the loop goes over: range, times (a fixed number of rounds), list, datatable, condition (while), excelrows, eachfile, eachfolder, queue or mail (each e-mail of a mailbox folder; see Looping over e-mails). Every type except times and condition names a loop variable with --variable; the variable of a range loop is a number.
loop --type:range --from:1 --to:10 --variable:i
log --message:"Round $i$"
endloop
loop --type:times --times:3
log --message:"Three times"
endloop
loop --type:list --list:$customers$ --variable:customer
log --message:"Customer $customer$"
endloop
loop --type:condition --condition:$pending$ > 0
number --action:decrement --name:pending
endloop
--index (times, list, datatable, excelrows, eachfile, eachfolder, queue and mail loops) names a number variable that receives the round's position, starting at 0 like $list[0]$. A condition loop tests its condition before every round; with --check:after it runs the body first and tests after every round (a do-while: the body always runs at least once).
loop --type:list --list:$customers$ --variable:customer --index:i
log --message:"Customer $i$: $customer$"
endloop
loop --type:condition --condition:"$hasNext$ = true" --check:after
capture --action:click --dompath:"//a[@rel='next']"
capture --action:exists --dompath:"//a[@rel='next']" --output:hasNext
endloop
--index and --check:after need the current agent: an older one is not sent such a task (the run is refused with a message to update the agent).
break leaves the innermost loop, continue goes on with its next round. Both are allowed only inside a loop.
loop --type:datatable --table:$orders$ --variable:row
if --condition:$row.Status$ = "cancelled"
continue
endif
if --condition:$row.Id$ = $lastId$
break
endif
log --message:"Order $row.Id$"
endloop
try / catch / finally
browser --action:open --url:"https://portal.example.com" --output:portal
try
capture --browser:$portal$ --action:click --dompath:"#export"
catch --error:err
log --message:"Export failed at line $err.CommandLine$: $err.Message$" --level:error
finally
browser --action:close --browser:$portal$
endtry
See Error handling and retries.
retry
retry runs its commands again when one of them fails: up to --times attempts in all (default 3), waiting --interval seconds (default 1) between them. With --until, it also tries again while that condition is false after the commands ran. After the last attempt the error goes on, so a try around it can catch it.
retry --times:3 --interval:5
browser --action:open --url:"https://portal.example.com" --output:portal
capture --browser:$portal$ --action:click --dompath:"#login"
endretry
See Error handling and retries.
step
step groups commands under a description. It changes nothing about how they run.
step --description:"Read the input file"
csv --action:read --file:C:\In\orders.csv --output:orders
endstep
Condition expressions
--condition of if, elseif and loop --type:condition is an expression:
| Element | Written as |
|---|---|
| Comparison | =, !=, >, <, >=, <= |
| Text tests | contains, startswith, endswith, matches (regular expression) |
| Combination | and, or (and binds first), parentheses |
| Negation | ! before a value or a parenthesis |
| Values | $variable$ references, numbers, "quoted text", true, false, null, and bare words (read as text) |
if --condition:$amount$ >= 100 and ($country$ = "DE" or $country$ = "AT")
log --message:"EU invoice above limit"
endif
if --condition:$customer$ != null and $customer.Name$ startswith "ACME"
log --message:"ACME order"
endif
if --condition:!$isApproved$
log --message:"Not approved yet"
endif
How values are compared:
- Two numbers compare as numbers, two dates by time; anything else compares as text, case-sensitive.
contains,startswith,endswithandmatchesare case-sensitive. Use a typed condition (--type:string --ignorecase:true) to ignore case.nullequals a missing value and empty text; it can only be used with=and!=.- A value on its own is true unless it is
false,0, empty text, an empty list or null. andandorstop as soon as the result is known, so$x$ != null and $x.Name$ = "a"never reads the field of a missing value.- Keywords (
and,or,contains…) are not case-sensitive. - Quoted text in an expression has no escapes; references inside it are filled in (
"INV-$no$").
Tip: Write the condition unquoted. If you do put the whole
--conditionvalue in quotes, every double quote inside it must be written as`".
How the editor checks the text
The Code view marks problems as you type, and the same checks run in the blocks view:
- unknown commands and parameters (with a "did you mean" hint), missing required parameters, values of the wrong kind (a number,
true/false, one of a list of choices, a variable name, a$variable$reference); - blocks that are not closed, end lines that do not match, sections in the wrong place;
input/outputbelow other commands,break/continueoutside a loop,throwwithout--messageoutside a catch section;- typed values that do not fit their type (for example
set --type:number --value:abc); - warnings for a
$namewithout its closing$, and for a--browser:$page$whose variable is never opened bybrowser --action:open.
A task with errors cannot be saved. Warnings do not block saving.