8

I'm a bit confused on the presence-channels in Pusher's platform, as I'm building a chat application from scratch. Now, I know some of you guys have seen tons of "realtime chat app" topics around, but, I'm looking for a peer-to-peer chat and not the site-wide global thingy. More like a facebook chat, where you can go one-to-one.

Now, I've seen an example in PubNub's demos (named Babel) but, that thing is far from what I'm looking for because I've checked the requests in the console and even if it's not shown, the sent messages between other users are shown in my network request logs too because it's being filtered in JS and not server-side and thats not something I want for sure.

So, coming back to the subject, I'm aware of the channel / private-channel / presence channel functionality, and I decided to do this:

  • When opening the app, every user subcribes to his private-user_id channel ( creates, if it doesn't exist already ).

  • At the same time ( while opening the app ) user1 subscribes to a presence-global channel where others keep track if friends are online.

  • When others want to send him a message, e.g. user2 to user1, he subscribes to private-1 thereafter javascript will process the events.

Now, I know something's wrong with this because.. if user3 would send a message to user1 he'd subscribe to private-user1 so I guess he'll see the events that user2 is triggering when sending messages to user1 too, right ? Or did I get this wrong ?

I've read in their docs that presence channel is actually a private channel extension, so I'm thinking now.. why using private channels anymore, and then, how can I notify all my friends I'm online.

But then, something else comes up in their docs , telling me that channels provide two important things (among others), from which, first is a way of filtering data and second is a way of controlling access.

How am I supposed to "filter data" since there's no link in their docs, or better, what do you have in mind for a one-to-one chat. I'm sorry if I got all their docs wrong, I had a look on their sample applications but none of them are using the one-to-one technique which I'm looking for.

I am new to Pusher and socket connections etc, but I've learned how to authenticate, how to create , detect and process the events in the channel, and I can create a simple global chat with online members, but, when it comes to private-channels I'm quite confused on how to create separate channels for two users.

Thanks in advance !

Eduard
  • 2,909
  • 5
  • 33
  • 57

1 Answers1

20

The purpose of private channels is to restrict who can subscribe to that channel. So, you can either:

  1. Use it to ensure only a users friends can subscribe to updates
  2. Use it for notifications only for that user

In one-to-one chat I'd suggest you choose the latter (No.2).

With this in mind I'd set out achieving one-to-one chat as follows:

The Forum

When users join the chat application they all subscribe to two channels:

  1. private-notifications-<user_id> where user_id is their unique user ID e.g. leggetter in my case. This channel is utilised for user-specific notifications.
  2. presence-forum for all users in that forum. The question called this presence-global.

This is achieved as follows:

var notifications = pusher.subscribe( 'private-notifications-user_one' );
var forum = pusher.subscribe( 'presence-forum' );

Upon subscription to each channel the channel authentication process will take place.

Within the forum you could have a general public chat on the presence-forum/presence-global presence channel by sending and receiving messages.

Starting one-to-one chat

When one user (user_one) wants to have a private chat with another user (user_two) you obviously need something in the UI to trigger this. Say user_one clicks on something next to user_two that indicates they want a one-to-one chat. When this happens a request should be made to the server (the authority) to indicate that user_one wants to initiate the private chat with user_two†.

Note: † if you chose a channel naming convention for one-to-one chat the private channel authentication could actually be used as the private one-to-one chat initiation

When the server receives this request it can generate a unique private channel name for this one-to-one chat. A really simple way of doing this is by concatenating the user IDs e.g. private-chat-<initiating_user>-<receiving_user> (there are other considerations e.g. maybe you want to ensure the channel name is always the same between the two users). In our simple scenario the channel name would be private-chat-user_one-user_two.

The server can then trigger a one-to-one-chat-request event on the private notification channel for each user delivering the one-to-one private chat channel name in the payload.

// Trigger event on both user channels with one call
var channels = [ 'private-notifications-user_one', 'private-notifications-user_two' ];
// Additional event data could also be sent
// e.g. more info on the initiating user
var eventData = {
                  'channel_name': 'private-chat-user_one-user_two',
                  'initiated_by': 'user_one'
                  'chat_with'   : 'user_two'
                };
pusher.trigger( channels, 'one-to-one-chat-request', eventData );

When user_one receives the one-to-one-chat-request they will subscribe to the eventData.channel_name channel and the auth process will take place for that channel.

// A lookup of private chats
// where the key is the user ID of the current user is chatting with
var privateChats = {};
notifications.bind( 'one-to-one-chat-request', function( data ) {

  // MY_USER_ID would need to be stored somewhere
  // and in this case the value would be 'user_one'.
  // expectingChatWith should make sure user_one is waiting for
  // a private chat response with the given user
  if( data.initiated_by === MY_USER_ID &&
      expectingChatWith( data.chat_with ) ) {
    startPrivateChat( data.chat_with, data.channel_name );
  }

} );

function startPrivateChat( withUserId, channelName ) {
  privateChats[ withUserId ] = pusher.subscribe( channelName );
}

When user_two receives the one-to-one-chat-request the user will need to be notified about the request and either accept or decline it. If the user accepts then the client-side code simply subscribes to the channel. If the user declines then a request should be sent to the server and an event triggered on private-notifications-user_one telling them their one-to-one chat request was declined. This will allow user_one to unsubscribe from the private chat channel.

var privateChats = {};
notifications.bind( 'one-to-one-chat-request', function( data ) {

  if( ... ) { ... }
  // has somebody request to chat with this user?
  else if( data.chatWith === MY_USER_ID ) {
    // Prompt the user
    // Note: more user info required
    displayChatPrompt( data );
  }

} );

// callback when the user accepts the chat request
function accepted( chatUserId, channelName ) {
  startPrivateChat( chatUserId, channelName );
}

// the user doesn't want to chat
function declined( chatUserId ) {
  // send info to the server indicating declined request
}

Private one-to-one chat success

With both user_one and user_two subscribed to private-chat-user_one-user_two they can trigger events on the channel and participate in their private one-to-one chat.

leggetter
  • 14,640
  • 1
  • 50
  • 58
  • 1
    I have to say I'm proud I thought about this method too, but I was wondering if I'm not complicating myself around this topic. I'm really glad you took your time and replied me and I'm willing to get my hands dirty with code right away ! Thanks ! – Eduard Dec 10 '14 at 07:29
  • Very well written! I'm having trouble understanding the if ( ... ) { ... } has somebody request to chat with this user? What should we fill in there.. Thank you! – Miguel Stevens Jul 28 '15 at 19:23
  • 1
    @Notflip , the previous code is .. more like pseudo-code since it doesn't work in real life. It's meant to give you an idea of how things work. I have made meanwhile my own chat system , which can be checked here: http://socialjs.eduardd.eu ( http://socialjs.eduardd.eu/static/js/chat.js ). Do check it , maybe it helps you understand how this works. Note that I did not adopt the "userid_userid" naming convention since I am creating groups of users so the channel name can't be user1_user2_user3...user37. – Eduard Jul 28 '15 at 21:54
  • 1
    Thank you very much! I will take a look! :) it's difficult to get your head around. Trying to build a sort of chatroom with private options. Edit: Your site looks really good! – Miguel Stevens Jul 29 '15 at 06:35
  • I'm close but wondering ( in the above example ) what the StartPrivateChat() method would look like? thank you guys! – Miguel Stevens Jul 29 '15 at 14:07
  • Your javascript is really complicated for me ( a php programmer ).. it's well written i think? How do you call your written javascript in the file you linked? how do you like mingle it with your application. thanks! – Miguel Stevens Jul 29 '15 at 14:58
  • @edduvs would you mind if i contact you for 1 quick question on google+ or email? – Miguel Stevens Jul 29 '15 at 15:42
  • @Notflip , edi@02.ro is my email. – Eduard Jul 30 '15 at 22:09
  • This post is old but people who looks to create pusher one to one chat will likely end up seeing this page. It's important to note that presence channel have a limitation of 100 members. Hence if you have more members its more logical to use pusher Webhook and a local database to keep track on who is on/offline. – Someone Special Dec 02 '16 at 09:48
  • If you are nice to the guys you can get a limit raise up to 200, depending on your app. If that is still not enough, there's Firebase nowadays... though it's not that simple. – Eduard Jan 11 '17 at 13:55
  • Has anybody implemented this algorithm? are you kind enough to share the code? – awebartisan Mar 31 '17 at 15:46
  • The question is what would you do if user refreshes the browser window? Then there would be no subscription to private channel – GorillaApe Mar 15 '19 at 09:18