Retrieve groupchat history using strophe.js - javascript

I am using ejabberd 15.06 version with Strophe.js. Retrieving the one-to-one chat from my backend database works fine. But how can I retrieve the groupchat history from the database??
For example, if I have a "strophe" group. When new users joins in the strophe group, then the chat history done in the group by other users should be displayed.
I am using this code
var pres = $pres({ to: room + "/" + nickname, from: connection.jid });
connection.send( msg.c('x', {xmlns: NS_MUC}));
if(chat_history != null){
var msg_history = msg.c('x', { "xmlns": "http://jabber.org/protocol/muc"}).c("history", chat_history, {maxstanzas: 50});
debugger;
console.log(msg_history);
}
In my console it looks like
h.Builder {nodeTree: presence, node: x}
I am stuck how to fetch the history of groupchat. Please help

Usually, unless the room has been configured not to send any history, send the join presence should be enough to let you receive the latest chat room messages.
Please, note that old messages have a delay tag on them to provide the time at which the original message was send, so make sure your client is not discarding those messages.
If you want control about the history size, you can use the Strophe MUC plugin to join the room and send the max stanzas and time limit as the history_attrs variable. Your server and room must also be configured to provide the history.
conn.muc.join(room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password,{ maxstanzas: 10, seconds: 3600 });

Related

Is it possible to change the URL request on a WebSocket connection?

I am trying to change the URL request on an already connected socket but I can't figure out how or if it is even possible.
I am working with the WebSocket API and CoinCap.
What I am doing right now is closing the connection and creating a new one with the new parameters.
// Create a new WS connection
const webSocketURL = `wss://ws.coincap.io/prices?assets=${loadedKeys}`
// loadedKeys could be a string of one coin (e.g. bitcoin) or an array
// or an array (e.g. bitcoin,ethereum,monero,litecoin), has to be dynamic.
pricesWs = new WebSocket(webSocketURL);
pricesWs.onopen = function () {
console.log(`conected: ${pricesWs.readyState}`)
}
pricesWs.onmessage = function (msg) {
handleUpdateCB(msg.data);
}
// then when I need to receive different coin prices
// I close the connection and reopen a new one.
anotherFunction() {
pricesWs.close();
pricesWs = new WebSocket(aNewWebSocketURL);
}
I tried sending parameters as messages with send() function without success, I keep receiving the same data, let's say I first connect asking for bitcoin and the I want to receive bitcoin and ethereum I tried this
pricesWs = new WebSocket(`wss://ws.coincap.io/prices?assets=bitcoin);
//then tried
pricesWs.send(bitcoin,ethereum)
this doesn't work, I also tried sending as JSON but I kept getting the same data just for the first query(bitcoin)
UPDATE:
This is the the Git for the app, if you are interested seeing the whole thing together.
Git
UPDATE 2:
I created this pen to make it easier to understand, note that the pen is made on VueJS, but that isn't important. The important part is on line 60 JS panel
Is there any reason why you want to switch the URL?
According to the coin cap documentation, you can request information about multiple crypto currency at once. Is it not an option for you?
Generally you should avoid opening and closing connections to a socket as there is slight latency albeit very insignificant. Leaving the connection open is better since you will be notified if price is changed for any of the currencies you are interested it.
The answer to your original question "Is it possible to change URL for a web socket connection?" is no! You can't change URL however you can create as many connections as you need. In your case you are closing the connection and opening it immediately but in the comments I noticed that you mentioned that it is based on user interaction. You can open connection just for the currencies you care about when user requests it and keep the connection opened until user switches the currency again because at that point you'll probably switch to another currency.
I also agree with #Taylor Spark, you can also just hide the dom for the currencies user don't care and render the ones they are interested in.

How do you link GCM chrome push notifications and payload data?

Push notifications in Chrome via GCM are driving me crazy.
I've got everything up and running. I serve the push using my python server to GCM. A service worker displays the push notification fine.
To my knowledge, there is NO data passed with push events. Sounds like it's coming soon but not available yet.
So just before the push notification shows, I call my server to get extra data for the push notification. But I have no information on the push notification to send to my server to match and return relevant data.
Everything I can think of to match a notification and user data is purely speculative. The closest thing I can find is a timestamp object on the PushEvent{} that roughly matches the successful return of the GCM call for each user.
So how are other people handling custom payload data to display Chrome push notifications?
The PushEvent{} does not seem to have any ID associated with it. I know the user that the push is for because I've previously stored that information at the time of registration.
But once I receive a push, I have no idea of knowing what the push was for.
I would like to avoid:
Trying to match based on timestamp (since notifications displays are not guaranteed to be instant).
Trying to pull the 'latest' data for a user because in my case, there could be several notifications that are sent for different bits of data around the same time.
How are other sites like Whatsapp and Facebook linking custom payload data with a seemingly sterile event data as a result of a push notification?
How are you doing it? Any suggestions?
Here's what my receiver code looks like:
self.addEventListener('push', function(event) {
event.waitUntil(
fetch("https://oapi.co/o?f=getExtraPushData&uid=" + self.userID + "&t=" + self.userToken).then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
return response.json().then(function(data) {
if (data.error || !data.notification) {
console.error('The API returned an error.', data.error);
throw new Error();
}
var title = data.notification.title;
var message = data.notification.message;
var icon = data.notification.icon;
return self.registration.showNotification(title, {
body: message,
icon: icon,
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this push message';
var icon = "https://oapi.co/occurrences_secure/img/stepNotify_1.png";
var notificationTag = 'notification-error';
return self.registration.showNotification(title, {
body: message,
icon: icon,
tag: notificationTag
});
})
);
});
I understand your problem, and i've been fiddling with the same when i wanted to use chrome notification. You can use indexedDB to save ObjectStore and retrieve data in webServices.
IndexedDB is accessible to webservices. I am using it to store user information and when the user recieves a push notification i pass the stored access key to identify the user and pass him relevent information.
Here's matt gaunt's tutorial which says indexed db is accessible to web services:
http://www.html5rocks.com/en/tutorials/service-worker/introduction/
Here's a good indexedDB tutorial:
http://blog.vanamco.com/indexeddb-fundamentals-plus-a-indexeddb-example-tutorial/
Assuming you are still in the past. That is, sending only a push trigger to your browser with no payload it's now time to move on with time. You can now send payload in your push events. Since you seem familiar with GCM, it's ok to go with that though there is now the Web Push Protocol which is browser vendor independent.
Briefly, to make that work you need to encrypt your payload with specifications found here, in your server.
There is a node by google chrome and PHP implementations for that, that I know of.
You may check out the PHP Web Push here.
In the browser you would need to provide the subscription object now with the p256dh and auth on top of the endpoint as before.
You may check this out for more details.

Chat application how to generate an id for a particular chat?

Let's say I have two users, "Matt" & "Kevin". Matt wants to message Kevin, by clicking a chat button to send Kevin a direct message a chat box boots up, he sends a message and Kevin receives it.
I generate the chat id by taking the person who sent it (Matt) and the person who received the message (Kevin) and concatenating it into an id.
var me = "Matt";
var user = "Kevin";
var uniqueChatID = me+user;
As I save the message server side (with mongoDB) the message object has a chatID of MattKevin. So now when I want to get back to that chat I can pull in all messages with the chatID of MattKevin.
This works fine, until Kevin wants to boot up a chat with Matt, then the id becomes KevinMatt. Now I am referencing a different chat, it's backwards. So If I want to pass uniqueChatID to get the messages it will pull a different set.
var me = "Kevin";
var user = "Matt";
var uniqueChatID = me+user;
So I am curious how can I set this up a bit better so that my program knows, ok Matt and Kevin have a chat, so if Matt messages Kevin it pulls in their chat or visa versa, Kevin messages Matt and it gets the same messages?
Sort them alphabetically:
var me = "Kevin";
var user = "Matt";
var uniqueChatID = [me, user].sort().join('');
That said, while this technically works, I'd recommend you do a little housekeeping - ensure they're always lowercase, and ensure on your db that you enforce unique usernames. Or, I'd even suggest giving the user a unique identifier (like a UUID) and use that instead to create the UCID:
var me = CurrentUser.uuid(); // 8cb3ebb8-30f9-11e5-a151-feff819cdc9f
var targetUser = Chat.targetUser(); // Matt: 9bc1ef9c-6719-4041-afd3-c5b87c90690d
var uniqueChatID = [me, targetUser].sort().join(',');
// 8cb3ebb8-30f9-11e5-a151-feff819cdc9f,9bc1ef9c-6719-4041-afd3-c5b87c90690d
And lastly, if your db supports relationships or connections, your best option is to separate chat table/collection for each chat and "connect" (or create a relationship) between both users and the chat. Then the next time you go and load it up, the connection will lead you to a unique chat that's connected to both users.
I think you approach is too complex. Furthermore, it looks like you want to embed the individual chat messages into the document bearing the created _id. The problem here is that there is a 16 MB size limit on BSON documents at the time of this writing. Upon reaching this limit, your users simply could not communicate any more. Increasing the size of documents may also lead to frequent document relocations, which is a very costly operation unless you use the new WiredTiger storage engine introduced in version 3.0 of MongoDB.
So we need a more scalable approach.
Here is how I would do it:
User:
{
_id: "Kevin",
email: "kevin#example.com"
/* Put further user details as you see fit*/
}
Message:
{
_id: new ObjectId(),
from: "Kevin",
/* You might want to have multi-person chats, hence the array */
to: ["Matt"],
ts: new ISODate(),
message: "Hi, Matt!"
}
Index:
db.messages.ensureIndex({from:1,to:1,ts:1})
Query for reconstructing all messages a user received:
var user = "Matt"
db.messages.find({"to": user}).sort({ts:1})
Now you can iterate over the result set and open a chat window for each "from" you find.
Query for reconstructing a defined chat
var user = "Matt"
var sender = "Kevin"
db.messages.find({"from": sender, "to":user}).sort({ts:1})
will give you all messages sent to Matt by Kevin, ordered by time. Since both queries should utilize the index, they should be pretty fast. You can use .limit(x) to query only the last x messages sent to user.
With this approach, you don't need an artificial _id, the index created allows you to do every query related to the participants efficiently and the messages can be sorted in order. Because each message is saved individually and does not change any more, you can store an almost indefinite number of messages and bypass the document relocation problem.

Sending push notification to specific device knowing phone number, parse.com

I would just to get things clear here or get other suggestions if possible if it is better.
This is how my application works now:
1) Anonymous user is created if its the first time the user open the application
2) Phone verification is needed to be done. If verified, i save the phone number in a custom field in user object ( do i need to make this user a real user after this or can i still go with anonymous user?)( verification is one time only of course)
3) The user will be able to pick a friend from his contact list(ABPeoplePicker) and then send a push notification to that friend's device.
Now i have set up a relationship with the User and the installation object with this code:
PFInstallation *installation = [PFInstallation currentInstallation];
installation[#"user"] = [PFUser currentUser];
[installation saveInBackground];
And this created a pointer to the users ObjectId
So my question is how would i create a query and send a push notification to a number retrieved from that Users friend list?. I am having a hard time to connect how i can get from a phone number to the installation device that i need to send the notification to. If you could provide help in javascript since i read it is safer to send it through cloud code!
Also a subquestion mentioned above if i need to make the anonymous user to a real user.
many thanks!!
I'd recommend subscribing each user to their own channel where the channel name is equal to their phone number (Subscription can't be done in Javascript):
NSString *userPhoneNumber = ...
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
// "x" added to the beginning of the userPhoneNumber since
// Parse doesn't allow channels to begin with a number
[currentInstallation addUniqueObject:[NSString stringWithFormat:#"x%#", userPhoneNumber] forKey:#"channels"];
[currentInstallation saveInBackground];
That way no query is required before pushing the notification:
var friendPhoneNumber = ...
var friendChannel = "x" + friendPhoneNumber;
Parse.Push.send({
channels: [ friendChannel ],
data: {
alert: message
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});

Strophe.js MUC: creating a room and joining more than one room

I'm creating a chat website and I'm using Strophe.js and the Strophe.muc.js plugin. The single chat functionalities work fine, but I also wan't to implement a group chat function where users can create rooms and invite other users to their room. Using the muc plugin, I can create a room, but the problem is that until I don't configure it (I guess), other users can't join and the room isn't persistent. I know that the muc plugin has configuration methods, but I don't know how to create the config Form object, I have no idea how should it look. This would be my first problem.
Second: Is it possible that I join more then one room and get messages from all the rooms I'm in? If not, then there's no need to give me an answer to my first question...
After trying Mark S' solution, I found I have to send presence first for creating the room. I list the whole code below and hope this helps.
//before executing the code below, you need to connect to IM server (var conn is Strophe.Connection)
var userName = "steve",
serverName = "example.com",
userJid = userName + '#' + serverName,
roomJid = 'testRoom' + '#conference.' + serverName,
iq;
//send presence first for creating room
var d = $pres({'from': userJid, 'to': roomJid + '/' + userName})
conn.send(d.tree());
iq = $iq({
to: roomJid,
type: 'set'
}).c("query", {
xmlns: Strophe.NS.MUC_OWNER
});
iq.c("x", {
xmlns: "jabber:x:data",
type: "submit"
});
//send configuration you want
iq.c('field', { 'var': 'FORM_TYPE' }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
iq.c('field', { 'var': 'muc#roomconfig_publicroom' }).c('value').t('1').up().up();
conn.sendIQ(iq.tree(), function () { console.log('success'); }, function (err) { console.log('error', err); });
I found if I don't send any configuration, the Instant Message server which is openfire only writes the room to cache, not database, so the room will disappear after restarting Instant Message server.
You can set the rooms to be persistent by default on your jabber server.
Creating rooms is a 2 step process. First creating the room then configuring the room.
You can join as many rooms as you like.
A room config is like (you'll get a form on the first step of available fields if you check the response from the server).
The 2nd step looks like:
var iq, stanza;
iq = $iq({
to: newroomjid,
type: "set"
}).c("query", {
xmlns: Strophe.NS.MUC_OWNER
});
iq.c("x", {
xmlns: "jabber:x:data",
type: "submit"
});
iq.c('field', { 'var': 'FORM_TYPE' }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
iq.c('field', { 'var': 'muc#roomconfig_roomname' }).c('value').t(roomName).up().up();
stanza = iq.tree();

Categories

Resources