-
-
Notifications
You must be signed in to change notification settings - Fork 193
Universal Connector node
The Universal Connector node
allows to connect the the RedBot ecosystem any kind of messaging service (like email, an SMS gateway, a testing stub, etc). It's an advanced component, a good kwowledge of JavaScript and Promises is required in order to use it.
Unlike other receiver nodes the Universal Connector node
has an input pin, this is where the external service will send the payload in order to be translated and injected in the RedBot flow.
The Universal Connector node
takes care of providing the chat context, the pass thru and track features, the production/development configuration, etc; while the implementation detail about how the incoming message is implemented is left to the user and must be implemented with an Extend node.
In order to properly translate an incoming message the implementation in the Universal Connector node
must:
- Extract a chatId from the payload: it's a unique identifier of the conversation in the connected platform (for example the mobile number for a SMS gateway, the email for an email system, etc)
- Extract or infer the message type. Some messaging platform support different type of messages (for example Telegrams supports message, audio, photo, etc) while a service like a SMS gateway just support one type of message.
- Extract the message content it also should (but is not mandatory)
- Extract the timestamp of the message
- Extract a messageId to properly reference inbound and outbound messages in the external service timeline
Like explained in Extend node a connector handles inbound messages with a chain of middlewares: chunck of codes executed sequentially in order to accomplish steps 1 to 3. In order to keep the code simple and maintanable is a good practice to let middleware takes care of detecting and translating one kind of message in each middleware, as soon as a message has been resolved (means that a message type is assigned to the incoming message), the rest of middlewares chain is skipped and the message is injected in the RedBot's flow.
For example suppose that a SMS gateway calls the Node-RED instance webhook with this payload
{
sms: {
from: '+39347123456',
to: '+39338654321',
id: '_xyz',
text: 'Hello there!',
sender_name: 'Alan Turing'
}
}
The from key is a good candidate for the chatId while the content of the message is in the text key.
the code in the Extend node
to handle this
// initial stage of the process, here the message is still not enriched with all the helpers and
// variables of a RedBot message like .api(), .chat(), chatId, etc
node.chat.onChatId(message => message.sms.from);
node.chat.onMessageId(message => message.sms.id);
node.chat.in(message => {
return new Promise((resolve, reject) => {
// check that the incoming payload is an actual "text" message
if (message.originalMessage.sms != null && message.originalMessage.sms.text != '') {
message.payload.type = 'message';
message.payload.content = message.originalMessage.sms.text;
}
resolve(message);
});
});
When a new payload arrives to the input pin the chatId value is extracted from the callback .onChatId()
(there are callbacks for other information like messageId, timestamp, language, etc) and a neutral version of the RedBot message is passed to the middlewares.
A neutral message is a regular RedBot message (the messages that flow out of a receiver node) except the type and content key are left blank: is up to the developer to fill in this keys using the information contained in originalMessage, all values needed to build a neutral message (chatId, messageId, timestamp, language) are extracted using callbacks.
If the callback in .onChatId()
fails to extract the chatId then an exception is raised and the message will never go through the middlewares.
In the example above there is additional information in the payload, it's a good practise to use the chat context for storing this information, for example
node.chat.onChatId(payload => payload.sms.from);
node.chat.onMessageId(payload => payload.sms.id);
node.chat.in(message => {
const chat = message.chat();
// check that the incoming payload is an actual message, if not return and resolve immediately
// this will skip to the next middleware
if (message.originalMessage.sms == nulll || message.originalMessage.sms.text == null) {
return Promise.resolve(message);
}
// not all context provider return a promise
return Promise.resolve(chat.set('name', message.originalMessage.sms.sender_name))
.then(() => {
message.payload.type = 'message';
message.payload.content = message.originalMessage.sms.text;
return message;
});
});
Note that a middleware should always return a Promise that returns the received message (passed as parameter of the middleware) and that will be injected in the flow (a warning on the console will log any middleware not returning a Promise).
If none of the middlewares in the inbound chain is able to infer a message type and fill in the type and content of the neutral message, then the message is discarded (enable the debug option in the receiver configuration to log the discarded messages in the system console).
The Universal sender node
has always an output pin used for following up the conversation or to chain multiple messages to be sent in a specific order.
In the example above the code to send a SMS message to a custom API
// this middleware handles the message of type 'message'
node.chat.out('message', message => {
return request('https://my.api/send', data: {
text: message.payload.content,
to: message.payload.chatId
}).then(response => {
message.status = response.statusCode;
// this make the chain of promises returns the message object enriched with the status code
// returned by API to be used by a downstream node
return message;
});
});
Like all other sender nodes there are two options:
- Track option: allows to track a conversation from the user: the answer of the user to a message sent with a sender with the track option will appear in the output pin in order to follow up the conversation (basically the message is teleported here). Pay attention to this option while debugging the Universal connector and the Extend node, it will result in a alternate and strange results on the output pin
- Pass through option: it will forward the RedBot message to the output pin. The Red message is the object that passes through the RedBot's nodes, it contains helpers and data like originalMessage, .api(), .chat(). The main purpose of this is to send multiple messages to the user in a specific order. If the Pass through option is not checked the output pin is still present and it's used to forward the final result of the chain of promises of an output middleware (stripped out of all helpers and data like originalMessage, .api(), .chat()). In the example above it's used to forward to a downstream node the result of the call to an external custom API.
For a list of all methods available in the msg.chat object see the Extend node.