Built-in variables
Asgard's Expression and Template systems provide built-in variables, so a workflow can read the conversation, file details and channel state as it runs.
The variables
Conversation
prevMessage
- Type:
string - What it is: the previous user message
- Using it:
// Expression
prevMessage || 'no message'
prevBlobs
- Type:
array<Blob> - What it is: the files attached to the previous message
- Using it:
// Expression
prevBlobs && prevBlobs.length > 0 ? `Received ${prevBlobs.length} files` : 'no files'
prevPayload
- Type:
object - What it is: the payload carried by the previous message
- Using it:
// Expression
prevPayload && prevPayload.userAction
prevError
- Type:
string - What it is: the most recent processor error
- Using it:
// Expression
prevError ? `An error occurred: ${prevError}` : 'processed normally'
Channel
customChannelId
- Type:
string - What it is: the custom channel id
- Using it:
// Expression
`Channel: ${customChannelId}`
customMessageId
- Type:
string - What it is: the previous message's custom id, an empty string when the client did not supply one
- Using it:
// Expression
customMessageId || 'no custom id'
The Blob object
interface Blob {
blobId: number; // the blob's id
fileType: FileType; // the file's type
fileName?: string; // the original filename, optional
size: number; // size in bytes
mime: string; // MIME type
}
type FileType = 'BINARY' | 'IMAGE' | 'VIDEO' | 'AUDIO' | 'DOCUMENT';
Reading variables
Reading them safely
Expression mode
// check a variable exists
prevMessage && prevMessage.length > 0 ? prevMessage : 'no message'
// use a logical operator
prevMessage || 'a default message'
// index into an array safely
prevBlobs && prevBlobs[0] && prevBlobs[0].fileName
// check an object property
prevPayload && 'property' in prevPayload ? prevPayload.property : 'a default'
Template mode
Worked examples
Checking the conversation's state
// Expression: a full state check
(() => {
let status = [];
if (prevMessage) {
status.push(`Message: ${prevMessage.substring(0, 50)}...`);
}
if (prevBlobs && prevBlobs.length > 0) {
status.push(`Files: ${prevBlobs.length}`);
}
if (prevError) {
status.push(`Error: ${prevError}`);
}
return status.length > 0 ? status.join(' | ') : 'no state';
})()
Handling files
// Expression: counting files by type
(() => {
if (!prevBlobs || prevBlobs.length === 0) {
return 'no files';
}
const stats = {};
prevBlobs.forEach(blob => {
stats[blob.fileType] = (stats[blob.fileType] || 0) + 1;
});
return Object.entries(stats)
.map(([type, count]) => `${type}: ${count}`)
.join(', ');
})()
Common questions
Q: How do I check whether a variable exists?
// Expression
typeof prevMessage !== 'undefined' && prevMessage !== null
// checks undefined and null at once
prevMessage != null
Q: How do I handle an empty array?
// Expression
prevBlobs && Array.isArray(prevBlobs) && prevBlobs.length > 0
Q: How do I read a file property safely?
// Expression
prevBlobs && prevBlobs[0] && prevBlobs[0].fileName
Q: How do I work with the payload?
// Expression: reading a payload property safely
prevPayload && typeof prevPayload === 'object' && 'action' in prevPayload ? prevPayload.action : 'no action'
Using these variables properly is what makes a conversation feel informed and personal.