NodeJs Application to send alerts to users email ID - node.js

I'm creating a NodeJs application that sends alerts to a user's email ID when the price of the bitcoin goes above the price specified by the user. For scheduling tasks, I'm using cron. Also, I've used the bull as a message broker. When I'm running this program, It isn't working and it isn't sending emails when the price is above the specified price. Please help me to find out what is the problem.
require('dotenv').config({path: require("find-config")(".env")});
const CronJob = require("cron").CronJob;
let Queue = require("bull");
const Alert = require("../models/alert");
const { currentPrice } = require("../utilities/currentPrice");
const { sendEmail } = require("../utilities/sendEmailNotification");
//Creating a Queue
let alertQueue = new Queue("alerts", process.env.RedisURL);
//Consumer Process
alertQueue.process(async function(job, done) {
const {mailTo, title, text} = job.data;
const mailObj = {
from: process.env.SendGridForm,
recipients: mailTo,
subject: title,
message: text
}
const response = sendEmail(mailObj);
if(response.error) {
done(new Error("Error Sending Alert!!!"));
}
done();
})
let sendAlert = new CronJob("*/25 * * * * *", async function () {
let priceObj = await currentPrice();
if (priceObj.error)
return;
let price = priceObj.data;
const alerts = await Alert.find({status: "Created"});
alerts.forEach((alert) => {
if(alert.price <= price) {
mailTo = alert.email;
title = `Bitcoint is UP!`;
text = `Price of Bitcoin has just exceeded your alert price of ${alert.price} USD. Current price is ${price} USD.`;
alertQueue.add(
{mailTo, title, text},
{
attempts: 3,
backoff: 3000
}
)
alert.status = "Triggered";
alert.save();
}
})
});
sendAlert.start();

Related

UniswapV2 swapExactETHForTokens method fails with error status 'UniswapV2: TRANSFER_FAILED'

I am using web3.js library and I am trying to buy a token by calling swapExactETHForTokens method from
UniswapV2Router02 smart contract, but I don't know why my transaction fails. I approved WETH for this transaction, but still get an error with the following status:
Fail with error 'UniswapV2: TRANSFER_FAILED'
My code:
const swapTokens = async () => {
const PRIVATE_KEY = 'my private key goes here';
web3.eth.accounts.wallet.add(PRIVATE_KEY);
const myAccount = web3.eth.accounts.wallet[0].address;
const WETHAddress = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const swapRouterAddress = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D';
const routerContract = new web3.eth.Contract(
UNISWAP_V2_ROUTER_ABI,
swapRouterAddress,
);
const tokenToBuyAddress = '0x0913dDAE242839f8995c0375493f9a1A3Bddc977';
const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
const block = await web3.eth.getBlock('latest');
const gasLimit = Math.round(block.gasLimit / block.transactions.length);
const amountToBuy = 0.01;
const result = await routerContract.methods
.swapExactETHForTokens(
web3.utils.toHex(0),
[WETHAddress, tokenToBuyAddress],
myAccount,
deadline,
)
.send({
from: myAccount,
gasLimit,
value: web3.utils.toWei(`${amountToBuy}`, 'ether'),
});
console.log('result: ', result);
}
swapTokens();
Transaction details on etherscan: https://etherscan.io/tx/0x4c6f507ed95b2889bdb929a34dbbe0114db168c2462ce21778eeed9dc4a894eb
Smart contract of token which I am trying to buy: https://etherscan.io/address/0x0913dDAE242839f8995c0375493f9a1A3Bddc977#code
Your transaction ran out of gas. Increase your gas limit

Get number of requisitions made by the user in a day

I have a POST ENDPOINT in my API where i want to register all work journals a worker made.
export const registerPoint = async (req: Request, res: Response) => {
const user = res.locals.decoded;
const {id} = user;
const ponto = new Ponto;
ponto.datePoint = new Date();
ponto.user_id = id;
//this numPoint should restart every day for that user...
ponto.numPoint = 1;
res.json({ msg: "Register point Route", user, id });
return;
}
how can i control how many times this requisition was made by a worker?
I want to control the variable numPoint, when the user makes this requisition it should increase by 01 and then in the final of the day returns it to 0.
Anyone knows about a solution or about a npm package that can handle this?
EDIT: Im storing all the data with SEQUELIZE + MYSQL.
As a starting point you could use a simple database for storing the data such as https://www.npmjs.com/package/sqlite3 or MySQL.
Running jobs daily you could consider https://www.npmjs.com/package/node-cron , for example having a daily job (outside of an API call function);
var cron = require('node-cron');
cron.schedule('0 0 1 * *', () => {
console.log('running every minute to 1 from 5');
});
From what I understand, you need a logging/audit trail mechanism. Since you are using MySQL you can create a new table with columns like (datetime, user_id, action). Every time the user does any action, it will be logged here. Then you can easily find out the count by aggregation. You won't need to reset any count for any user, if data doesn't exist for a given date then it's count will be 0.
I've made a solution that it worked for what i want.
Here is the solution below:
export const registerPoint = async (req: Request, res: Response) => {
const decodedUser = res.locals.decoded;
const user = await User.findByPk(decodedUser.id);
const TODAY_START = new Date().setHours(0, 0, 0, 0);
const NOW = new Date();
const markedPointsOfDay = await Ponto.findAll({
where: {
datePoint: {
[Op.gt]: TODAY_START,
[Op.lt]: NOW
},
}
});
const markedPointsOfDayByUser = await Ponto.findAll({
where: {
datePoint: {
[Op.gt]: TODAY_START,
[Op.lt]: NOW
},
UserId: (user !== null) ? user.id : decodedUser.id
}
})
if (!markedPointsOfDay || markedPointsOfDay.length === 0) {
const ponto = new Ponto;
ponto.datePoint = new Date();
if (user) {
console.log(user)
ponto.UserId = user.id as number;
console.log(ponto.UserId);
}
if (markedPointsOfDayByUser) {
ponto.numPoint = markedPointsOfDayByUser.length + 1;
}
const newPoint = await ponto.save();
res.json({ msg: "Ponto registrado com sucesso", msg2: "Caiu no IF de quando nao encontrou ponto do DIA", newPoint })
return;
}
if (markedPointsOfDay) {
const ponto = new Ponto;
ponto.datePoint = new Date();
if (user) {
ponto.UserId = user.id as number;
}
if (markedPointsOfDayByUser) {
ponto.numPoint = markedPointsOfDayByUser.length + 1;
}
const newPoint = await ponto.save();
res.json({ msg: "ponto registrado", markedPoint: newPoint, markedPointsOfDayByUser });
return;
}
return;
}

How do I properly send emails to users exactly by 12am in their own time zone

I am working on a nodejs project that involves sending emails to users exactly by 12 am in their own timezone. I tried collecting the user's timezone during registration and storing it in the database. Then I tried looping through the users collection and mailing the user based on his/her timezone but it ended up mailing all users with the same time zone alongside users with no timezone and skipping those with a different timezone entirely.
I'm using expressjs, MongoDB for database management, and node-cron to schedule the email sending.
I will really appreciate your help guys.
email service file
const nodemailer = require("nodemailer");
const mg = require("nodemailer-mailgun-transport");
const handlebars = require("handlebars");
const fs = require("fs");
const path = require("path");
let cron = require("node-cron");
const { User } = require("../models/user");
const verses = require("kjv/json/verses-1769.json");
const store = require("store2");
const { bible_books } = require("./data");
const _ = require("lodash");
async function getUsersEmail() {
const users = await User.find().select(["email","timezone"]);
return users;
}
module.exports = async function () {
const emailTemplateSource = fs.readFileSync(
path.join(__dirname, "../views/template.hbs"),
"utf8"
);
const mailgunAuth = {
auth: {
api_key: process.env.mailgun_apikey,
domain: process.env.mailgun_domain,
},
};
const smtpTransport = nodemailer.createTransport(mg(mailgunAuth));
const template = handlebars.compile(emailTemplateSource);
(await getUsersEmail()).map(async function (value) {
cron.schedule("0 0 * * *", async function () {
console.log("running task every day at 12:00 am");
const bible_book = _.sampleSize(bible_books, 1);
store("bible", bible_book[0]);
let verse = store("bible");
let bible_passage = verse;
let bible_text = verses[bible_passage];
await User.findByIdAndUpdate(
value?._id,
{
bible: {
verse: bible_passage,
text: bible_text,
},
},
{ new: true }
);
const htmlToSend = template({
verse: bible_passage,
text: bible_text,
imageUrl:"https://world.png",
redirectUrl: "https://redirecturl.com/home",
year: new Date().getFullYear(),
});
const mailOptions = {
from: "from#domain.org",
to: value?.email,
subject: "Send",
html: htmlToSend,
};
smtpTransport.sendMail(mailOptions, function (error, response) {
if (error) {
console.log(error);
} else {
console.log(`Successfully sent email to ${mailOptions.to}.`);
}
});
}, {
scheduled: true,
timezone:value?.timezone
});
console.log(value?.timezone)
});
};
calling my service file in my index.js
require("./service/emailServiceFile")();
There are many approaches to do this but let's discuss your solution
The time is midnight[12:00] somewhere around the globe now, so the schedule should run every hour not day.
cron.schedule("0 * * * *", ...
You need to check if it is after 12:00 am in the user's timezone with something like this.
if (new Date(new Date().toLocaleString('en-us', { timeZone: value?.timezone })).getHours() == 0) { //Send Emails }

Values get overwritten by latest person to request the bot?

I have made a raffle ballot discord bot that allows a user to DM the bot their name and raffle entry amount. Once they have set the values they can start the entry of the raffle by DMing !enter. Once this has happend a function is called which then starts a for-loop the for loop will run based on the specified entry amount. I have also added in a delay within the for-loop due to the service to get the raffle tickets takes some time (Code is edited for SO Post due to sensitive API info)
Once this is complete it then sends a DM back to the user that had DMed the bot originally. The problem I am facing is that if multiple users DM at the same time or while it is running from the first DM the variables get overwritten by the latest person requesting the bot.
I assumed that by using a Discord.js bot each time a user DMs it creates a new instance of the script or node process?
Is it possible for the function that the bot calls once DMed to create a new process within the main node process so it doesn't get overwritten?
const Discord = require('discord.js');
const botconfig = require('./discordBotConfig.json');
const bot = new Discord.Client({disableEveryone: true});
const c = require('chalk');
// Chalk Theme
const ctx = new c.constructor({level: 2});
const error = c.red;
const waiting = c.magenta;
const success = c.green;
const discordBot = c.yellow;
// Current Raffles (API Link Later)
let activeRaffles = 'Raffle 1';
// User Parmas
let usrName = '';
let entryAmount = 0;
// Ticket
let raffleTicket = [];
let retryDelay = 3000;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Enter
const enterIn = async () => {
console.log('User name', usrName);
raffleTicket.push(Math.random(0, 50));
}
// Init Raffle Entry
const raffleInit = async (entryAmount) => {
for (let i = 0; i < entryAmount; i++) {
enterIn();
await sleep(retryDelay);
}
dmUser();
}
const dmUser = () => {
// Discord Message Complete
let botCompleteMsg = new Discord.RichEmbed()
.setTitle('Finished!')
.setColor('#25E37A')
.addField('Name: ', usrName)
.addField('Tickets: ', raffleTicket)
.addField('Last Update: ', bot.user.createdAt);
bot.fetchUser(userID).then((user) => {
user.send(botCompleteMsg);
});
return; // End the application
}
// Discord Bot Setup
bot.on('ready', async () => {
console.log(discordBot(`${bot.user.username} is Online!`));
bot.user.setActivity('Entering Raffle');
});
bot.on('message', async message => {
if (message.channel.type === 'dm') {
let prefix = botconfig.prefix;
let messageArray = message.content.split(' ');
let cmd = messageArray[0];
if (cmd === `${prefix}name`) {
if (messageArray.length === 3) {
userID = message.author.id;
usrName = messageArray[1];
entryAmount = messageArray[2];
// Raffle summary
let raffleSummary = new Discord.RichEmbed()
.setTitle('Entry Summary')
.setColor('#8D06FF')
.addField('Name: ', usrName)
.addField('Entry Amount: ', entryAmount)
return message.author.send(raffleSummary), message.author.send('Type **!start** to begin entry or type **!set** again to set the entry details again.');
}
}
if (cmd === `${prefix}enter`) {
// Raffle summary
let startMessage = new Discord.RichEmbed()
.setTitle('Entering raffle!')
.setDescription('Thanks for entering! :)')
.setColor('#8D06FF')
return message.author.send(startMessage), raffleInit(entryAmount);
}
}
});
bot.login(botconfig.token);
You can store your user data in a list with classes.
var userData = [
{name: "sample#0000", entryNum: 0}
];

Node Js value not showing in notification when testing

I am making a notification using firebase cloud functions with node js and my app made on swift
this is my payload
var payload = {
notification: {
title: (goOfflineTimePeriod,"Inactive for minutes you are now offline"),
body: "Time period for inactive log off can be changed in your settings"
though my notification my notification only shows as; "Inactive for minutes you are now offline", "Time period for inactive log off can be changed in your settings"
so the variable; goOfflineTimePeriod does not show in the notification
I am only new to node js is there a reason why "goOfflineTimePeriod" does not show in the notification?
here is my full node js function code;
exports.goOfflineAlert = functions.firestore
.document('/goneOffline/{uid}')
.onCreate((snap, context) => {
var db = admin.firestore();
var uid = context.params.uid;
const newValue = snap.data();
const goOfflineTimePeriod = newValue.goneOffline;
console.log('uid is',uid)
var cityRef = db.collection('Users').doc(uid);
var getDoc = cityRef.get()
.then(doc => {
if (!doc.exists) {
return console.log('No such document!');
} else {
const newValue = doc.data();
const age = newValue.Age;
const name = newValue['Display Name'];
const fcmToken = newValue.fcmToken;
const goOfflineTimePeriod = newValue.goOfflineTimePeriod;
console.log('Document data:', doc.data(),age,fcmToken,name,goOfflineTimePeriod);
var payload = {
notification: {
title: (goOfflineTimePeriod,"Inactive for minutes you are now offline"),
body: "Time period for inactive log off can be changed in your settings"
}
}
A , operator does not concatenate strings in JavaScript, but it returns the second operand. Instead you should use + or template string. For example:
title: goOfflineTimePeriod+" Inactive for minutes you are now offline",

Resources