This tutorial explains how to build an application that listens for chat messages from the already included and compiled Chat Module. When it receives a chat message it passes it into a function that analyses the transaction and takes appropriate action.
Follow the instructions in previous tutorials to create your module directory and application javascript file. We start this tutorial assuming that your application contains a constructor but no other files or content.
We want our application to listen for messages that have been sent to the Chat module, so we add the following function which will ensure that whenever Saito receives a Chat message, it will be forwarded to our module for processing.
shouldAffixCallbackToModule(modname) {
if (modname == 'Chat') { return 1; }
return 0;
}
We now add the functions that will receive Chat transactions when they arrive. This can be done using onConfirmation() or handlePeerTransaction(). The code in this tutorial will use both, so it's important to understand the difference between them:
The first function onConfirmation() runs whenever transactions are received on-chain (in a block).
onConfirmation() checks the variable conf which contains some BigInt defining how many block confirmations to wait before running. Processing a transaction when (conf == 0) means processing it as soon as it is received.
If you check when (conf == 1) your code will run on the first confirmation (i.e. when a block is added which re-inforces the position of the transaction in the chain.
Increasing this value increases the re-org security of the data (decreases the chances it will be reverted). It's the same logic that causes users to wait 6 block confirmations for a Bitcoin transaction to settle, but tunable for any data and any security parameters in your application.
The second function, handlPeerTransaction(), is similar, but runs whenever a transaction arrives off-chain, outside of a block. It doesn't wait for the transaction to be included in or confirmed by any blocks - it runs as soon as the data arrives from a peer.
This function is ideal for situations where security against re-orgs is less important than speed and responsiveness - in many cases there is little to no security penalty, but it's the responsibility of the module creator to discern when and where it is appropriate.
You can use functions inside app.network such as sendTransactionWithCallback or sendRequestAtTransaction in order to communicate with an immediate peer (i.e. from a browser to the full node). Alternatively, Saito has a utility module called Relay which allows you to specify a target publicKey address for the transaction.
These two functions are used in our module like so:
//
// receive on-chain transactions
//
async onConfirmation(blk, tx, conf) {
let txmsg = tx.returnMessage();
if (conf == 0) {
this.processChatTransaction(txmsg, "on chain");
}
}
//
// receive peer-to-peer transactions
//
async handlePeerTransaction(app, tx, peer, mycallback = null) {
let txmsg = tx.returnMessage();
//
// Chat transactions
//
if (txmsg.request == 'chat relay') {
let inner_tx = new Transaction(undefined, txmsg.data);
await inner_tx.decryptMessage(app);
let inner_txmsg = inner_tx.returnMessage();
this.processChatTransaction(inner_txmsg, 'off chain');
}
}
What is happening here is that we embed our chat transaction inside a "meta" transaction so that an intermediate Chat server can relay it to off-chain to users who it may or may not be addressed to. Community Chat is open to everyone. Thus when we receive a "chat relay", we need to reconstruct the "inner" transaction from the data-field of the Relay transaction. We can then ask Saito to provide us with the txmsg from the inner transaction, which will be our chat transaction. This can then be send to our processChatTransaction() function as if it arrived directly from the original sender.
Let's move on and write a function that we call upon after these Chat transactions are received. It will take the txmsg contained within the transaction and examine it to decide whether to take action:
processChatTransaction(txmsg, source = "") {
//
// am I a browser?
//
if (this.app.BROWSER) {
//
// is there a chat message here?
//
if (txmsg.message) {
//
// examine the message and...
//
if (txmsg.message.indexOf("huzzah") > -1) {
//
// do something !
//
console.log('Huzzah! -- ' + source);
alert('Huzzah! -- ' + source);
}
}
}
}
Compile this application and load chat in two browsers. Try typing a message containing "huzzah" in a direct message. Do you get two alerts, both for the message relayed off chain and when the block arrives?
Now try typing "huzzah" into the community chat and see what is the differences? Do both browsers have two alerts? Why not?
Answer: we always include our own address when sending a chat transaction, so we receive our own community chat on chain, but other users to do not receive it in the lite blocks. Lite blocks only contain transactions that are addressed to us (or to watched keys in their keychain).
In this tutorial, we learned:
In our next tutorial we'll explore how to integrate your application into the various default menus the Saito-Lite-Rust software has to offer.
tutorial04 code can be referenced here.