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 action if the transaction passes is valid.
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 == this.name) { return 1; }
if (modname == 'Chat') { return 1; }
if (modname == 'Relay') { 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 integer 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.
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 && txmsg.module === "Chat") {
this.processChatTransaction(txmsg);
}
}
//
// receive peer-to-peer transactions
//
async handlePeerTransaction(app, tx, peer, mycallback = null) {
let txmsg = tx.returnMessage();
//
// Chat transactions
//
if (txmsg.module == "Chat") {
this.processChatTransaction(txmsg);
}
}
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:
async processChatTransaction(txmsg) {
//
// 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 !
//
alert("Huzzah!");
}
}
}
}
Compile this application and load it up in your browser. If you send a chat message directly to another user and include the word "huzzah" somewhere in the message, you will see an alert pop-up. Note that this will only work when you send and receive a message with a specific person. It will not work in Community Chat. The reason for that is that community chat messages are shared publicly and bounce around the network through use of something we call the Relay module.
If you would like, you need to add support for "Relay-embedded Chat Messages". You can do this by modifying your handlePeerTransaction()
function to include support for transactions that are produced by the Chat module for distribution via the peer-to-peer relay network. This code will works:
//
// receive peer-to-peer transactions
//
async handlePeerTransaction(app, tx, peer, mycallback = null) {
let txmsg = tx.returnMessage();
//
// Chat transactions
//
if (txmsg.module == "Chat") {
this.processChatTransaction(txmsg);
}
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);
}
}
What is happening here is that we 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 another peer.
tutorial04
code can be referenced here.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.