How to show real timer clock in pubnub publish and subscribe method - javascript

let timer =0;
function showTime() {
var d = new Date();
document.getElementById("demo").innerHTML = d.toLocaleTimeString();
}
function letsGo(duration) {
const pubnub = new PubNub({
publishKey: 'pub-c-1bda0482-9e4d-4ae7-b95d-232b2d452050',
subscribeKey: 'sub-c-e515c58a-69e0-11eb-b914-eedc703588a5',
uuid: "myUniqueUUID"
});
var timer = duration;
function publishSampleMessage(){
var publishPayload ={
channel : "game-time",
message: {
data:setInterval(function () {
var minutes, seconds;
if(timer>0)
{
minutes = parseInt(timer / 60, 10);
seconds = parseInt(timer % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
//console.log(minutes+":"+seconds);
document.getElementById("demo").innerHTML = minutes + "m " + seconds + "s ";
timer--;
if (timer==0) {
document.getElementById("demo").innerHTML ="Game is Over";
document.getElementById("game").innerHTML ="";
}
//timesender(timer);
}
}, 1000),
mytime: --timer
}
}
pubnub.publish(publishPayload, function(status, response) {
console.info("status publish function "+status);
console.info("response publish function "+ response.timetoken);
console.info(publishPayload);
})
}
pubnub.addListener({
status: function(statusEvent) {
if (statusEvent.category === "PNConnectedCategory") {
publishSampleMessage();
}
},
message: function(msg) {
console.log("sanu ki "+msg.message.data);
if(msg.message.mytime>0) {
console.log("tanu ki " + msg.message.mytime);
msg.message.mytime--;
}
},
presence: function(presenceEvent) {
// This is where you handle presence. Not important for now :)
console.log("presence "+presenceEvent.action);
}
})
console.log("Subscribing...");
pubnub.subscribe({
channels: ['game-time'],
withPresence: true
});
}
function timesender(time){
console.log("time is this : "+time);
}
I am trying to publish a real-time countdown timer in PubNub through javascript but whenever I publish a message it appears something like a fixed number like 6 and it never appears like a countdown clock but in the console log it is appearing like a countdown clock. Please suggest me how to publish the countdown timer clock as an admin so that other users get listen to it. The basic idea is I have created an admin portal in PHP and JS where I am starting a countdown timer and unity mobile game users will be listening to that timer
Updated code after Stephen help:
function letsGo(duration) {
const channel = 'game-time';
const pubnub = new PubNub({ publishKey : 'pub-c-1bda0482-9e4d-4ae7-b95d-232b2d452050', subscribeKey : 'sub-c-e515c58a-69e0-11eb-b914-eedc703588a5' });
let countdown = 65; // seconds
const countdownId = setInterval( countdownUpdate, 1000 );
function countdownUpdate() {
pubnub.publish({
channel : channel,
message : { 'countdown' : --countdown },
});
if (countdown <= 0) clearInterval(countdownId);
}
console.log("Subscribing...");
// Public user receiving updates on the countdown
pubnub.subscribe({
channels: [channel]
});
pubnub.addListener({ message: event => {
const timeLeft = event.message.countdown;
console.log("time left "+timeLeft)
const minutes = Math.floor(timeLeft / 60) + '';
const seconds = Math.floor(timeLeft % 60) + '';
let dd = `${minutes.padStart(2,'0')}:${seconds.padStart(2,'0')}`;
document.querySelector('#demo').innerHTML =
`${minutes.padStart(2,'0')}:${seconds.padStart(2,'0')}`;
}});
}
I am getting a minor problem now. As basically I have created a letsGo function on a click button so whenever I am opening a timer page in another browser I am getting two clocks one is the continuation of the previous browser but there is another one that is getting started from beginning again

JavaScript Countdown Timer
The bug in the code: an int eventId value is published via PubNub. This isn't the intended design. The reason for this is explained following usage of setInterval function returning the eventId for the interval timer.
let eventId = setInterval(...) returns an integer that references the setInterval eventId on the browser's event loop stack. The idea is that you can use the eventId to stop the interval later on with clearInterval(eventId).
Recommended Approach:
(()=>{
'use strict';
const channel = 'game-time';
const pubnub = new PubNub({ publishKey : 'demo', subscribeKey : 'demo' });
let countdown = 65; // seconds
const countdownId = setInterval( countdownUpdate, 1000 );
// Secure Server --> this should be running on your node.js server.
function countdownUpdate() {
pubnub.publish({
channel : channel,
message : { 'countdown' : --countdown },
});
if (countdown <= 0) clearInterval(countdownId);
}
// Public user receiving updates on the countdown
pubnub.subscribe({
channels: [channel]
});
pubnub.addListener({ message: event => {
const timeLeft = event.message.countdown;
const minutes = Math.floor(timeLeft / 60) + '';
const seconds = Math.floor(timeLeft % 60) + '';
document.querySelector('#timer').innerHTML =
`${minutes.padStart(2,'0')}:${seconds.padStart(2,'0')}`;
}});
})();
#timer {
font-size: 60px;
}
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.29.11.min.js"></script>
<div id="timer">00:00</div>

Related

I can't edit a message from discord.js bot

I want to make a countdown timer that uses a command /timer minutes seconds where the user chooses how long the countdown will be. I want the bot to send the timer as a reply to the command which I've successfully done. However, I also want the message to update along with the timer.
I can console.log the timer and it works but I can't figure out how to make the bot edit its own message. I have tried to just simply edit the bots' message without the timer but with no success.
Code:
const { SlashCommandBuilder,} = require("#discordjs/builders")
const { MessageEmbed, Channel, Message, MessageReaction, Client } = require('discord.js');
const { Countdown } = require('countdown');
module.exports = {
data: new SlashCommandBuilder()
.setName("timer")
.setDescription("Starta en timer")
.addNumberOption((option) => option.setName('minutes').setDescription('Set amount of minutes').setRequired(true))
.addNumberOption((option) => option.setName('seconds').setDescription('Set amount of seconds').setRequired(true)),
execute: async ({ client, interaction}) => {
let minutes = interaction.options.getNumber("minutes")
let seconds = interaction.options.getNumber("seconds")
let minutesToSeconds = minutes * 60;
let time = minutesToSeconds + seconds;
let duration = time;
let getTime = "Timer: " + duration
let interval = setInterval((timerMessage) => {
duration--;
if(duration == 0) {
clearInterval(interval);
getTime ="Timer is done"
}
getTime.edit("Timer: " + duration)
console.log(duration)
}, 1000)
await interaction.reply(
getTime
);
},
}
What you are looking for is interaction.editReply() to update the interaction message after an x amount of time using your timer. This is an example of how you can use it:
bot.on("interactionCreate", async interaction => {
let seconds = 10;
let timerMessage = "Timer is started.";
let interval = setInterval(() => {
seconds--;
timerMessage = `Timer: ${seconds} seconds left.`;
if(seconds === 0) {
clearInterval(interval);
interval = null;
interaction.editReply(`Timer has finished!`);
} else {
interaction.editReply(timerMessage)
}
}, 1000);
await interaction.reply({
content: timerMessage
});
});
This also works if ephemeral is set to true. Note that this is a raw example, and you should adjust the code by your needs.

session destroy after count down timer reach 0

use countdown timer for logout user,if user is inactive for 2hrs..
function Timer(duration, display) {
var timer = duration, hours, minutes, seconds;
setInterval(function () {
hours = parseInt((timer /3600)%24, 10)
minutes = parseInt((timer / 60)%60, 10)
seconds = parseInt(timer % 60, 10);
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.text(hours +":"+minutes + ":" + seconds);
if(hours == 0 && minutes == 0 && seconds == 0){
$.ajax({
url:'../ajax/logoutuser.php',
success: function(data){
console.log('logout');
}
});
}else{
--timer;
}
}, 1000);
}
window.onload = function () {
var twentyFourHours = 1 * 1 * 3;
var display = $('#time');
Timer(twentyFourHours, display);
};
and since i cant call session destroy on jquery function i use ajax to make request..
<?php
require_once '../config/database.php';
require_once '../includes/dboperations/user.php';
$database = new Database();
$conn = $database->getConnection();
$db = new User($conn);
$user = json_decode($_SESSION["user"]);
$db->offlineStatus($user->user_id);
session_destroy();
echo "<script>window.location.href = '../index.php'</script>";
?>
but what happens is, it doesnt call the session destroy function after making request. it runs the console.log inside success function..
is there a way to log out user if inactive for 2hrs
You can achieve by php way. Something like
$expiry = 1800 ;//session expiry required after 30 mins
if (isset($_SESSION['LAST']) && (time() - $_SESSION['LAST'] > $expiry)) {
session_unset();
session_destroy();
}
$_SESSION['LAST'] = time();
Instead of echo script from php file out it in success function of ajax code
success: function(data){
console.log('logout');
window.location.href = '../index.php';
}

scope in call back function in setTimeout() in nodejs 8

I am using nodejs 8 and class feature, but found this binding is unexpected.
In my below code, xxx variable should be accessible in the closure created by setTimeout, but it is not. Why?
class Wechat {
constructor(option) {
this.option = option
}
verifySource() {
console.log('verify source...')
}
async setMenu() {
console.log('setMenu...')
let thisObject = this
let nick = 'nick xu'
var xxxx = 'xxxx nick xu'
let accessToken = await this.getAccessToken()
console.log('accessToken:', accessToken)
setTimeout( function() {
// let abc = await thisObject.getAccessToken()
// no access to xxxx and nick variables
// this point to Timeout object
console.log('2nd time token:', '000000')
}, 5000 )
return Promise.resolve(33)
}
async getAccessToken() {
/*
this.access_token = 'kkkkkk'
this.expires_in = 7200
this.access_token_receive_time = 123456
*/
const timeBuffer = 60 * 10 * 1000 // 10 min
if (this.access_token && this.expires_in && this.access_token_receive_time) {
// if any of the above data exist
// check expire
let currentTime = new Date().getTime()
if (currentTime - this.access_token_receive_time > timeBuffer + this.expires_in * 1000) {
// token is valid
return this.access_token
}
}
let result = await rp.get(config.baseUrl + '/token?' +
'grant_type=client_credential&appid=' + config.appID +
'&secret=' + config.appSecret)
let resultJson = JSON.parse(result)
console.log('result of token request:', result)
this.access_token = resultJson.access_token
this.expires_in = resultJson.expires_in
this.access_token_receive_time = new Date().getTime()
return this.access_token
}
static distance() {
console.log('static method distance')
}
}
when at the debug inside the setTimeout callback.
this point to Timeout. What is going on?
check the watcher, xxxx and nick are not available...
if you just want to bind correct "this
setTimeout.call(this, function(){}) //or put whatever scope you want for this
if you wanted add argument in settimeout, you can use this
let thisObject = this
let nick = 'nick xu'
var xxxx = 'xxxx nick xu'
setTimeout (function (thisObject,nick,xxxx) {},1000,thisObject,nick,xxxx);

Count down timer not working with AJAX

I am trying to make a counter with JS. So far I've managed to pull that off, however I now encounter a problem. While using AJAX to retreive the time to count down I can't make it work. It's weird because it works on my original file but not with a php file called by AJAX.
This works fine :
https://jsfiddle.net/6kvp25vv/
I have no idea what the problem is. This is the HTML page :
<button onclick="upgrade('meat_max')" id="up_meat_max">+</button>
When I click on the button, it runs the function inside this js file which creates a GET request on upgrade.php :
function upgrade(building) {
var file = 'upgrade.php?building=' + building;
ajax(file, function(response) {
document.getElementById('construction').innerHTML += response;
})
}
function ajax(file, fn) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
fn(xmlhttp.responseText);
}
};
xmlhttp.open('GET', file, true);
xmlhttp.send();
}
And this is the code from upgrade.php (variables sent to this file with AJAX are not used for the purpose of testing the code) :
<div class="time">Time: <span id="timer">?</span></div>
var hour = 2;
var minute = 46;
var second = 45;
// function to make a counter
function clockIt() {
function clockO(digit) {
if(digit<10) {
return '0';
} else {
return '';
}
}
document.getElementById('timer').textContent = hour + ':' + clockO(minute) + minute + ':' + clockO(second) + second;
if(second>0) {
second -= 1;
} else if(minute>0) {
minute -= 1;
second += 59;
} else if(hour>0) {
hour -= 1;
minute += 59;
}
}
// runs the function every seconds
clockIt();
setInterval(function (){clockIt()}, 1000);
innerHTML does not execute ajax loaded scripts, what I would do in your case is to return a JSON encoded string with the variables that you need and have a function on your main script (the one thats already loaded) with this provided script, that way you already have the function ready and only pass parameters with the ajax response.
You can decode a json string with:
obj = JSON.parse(jsonString);
For example:
Ajax JSON response string:
{"time": {"hour":2, "minute":46, "second": 45}, "html": "<div class=\"time\">Time: <span id=\"timer\"></span></div>"}
Modified upgrade function:
function upgrade(building) {
var file = 'upgrade.php?building=' + building;
ajax(file, function(response) {
obj = JSON.parse(response);
time = obj.time;
document.getElementById('construction').innerHTML += obj.html;
startCountdown(time.hour, time.minute, time.second);
})
}
New function
function startCountdown(hour, minute, second) {
// function to make a counter
function clockIt() {
function clockO(digit) {
if(digit<10) {
return '0';
} else {
return '';
}
}
document.getElementById('timer').textContent = hour + ':' + clockO(minute) + minute + ':' + clockO(second) + second;
if(second>0) {
second -= 1;
} else if(minute>0) {
minute -= 1;
second += 59;
} else if(hour>0) {
hour -= 1;
minute += 59;
}
}
// runs the function every seconds
clockIt();
setInterval(function (){clockIt()}, 1000);
}
My problem was with the method for the countdown timer. When you specify an interval of 1000 milliseconds, you can't count on it being 1000 milliseconds. It's actually as soon as the timer loop gets to it after 1000 milliseconds have passed. Over a period of time, there are going to be some delays. What you want to do, if you want accurate timing, is to store the initial settings and then measure the time from when the countdown was started to the current time. See the code below which bases the timer on the internal clock rather than the interval counter. Once you have the number of seconds, you can easily it convert it to hours, minutes, and seconds by dividing by 3600 for the hours and using division and modulo arithmetic for the minute and second.
See https://www.sitepoint.com/creating-accurate-timers-in-javascript/
How to create an accurate timer in javascript?
<!DOCTYPE html />
<html>
<head>
<meta encoding="UTF-8" />
<title>Testing XMLHttpRequest</title>
<script>
var request;
var button1;
var display1;
var display2;
var display3;
var start;
var counter;
function second() {
display2.value = display2.value + "\r\nreadyState=" + request.readyState + " status=" + request.status + "\r\n";;
if (request.readyState == 4 && request.status == 200) {
display1.value = display1.value + request.responseText + "\r\n";
}
}
function first() {
display2.value = display2.value + "\r\n" +
"Starting page \r\n";
request = new XMLHttpRequest();
request.onreadystatechange = second;
var file = "http://localhost:80/";
request.open('GET', file, true);
request.send();
setInterval(timed, 1000);
}
function starter() {
display1 = document.getElementById("display1");
display2 = document.getElementById("display2");
display3 = document.getElementById("display3");
button1 = document.getElementById("button1");
button1.onclick = first;
start = new Date();
counter = 60;
}
function timed() {
var duration = (start.getTime() - new Date().getTime()) / 1000.0;
display3.value = (duration + counter).toFixed(0);
}
window.onload = starter;
</script>
</head>
<body>
<form>
<p>
<input type="button" id="button1" value="Start" />Timer:
<input type="text" readonly="readonly" id="display3" />
</p>
<p>Status:
<textarea rows="5" cols="30" id="display2"></textarea>
</p>
<p>Response:
<textarea rows="60" cols="80" id="display1"></textarea>
</p>
</form>
</body>
</html>
Found a way to compensate the delays, using the original version :
function upgrade(building) {
var file = 'upgrade.php?building=' + building;
ajax(file, function(response) {
var obj = JSON.parse(response);
var time = obj.time;
document.getElementById('construction').innerHTML += obj.html;
run_clockIt(time.hour, time.minute, time.second);
})
}
// general AJAX launcher
function ajax(file, fn) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
fn(xmlhttp.responseText);
}
};
xmlhttp.open('GET', file, true);
xmlhttp.send();
}
// count down timer with adjustments to compensate delay
function clockIt(hour, minute, second, finished, nbOfLoop) {
// every 5 seconds, run a delay correction
if(nbOfLoop%5 == 0) {
var actualTimeLeft = adjustClock(finished);
minute = actualTimeLeft[0];
second = actualTimeLeft[1];
}
nbOfLoop += 1;
// add a string "0" if necessary
function clockO(digit) {
if(digit<10) {
return '0';
} else {
return '';
}
}
document.getElementById('timer').textContent = hour + ':' + clockO(minute) + minute + ':' + clockO(second) + second;
// update displayed timer
if(second>0) {
second -= 1;
} else if(minute>0) {
minute -= 1;
second += 59;
} else if(hour>0) {
hour -= 1;
minute += 59;
}
// waits 1 sec before launching the next one
setTimeout(function() {
clockIt(hour, minute, second, finished, nbOfLoop);
}, 1000);
}
// runs the function for the first time
function run_clockIt(hour, minute, second) {
var finished = new Date();
finished.setUTCHours(finished.getUTCHours() + hour);
finished.setUTCMinutes(finished.getUTCMinutes() + minute);
finished.setUTCSeconds(finished.getUTCSeconds() + second);
clockIt(hour, minute, second, finished, 1);
}
function adjustClock(finished) {
var now = new Date();
var diff = new Date(Math.abs(now - finished));
return [diff.getUTCMinutes(), diff.getUTCSeconds()];
}
This way, the count down timer is smooth, no lagging and most of all you can define the interval on which the adjust function will run and correct the timer.
This is the php file, preparing the JSON object (thanks #Miguel) :
<?php
header('Content-Type: application/json');
// retreive variables from AJAX call
$building = $_REQUEST['building'];
// processing variable with database...
// prepare JSON object
$jsonData = '
{
"time":{"hour":2, "minute":46, "second": 45},
"html": "<div class=\"time\">Time: <span id=\"timer\"></span></div>"
}
';
echo $jsonData;

Momentjs alternates between 'a few seconds ago' and 'a minute ago' with a dynamic timestamp

In my web application, I have a status page that gets data from a server sent event every couple seconds. In that data there's a UTC timestamp that I am formatting with moment.js fromNow() so I know how long ago the data was pulled. It works fine until I try to update the timestamp dynamically with a setInterval().
The issue occurs when I switch to another tab for about a minute and the timestamp transition from 'a few seconds ago' to 'a minute ago' or more. The issue is that when I go back to the original tab, the timestamp will alternate between 'a few seconds ago' and 'a minute ago' every second or so.
The code:
$(document).ready(function() {
var source = new EventSource("{{ url_for('status.stream') }}");
source.onmessage = function (event) {
var data = JSON.parse(event.data);
if (data.hasOwnProperty('errors')) {
var errors = data.errors;
console.error(errors)
} else if (data.hasOwnProperty('result')) {
var result = data.result;
$.each(['critical', 'warning', 'unknown'], function(index, key) {
var label = $('#'+key);
if (result[key] > 0) {
label.text(result[key] + ' ' + key.toUpperCase());
} else {
label.text('')
}
});
if (result.hasOwnProperty('timestamp')) {
var datetime = $('#datetime');
var timestamp = result.timestamp;
var update = function () {
var date = moment.utc(timestamp, 'YYYY-MM-DD HH:mm:ss').fromNow();
datetime.text(date)
};
update();
setInterval(update, 1000)
} else {
console.error('Error getting timestamp!')
}
} else {
console.error('Error getting data from the server!')
}
};
})
How can I fix this issue?
Thank you!
Fixed it by moving update() out of source.onmessage as #RobG mentioned.
Working code:
$(document).ready(function() {
var source = new EventSource("{{ url_for('status.stream') }}");
var datetime = $('#datetime');
var timestamp = null;
source.onmessage = function (event) {
var data = JSON.parse(event.data);
if (data.hasOwnProperty('errors')) {
var errors = data.errors;
console.error(errors)
} else if (data.hasOwnProperty('result')) {
var result = data.result;
$.each(['critical', 'warning', 'unknown'], function(index, key) {
var label = $('#'+key);
if (result[key] > 0) {
label.text(result[key] + ' ' + key.toUpperCase())
} else {
label.text('')
}
});
if (result.hasOwnProperty('timestamp')) {
timestamp = result.timestamp
} else {
console.error('Error getting timestamp!')
}
} else {
console.error('Error getting data from the server!')
}
};
var update = function () {
if (!timestamp) {
return false
} else {
var date = moment.utc(timestamp, 'YYYY-MM-DD HH:mm:ss').fromNow();
datetime.text(date)
}
};
update();
setInterval(update, 1000)
})

Categories

Resources