OpenTok Session Connection Issue (Javascript/Rails Application) - javascript

This is a basic video chat I wanted to create with 2 users connected.
After having done a bit research on opentok Tutorials, I was able to create the 2 way chat without Server SDK.
But When I created the rails server side app, I was unable to get the 2 way chat, rather the link was only creating single session and was not accepting any other users.
I have included the gem opentok in Gemfile.
Problem:
Only Single Video Div appearing, even after a existing screen present in another tab. i.e., I open the link, I get a video screen, When I open the same link in another tab, I get a single video screen instead of two. (Some issue with session connections).
Code from Controller:
class AuthController < ApplicationController
require "opentok"
def index
config_opentok
session = #opentok.create_session
#id = session.session_id
#token = session.generate_token
end
private
def config_opentok
if #opentok.nil?
#opentok = OpenTok::OpenTok.new API_KEY, 'SECRET'
end
end
end
I have entered correct Api_key and Secret above as given from Opentok dashboard.
Code from View:
<div id='myPublisherDiv'></div>
<div id='subscribersDiv'></div>
<script src='//static.opentok.com/v2/js/opentok.min.js'></script>
<script>
var apiKey = API_KEY; // used correct Api_key in APP
var sessionId = '<%= #id %>';
var token = '<%= #token %>';
var session = OT.initSession(apiKey, sessionId);
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'subscribersDiv', {insertMode: 'append'});
}
});
session.connect(token, function(error) {
if (error) {
console.log(error.message);
} else {
session.publish('myPublisherDiv', {width: 320, height: 240});
}
});
</script>
I have tried some other variants, adding event handlers etc, but nothing worked for me. Any Help appreciated. Provide sample codes if possible.

The problem is you are creating a new session for each page load. That means each visitor is getting a different session ID so the two will never be able to subscribe to each other.
Instead, you need a way to store a generated session ID (such as a database). Then you think about how you want to place visitors in the same session, such as using the URL for unique "rooms". When you have a page load that should serve a session ID that was previously generated, read the value from the database.

Related

SignalR multi user live chat desynchronisation

I have a live chat in which multiple people would be connected simultaneously. All public messaging works fine, but sometimes private messaging to a specific id doesn't work. I believe i narrowed it down to when people disconnected and reconnected that they connected to a different instance (perhaps IIS had recycled and started a new hub).
I thought I had fixed it, but I haven't and now I'm here because I'm stuck. What I thought would fix it was changing the connection variable within the startChat() function to refresh it with the correct information.
This is a cut down version of the code, as I didnt thing the rest would be necesary.
Issue is that when connected to signalR recipients of a message directly to them doean't come through, even though the chat Id it's being sent to it correct. Possible hub/socket mismatch?
var chat = $.connection.chatHub;
$(document).ready(function () {
// Start the chat connection.
startChat();
//restart chat if disconnected
$.connection.hub.disconnected(function () {
setTimeout(startChat(), 5000);
});
$.connection.hub.error(function (error) {
$('#messagebar').html('Chat ' + error + '. If this message doesn\'t go away, refresh your page.');
});
chat.client.addToChat = function (response) {
$('#chat-' + response.Type).prepend(response.Message);
};
});
function startChat() {
chat = $.connection.chatHub;
$.connection.hub.start().done(function () {
//get recent chat from db and insert to page.
//also saves user's chat id to their user for lookup when private messaging
$.ajax({
method: 'POST',
url: '/api/Chat/SetupChat/'
});
$('#messagebar').html('Connected to chat.');
});
}
Any help appreciated, Thanks.
Not sure exactly how your message is going missing, but you should send messages to a User instead of by connection id. This way you be able to identify on the server that a User has at least one connection, and send messages to all connections for that User. if a given connection id is no longer valid, but another one is (because the client has refreshed the page for example) the message wont be lost.
From the docs https://learn.microsoft.com/en-us/aspnet/core/signalr/groups:
The user identifier for a connection can be accessed by the
Context.UserIdentifier property in the hub.
public Task SendPrivateMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", message);
}
Not sure how this relates exactly to your server code, but you could add it if you're unclear how to progress.

Push live updates to client through Django Channels & Websockets

I'm trying to make a page which shows live-updating data to the client. The rest of the site is built with Django, so I'm trying to use Channels for this.
The data I am displaying is saved in both a JSON file and a MySQL database for further calculations in other parts of the site. Ideally, I would like to display the latest data received (that is, when the file updates) to the client as it is received.
And even though as I understand Channels are built exactly for this purpose, I am having trouble doing it.
I have tried sending multiple requests from the client-side with delay and loops in the consumer, but it either (ironically) only updates on refresh or updates instantly. However, neither of these approaches are triggered by a change in the file or database.
This is the code that "works", but doesn't really do what's required. (also, admittedly, there's basically nothing there...)
# consumers.py
def ws_connect(message):
message.reply_channel.send({"accept": True})
def ws_receive(message):
with open("data.json") as jsonfile:
jsondata = json.load(jsonfile)
res = json.dumps(jsondata)
message.reply_channel.send({ "text": res, })
#routing.py
from channels.routing import route
from .consumers import ws_receive, ws_connect
channel_routing = [
route("websocket.receive", ws_receive, path=r"^/websockets/$"),
route("websocket.connect", ws_connect, path=r"^/websockets/$"),
]
JS used:
<script>
var wsurl = "ws://" + "mywebsite.com" + "/websockets/";
socket = new WebSocket(wsurl);
socket.onopen = function() {
socket.send("this is a request");
console.log('sent');
}
socket.onmessage = function(message) {
console.log(message.data);
document.getElementById("livedata").innerHTML = message.data;
}
</script>
I'd be perfectly happy with a link to the docs that would help me achieve something like this, as I've managed to not find the solution for a whole week.
Add user to django channels Group on ws_connect
from channels.auth import channel_session_user_from_http
from channels import Group
#channel_session_user_from_http
def ws_connect(message, **kwargs):
http_user = message.user
if not http_user.is_anonymous:
message.reply_channel.send({'accept': True})
Group('user-'+str(http_user.id)).add(message.reply_channel)`
send any new updates data to the user by
Group('user-1').send({'text': 'hello user 1'})
Group('user-2').send({'text': 'hello user 2'})

How to retrieve the Azure Active Directory logged in user information from Javascript?

I have a web app where the client side is developed in Javascript. I have already enabled Azure AD login for my app by configuring it at portal.azure.com. Then, every time when this app loads, users are required to log in, if they have not.
I would like to have some Javascript in my client side so the app knows the user name. Here is the code I have.
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.11/js/adal.min.js"></script>
<script type="text/javascript">
var authContext = new AuthenticationContext({
clientId: 'xxxx-xxx-xxxx',
postLogoutRedirectUri: window.location
});
var user = authContext.getCachedUser();
if (user) {
window.alert("Signed in as " + user.userName);
} else{
window.alert("Failed to get the user information");
}
</script>
However, the variable user is always null. Can anybody help?
That seems you are using "Authentication / Authorization" feature of azure app service and the identity provide is azure ad . If you want to access the tokens from a client (like JavaScript in a browser), or if you want to get a richer set of information about the logged in user, you can also send an authenticated GET request to the /.auth/me endpoint. Javascript code below to get user claims is for your reference:
<script type="text/javascript">
$(document).ready(function ()
{
$.get("https://xxxxxx.azurewebsites.net/.auth/me", function (data, status) {
for (var key in data[0]["user_claims"]) {
var obj = data[0]["user_claims"][key];
alert(obj["typ"]); //claim type in user_claims
alert(obj["val"]) //claim value in user_claims
}
});
});
</script>
Thanks, Yan. That pretty solves my problem, only with little revision to your code. My situation is that I need to retrieve the user name first before generating the later part of my app. So, I removed the outer wrapper $(document).ready(function(){}. Correct me if I am wrong. Probably this out wrapper is telling this chunk of code to run after the entire app is loaded. Then, the final code is like this:
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script type="text/javascript">
$.get("https://xxxxx.azurewebsites.net/.auth/me", function (data) {
labeler = data[0]['user_id'];
window.alert("You logged in as " + data[0]['user_id']);
});
</script>

Implementing notification/alert popup on job completion in Ruby on Rails

I am implementing background processing jobs in Rails using 'Sidekiq' gem which are run when a user clicks on a button. Since the jobs are run asynchronously, rails replies back instantly.
I want to add in a functionality or a callback to trigger a JavaScript popup to display that the job processing has finished.
Controller Snippet:
def exec_job
JobWorker.perform_async(#job_id,#user_id)
respond_to do |wants|
wants.html { }
wants.js { render 'summary.js.haml' }
end
end
Edit 1:
I am storing the 'user_id' to keep a track of the user who triggered the job. So that I can relate the popup to this user.
Edit 2:
The 'perform' method of Sidekiq does some database manipulation(Update mostly) and log creation, which takes time.
Currently, the user gets to know about the status when he refreshes the page later.
Edit 3(Solution Attempt 1):
I tried implementing 'Faye Push Notification' by using subscribe after successful login of user(with channel as user_id).
On the server side, when the job completes execution, I created another client to publish a message to the same channel (Using faye reference documents).
It is working fine on my desktop, i.e., I can see an alert popup prompting that the job has completed. But when I try testing using another machine on my local network, the alert is not prompted.
Client Side Script:
(function() {
var faye = new Faye.Client('http://Server_IP:9292/faye');
var public_subscription = faye.subscribe("/users/#{current_user.id}", function(data) {
alert(data);
});
});
Server Side Code:
EM.run {
client = Faye::Client.new('http://localhost:9292/faye')
publication = client.publish("/users/#{user.id}", 'Execution completed!')
publication.callback do
logger.info("Message sent to channel '/users/#{user.id}'")
end
publication.errback do |error|
logger.info('There was a problem: ' + error.message)
end
}
Rails 4 introduced the ActionController::Live module. This allows for pushing SSEs to the browser. If you want to implement this based on a database update you will need to look into configuring Postgresql to LISTEN and NOTIFY.
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello world\n"
sleep 1
}
ensure
response.stream.close
end
end
Here is a good article on it: http://ngauthier.com/2013/02/rails-4-sse-notify-listen.html
Thanks fmendez.
I looked at the suggestions given by other users and finally, I have implemented a 'Faye' based push notification by:
Subscribing the user on successful login to a channel created using 'user_id'
Replying to this channel from server side after job completion (By fetching user_id)
For better understanding(so that it may be helpful for others), check the edit to the question.

Removing someone from a user hash on navigating away from a page / page close

I'm building a Node.js / Socket.io project.
I have a hash of Users based on their websocket id. When a user makes the following request i'd like to add them to a group of users viewing that page.
app.get('/board/:id', function(req, res){}
I'd like to keep something like
Browsing = {
username : id,
username : id,
...
}
However i'm unsure how to remove a user lets say if they navigate off the page. Is there some kind of function that gets called upon page leave?
Partial Solution:The following seems to do the trick on Chrome:
$(window).unload(function(){
var username = $('#username').text();
var pid = currentProject;
var data = {
username: username,
id : pid
}
socket.emit('leaving-page', data);
})
... Is there some kind of function that gets called upon page
leave? ...
Yes, but it is not reliable.
The way the people keep track of who is online and who isn't, is usually like this:
Add the time when the user last refreshed/visited a page
set a limit to you consider them offline
You could intercept the event which corresponds to leaving a page. There are several ways to do it, have a look at the following links and let me know if any suits your needs and if I can answer any more explicit questions about them:
Intercept page exit event
Best way to detect when a user leaves a web page?
jquery unload
with the last link you could do something like this:
$(window).unload(function() {
//remove the user from your json object with delete json_object[key];
});
Hope this helps.
Since you're using Socket.io, the server will know when the user has left the page because the socket will be disconnected. Simply listen for the disconnect event.
io.sockets.on('connection', function (socket) {
...
socket.on('disconnect', function () {
// The user on `socket` has closed the page
});
});
Better yet, take advantage of namespaces and let Socket.io handle all of the connection/disconnection (it's doing it anyway -- no need to duplicate effort).
On the client,
socket = io.connect('http://example.com/pagename');
pagename need not point to a valid URL on your domain – it's just the namespace you'll use in the server code:
io.sockets.clients('pagename')
Gets you all of the clients currently connected to the pagename namespace.

Categories