Skip to main content

Slider Notification Settings in Showrunners

This example is intended to get you understand slider based notification settings with a real-world application. For the example we are going to look at a scenario where users can choose a time interval and showrunners framework will notify them as per their request. Checkout Showrunners Docs, Showrunners Framework, Channel Settings Docs and Channel Settings Demo for better understanding!

What we gonna build?

Imagine you are a crypto trader or a general crypto enthusiast. You want to be notified every once in a while about the price movements and activities in the market. But you either lose track of time or forget about it. To solve this exact problem, we will be looking into a slider type notification settings implementation where you as a user can specify the time interval after which you would like to get notified.

We will choose the Eth Tracker channel to demonstrate this example.

Creating Eth Tracker in Showrunners

Step 1: Setup the Showrunners in your local machine

For detailed, step-by-step guide visit the Showrunners docs. First we need to create a folder in src/showrunners/<your_channel_name>

Step 2: Install Dependencies & start up

Navigate to the SDK directory and install required dependencies.

cd push-showrunners-framework
yarn install
docker-compose up
yarn run dev

Step 3: Import the Push SDK

After you have created a channel folder. Refer to Showrunners docs. Move to the [name]Channel.ts file and import the dependencies.

import { PushAPI } from '@pushprotocol/restapi';

Channel File

In order to send notification, we need to have the instance of the user channel. To get that, we need to add the below function in our 'Channel class' .

const provider = new ethers.providers.JsonRpcProvider(settings.providerUrl);
const signer = new ethers.Wallet(keys.PRIVATE_KEY_NEW_STANDARD.PK, provider);
const userAlice = await PushAPI.initialize(signer, {
env: CONSTANTS.ENV.STAGING,
});

Here, you can use any provider of your choice and fetch the signer using the private key of the wallet that was used to create the channel. The userAlice is an instance of the channel using the PushAPI from the sdk. This will allow us to fetch data of subscribers and their notification settings to build our logic around.

Let's get to building it.

Fetch subscribers data

To fetch a list of all users who have opted into receiving notifications along with their opted value from userAlice, you can use the subscribers method. You can read about this method in detail here.


const userData: any = await userAlice.channel.subscribers({
page: i,
limit: 10,
setting: true,
});

// Output :
{
"itemcount": 5,
"subscribers": [
{
"subscriber": "0x279c00e16c638a752ea42ae5e09db3c3992f70ad",
"settings": "[{\"type\": 2, \"user\": 8, \"index\": 1, \"ticker\": 1, \"default\": 1, \"enabled\": true, \"lowerLimit\": 1, \"upperLimit\": 10, \"description\": \"Price Range\"}]"
},
{
"subscriber": "0x49403ae592c82fc3f861cd0b9738f7524fb1f38c",
"settings": "[{\"type\": 2, \"user\": 1, \"index\": 1, \"ticker\": 1, \"default\": 1, \"enabled\": true, \"lowerLimit\": 1, \"upperLimit\": 10, \"description\": \"Price Range\"}]"
},
{
"subscriber": "0x71ffa5771e8019787190d098586efe02026a3c8c",
"settings": "[{\"type\": 2, \"user\": 2, \"index\": 1, \"ticker\": 1, \"default\": 1, \"enabled\": true, \"lowerLimit\": 1, \"upperLimit\": 10, \"description\": \"Price Range\"}]"
},
{
"subscriber": "0x7a45f2e84055b0c79696c9533c97a4b21dee30d3",
"settings": "[{\"type\": 2, \"user\": 2, \"index\": 1, \"ticker\": 1, \"default\": 1, \"enabled\": true, \"lowerLimit\": 1, \"upperLimit\": 10, \"description\": \"Price Range\"}]"
},
{
"subscriber": "0xc1836ce1eb918cfc8e9acab71ce9c6e1ebe0dff0",
"settings": "[{\"type\": 2, \"user\": 7, \"index\": 1, \"ticker\": 1, \"default\": 1, \"enabled\": true, \"lowerLimit\": 1, \"upperLimit\": 10, \"description\": \"Price Range\"}]"
}
]
}

The main parameters that we need for our use-case is type, user, index and enabled. Let's go one by one in brief.

  1. type - Type here tells us if a notification type is boolean or slider type. There are: 1 ---> boolean and 2 ---> slider.
  2. user - This parameter tells us the value a specific user chose in their settings. A value in case of a slider and true/false in case of boolean.
  3. index - This tells us the index of the notification settings as per their creation by the channel owner. The first channel settings gets the index value 1, the next gets 2 and so on.
  4. enabled - As the name suggests, it tells us if an user have opted in for a settings or not. It's values can be true or false.

Basic logic implementation

let i = 1;

while (true) {
const userData: any = await userAlice.channel.subscribers({
page: i,
limit: 10,
setting: true,
});

if (userData.itemcount != 0) {
i++;
} else {
console.log("Breakkkk.")
i=1;
break;
}

// Loop through the `settings` array for the required type (say 2 here) --> Time interval
userData.subscribers.map(async (subscriberObj) => {
const userSettings = JSON.parse(subscriberObj.settin
const mappedValue = await ethTickerModel.findOne({ subscriber: subscriberObj.subscriber });

if (userSettings !== null) {
const temp = userSettings.find((obj) => obj.index == 2); // for time interval
let userValue: number;

//IF user has enabled notification then enter
if (temp.enabled === true) {
userValue = temp.userValue;

if (mappedValue.lastCycle + userValue == CYCLES) {
recipients.push(subscriberObj.subscriber);

// UPDATE the users mapped value in DB
await ethTickerModel.findOneAndUpdate(
{ subscriber: subscriberObj.subscriber },
{ lastCycle: mappedValue.lastCycle + userValue},
{ upsert: true },
);
}
}
});
}

Let's go step-by-step here.

  1. We run a loop through all the subscribers of a channel. Remember to handle pagination as the subscribers' list is paginated.
  2. Here, we can consider a global variable CYCLES that we store in our database that is responsible to track the iterations of the cron-job we setup for 1 hour.
  3. We check if a new subscriber is added to the channel at the start of every cron-job, if yes then we add subscriber address mapped to the current CYCLES value in MongoDB.
  4. For sending notification, if the mappedValue + userValue == CYCLES, we send a notification.
  5. We then update the value in DB, mappedValue += userValue
  6. After the entire logic is done, we update the CYCLES variable, CYCLES++

This is the basic logic behind the notification trigger.

Build the notification payload

Design your own payload with custom values in the when you want to trigger notifications. To learn more about notification settings, refer to docs

const payload = {
notification: {
title: 'Title',
body: 'Notif Body',
},
payload: {
title: 'Title',
body: 'Payload body',
cta: 'https://google.com/',
embed: 'https://avatars.githubusercontent.com/u/64157541?s=200&v=4',
// index of the notification the channel wants to trigger, in this for 1nd index which is for Boolean type
category: 1, // Depending upon your use-case
},
};

Setup Notification trigger

public async sendMessageToNode(simulate) {
const logger = this.logger;
this.getNewPrice()
.then(async (payload: any) => {
for (let i = 0; i < payload.recipients.length; i++) {
this.sendNotification({
recipient: payload.recipients[i], // new
title: payload.notifTitle,
message: payload.notifMsg,
payloadTitle: payload.title,
payloadMsg: payload.msg,
notificationType: payload.type,
simulate: simulate,
image: null,
});
}
})
.catch(err => {
logger.error(`[${new Date(Date.now())}]-[ETH Ticker]- Errored on CMC API... skipped with error: %o`, err);
});
}

How does it actually work?

Summarizing what we implemented in the example, we use a concept of a global clock (say CYCLES) using a variable that is stored in our database and it gets updated after every iteration of the cron-job that runs for every 1 hour. We check if any new users have subscribed to the channel after the last iteration and store a mapping of the user address and the current CYCLES value. This mapping will allow us to calculate the correct dispatch time of notification as per user.

Well, that's it. You now have a clear understanding of how notification slider settings work and you can use it to build amazing use-cases. We'll see you in another one. Until then keep building🔥