Get number of requisitions made by the user in a day - node.js

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;
}

Related

Caching in NODEJS

I am trying to cache the results of the Twitter query as they have rate limiting and I cannot seem to figure it out.
I am following this tutorial here.
The Cache.js file looks like so (taken from tutorial):
import NodeCache from 'node-cache';
class Cache {
constructor(ttlSeconds) {
this.cache = new NodeCache({ stdTTL: ttlSeconds, checkperiod: ttlSeconds * 0.2, useClones: false });
}
get(key, storeFunction) {
const value = this.cache.get(key);
if (value) {
return Promise.resolve(value);
}
return storeFunction().then((result) => {
this.cache.set(key, result);
return result;
});
}
del(keys) {
this.cache.del(keys);
}
delStartWith(startStr = '') {
if (!startStr) {
return;
}
const keys = this.cache.keys();
for (const key of keys) {
if (key.indexOf(startStr) === 0) {
this.del(key);
}
}
}
flush() {
this.cache.flushAll();
}
}
export default Cache;
My Twitter query file looks like so :
import TwitterCount from "../models/TwitterModel.js";
import { TwitterApi } from 'twitter-api-v2';
import dotenv from "dotenv";
dotenv.config();
import CacheService from '../middleware/Cache.js';
const twitterClient = new TwitterApi(process.env.TWITTER_API_BEARER_TOKEN);
const readOnlyClient = twitterClient.readOnly;
const ttl = 60 * 60 * 1; //cache for 1 hour
const cache = new CacheService(ttl);
export const getCount = async(req, res) => {
try {
const twit = await TwitterCount.findOne({
attributes:['followersCount']
});
const key = `Twit_${String(twit.dataValues.followersCount)}`;
const twitterFollowers = await readOnlyClient.v2.followers('1563787278857785350'); //HejOfficial account Twitter
const results = cache.get( key , () => twitterFollowers.data.length );
if (twit.dataValues.followersCount === results) {
console.log('same results');
} else {
await TwitterCount.create({
followersCount: results
});
console.log("Twitter Data added Successful");
}
res.json(twit);
cache.del(key);
} catch (error) {
console.log(error);
}
}
I presume that I am not using the key properly. Please assist if you have encountered this issue before, or provide a better caching alternative.
Thank you.
Try to use the account id as the key, rather than follower count. This way you are caching the accounts followers for an hour.
Something like this should get you going with the cache, it doesn't have the database inserts, but they can be handled now in the getCount method.
export const getCount = async(req, res) => {
const result = await getCountFromAPI("1563787278857785350") //HejOfficial account Twitter
res.json(result);
};
const getCountFromAPI = async(account) => {
const key = `Twit_${account}`;
return cache.get(key, async() => {
const twitterFollowers = await readOnlyClient.v2.followers(account);
return twitterFollowers;
});
};
Note that if you save the results to your database every time, it defeats the purpose of the cache. I'd suggest only using the API for this data, and if you want to use database as well, maybe the cache is unnessesary? Save the follower count and account id with time-updated value, and if time updated is more than one hour ago, then query the value again from Twitter.

Do node js worker never times out?

I have an iteration that can take up to hours to complete.
Example:
do{
//this is an api action
let response = await fetch_some_data;
// other database action
await perform_operation();
next = response.next;
}while(next);
I am assuming that the operation doesn't times out. But I don't know it exactly.
Any kind of explanation of nodejs satisfying this condition is highly appreciated. Thanks.
Update:
The actual development code is as under:
const Shopify = require('shopify-api-node');
const shopServices = require('../../../../services/shop_services/shop');
const { create } = require('../../../../controllers/products/Products');
exports.initiate = async (redis_client) => {
redis_client.lpop(['sync'], async function (err, reply) {
if (reply === null) {
console.log("Queue Empty");
return true;
}
let data = JSON.parse(reply),
shopservices = new shopServices(data),
shop_data = await shopservices.get()
.catch(error => {
console.log(error);
});
const shopify = new Shopify({
shopName: shop_data.name,
accessToken: shop_data.access_token,
apiVersion: '2020-04',
autoLimit: false,
timeout: 60 * 1000
});
let params = { limit: 250 };
do {
try {
let response = await shopify.product.list(params);
if (await create(response, shop_data)) {
console.log(`${data.current}`);
};
data.current += data.offset;
params = response.nextPageParameters;
} catch (error) {
console.log("here");
console.log(error);
params = false;
};
} while (params);
});
}
Everything is working fine till now. I am just making sure that the execution will ever happen in node or not. This function is call by a cron every minute, and data for processing is provided by queue data.

How to make a command randomly fail

Okay so on my bot I have a beg command but the peoblem is I want it to sometimes fail.
For example one time it might say "Someone gave you $5" but if it fails it would say "Famous Person: NOPE"
The code below chooses a random text from a file where all the text is.
Is there anyway to mke it randomly fail so a user does not get money?
const { RichEmbed } = require("discord.js");
const { stripIndents } = require("common-tags");
const { prefix } = require("../../botconfig.json");
const db = require('quick.db')
let bal = require("../../database/balance.json");
const fs = require('fs');
const cooldowns = new Map();
const humanizeDuration = require('humanize-duration');
//Set cooldown
module.exports = {
name: "beg",
aliases: [],
category: "economy",
description: "Gets you money",
usage: "[command | alias]",
run: async (client, message, args) => {
const cooldown = cooldowns.get(message.author.id);
if (cooldown) {
const remaining = humanizeDuration(cooldown - Date.now(),{ units: ['s'],round: true });
let cEmbed = new RichEmbed()
.setColor("RANDOM")
.setTitle("Slow down, cmon!")
.setDescription(`You dont want to be like a cry baby! You will be able to beg in \`${remaining}\` just you wait!\n\nWhile you wait why not follow our [Twitter](https://twitter.com/switchoffical)`)
return message.channel.send(cEmbed)
.catch(console.error);
} else {
if(!bal[message.author.id]){
bal[message.author.id] = {
balance: 0
};
}
const Jwork = require('../../beg.json');
const JworkR = Jwork[Math.floor(Math.random() * Jwork.length)];
var random = Math.floor(Math.random() * 20) + 3;
let curBal = bal[message.author.id].balance
bal[message.author.id].balance = curBal + random;
fs.writeFile('././database/balance.json', JSON.stringify(bal, null, 2), (err) => {
let embed = new RichEmbed()
.setColor("RANDOM")
.setDescription(`**\ ${message.author.username}**, ${JworkR} 💵 **${random}**`)
message.channel.send(embed)
if (err) console.log(err)
});
//Adds the user to the set so that they can't talk for a minute
cooldowns.set(message.author.id, Date.now() + 10000);
setTimeout(() => cooldowns.delete(message.author.id), 10000);
}
}
}
I just don't know how to make it fail
So what you can do is run a Math.floor((Math.random()-0.001)*4) store it to a variable. Now you have a random number from 0 to 3 (4 different numbers/outcomes). And then check whether your new variable equals 0. if(failChance === 0) if it's true just don't do the add bal cmd.
Example:
...
} else {
var failChance = Math.floor((Math.random()-0.001)*4);
if(failChance === 0){
message.channel.send('FAILURE');
return;
}
if(!bal[message.author.id]){
...

Discord.js Command Cooldown + Time Remaining

Okay so I am looking to make it so that the cooldown shows how much longer the user needs to wait until they can work again. The cool down works butI want it to show the time remainign rather than it saying you need to wait 15 mnutes before typing this command. Is it Possible?
const { RichEmbed } = require("discord.js");
const { stripIndents } = require("common-tags");
const { prefix } = require("../../botconfig.json");
const db = require('quick.db')
let bal = require("../../database/balance.json");
let works = require('../../database/works.json');
const fs = require('fs');
const talkedRecently = new Set();
//Set cooldown
module.exports = {
name: "work",
aliases: [],
category: "economy",
description: "Gets you money",
usage: "[command | alias]",
run: async (client, message, args) => {
if (talkedRecently.has(message.author.id)) {
message.channel.send("You have to wait TIME minutes before you can work again")
} else {
if(!bal[message.author.id]){
bal[message.author.id] = {
balance: 0
};
}
if(!works[message.author.id]) {
works[message.author.id] = {
work: 0
};
}
const Jwork = require('../../work.json');
const JworkR = Jwork[Math.floor(Math.random() * Jwork.length)];
var random = Math.floor(Math.random() * 20) + 3;
let curBal = bal[message.author.id].balance
bal[message.author.id].balance = curBal + random;
let curWork = works[message.author.id].work
works[message.author.id].work = curWork + 1;
fs.writeFile('././database/works.json', JSON.stringify(works, null, 2), (err) => {
if (err) console.log(err)
})
fs.writeFile('././database/balance.json', JSON.stringify(bal, null, 2), (err) => {
let embed = new RichEmbed()
.setColor("RANDOM")
.setDescription(`
**\💼 | ${message.author.username}**, ${JworkR} 💴 **${random}**
`)
message.channel.send(embed)
if (err) console.log(err)
});
// Adds the user to the set so that they can't talk for a minute
talkedRecently.add(message.author.id);
setTimeout(() => {
// Removes the user from the set after a minute
talkedRecently.delete(message.author.id);
}, 900000);
}
}
}
Unfortunately, your current system won't be any help. You'll have to store more than just the user if you want to use the timings of their cooldown.
Let's use a Map for our variable so we can have key-value pairs. This will make it easier to keep track of the information we need
// Replace talkedRecently's declaration with this...
const cooldowns = new Map();
To put a user on cooldown, use Map.set() to add the user and the time at which their cooldown should expire to cooldowns. Then, use Map.delete() when the cooldown should run out to allow the user access to the command again.
// Replace the talkedRecently.add(...) section with this...
cooldowns.set(message.author.id, Date.now() + 900000);
setTimeout(() => cooldowns.delete(message.author.id), 900000);
In order to determine the amount of time remaining on the cooldown, we have to subtract the current time from that at which it expires. However, this will give us milliseconds, rendering the value unreadable to us. A simple, easy way to convert duration into words is by using humanize-duration (moment is also an option). Finally, we can send the desired message, letting the user know how much time they have left on their cooldown.
// Put this where you require your other dependencies...
const humanizeDuration = require('humanize-duration');
// Replace the if (talkedRecently.has(...)) part with this...
const cooldown = cooldowns.get(message.author.id);
if (cooldown) {
const remaining = humanizeDuration(cooldown - Date.now());
return message.channel.send(`You have to wait ${remaining} before you can work again`)
.catch(console.error);
}

Why is Cloud Functions for Firebase taking 25 seconds?

For clarity I have other cloud functions that all run intermittently (i.e from 'cold' in around 2-6 seconds, and all use the same boilerplate set up of importing an admin instance and exporting the function as a module)
I've seen other similar posts but this is really bugging me. I have a cloud function like so:
const admin = require('../AdminConfig');
const { reportError } = require('../ReportError');
module.exports = (event) => {
const uid = event.params.uid;
const snapshot = event.data;
if (snapshot._newData === null ) {
return null;
}
console.log('Create org begin running: ', Date.now());
const organisation = event.data.val();
const rootRef = admin.database().ref();
const ref = rootRef.child('/organisations').push();
const oid = ref.key;
const userData = {
level: 'owner',
name: organisation.name,
};
const orgShiftInfo = {
name: organisation.name,
startDay: organisation.startDay || 'Monday',
};
const updatedData = {};
updatedData[`/users/${uid}/currentOrg`] = oid;
updatedData[`/users/${uid}/organisations/${oid}`] = userData;
updatedData[`/organisations/${oid}`] = organisation;
updatedData[`/org_shift_info/${oid}`] = orgShiftInfo;
rootRef.update(updatedData, (err) => {
if (err) {
return rootRef.child(`/users/${uid}/addOrgStatus`).set({ error: true })
.then(() => {
console.log(`error adding organisation for ${uid}: `, err);
return reportError(err, { uid });
});
}
console.log('Create org wrote succesfully: ', Date.now());
return rootRef.child(`/users/${uid}/addOrgStatus`).set({ success: true });
});
}
I understand the 'cold start' thing but I think something is seriously wrong that it's taking 25 seconds. The logs don't return any error and are as so:
Is there some deeper way I can debug this to try and figure out why it's taking so long? It's unusable at the moment. Thanks a lot.
Solved:
Sorry,
I misunderstood the API a bit. I should have watched the promise video first!
I needed to put
return rootRef.update...
instead of
rootRef.update...

Resources