This tutorial demonstrates the automatic filtering of messages based based on conditions defined by the module. It will also show how to save data to the user's wallet so it can be used between sessions.
Note: This tutorial uses Tutorial 03 as a skeleton, so be sure you understand it.
We begin with a familiar setup for our constructor. Inside the render()
function, we initialize and introduce a new object this.app.options
, a special object which exists within every application for persistent storage.
The first thing Tutorial07
does on launch is making sure we can find a set of keywords which we will filter incoming transactions against.
class Tutorial07 extends ModTemplate {
constructor(app) {
super(app);
this.name = "Tutorial07";
this.slug = "tutorial07";
this.description = "Filtering";
this.categories = 'Dev educational';
this.ui = new MainUI(this.app, this);
return this;
}
async render() {
if (!this.app.options.tutorial07) {
this.app.options.tutorial07 = {};
}
if (!this.app.options.tutorial07.keywords) {
this.app.options.tutorial07.keywords = new Set(["PoS"]);
}
console.log("| - - - | this.app.options | - - - | ", this.app.options);
this.ui.render();
}
...
Later in we'll learn how to save those options to the wallet so they persist between sessions. Inside that object we've initiated a Javascript Set
which can efficiently test for membership - perfect for filtering any strings which happen to match an element within it.
The transaction logic is almost identical to Tutorial 3, but instead of sending random numerical values in newtx.msg
we let the user input strings from an input field. That data is still displayed in the browser when the transaction receives a block confirmation.
lib/main.js
render() {
...
let btn = document.querySelector('.tutorial07-button');
if (btn) {
btn.onclick = (e) => {
const textInput = document.querySelector('#tutorial07-text');
let text = textInput ? textInput.value : '';
this.mod.sendTutorial07Transaction(text);
}
...
tutorial07.js
async sendTutorial07Transaction(text) {
let address = await this.app.wallet.getPublicKey();
let newtx = await this.app.wallet.createUnsignedTransaction(address);
newtx.msg = {
module: this.name,
data: text
};
await newtx.sign();
this.app.network.propagateTransaction(newtx);
}
To utilize the core Saito moderation module, we'll need to use a new application function, respondTo()
. Here we provide 'saito-moderation-app'
to let Tutorial07
interoperate and define some parameters for moderating transactions it receives.
The most important part of this code is the function definition filter_func
that this tutorial module provides. It takes incoming transactions and tests them against whatever conditions are defined, and returns a 1
, -1
, or 0
to determine if the lite client will process the transaction or not.
respondTo(type = '', obj = null) {
// this moderation-level examines ALL transactions that are sent into specific
// applications and checks to see if they are permitted. it will block applications
// from being processed if they do not meet criteria.
//
// 1 = definitely show
// -1 = definitely filter
// 0 = no preference
if (type === 'saito-moderation-app') {
return {
filter_func: (app = null, tx = null) => {
if (tx == null || app == null) {
return 0;
}
if (tx.msg.module === "Tutorial07" &&
if (tx.msg.module === "Tutorial07" && this.app.options.tutorial07.keywords.has(tx.msg.data)) {
console.log("Keywords: ", this.app.options.tutorial07.keywords);
console.log("FILTERING CHAT MESSAGEE");
return -1;
}
return 0;
}
};
}
}
The definition of filter_func
above checks to see if the any element of the Set
we defined in this.app.options.tutorial07.keywords
matches the user provided data in tx.msg.data
.
Recall that "PoS"
was included in the set on its initialization. You can confirm the filtering is working by attempting to send that string in the user interface; the web console will print a message indicating the message was filtered, and that transaction will not be processed by the application.
The tutorial also lets you add custom filter words. The logic which binds those user interface functions to the Saito module is all contained within lib/main.js
. The following section shows how custom words can be saved to the wallet so they persist between sessions.
Save modifications an to the options this.app.options
with:
this.mod.app.storage.saveOptions();
After saving options with this method, this options object will keep whatever modifications were made to it in browser storage. When the application restarts, those modifications can be recovered and reused.
These options don't just persist between sessions, but live within the wallet. Therefore, a standard wallet backup the user performs will contain these saved module options so they can be easily transferred across browsers and devices.
The complete tutorial06 code can be referenced here.