Skip to content

Commit

Permalink
EthTracker notif settings implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
Arunava-Barua committed Dec 3, 2023
1 parent 09bc5fb commit fd3a945
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/sample_showrunners/bank/bankChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default class BankChannel extends EPNSChannel {

}


// async apyNotif(userAlice, apy, simulate) {
// try {
// this.logInfo("Getting events ---> apyNotif");
Expand Down Expand Up @@ -144,7 +145,7 @@ export default class BankChannel extends EPNSChannel {
try {
const notifRes = await userAlice.channel.send(['*'], payload);

return notifRes
return notifRes;
} catch (error) {
this.logInfo("ERROR🔴 from sendThroughNotifSettings: ", error);
}
Expand Down
171 changes: 171 additions & 0 deletions src/sample_showrunners/ethTicker/ethTickerChannel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// @name: ETH Tracker Channel
// @version: 1.0
// @recent_changes: ETH Price Tracker

import { Service, Inject } from 'typedi';
import config from '../../config';

import axios from 'axios';

import { EPNSChannel } from '../../helpers/epnschannel';
import { Logger } from 'winston';

// Import the Push SDK
import { PushAPI } from "@pushprotocol/restapi";

import { ethers } from "ethers";
import { mongo } from 'mongoose';

import { ethTickerModel } from './ethTickerModel';


const bent = require('bent'); // Download library

const NETWORK_TO_MONITOR = config.web3MainnetNetwork;
// const ethTickerSettings = require('./ethTickerSettings.json')

@Service()
export default class EthTickerChannel extends EPNSChannel {
constructor(@Inject('logger') public logger: Logger) {
super(logger, {
networkToMonitor: NETWORK_TO_MONITOR,
dirname: __dirname,
name: 'ETH Ticker',
url: 'https://epns.io/',
useOffChain: true,
});
}

/*
1. Fetch all the user settings - userAlice.channel.subscribers()
2.
*/

// To form and write to smart contract
public async sendMessageToContract(simulate) {
const logger = this.logger;

this.getNewPrice()
.then(async (payload: any) => {

for (let i = 0; i < payload.recipients.length; i++) {
this.sendNotification({
// recipient: this.channelAddress,
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);
});
}

public async getNewPrice() {
const logger = this.logger;
logger.debug(`[${new Date(Date.now())}]-[ETH Ticker]-Getting price of eth... `);

return await new Promise((resolve, reject) => {
const getJSON = bent('json');

const cmcroute = 'v1/cryptocurrency/quotes/latest';
const cmcEndpoint = 'https://pro-api.coinmarketcap.com/'
const pollURL = `${cmcEndpoint}${cmcroute}?symbol=ETH&CMC_PRO_API_KEY=${'1bbe0bab-4ee7-4a38-8b03-b49a0b4fff4e' || config.cmcAPIKey}`;

console.log(`CMC Cnnfig: ${cmcEndpoint}, CMC_PRO_API_KEY = ${'1bbe0bab-4ee7-4a38-8b03-b49a0b4fff4e' || config.cmcAPIKey}`);

getJSON(pollURL)
.then(async (response: any) => {
if (response.status.error_code) {
reject(`CMC Error: ${response.status}`);
}

logger.info(`[${new Date(Date.now())}]-[ETH Ticker]-CMC Response: %o`, response);

// Get data
const data = response.data['ETH'];

// construct Title and Message from data
const price = data.quote.USD.price;
const formattedPrice = Number(Number(price).toFixed(2)).toLocaleString();


const hourChange = Number(data.quote.USD.percent_change_1h);
const dayChange = Number(data.quote.USD.percent_change_24h);
const weekChange = Number(data.quote.USD.percent_change_7d);

const hourChangeFixed = hourChange.toFixed(2);
const dayChangeFixed = dayChange.toFixed(2);
const weekChangeFixed = weekChange.toFixed(2);

// Initialize arr for all recepients
let recipients: string[] = [];

// 1. Store prev price in MongoDB
// Store
await ethTickerModel.findByIdAndUpdate(
{ _id: 'prev_eth_price' },
{ prevEthPrice: Number(formattedPrice) },
{ upsert: true },
);

// Retrieve
// const prevPrice = 0; //= mongo.findOne();
const prevPrice = await ethTickerModel.findOne({ _id: 'prev_eth_price' });

// 2. Calculate percentage change. |prevValue - currentValue| / prevValue
const changePercentage = Math.abs(prevPrice.prevEthPrice - price) / prevPrice.prevEthPrice;

// 3. Get the list of all the addresses opted in for the setting - /subscribers?category=2&setting=true
const { data: userData } = await axios(`https://backend-staging.epns.io/apis/v1/channels/eip155:${'11155111'}:${'0x9C2dA92ff312b630B67cEa1d2C234250c2d3410e'}/subscribers?category=${'2'}&setting=${'true'}`);

// 4. Loop through the `settings` array for the required type (say 2 here) and get the `user` value
userData.subscribers.map((subscriberObj) => {
const userSettings = JSON.parse(subscriberObj.settings);
const temp = userSettings.find((obj) => obj.index === 1); // arr[0] = index: 1 ---> arrIndex + 1
let userValue : number;

if (temp.enabled === true) {
userValue = temp.user;

// Construct payload if optted in or pass
// 5. If the value matches the percentage change, send the notification, else pass
if (userValue === hourChange) {
recipients.push(subscriberObj.subscriber);
}
}
});

const title = 'ETH at $' + formattedPrice;
const message = `\nHourly Movement: ${hourChangeFixed}%\nDaily Movement: ${dayChangeFixed}%\nWeekly Movement: ${weekChangeFixed}%`;

const payloadTitle = `ETH Price Movement`;
const payloadMsg = `ETH at [d:$${formattedPrice}]\n\nHourly Movement: ${
hourChange >= 0 ? '[s:' + hourChangeFixed + '%]' : '[t:' + hourChangeFixed + '%]'
}\nDaily Movement: ${
dayChange >= 0 ? '[s:' + dayChangeFixed + '%]' : '[t:' + dayChangeFixed + '%]'
}\nWeekly Movement: ${
weekChange >= 0 ? '[s:' + weekChangeFixed + '%]' : '[t:' + weekChangeFixed + '%]'
}[timestamp: ${Math.floor(Date.now() / 1000)}]`;

const payload = {
type: 1, // Type of Notification
notifTitle: title, // Title of Notification
notifMsg: message, // Message of Notification
title: payloadTitle, // Internal Title
msg: payloadMsg, // Internal Message
recipients: recipients // Recipients Array
};

resolve(payload);
})
.catch(err => reject(`Unable to reach CMC API, error: ${err}`));
});
}
}
48 changes: 48 additions & 0 deletions src/sample_showrunners/ethTicker/ethTickerJobs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Do Scheduling
// https://github.com/node-schedule/node-schedule
// * * * * * *
// ┬ ┬ ┬ ┬ ┬ ┬
// │ │ │ │ │ │
// │ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
// │ │ │ │ └───── month (1 - 12)
// │ │ │ └────────── day of month (1 - 31)
// │ │ └─────────────── hour (0 - 23)
// │ └──────────────────── minute (0 - 59)
// └───────────────────────── second (0 - 59, OPTIONAL)
// Execute a cron job every 5 Minutes = */5 * * * *
// Starts from seconds = * * * * * *

import config from '../../config';
import logger from '../../loaders/logger';

// Import the Push SDK
import { PushAPI } from "@pushprotocol/restapi";

import { ethers } from "ethers";

import { Container } from 'typedi';
import schedule from 'node-schedule';

import EthTickerChannel from './ethTickerChannel';

export default async () => {
const startTime = new Date(new Date().setHours(0, 0, 0, 0));

const sixHourRule = new schedule.RecurrenceRule();
sixHourRule.hour = new schedule.Range(0, 23, 6);
sixHourRule.minute = 0;
sixHourRule.second = 0;
const channel = Container.get(EthTickerChannel);
channel.logInfo(`🛵 Scheduling Showrunner`);

schedule.scheduleJob({ start: startTime, rule: sixHourRule }, async function() {
const taskName = 'ETH Ticker Fetch and sendMessageToContract()';
try {
await channel.sendMessageToContract(true);
logger.info(`[${new Date(Date.now())}] 🐣 Cron Task Completed -- ${taskName}`);
} catch (err) {
logger.error(`[${new Date(Date.now())}] ❌ Cron Task Failed -- ${taskName}`);
logger.error(`[${new Date(Date.now())}] Error Object: %o`, err);
}
});
};
8 changes: 8 additions & 0 deletions src/sample_showrunners/ethTicker/ethTickerKeys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"PRIVATE_KEY_NEW_STANDARD": {
"PK": "0x929bd276327aea16fde459a68e2a6c2ea487685203497b597b971707164ace20",
"CHAIN_ID": "eip155:42"
},
"PRIVATE_KEY_OLD_STANDARD": "0x929bd276327aea16fde459a68e2a6c2ea487685203497b597b971707164ace20"
}

17 changes: 17 additions & 0 deletions src/sample_showrunners/ethTicker/ethTickerModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable prettier/prettier */
import { model, Schema } from 'mongoose';

export interface EthTickerData {
prevEthPrice?: number;
}

const ethTickerSchema = new Schema<EthTickerData>({
_id: {
type: String,
},
prevEthPrice: {
type: Number,
},
});

export const ethTickerModel = model<EthTickerData>('ethTickerDB', ethTickerSchema);
37 changes: 37 additions & 0 deletions src/sample_showrunners/ethTicker/ethTickerRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Router, Request, Response, NextFunction } from 'express';
import { Container } from 'typedi';
import EthTickerChannel from './ethTickerChannel';
import middlewares from '../../api/middlewares';
import { celebrate, Joi } from 'celebrate';
import { Logger } from 'winston';

const route = Router();

export default (app: Router) => {
app.use('/showrunners/ethticker', route);

// to add an incoming feed
route.post(
'/send_message',
celebrate({
body: Joi.object({
simulate: [Joi.bool(), Joi.object()],
}),
}),
middlewares.onlyLocalhost,
async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger');
logger.debug('Calling /showrunners/ethticker endpoint with body: %o', req.body);

try {
const ethTicker = Container.get(EthTickerChannel);
const response = await ethTicker.sendMessageToContract(req.body.simulate);

return res.status(201).json(response);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
);
};
3 changes: 3 additions & 0 deletions src/sample_showrunners/ethTicker/ethTickerSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cmcEndpoint": "https://pro-api.coinmarketcap.com/"
}

0 comments on commit fd3a945

Please sign in to comment.