I have a website that allows users to send themselves a message at a date they choose, but I have no idea how to send it at that specific time. I know there exist CronJobs, but here, I'm not doing anything recurring. It's a one-time event trigger that I need.
I first tried using the native setTimeout like this:
const dueTimestamp = ...;
const timeLeft = dueTimestamp - Date().now();
const timeoutId = setTimeout(() => sendMessage(message), timeLeft);
It works perfectly for short periods, however, I'm not sure if it is reliable for long periods such as years or even decades. Moreover, it doesn't offer much control because if I'd like to modify the dueDate or the message's content, I'd have to stop the Timeout and start a new one.
Is there any package, a library, or a service that allows you to run a NodeJS function at a scheduled time? or do you have any solutions? I've heard of Google Cloud Schedule or Cronhooks, but I'm not sure.
You can use node-schedule library. for example :
you want to run a funcation at 5:30am on December 21, 2022.
const schedule = require('node-schedule');
const date = new Date(2022, 11, 21, 5, 30, 0);
const job = schedule.scheduleJob(date, function(){
console.log('The world is going to end today.');
});
As recommended by user3425506, I simply used a Cron job to fetch the messages from a database and to send the message of those whose timestamps have passed.
Dummy representation:
import { CronJob } from "cron";
import { fakeDB } from "./fakeDB";
const messages = fakeDB.messages;
const job = new CronJob("* * * * * *", () => {
const currentTimestamp = new Date().getTime();
messages.forEach((message, index) => {
if (message.timestamp > currentTimestamp) return;
console.log(message.message);
messages.splice(index, 1);
});
});
job.start();
Related
I'm trying to make a discord bot that gives you weekly reminders. Im using momentjs to get the time. As well as using discord.js-commando. I found the best way to call a function multiple times is to use setInterval.
const moment = require('moment');
const Commando = require('discord.js-commando');
const bot = new Commando.Client();
bot.on('ready', function () {
var day = moment().format('dddd');
var time = moment().format('h:mm a');
function Tuesday() {
if (day == 'Tuesday' && time == '6:30 pm') {
const channel = bot.channels.get('933047404645724234');
console.log('Sending Message');
channel.send('Reminder: You have a week to complete your To-Do List!');
} else {
console.log('Not Sending Message');
}
}
console.log('Bot is now ready!');
setInterval(Tuesday, 100);
});
I noticed the problem with the setInterval is that it is only called once. I also tried using an async function but that failed as well.
Well executing a function every 100ms is not quite optimal. I don't know what is the reason to run just once (it should run infinitely) but there is a better way to do what you need.
You will need the package called "node-schedule". It's really useful for such things.
Here's an example:
const schedule = require("node-schedule");
schedule.scheduleJob({hour: 18, minute: 30, dayOfWeek: 2}, function(){
// your code here
});
You can read more in the documentation of node-schedule here.
I'm trying to calculate time when user open a specific screen,When he enters in the screen the the time starts and when I exit from the screen the time stops and gives the time spend on the screen
here is my code:
componentDidMount = () => {
let date = new Date();
let hours = date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();
this.setState({
startHour: hours,
startMin: minutes,
startSeconds: seconds,
});
}
Here is ComponentWillunmount
componentWillUnmount() {
let date = new Date();
let endHours = date.getHours();
let endMinutes = date.getMinutes();
let endSeconds = date.getSeconds();
console.log(`${endHours}:${endMinutes}:${endSeconds}`);
console.log(
`${this.state.startHour}:${this.state.startMin}:${this.state.startSeconds}`,
);
}
When he enters in the screen the the time starts and when I exit from
the screen the time stops and gives the time spend on the screen
It's a very important Feature Request by many for web 2.0 applications. So, I write a detailed, working and simple solution on the subject here.
You are requesting a feature already created for this workflow. It's a plugin you can include in any website. It's none other than time-on-site tracking in timeonsite.js
Look at the following code (Use latest version; don't just copy/paste),
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/timeonsite/1.2.0/timeonsitetracker.js"></script>
<script>
var config = {
// track page by seconds. Default tracking is by milliseconds
trackBy: 'seconds',
trackHistoryChange: true, //Single-page React/Angular app
callback: function(data) { /* callback denotes your data tracking is real-time */
console.log(data);
var endPointUrl = 'http://example.com' //Replace with your actual backend API URL http://localhost/tos
if (data && data.trackingType) {
if (data.trackingType == 'tos') {
if (Tos.verifyData(data) != 'valid') {
console.log('Data abolished!');
return;
}
}
// make use of sendBeacon if this API is supported by your browser.
if (navigator && typeof navigator.sendBeacon === 'function') {
data.trasferredWith = 'sendBeacon';
var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
navigator.sendBeacon(endPointUrl, blob);
}
}
}
};
var Tos;
if (TimeOnSiteTracker) {
Tos = new TimeOnSiteTracker(config);
}
</script>
</head>
Then, when you refresh, reload or navigate the React app page,
You'll see following object directly saved into your table/printed in browser console. Select "persist" in logs,
{
TOSId: 1129620185532,
TOSSessionKey: "14802525481391382263",
TOSUserId: "anonymous",
title: "Test application - Home page",
URL: "http://nature-blogs/pages/home.php"
entryTime: "2021-11-27 13:15:48.663",
currentTime: "2021-11-27 13:17:31.663",
timeOnPage: 103,
timeOnSite: 103,
timeOnPageTrackedBy: "second",
timeOnPageByDuration: "0d 00h 01m 43s",
timeOnSiteByDuration: "0d 00h 01m 43s",
trackingType: "tos",
}
As you can see, the actions
"entryTime" is captured
"exitTime" is captured in seconds/milliseconds depending upon configuration
"type:time_on_site" is captured
"timeOnPage" is captured // individual page time
"timeOnSite" is captured // session overall page time
What else you need? Since it's stored in SQL DB table, you can do analysis/reporting queries yourself. The same is the case for NoSQL as well. Timeonsite.js is supporting both RDBMS and NoSql DB types.
On top of it, 1.Minimize tab, 2.Inactive tab and 3.Switch tab's idle time are all computed and ignored automatically by the tracker itself.
The only thing to note in configuration part is,
trackHistoryChange: true
If the page routing depends on Location Hash or also known as single-page app, include this setting. On the other hand if your web application is a normal page like Wikipedia, avoid setting this line. You are done. For showing the real-time stay time on screen, check this SO post. It's using Jquery to show the results. You can customize it for your React app.
This tracker can be plugged-in in any library not only in React app but also Angular, Jquery, MooTools etc. since it's plain vanilla JS library.
Let me know if you need more input on the subject. I can assist you on this.
I'm not a react dev but this is fairly simple and this would be the approach.
componentDidMount = () => {
/* On init set the start time
Also: multiplying new Date() by 1 will return a timestamp
*/
this.startTime = new Date() * 1;
}
componentWillUnmount() {
/* Then on view destroy set the endTime */
let endTime = new Date() * 1;
/* Subtract the end time with start time, since endTime has greater value. The result
is the difference between start and end time in milliseconds.
*/
let elapsed = endTime - this.startTime;
/* The result is in milliseconds so:
elapsed / 1000 -> is seconds
elapsed / 1000 / 60 -> is minutes
etc.
*/
);
I agree with #masterpreenz
componentDidMount = () => {
this.startTime = new Date() * 1;
}
componentWillUnmount() {
let endTime = new Date() * 1;
let elapsed = endTime - this.startTime;
$('viewPageStayTime').html('You spent '+elaspsed+ ' duration.');
);
I currently have an SPA built with MERN and I want to improve it further by adding a scheduled update to a particular collection in my MongoDB database by setting a boolean field in all of the documents in a collection to false every midnight.
Can someone point me to the right direction on how to accomplish this?
I want to be able to scale it as well at some point - for example, have a value saved in a document in another collection to indicate the time where these boolean fields will be invalidated in the front end?
I'm using a MERN stack. Thanks for your help!
you can use cron job
const moment = require('moment');
const CronJob = require('cron').CronJob;
const updateCollections = async ()=>{
await someQueriesServices()
}
new CronJob('0 0 * * *', async () => {
await updateCollections()
}, null, true, 'America/Los_Angeles');
or you can use setInterval
const timeInSec = moment().endOf('day').valueOf()
const Interval = Date.now() - timeInSec;
setInterval(async ()=>{
await updateCollections()
},Interval)
I usually use node-schedule
const schedule = require('node-schedule');
const j = schedule.scheduleJob('42 * * * *', function(){
console.log('The answer to life, the universe, and everything!');
});
I'm trying to make the bot writing messages at specific times. Example:
const Discord = require("discord.js");
const client = new Discord.Client();
client.on("ready", () => {
console.log("Online!");
});
var now = new Date();
var hour = now.getUTCHours();
var minute = now.getUTCMinutes();
client.on("message", (message) => {
if (hour === 10 && minute === 30) {
client.channels.get("ChannelID").send("Hello World!");
}
});
Unfortunately, it only works once I trigger another command like:
if (message.content.startsWith("!ping")) {
message.channel.send("pong!");
}
my message: !ping (at 10:10 o'clock)
-> pong!
-> Hello World!
I guess it needs something that constantly checks the time variables.
I would use cron: with this package you can set functions to be executed if the date matches the given pattern.
When building the pattern, you can use * to indicate that it can be executed with any value of that parameter and ranges to indicate only specific values: 1-3, 7 indicates that you accept 1, 2, 3, 7.
These are the possible ranges:
Seconds: 0-59
Minutes: 0-59
Hours: 0-23
Day of Month: 1-31
Months: 0-11 (Jan-Dec)
Day of Week: 0-6 (Sun-Sat)
Here's an example:
var cron = require("cron");
function test() {
console.log("Action executed.");
}
let job1 = new cron.CronJob('01 05 01,13 * * *', test); // fires every day, at 01:05:01 and 13:05:01
let job2 = new cron.CronJob('00 00 08-16 * * 1-5', test); // fires from Monday to Friday, every hour from 8 am to 16
// To make a job start, use job.start()
job1.start();
// If you want to pause your job, use job.stop()
job1.stop();
In your case, I would do something like this:
const cron = require('cron');
client.on('message', ...); // You don't need to add anything to the message event listener
let scheduledMessage = new cron.CronJob('00 30 10 * * *', () => {
// This runs every day at 10:30:00, you can do anything you want
let channel = yourGuild.channels.get('id');
channel.send('You message');
});
// When you want to start it, use:
scheduledMessage.start()
// You could also make a command to pause and resume the job
As Federico stated, that is the right way to solve this problem but the syntax has changed and now with the new update of discord.js (v12) it would be like:
// Getting Discord.js and Cron
const Discord = require('discord.js');
const cron = require('cron');
// Creating a discord client
const client = new Discord.Client();
// We need to run it just one time and when the client is ready
// Because then it will get undefined if the client isn't ready
client.once("ready", () => {
console.log(`Online as ${client.user.tag}`);
let scheduledMessage = new cron.CronJob('00 30 10 * * *', () => {
// This runs every day at 10:30:00, you can do anything you want
// Specifing your guild (server) and your channel
const guild = client.guilds.cache.get('id');
const channel = guild.channels.cache.get('id');
channel.send('You message');
});
// When you want to start it, use:
scheduledMessage.start()
};
// You could also make a command to pause and resume the job
But still credits to Federico, he saved my life!
I would like to delete data that is older than two hours. Currently, on the client-side, I loop through all the data and run a delete on the outdated data. When I do this, the db.on('value') function is invoked every time something is deleted. Also, things will only be deleted when a client connects, and what might happen if two clients connect at once?
Where can I set up something that deletes old data? I have a timestamp inside each object created by a JavaScript Date.now().
Firebase does not support queries with a dynamic parameter, such as "two hours ago". It can however execute a query for a specific value, such as "after August 14 2015, 7:27:32 AM".
That means that you can run a snippet of code periodically to clean up items that are older than 2 hours at that time:
var ref = firebase.database().ref('/path/to/items/');
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var old = ref.orderByChild('timestamp').endAt(cutoff).limitToLast(1);
var listener = old.on('child_added', function(snapshot) {
snapshot.ref.remove();
});
As you'll note I use child_added instead of value, and I limitToLast(1). As I delete each child, Firebase will fire a child_added for the new "last" item until there are no more items after the cutoff point.
Update: if you want to run this code in Cloud Functions for Firebase:
exports.deleteOldItems = functions.database.ref('/path/to/items/{pushId}')
.onWrite((change, context) => {
var ref = change.after.ref.parent; // reference to the items
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value', function(snapshot) {
// create a map with all children that need to be removed
var updates = {};
snapshot.forEach(function(child) {
updates[child.key] = null
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
This function triggers whenever data is written under /path/to/items, so child nodes will only be deleted when data is being modified.
This code is now also available in the functions-samples repo.
I have a http triggered cloud function that deletes nodes, depending on when they were created and their expiration date.
When I add a node to the database, it needs two fields: timestamp to know when it was created, and duration to know when the offer must expire.
Then, I have this http triggered cloud function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
/**
* #function HTTP trigger that, when triggered by a request, checks every message of the database to delete the expired ones.
* #type {HttpsFunction}
*/
exports.removeOldMessages = functions.https.onRequest((req, res) => {
const timeNow = Date.now();
const messagesRef = admin.database().ref('/messages');
messagesRef.once('value', (snapshot) => {
snapshot.forEach((child) => {
if ((Number(child.val()['timestamp']) + Number(child.val()['duration'])) <= timeNow) {
child.ref.set(null);
}
});
});
return res.status(200).end();
});
You can create a cron job that every X minutes makes a request to the URL of that function: https://cron-job.org/en/
But I prefer to run my own script, that makes a request every 10 seconds:
watch -n10 curl -X GET https://(your-zone)-(your-project-id).cloudfunctions.net/removeOldMessages
In the latest version of Firebase API, ref() is changed to ref
var ref = new Firebase('https://yours.firebaseio.com/path/to/items/');
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var old = ref.orderByChild('timestamp').endAt(cutoff).limitToLast(1);
var listener = old.on('child_added', function(snapshot) {
snapshot.ref.remove();
});
If someone will have the same problem, but in Firestore. I did a little script that at first read documents to console.log and then delete documents from a collection messages older than 24h. Using https://cron-job.org/en/ to refresh website every 24h and that's it. Code is below.
var yesterday = firebase.firestore.Timestamp.now();
yesterday.seconds = yesterday.seconds - (24 * 60 * 60);
console.log("Test");
db.collection("messages").where("date",">",yesterday)
.get().then(function(querySnapshote) {
querySnapshote.forEach(function(doc) {
console.log(doc.id," => ",doc.data());
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
db.collection("messages").where("date","<",yesterday)
.get().then(function(querySnapshote) {
querySnapshote.forEach(element => {
element.ref.delete();
});
})
You could look into Scheduling Firebase Functions with Cron Jobs. That link shows you how to schedule a Firebase Cloud Function to run at a fixed rate. In the scheduled Firebase Function you could use the other answers in this thread to query for old data and remove it.