I've been studying Nodejs and Socket.io, the problem is I'm struggling on how to get an url parameter (www.example.com/sample/:sampleid) and use it on my socket connection.
Here's an example:
var roomname;
var username;
app.get('/room/:roomname/user/:username', function(req, res){
var room = {
username: req.params.username,
roomname: req.params.roomname
};
roomname = room.roomname;
username = room.username;
res.render('room', room);
});
var users = {};
io.sockets.on('connection', function (socket) {
socket.username = username;
socket.room = roomname;
users[username] = username;
console.log("joining room: " + roomname)
socket.join(roomname);
console.log("emiting to room: " + roomname)
socket.broadcast.to(roomname).emit('newUser', username);
});
I'm simply storing the name on roomname and using it inside io.sockets.on, but I think that's a hack and should be a better way to achieve this...
What's a good way to solve this kind of situations?
Since you're using res.render and you're already passing room as a data parameter, I think it's best to make use of that and do something like this (I use EJS in my example since I'm used to that):
Client:
<script>socket.emit('join', {room: '<%- room.roomname %>' });</script>
Server:
io.on('connection', (socket) => {
socket.room = '';
socket.on('join', (msg) => {
socket.room = msg.room;
});
});
Otherwise it could be an idea to emit document.location.pathname, but then you'd have to parse the path yourself to get the roomname out of it.
EDIT:
What you're doing at the moment is actually very dangerous, because of the following scenario:
User 1 opens the page, "global" variables username and room get set (let's set them both to foo for now), but user 1 has slow internet, so it takes some time to load and for socket.io to connect.
Now User 2 opens the page, the "global" variables username and room are changed to the new values (let's use bar), before user 1 opens up the socket.io connection.
Now User 1 and User 2 connect with socket.io, but since user 2 has faster internet than user 1, var username and var roomname are now both set to value bar even though User 1 has username and roomname foo.
Just a heads up.
socket can emit any event because socket object is EventEmitter so that it can fire(emit) event in any time and on anther side server can listen to this events by .on function
example
socket.emit('join',{name:"ahmed"})
server
io.sockets.on('join',function(data){
console.log(data.name)
})
I had the same question and arrived at a different solution, where I pass the parameters at the time of connection.
//it's my connection in client side
let socket = socketio(`http://localhost:4444?user=${uuid}&room=${room}`);
// where UUID is my user ID and room id
So on the server side we can get that parameters like below
let userUUID = socket.handshake.query.user;
let roomId = socket.handshake.query.room;
The good thing is that when connection is lost you do not depend on the fact that the client will call your custom connection event again, you can put your logic into the default io.on('connection',);
More info here https://socket.io/docs/server-api/#socket-handshake
Related
How to call to a phone number using Twilio.Device?
I am doing the click to call feature for my widget.
I am able to get the Capability token required in setting up the Twilio.Device. I am able also to connect the Twilio.Device to twilio by setup function provided in client javascript library.
What can I use to call a number using Twilio.Device?
Its been a while since I played with it, but looking back at the code then once you have the token and are connected to Twilio it's just a case of setting the number you want to call and then initiating the call. Something like this:
document.getElementById('button-call').onclick = function () {
// get the phone number to connect the call to
var params = {
Caller: document.getElementById('phone-number').value
};
console.log('Calling ' + params.Caller + '...');
Twilio.Device.connect(params);
};
You have to make sure your token allows outgoing calls. My token.php file contains the following:
$capability = new ClientToken($TWILIO_ACCOUNT_SID, $TWILIO_AUTH_TOKEN, 'ttl=20');
$capability->allowClientOutgoing($TWILIO_TWIML_APP_SID);
$token = $capability->generateToken();
I think this is all fairly standard stuff from the quickstart files. I only had a quick go with it, but I don't remember it being complicated. Have fun!
I am using Tweetinvi for posting images to Twitter.
From our App servers its working fine to post to Twitter.
But, When tried from our load balancer getting this error -
Error:The credentials are required as the URL does not contain the
credentials identifier.
Stack Trace: at Tweetinvi.AuthFlow.CreateCredentialsFromVerifierCode(String
verifierCode, String authorizationId, IAuthenticationContext
authContext)
My code snippet is like this -
var verifierCode = Request.Params.Get("oauth_verifier");
var authorizationId = Request.Params.Get("authorization_id");
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, authorizationId);
I see these parameters(oauth_verifier, authorization_id,..) being passed to the callback page. But still seeing the above error in the call back page.
Note: this issue is only when I try posting to Twitter on our loadbalancer (using the individual servers working fine).
Should I use a different overloaded function?
So the problem comes from the fact that you are actually using a load balancer. But let me explain how the authentication works and how you can solve your problem.
var appCredentials = new ConsumerCredentials("", "");
var authContext = AuthFlow.InitAuthentication(appCredentials, "");
When you call AuthFlow.InitAuthentication, it returns an IAuthenticationContext. This context contains all the information required to process the callback from Twitter.
But in addition to this, Tweetinvi adds a parameter authorization_id to the callback so that it can map the callback request to an actual IAuthenticationContext.
var authorizationId = Request.Params.Get("authorization_id");
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, authorizationId);
When you call AuthFlow.CreateCredentialsFromVerifierCode with an authorization_id as a parameter it will look into the local dictionary and try to get the IAuthenticationContext.
Because you are using a load balancer, the server executing the AuthFlow.InitAuthentication can be different from the server your receiving the callback request.
Because your callback arrives at a different server, it actually result in the AuthenticationContext being null.
This is what I tried to explain in the documentation.
How to solve this?
What you need to do is to store the IAuthenticationContext information required for the CreateCredentialsFromVerifierCode to continue its work when it receives the callback. I would suggest you store this in your database.
When you receive your callback you will have to get back these information from your db. To do that I would suggest that when you initally call the `` you add to the callback url a parameter with the value storing the authentication id in your database (e.g. my_auth_db_id=42).
var authContext = AuthFlow.InitAuthentication(appCredentials, "http://mywebsite.com?my_auth_db_id=42");
When your callback arrives you will be able to do :
var myDBAuthId = Request.Params.Get("my_auth_db_id");
With this value you can now create a new token with the required information (stored in the db).
var token = new AuthenticationToken()
{
AuthorizationKey = "<from_db>",
AuthorizationSecret = "<from_db>",
ConsumerCredentials = creds
};
Now you are ready to complete the operation:
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, token );
I realize this is a big post, but I wanted to explain how it works.
Please let me know if anything does not makes sense.
twilio javascript client set from number , Also how I can get the call sid after connect?
I tried to set the from Number in the call options like the next lines before connect and still the same issue in the javascript
$(document).ready(function () {
Twilio.Device.setup(token);
var connection = null;
$("#call").click(function () {
var params = { "Phone": $('#Phone').val(), "from":$('#Phone').val() };
connection = Twilio.Device.connect(params);
return false;
});
});
-- and inside the server side code vbnet when I am generating the token I added the next code but this doesn't solve the from number issue
Dim options As New CallOptions()
options.Record = True
options.From = model.FromNumber
Dim cap = New TwilioCapability(_settings.AccountSID, _settings.AuthToken)
cap.AllowClientOutgoing(_settings.ClientCallApplicationSID, options)
dim token = cap.GenerateToken()
Twilio evangelist here.
The params collection that you pass into the connect function is just a dictionary of key/value pairs. Those key/values simply get passed as parameters to the Voice URL that Twilio requests when Client makes its connection to Twilio, and you can use those parameters to dynamically generate some TwiML markup. Twilio does not do anything with them.
For example, if this is a PHP application, in the Voice URL you could do something like:
<Response>
<Dial>$_REQUEST['From']</Dial>
</Response>
One note of caution, Twilio already adds a parameter called from (which in the case of Client will be the client identifier set when you made your capability token) to the parameters sent to the Voice URL, so you might want to choose a different key name for your dictionary entry. I normally use a name like target for the key that holds the number that I want to dial.
Hope that helps.
To get the call sid, you can get it in connect event.
Please note that I am using Twilio JS v1.9.7
device.on('connect', function (conn) {
log('Successfully established call!');
//Get the CallSid for this call
callUUID = conn.outboundConnectionId;
console.log('CallSid: ' + callUUID);
});
I'd like to pass the user information from my user registration form to a web-component. Is this possible. Something like below:
app.addRequestHandler(
(req) => req.method == 'POST' && req.path == '/newUser',
(req, res) {
...
input.onClosed = () {
...
var user = new User();
user.name = params["user[first_name]"];
user.email = params["user[email]"];
res.outputStream.writeString('<x-MyWebComponent data-value="User: user"></x-MyWebComponent>');
res.outputStream.close();
};
...
}
);
Thanks for the question!
Sending data to a web component is no different than sending data to any web page. That is, your web page or component can open an AJAX (aka XMLHttpRequest aka HttpRequest) request to the server and get JSON data back.
Because web components need to be compiled into vanilla JavaScript and HTML (until the features land in the browser... coming soon!) you can't send back raw HTML that contains your custom element (like you have in your example).
Basically, create a handler on your server that creates the User in the database and sends a JSON response containing the user details. Your web page (or component) will receive the JSON response and can then bind that, via components, to the page.
There's a lot of moving parts here so I think we need an end-to-end sample. In the meantime, you could do something like this:
var user = new User();
user.name = params["user[first_name]"];
user.email = params["user[email]"];
res.headers.add('Content-Type', 'application/json');
res.outputStream.writeString(user.toJson());
res.outputStream.close();
This assumes you added a String toJson(); method to User class.
I am trying to connect many socket.io clients for different URLs in Node.js like so :
app.get('/:id',function(req,res){
io.of('/'+id).on('connection',function(socket){
socket.emit('hello');
})
});
This works however there is a problem :
When a browser refreshs the page http://localhost:3000/xyz for example, the event socket.emit gets fired two times.
If someone accesses the page http://localhost:3000/xyz 10 times, then the event fires 10 times.
This is not good because everytime the user visits that page, the socket events will be fired n+1 times.
What should be done so that I can register sockets to different URLs and at the same time not have this anomaly .
Another thing :
If I do this :
var sock;
io.of('/'+xyz).on('connection',function(socket){
sock=socket;
})
app.get('/:id',function(req,res){
sock.emit('hello');
})
If I use the above code then the socket doesn't get saved succesfully to the sock variable in time. What that means is , I have to do a setInterval of about 1000 .. so that the
sock=socket
line gets fired.
Please help me.
Because with this, in each request to http://localhost:3000/id, you register a new handler, you should be doing that once, not at every request.
app.get('/:id',function(req,res){
io.of('/'+id).on('connection',function(socket){
socket.emit('hello');
})
});
I use below approach to achieve this goal:
client side:
var socket = io.connect('http://localhost:8183/?clientId='+clientId,{"force new connection":true});
server side:
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(socket) {
console.log("url"+socket.handshake.url);
clientId=socket.handshake.query.clientId;
console.log("connected clientId:"+clientId);
});
reference:https://github.com/LearnBoost/socket.io/wiki/Authorizing#global-authorization