I am building an application in node.js with socket.io where I want people to be able to vote on questions and have a timer included which will give them a time limit for answering questions. The issue that I am running into (which I will highlight below) is that every time someone connects to the page, it will log the tick from the stopwatch object another time. Even after people leave, it will continue to log it one time for every time someone connected. Here is my code for the project thus far.
Stopwatch.js:
function Stopwatch() {
if (false === (this instanceof Stopwatch)) {
return new Stopwatch();
}
this.hour = 3600000;
this.minute = 60000;
this.second = 1000;
this.time = this.hour;
this.interval = undefined;
events.EventEmitter.call(this);
_.bindAll(this, 'start', 'stop', 'reset', 'onTick');
};
util.inherits(Stopwatch, events.EventEmitter);
Stopwatch.prototype.start = function() {
console.log("Starting the Timer!");
this.interval = setInterval(this.onTick, this.second);
this.emit('start');
};
Stopwatch.prototype.onTick = function() {
var remainder = this.time,
numhours,
numMinutes,
numSeconds,
output = "";
if (this.time === 0) {
this.stop();
return;
}
numHours = String(parseInt(remainder / this.hour, 10));
remainder -= this.hour * numHours;
numMinutes = String(parseInt(remainder / this.minute, 10));
remainder -= this.minute * numMinutes;
numSeconds = String(parseInt(remainder / this.second, 10));
output = _.map([numHours, numMinutes, numSeconds], function(str) {
if (str.length === 1) {
str = "0" + str;
}
return str;
}).join(":");
this.emit('tick', output);
this.time -= this.second;
}
module.exports = Stopwatch;
}
socket.js:
var Stopwatch = require('../models/stopwatch');
var people = {};
var stopwatch = Stopwatch();
module.exports = function (io) {
io.on('connection', function (socket) {
//console.log("Someone joined...");
//socket.on('join', function () {
if (Object.keys(people).length === 0) {
console.log('host joined');
people[socket.id] = {"name": "host", "image": "fake picture", "host": true};
} else {
console.log("Someone else joined");
people[socket.id] = {"name": "person", "image": "other picture", "host": false};
}
console.log(people);
//});
socket.on('startTimer', function() {
stopwatch.start();
});
socket.on('disconnect', function() {
delete people[socket.id];
console.log("someone left");
console.log(people);
});
stopwatch.on('tick', function(time) {
console.log(socket.id + '---stopwatch tick!' + time);
socket.emit('timer', { countdown: time });
});
});
};
The javascript on the client:
var socket;
$(document).ready(function() {
socket = io();
socket.on('timer', function (data) {
$('#timer').html(data.countdown);
});
$('#start-timer').click(function() {
socket.emit('startTimer');
});
});
Thanks in advance! I can't figure out why it still logs each user and the time, even after they disconnect from the server.
What's happening is that every time a new client opens a connection to the server, you're assigning an event listener to the stopwatch and never remove it, this causes the behaviour you're experiencing. You must remove your eventListener when the socket disconnects as stated in the node event emitter class.
You should end up with something like this:
io.on('connection', function (socket) {
//console.log("Someone joined...");
//socket.on('join', function () {
if (Object.keys(people).length === 0) {
console.log('host joined');
people[socket.id] = {"name": "host", "image": "fake picture", "host": true};
} else {
console.log("Someone else joined");
people[socket.id] = {"name": "person", "image": "other picture", "host": false};
}
console.log(people);
socket.on('startTimer', function() {
stopwatch.start();
});
var _tickListener = function(time) {
console.log(socket.id + '---stopwatch tick!' + time);
socket.emit('timer', { countdown: time });
};
socket.on('disconnect', function() {
delete people[socket.id];
stopwatch.removeListener('tick', _tickListener);
console.log("someone left");
console.log(people);
});
stopwatch.on('tick', _tickListener);
});
Related
So basically when I switch tabs, the countdown timer on a specific page just stops counting down and resumes when you return to the tab. Is there anyway to mitigate that so that it counts in the background or it accounts for the time you spend on another tab?
This is basically what I have for js:
document.getElementById('timer').innerHTML =
05 + ":" + 01;
startTimer();
function startTimer() {
var presentTime = document.getElementById('timer').innerHTML;
var timeArray = presentTime.split(/[:]+/);
var m = timeArray[0];
var s = checkSecond((timeArray[1] - 1));
if(s==59){m=m-1}
if(m<0){
return
} else if (m == 0 && s == 0) {
location.reload();
}
document.getElementById('timer').innerHTML =
m + ":" + s;
setTimeout(startTimer, 1000);
}
function checkSecond(sec) {
if (sec < 10 && sec >= 0) {sec = "0" + sec};
if (sec < 0) {sec = "59"};
return sec;
}
Any ideas whether the time could be done server side or something so that it can't be modified client side? If not, then whatever, but mainly just want to figure out how to make the countdown still work (or account for the time spent) when on another tab.
We can store the variable m and s values either globally or use the local storage to set the values after setting the inner HTML and get the stored values back whenever tabs were switched as:
Set values:
window.localStorage.setItem('minutes', m.toString()); //same for the seconds
Get values:
window.localStorage.getItem('minutes'); //same for the seconds
Hope this answers your questions.
Just a simple solution:
Add this piece of code.
<html>
<head>
<script>
(function() {
var $momentum;
function createWorker() {
var containerFunction = function() {
var idMap = {};
self.onmessage = function(e) {
if (e.data.type === 'setInterval') {
idMap[e.data.id] = setInterval(function() {
self.postMessage({
type: 'fire',
id: e.data.id
});
}, e.data.delay);
} else if (e.data.type === 'clearInterval') {
clearInterval(idMap[e.data.id]);
delete idMap[e.data.id];
} else if (e.data.type === 'setTimeout') {
idMap[e.data.id] = setTimeout(function() {
self.postMessage({
type: 'fire',
id: e.data.id
});
// remove reference to this timeout after is finished
delete idMap[e.data.id];
}, e.data.delay);
} else if (e.data.type === 'clearCallback') {
clearTimeout(idMap[e.data.id]);
delete idMap[e.data.id];
}
};
};
return new Worker(URL.createObjectURL(new Blob([
'(',
containerFunction.toString(),
')();'
], {
type: 'application/javascript'
})));
}
$momentum = {
worker: createWorker(),
idToCallback: {},
currentId: 0
};
function generateId() {
return $momentum.currentId++;
}
function patchedSetInterval(callback, delay) {
var intervalId = generateId();
$momentum.idToCallback[intervalId] = callback;
$momentum.worker.postMessage({
type: 'setInterval',
delay: delay,
id: intervalId
});
return intervalId;
}
function patchedClearInterval(intervalId) {
$momentum.worker.postMessage({
type: 'clearInterval',
id: intervalId
});
delete $momentum.idToCallback[intervalId];
}
function patchedSetTimeout(callback, delay) {
var intervalId = generateId();
$momentum.idToCallback[intervalId] = function() {
callback();
delete $momentum.idToCallback[intervalId];
};
$momentum.worker.postMessage({
type: 'setTimeout',
delay: delay,
id: intervalId
});
return intervalId;
}
function patchedClearTimeout(intervalId) {
$momentum.worker.postMessage({
type: 'clearInterval',
id: intervalId
});
delete $momentum.idToCallback[intervalId];
}
$momentum.worker.onmessage = function(e) {
if (e.data.type === 'fire') {
$momentum.idToCallback[e.data.id]();
}
};
window.$momentum = $momentum;
window.setInterval = patchedSetInterval;
window.clearInterval = patchedClearInterval;
window.setTimeout = patchedSetTimeout;
window.clearTimeout = patchedClearTimeout;
})();
</script>
</head>
</html>
I've made a quiz game using socket.io. When a new socket connects, this runs:
io.on('connection', function (socket) {
let gameTimer = null;
After receiving a certain socket from the client, this function starts:
gameTimer = setTimeout(function () {
//Reveal if the players' answers are right or wrong
revealAnswer(answeredPlayers, playerData[socket.id].room);
//Start the countdown for the next question
countToNextQuestion(playerData[socket.id].room);
}, 21000)
When a player answers, this socket function runs in the server:
socket.on('Answered Question', function (userAnswer, answeredPlayerUsername, timeAnswered) {
//Stop the count
clearTimeout(gameTimer);
/*
*Extra code below that works...
*/
But this does not stop the timeout. Any reason why this is happening, and how to fix it?
EDIT: Here's the full socket code. It's a bit long, apologies:
io.on('connection', function (socket) {
console.log('Someone connected');
//Will hold the questions used for the session
let gameSessionQuestions = null;
//Holds the current question
let currentQuestion = null;
//Holds the correct answer to the current question
let currentQuestionAnswer = null;
//Give the socket player details
//and add this player to the dictionary
playerData[socket.id] = {
name: null,
room: null,
host: false,
score: 0
};
//Timer for questions
let gameTimer = null;
/*
SOCKET OPERATIONS
*/
socket.on('Get Total Score', async function(playerUsername){
let playerTotalScore = await gameController.getTotalScore(playerUsername);
socket.emit('Receive Total Score', playerTotalScore);
})
//Adds a room to the list of rooms
socket.on('Add Room', function (questionType) {
let newRoom = newRoomCreation(questionType);
//Since this player created the room, assign them the host value
playerData[socket.id].host = true;
playerData[socket.id].room = newRoom;
socket.join(newRoom);
io.sockets.to(newRoom).emit('Room Added', newRoom);
})
//Make the specified room a closed room
socket.on('Close the room', function (room) {
closeRoom(room);
})
socket.on('Room Availability', function (roomCode, questionType) {
if (!roomMap.get(roomCode)) {
socket.emit('Unavailable Room');
}
else {
//Check if the room has the question type the user chose
if (roomQuestionType.get(roomCode) == questionType) {
socket.emit('Available Room');
} else {
socket.emit('Unavailable Room');
}
}
})
socket.on('Open Room Availability', function (questionType) {
let roomFound = false;
for (let room of roomMap.keys()) {
if (!closedRoomsList.includes(room)) {
if (roomQuestionType.get(room) == questionType) {
roomFound = true;
socket.emit('Available Open Room', room);
}
}
}
if (roomFound == false) {
socket.emit('Unavailable Open Room');
}
})
socket.on('Join Room', function (values) {
//Give this player's 'name' variable a value
playerData[socket.id].name = values.p1;
//Give this player's 'room' variable a value
playerData[socket.id].room = values.p2;
//DELETE SOON: Checking the data before addition
console.log("Data in the room '" + values.p2 + "' before addition: ");
console.log(roomMap.get(values.p2));
//Put this player in the new room
socket.join(values.p2);
//Add the player's socketID and Name to the Room Map
let playersList = roomMap.get(values.p2);
playersList.push(values.p1);
//DELETE SOON: Checking the data after addition
console.log("Data in the room '" + values.p2 + "' after addition: ");
console.log(roomMap.get(values.p2));
io.sockets.to(values.p2).emit('Output Players', playersList);
if (playerData[socket.id].host == true) {
io.sockets.to(playerData[socket.id].room).emit('Current Host');
}
})
socket.on('Request Game Start', async function (room) {
//Checks if the user who started the game truly is the host
if (playerData[socket.id].host == false) {
return;
}
//Holds all the questions to be asked and their answers
gameSessionQuestions = await selectedQuestions(2, roomQuestionType.get(room));
//Reset the 'answered player' count
answeredPlayers.set(playerData[socket.id].room, 0);
io.sockets.to(room).emit('Game Start');
})
socket.on('Start Game Countdown', function () {
let pos = 0;
let countArr = ["2", "1", "Go"];
let counter = setInterval(function () {
//Update the count for all those in the room
io.sockets.to(playerData[socket.id].room).emit('Update Countdown', countArr[pos]);
if (pos == countArr.length) {
clearInterval(counter);
io.sockets.to(playerData[socket.id].room).emit('End Countdown');
currentQuestion = getQuestion(gameSessionQuestions[0], playerData[socket.id].room);
io.sockets.to(playerData[socket.id].room).emit('Receive Questions', currentQuestion);
//Holds the answer for the current question
currentQuestionAnswer = currentQuestionCorrectAnswer(gameSessionQuestions[0], playerData[socket.id].room);
//The answer to the question presented in this room is stored here
questionAnswers.set(playerData[socket.id].room, currentQuestionAnswer);
gameTimer = setTimeout(function () {
//Reveal if the players' answers are right or wrong
revealAnswer(answeredPlayers, playerData[socket.id].room);
//Start the countdown for the next question
countToNextQuestion(playerData[socket.id].room);
}, 21000)
} else {
pos++;
}
}, 1000);
})
socket.on('Answered Question', function (userAnswer, answeredPlayerUsername, timeAnswered) {
socket.broadcast.to(playerData[socket.id].room).emit('Display Answered Player', answeredPlayerUsername);
//DELETE SOON: Check the current question
console.log("Current Question's Answer: " + questionAnswers.get(playerData[socket.id].room));
console.log("Your answer: " + userAnswer);
let answerResult;
if (questionAnswers.get(playerData[socket.id].room) === userAnswer) {
answerResult = "btn-success";
//Add to the person's overall score
playerData[socket.id].score = addToScore(playerData[socket.id].score, timeAnswered);
socket.emit('Answer Check', answerResult);
}
else {
answerResult = "btn-danger";
socket.emit('Answer Check', answerResult);
}
//DELETE SOON: Checks the scores for the user
console.log("The score for '" + playerData[socket.id].name + "' is '" + playerData[socket.id].score + "'")
//Add 1 to the number of people who have answered in this room
answeredPlayers.set(playerData[socket.id].room, (answeredPlayers.get(playerData[socket.id].room)) + 1)
//DELETE SOON: Checks if the value is updating
console.log("Number of people who have answered: " + answeredPlayers.get(playerData[socket.id].room))
//If all users in the room have answered...
if (answeredPlayers.get(playerData[socket.id].room) == (roomMap.get(playerData[socket.id].room)).length) {
//DELETE SOON: Outputs message confirming all users answering
console.log("Everyone has answered");
//Stop the count
clearTimeout(gameTimer);
//Reveal if the players' answers are right or wrong
revealAnswer(answeredPlayers, playerData[socket.id].room);
//Start the countdown for the next question
countToNextQuestion(playerData[socket.id].room);
}
})
//Get the next question from the host
socket.on('Find Host', async function () {
if (playerData[socket.id].host == true) {
//DELETE SOON: Check the number of questions before
console.log('Question count before: ' + gameSessionQuestions.length)
gameSessionQuestions.shift();
//DELETE SOON: Check the number of questions after removing the one just asked
console.log('Question count after: ' + gameSessionQuestions.length)
//If there are still questions to be asked, send the next question and set the new correct answer
if (gameSessionQuestions.length != 0) {
//Get the next question and send it
currentQuestion = getQuestion(gameSessionQuestions[0], playerData[socket.id].room);
io.sockets.to(playerData[socket.id].room).emit('Receive Questions', currentQuestion);
//Get this question's correct answer
currentQuestionAnswer = currentQuestionCorrectAnswer(gameSessionQuestions[0], playerData[socket.id].room);
questionAnswers.set(playerData[socket.id].room, currentQuestionAnswer);
gameTimer = setTimeout(function () {
//Reveal if the players' answers are right or wrong
revealAnswer(answeredPlayers, playerData[socket.id].room);
//Start the countdown for the next question
countToNextQuestion(playerData[socket.id].room);
}, 21000)
} else {
//Holds the username and scores for all users in this room
let players_Scores = [];
let roomCode = playerData[socket.id].room;
let playerKeys = Object.keys(playerData);
for (let i = 0; i < playerKeys.length; i++) {
if (playerData[playerKeys[i]].room == roomCode) {
let scoreDetails = {
username: playerData[playerKeys[i]].name,
score: playerData[playerKeys[i]].score
}
players_Scores.push(scoreDetails)
await gameController.uploadScore(playerData[playerKeys[i]].name, playerData[playerKeys[i]].score, roomQuestionType.get(roomCode));
}
}
io.sockets.to(roomCode).emit('Show Scores', players_Scores);
}
}
})
//Remove details while reloading the menu page
socket.on('Back to Start', function () {
//Remove the player from the room in socketio
socket.leave(playerData[socket.id].room);
//If this is the host, delete the room
if (playerData[socket.id].host == true) {
roomMap.delete(playerData[socket.id].room);
roomQuestionType.delete(playerData[socket.id].room);
questionAnswers.delete(playerData[socket.id].room);
answeredPlayers.delete(playerData[socket.id].room);
}
//Remove this player's details
delete playerData[socket.id];
})
socket.on('disconnect', function () {
if (playerData[socket.id] && roomMap.get(playerData[socket.id].room) != undefined) {
//DELETE SOON: Check who left and from which room
console.log(playerData[socket.id].name + " has left the room '" + playerData[socket.id].room + "'");
//If on the lobby page, remove that player and update everyone's lists
let playerList = roomMap.get(playerData[socket.id].room);
playerList.splice(playerList.indexOf(playerData[socket.id].name), 1);
io.sockets.to(playerData[socket.id].room).emit('Output Players', playerList);
/*If this is the host,
* stop game timer
* alert all the players in the room,
* delete the room they were in*/
if (playerData[socket.id].host == true) {
clearTimeout(gameTimer);
socket.broadcast.to(playerData[socket.id].room).emit('Host Left');
roomMap.delete(playerData[socket.id].room);
roomQuestionType.delete(playerData[socket.id].room);
questionAnswers.delete(playerData[socket.id].room);
answeredPlayers.delete(playerData[socket.id].room);
}
delete playerData[socket.id];
}
})
})
Your issue in the smallest code to rep
let gameTimer;
let pos = 0;
let countArr = ["2", "1", "Go"];
let counter = setInterval(function () {
if (pos == countArr.length) {
console.log("Creating a new gameTimer", Date.now());
gameTimer = setTimeout(function () {
console.log('gameTimer executed')
}, 2100);
} else {
pos++;
}
}, 1000);
So your code keeps creating new timeouts. The previous timeouts exist, they do not get removed.
So you need to either remove the gameTimer before you create a new one, or do not create gameTimer if it is already running
let gameTimer;
let pos = 0;
let countArr = ["2", "1", "Go"];
let counter = setInterval(function () {
if (pos == countArr.length) {
if (gameTimer) return; // if gameTimer is defined, exit
console.log("Creating a new gameTimer", Date.now());
gameTimer = setTimeout(function () {
console.log('gameTimer executed')
}, 2100);
} else {
pos++;
}
}, 1000);
It seems to be an error when accessing the variable, but without the entire code, it is not viable.
I'm writing a game for Alexa but the user input is required. If there is no answer, then the game should end with a unique message of the user's score. Currently, I have the Alexa prompting the user like this.
this.emit(":ask", speech);
However, if the user chooses not to answer, the Alexa ends without any sort of message. I checked whether I could account for this in the Stop or Cancel handler, but the program doesn't seem to exit that way. How can I account for no input and specify a exit message? Here is my full code for reference.
'use strict';
const Alexa = require('alexa-sdk');
//Messages
const WELCOME_MESSAGE = "Welcome to three six nine. You can play three six nine with just me or with friends. Say help for how to play";
const START_GAME_MESSAGE = "OK. I will start!";
const EXIT_SKILL_MESSAGE = "Better luck next time!";
const HELP_MESSAGE = "Three six nine is a game where we take turns counting up from one. If the number is divisible by three, you're going to say quack. If the number has a three, six, or nine anywhere, you're going to say quack";
const speechConsWrong = ["Argh", "Aw man", "Blarg", "Blast", "Boo", "Bummer", "Darn", "D'oh", "Dun dun dun", "Eek", "Honk", "Le sigh",
"Mamma mia", "Oh boy", "Oh dear", "Oof", "Ouch", "Ruh roh", "Shucks", "Uh oh", "Wah wah", "Whoops a daisy", "Yikes"];
const states = {
START: "_START",
GAME: "_GAME"
};
//Game Variables
var counter = 1;
var numPlayers = 1; //By default is one
const handlers = {
//Game goes straight to play currently
"LaunchRequest": function() {
this.handler.state = states.START;
this.emitWithState("Start");
},
"PlayIntent": function() {
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"PlayWithFriends": function() {
const itemSlot = this.event.request.intent.slots.numFriends.value;
numPlayers = itemSlot;
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"AMAZON.HelpIntent": function() {
this.response.speak(HELP_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"Unhandled": function() {
this.handler.state = states.START;
this.emitWithState("Start");
}
};
//When skill is in START state
const startHandlers = Alexa.CreateStateHandler(states.START,{
"Start": function() {
this.response.speak(WELCOME_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"PlayIntent": function() {
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"PlayWithFriends": function() {
const itemSlot = this.event.request.intent.slots.Item;
numPlayers = itemSlot; //set number to slot value
this.handler.state = states.GAME;
this.emitWithState("NextNumber");
},
"AMAZON.StopIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
this.emit(":responseReady");
},
"AMAZON.CancelIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
this.emit(":responseReady");
},
"AMAZON.HelpIntent": function() {
this.response.speak(HELP_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"Unhandled": function() {
this.emitWithState("Start");
}
});
const gameHandlers = Alexa.CreateStateHandler(states.GAME,{
"Game": function() {
let speech = "";
let turnDivisible = (counter-1) % (numPlayers+1);
if (turnDivisible != 0) {
this.emit(":ask", speech);
} else {
this.emitWithState("NextNumber");
}
},
"NextNumber": function() {
//If the counter is at 1, the game is beginning with Alexa
if (counter == 1) {
this.attributes.response = START_GAME_MESSAGE + " ";
} else {
this.attributes.response = " ";
}
//check if number contains three, six, nine or divisible by nine
let speech = " ";
let divisible = counter % 3;
let counterString = counter.toString();
if (counterString.indexOf('3') > - 1 || counterString.indexOf('6') > - 1 || counterString.indexOf('9') > - 1 || divisible === 0) {
speech = this.attributes.response + "quack";
} else {
speech = this.attributes.response + counter;
}
//update variables
counter++;
this.emit(":ask", speech);
},
"AnswerIntent": function() {
let correct = checkAnswer(this.event.request.intent.slots, counter);
//Game continues when you get the correct value
if (correct) {
counter++;
this.emitWithState("Game");
}
//Game ends when the value is incorrect
else {
let speechOutput = endGame();
this.response.speak(speechOutput);
this.emit(":responseReady");
}
},
"AMAZON.StopIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
endGame();
this.emit(":responseReady");
},
"AMAZON.CancelIntent": function() {
this.response.speak(EXIT_SKILL_MESSAGE);
endGame();
this.emit(":responseReady");
},
"AMAZON.HelpIntent": function() {
this.response.speak(HELP_MESSAGE).listen(HELP_MESSAGE);
this.emit(":responseReady");
},
"Unhandled": function() {
this.emitWithState("Game");
}
});
function checkAnswer(slots, value)
{
for (var slot in slots) {
if (slots[slot].value !== undefined)
{
let slotValue = slots[slot].value.toString().toLowerCase();
let counterValue = value.toString();
let divisible = value % 3;
if (divisible === 0) {
if (slotValue == "quack") {
return true;
} else {
return false;
}
}
else if (counterValue.indexOf('3') > - 1 || counterValue.indexOf('6') > - 1 || counterValue.indexOf('9') > - 1) {
if (slotValue == "quack") {
return true;
} else {
return false;
}
}
else if (slotValue == value.toString().toLowerCase())
{
return true;
}
else
{
return false;
}
}
}
return false;
}
function endGame() {
let speechOutput = "";
let response = getSpeechCon(false);
response += "your final score is " + counter;
speechOutput = response + ". " + EXIT_SKILL_MESSAGE;
counter = 1;
numPlayers = 1;
return speechOutput;
}
function getSpeechCon(type) {
return "<say-as interpret-as='interjection'>" + speechConsWrong[getRandom(0, speechConsWrong.length-1)] + " </say-as><break strength='strong'/>";
}
function getRandom(min, max) {
return Math.floor(Math.random() * (max-min+1)+min);
}
exports.handler = (event, context) => {
const alexa = Alexa.handler(event, context);
//alexa.appId = APP_ID;
alexa.registerHandlers(handlers, startHandlers, gameHandlers);
alexa.execute();
};
You should look for the SessionEndedRequest when the session ends, so you can listen for that and respond accordingly. It should be request.type == 'SessionEndedRequest'. Much like LaunchRequest, it is not actually an intent, and I see you are already handling LaunchRequest, so it should be easy to add.
I have managed to use Strophe MAM to get the archived messages into the RAWInput, and display the last message(but only the last one). How do i display all the messages from the RAWInput?? But not just the last one?
And how do i extract who the message is from?
I have limited the messages to the last 5.
connection.mam.query("test3#macbook-pro.local", {
"with": "test4#macbook-pro.local","before": '',"max":"5",
onMessage: function(message) {
console.log( $(message).text());
},
onComplete: function(response) {
console.log("Got all the messages");
}
});
You can get all the messages using the `strophe.mam.js plugin
Here is my working code:
// Retrives the messages between two particular users.
var archive = [];
var q = {
onMessage: function(message) {
try {
var id = message.querySelector('result').getAttribute('id');
var fwd = message.querySelector('forwarded');
var d = fwd.querySelector('delay').getAttribute('stamp');
var msg = fwd.querySelector('message');
var msg_data = {
id:id,
with: Strophe.getBareJidFromJid(msg.getAttribute('to')),
timestamp: (new Date(d)),
timestamp_orig: d,
from: Strophe.getBareJidFromJid(msg.getAttribute('from')),
to: Strophe.getBareJidFromJid(msg.getAttribute('to')),
type: msg.getAttribute('type'),
body: msg.getAttribute('body'),
message: Strophe.getText(msg.getElementsByTagName('body')[0])
};
archive.val(archive.val() + msg_data.from + ":" + msg_data.message + "\n" + msg_data.to + ":" + msg_data.message + "\n");
archive.scrollTop(archive[0].scrollHeight - archive.height());
console.log('xmpp.history.message',msg_data.message);
} catch(err){
if(typeof(err) == 'TypeError'){
try {
console.log(err.stack)
} catch(err2){
console.log(err,err2);
}
}
}
return true;
},
onComplete: function(response) {
console.log('xmpp.history.end',{query:q, response:response});
}
};
$(document).ready(function)(){
archive = $("#archive-messages");
archive.val("");
$("#to-jid").change(function() {
$("#archive-messages").val("");
var to = {"with": $(this).val()};
$.extend(q, to, before, max);
// It takes around 800ms to auto login. So after this timeout we have to retrieve the messages of particular User.
setTimeout(function(){
connection.mam.query(Strophe.getBareJidFromJid(connection.jid), q);
}, 1000);
});
});
connection.mam.query("Your_Id", {
"with": "partner_id","before": '',"max":'',
onMessage: function(message) {
console.log("Message from ", $(message).find("forwarded message").attr("from"),
": ", $(message).find("forwarded message body").text());
return true;
},
onComplete: function(response) {
console.log("Got all the messages");
}
});
This will fetch all History for a user. If you want to limit, then provide max value.
Don't download strophe.mam.js(https://github.com/metajack/strophejs-plugins/tree/master/mam) from github. Its not working. Please copy below strophe.mam.js file.
**strophe.mam.js**
/* XEP-0313: Message Archive Management
* Copyright (C) 2012 Kim Alvefur
*
* This file is MIT/X11 licensed. Please see the
* LICENSE.txt file in the source package for more information.
*
* TODO:
* Get RSM from the reply
* Clean remove onMessage handler afterwards
* queryid?
*
*/
Strophe.addConnectionPlugin('mam', {
_c: null,
_p: [ "with", "start", "end" ],
init: function (conn) {
this._c = conn;
Strophe.addNamespace('MAM', 'urn:xmpp:mam:0');
},
query: function (jid, options) {
var _p = this._p;
var attr = {
type:"set",
id:jid
};
var mamAttr = {xmlns: Strophe.NS.MAM};
var iq = $iq(attr).c("query", mamAttr).c('x',{xmlns:'jabber:x:data'});
iq.c('field',{var:"FORM_TYPE"}).c('value').t("urn:xmpp:mam:0").up().up();
for (i = 0; i < this._p.length; i++) {
var pn = _p[i];
var p = options[pn];
delete options[pn];
if (!!p) {
var f
iq.c('field',{var:pn}).c('value').t(p).up().up();
}
}
iq.up();
var onMessage = options["onMessage"];
delete options['onMessage'];
var onComplete = options["onComplete"];
delete options['onComplete'];
iq.cnode(new Strophe.RSM(options).toXML());
this._c.addHandler(onMessage, Strophe.NS.MAM, "message", null);
return this._c.sendIQ(iq, onComplete);
}
});
The easiest way to retrieve all the messages is to include this line at the end of the onMessage() function:
return true;
I think the reason is that if a handler does not return true, it will be destroyed after the first time it is called.
I can't get jasmine testing working smoothly on nodejs. What I mean is that there are situations when the test just stops in the middle of the run, does not notify anything, but silently fails in error. One example I got a few moments ago is this:
In the validater.validateIDs. There is a callback "isOkFunc" that get sent to another modules function to a custom mongoose.find-function
validateIDs = function (data, dbFunc) {
data = data || selfData;
var
isOkFunc = (function () {
var ret = {};
ret.thisData = void 0;
ret.setData = function (data) {
ret.thisData = (data === undefined) ? true : data;
};
return ret;
})(),
findData = {
playerID: data.playerID,
gameID: data.gameID,
turnID: data.turnID,
objectID: data.objectID
};
dbFunc.find(findData, isOkFunc.setData);
return isOkFunc.thisData;
};
The callback get called in the custom find-function:
this.find = function (data, cb) {
models.Order.find(data,function (err, orders) {
if (err) {
console.log("error in getting orders");
cb(void 0);
} else {
console.log(orders);
cb(orders);
}
});
};
Now since I forgot to point the thisData to ret.thisData, it errored, since the variable did not exist. But the problem is that jasmine doesn't notify anything about this, it just silently fails and stops execution. How should this be done for better debugging? Or more like how it should be for ANY realistic debugging?
The actual part in spec-file that caused the problem was this:
isOk = validater.validateIDs(data, orderReceive);
After stripping few unrelated descibe-sections, here is the whole spec-file:
"use strict";
/* This is for making jasmine testing work in windows with netbeans. If this
* causes trouble for you, then try to find a better solution. I need this in
* windows enviroment */
if(process.platform.match(/^win/)) {
require("jasmine-node");
};
var uuid = require('node-uuid'),
orders = require("../../databases/modules/orders.js"),
orderReceive = orders.ordersAtDatabase("../testDB"),
data, UUID = uuid.v4();;
/* Counts the amount of finished-class received. When it reaches given point.
* Executes all functions. This is supposed to be executed last to remove database entries
*
* Without this we can not test the database ID validation fully, since we need
* to have data in database for this */
var toExecuteLast = (function toExecuteLast() {
var funcs = [], ret = {}, i,
count = 0,
max = 5;
ret.add = function(func) {
funcs.push(func);
};
ret.finished = function() {
count++;
};
ret.do = function() {
console.log("gogogo");
if(count >= max) {
console.log("gogogo2222");
for(i = 0; i < funcs.length; i++) {
funcs[i]();
}
clearInterval(toExecuteLast.do);
}
};
return ret;
})();
beforeEach(function() {
data =
{
"playerID": 1,
"gameID": 1,
"turnID": 1,
"objectID": UUID,
"type": "unit",
"action": "move",
"exec": {
"x": 10,
"y": 10
}
};
});
describe("Testing data validation from data, that would come from frontend", function() {
var validater;
validater = orders.orderValidation();
describe(". User is sending fishy data: ", function(){
it(". Test that everything works fine", function() {
expect(helper).not.toThrow();
});
it(". Something fishy with playerID", function() {
data.playerID = "kjdf ss";
expect(helper).toThrow(new Error("playerID was not an integer"));
data.playerID = 12322.33;
expect(helper).toThrow();
});
it(". Something fishy with gameID", function() {
data.gameID = "kjfkj";
expect(helper).toThrow(new Error("gameID was not an integer"));
});
it(". Something fishy with turnID", function() {
data.turnID = "| rm -r";
expect(helper).toThrow(new Error("turnID was not an integer"));
});
it(". Something fishy with objectID", function() {
data.objectID = "| rm -r";
expect(helper).toThrow(new Error("objectID was not an UUIDv4"));
});
it(". Something fishy with type", function() {
data.type = "| rm -r";
expect(helper).toThrow();
});
it(". Something fishy with action", function() {
data.action = "| rm -r";
expect(helper).toThrow(new Error("Action contains illegal character only a-z,A-Z,0-9,_ are allowed"));
});
it(". Something fishy with exec", function() {
data.exec = "| rm -r";
expect(helper).toThrow(new Error("Action was move, but exec.x was not an integer"));
});
toExecuteLast.finished();
});
describe(". Do a database check if the IDs that are given (player, game, turn, object) are correct? ", function(){
/* Remeber that the playerID is fetched from inner data / cookies / auth
* so it is supposed to be correct. We need it to be correct in the
* database too, so we can test the game, turn and object ID validations */
it(". Everything ok with ID validation?", function() {
var isOk;
isOk = validater.validateIDs(data, orderReceive);
waitsFor(function() {
return isOk !== 'undefined' ? true : false;
});
runs(function() {
expect(isOk).not.toBeTruthy();
isOk = false;
data.gameID = 10;
isOk = validater.validateIDs(data, orderReceive);
waitsFor(function() {
return (isOk !== 'undefined') ? true : false;
}, "", 1000);
runs(function() {
// THIS doesn't seem to work, why?
expect(true).toBeTruthy();
toExecuteLast.finished();
});
});
});
toExecuteLast.finished();
});
function helper() {
validater.validateRequest(data);
}
});
setInterval(toExecuteLast.do, 400);
orderReceive.showAll();
//orderReceive.removeAll();