NodeJS + Socket.IO : Problem accessing room variables - javascript

I am new to the technologies NodeJS and Socket.io and i am facing a problem with the variables stored in io.sockets.adapter.rooms.
I have my app.js :
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('message', function (room) {
socket.join(room);
console.log(io.sockets.adapter.rooms);
io.sockets.adapter.rooms[room].variable = "This is a test";
})
});
http.listen(3000, () => {
console.log('listening on *:3000');
});
The index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<input type="button" value="Press me" id="test">
<body>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
document.getElementById('test').addEventListener('click', function () {
socket.emit('message', 'room1');
})
</script>
</body>
</html>
And the server return this error :
TypeError: Cannot set property 'variable' of undefined
It means that "io.sockets.adapter.rooms[room].variable" is undefined but i do not understant how i can define it otherwise.
By the way I am using NodeJS 12.8.3 and express 4.17.1
Maybe I need a specific module ?
Thanks a lot to everyone who will take the time to answer this

In their changelog for v3.0.0 they mention the following
Socket#rooms is now a Set instead of an object
Which means it's now a Map object instead of a normal js object
So with v3.0.3 you get the following output when logging the rooms, which you can't access by key like a normal js object, and .set() has to be used instead.
Map(2) {
'II-OqYrpZYtv2bKrAAAD' => Set(1) { 'II-OqYrpZYtv2bKrAAAD' },
'room1' => Set(1) { 'II-OqYrpZYtv2bKrAAAD' }
}
Although I have no idea how to add an additional variable to the room. Since if you run the following code:
io.sockets.adapter.rooms.set(room, {
variable: "Test variable"
})
it replaces the socket id with the new variable. I can't find anything on how they want us to store variables using this new structure.
Map(2) {
'Xv6vbxuOgDuqp6dmAAAB' => Set(1) { 'Xv6vbxuOgDuqp6dmAAAB' },
'room1' => { variable: 'Test variable' }
}
When logging the rooms with v2.0.3 you get a normal object output which you can access with [key].
{
'tVnQpZyKDglSf4J-AAAA': Room { sockets: { 'tVnQpZyKDglSf4J-AAAA': true }, length: 1 },
room1: Room { sockets: { 'tVnQpZyKDglSf4J-AAAA': true }, length: 1 }
}
So if you revert socket.io back to v2.0.3 (the latest 2.x.x version) your code works fine.
You can revert socket.io by running the following
npm i socket.io#2.0.3
Or by changing the version number in package.json and then running
npm install

Related

Picking random image from array 60+ choices javascript

I understand this question has tons of answers already, but I am trying to figure out how to do this in the most efficient way. I have a website that sends an image with a button click to a phone number, but I want to choose between 60 or so photos and manually entering all of these image locations into an array does not seem ideal.
Here is my js file that performs the email action, this is all hosted on a free hosting service.
// server.js
const express = require("express")
const app = express()
app.use(express.static("public"))
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html")
/* this sends the "index.html" file when people go to app.glitch.me/ */
})
app.get("/send", (req, res) => {
// where node app starts
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.USER,
pass: process.env.PASS,
}
});
var mailOptions = {
from: process.env.USER,
to: process.env.RECIP,
subject: "As you requested",
text: '',
attachments: [
{
/*image location*/
path: 'https://post.healthline.com/wp-content/uploads/sites/3/2020/02/322868_1100-1100x628.jpg',
}
]
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
res.redirect("/sent.html") // after sending the email, redirect back to "index.html" at app.glitch.me/
})
app.listen(3000); //open for traffic
Here is my HTMl if its even relevant to my question
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello!</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- import the webpage's stylesheet -->
<link rel="stylesheet" href="/style_index.css">
click me for snazzy pics<!-- script to ping --!>
<!-- import the webpage's javascript file -->
<script src="/script.js" defer></script>
</head>
<body>
<h1>hello</h1>
<p>
I made this.
</p>
</body>
</html>
Try first logging all of your images from where they are hosted. If it is not a database you can call upon then you may need to create an array of them manually. Once they are in an object, you can simply use a variable to determine which position in the array that image link should come from. I hope the below helps.
For example:
imageChoices = ["https://post.healthline.com/wp-content/uploads/sites/3/2020/02/322868_1100-1100x628.jpg", "https://post.healthline.com/wp-content/uploads/sites/3/2020/02/322868_1100-1100x628.jpg", etc.]
randomIndexChooser = Math.floor(Math.random() * 60) + 1;
var mailOptions = {
from: process.env.USER,
to: process.env.RECIP,
subject: "As you requested",
text: '',
attachments: [
{
/*image location*/
path: imageChoices[randomIndexChooser],
}
]
};
you need to make an ajax service that calls an api, the api loops through all the files that are in the specified folder and returns the list of file paths. after you get the list from the api, you append them to the wanted array in your javascript code.
I will provide you a sample in asp.net c# , you may be working on another framework, but you can benefit from the idea at least.
here is a function in an api
[HttpGet]
public List<string> GetImageFilesPaths()
{
//getfiles returns all found files' paths in a specified directory
List<string> imageFilePaths = Directory.GetFiles("folderpath", "*.png", SearchOption.AllDirectories).ToList();
}
ajax service that calls the API
$.ajax({
url:'hostname/apiname/GetImageFilesPaths'
contentType: "application/json",
dataType: 'json',
success: function(result){
//here you append the result which is the list of file path
//into your wanted array, you can also loop
result.forEach((imagePath)=>{
arrayOfImages.push(imagePath)
})
}
})

Detect when a socket io connection has been changed with ember

Im using ember with socket.io and I want a computed property that changes to if the socket io connection is connected or disconnected.
I am using ember-websockets and here is what I have tried:
socketIOService: service('socket-io'),
socketRoute: 'http://localhost:8080/',
connected: computed('socketIOService',
function()
{
console.log('changed!');
//return (this.get('socketIOService').socketFor(this.get('socketRoute').socket.connected));
}),
startConnection()
{
this.get('connected');
const socket = this.socketIOService.socketFor(this.get('socketRoute'));
socket.on('initialised', this.initialised, this);
},
So this doesnt work because im guessing the service doesnt change. I would like to be able to computer a value from the following...
this.socketIOService.socketFor(this.get('socketRoute'));
But I cant get the sockerFor property in a computed property.
Looking at readme, I think you can use 'open' and 'close' events, w/co computed properties:
startConnection()
{
const socket = this.socketIOService.socketFor(this.get('socketRoute'));
socket.on('open', () => { this.set('connected', true); });
socket.on('close', () => { this.set('connected', false); });
}

Making .local resolve to IP address AND port (mdns)

I'm using the multicast-dns node module to attempt making this work.
Looking up custom.local in the browser gives me the console message I setup, but I'm unable to see my actual server running (which is doing so at localhost:12345, where 12345 is a dynamic number). I want to be able to see my local server when visiting custom.local. Is this possible?
Here's some code:
mdns.on("query", query => {
if (query.questions[0] && query.questions[0].name === "custom.local") {
console.log(query);
mdns.respond({
answers: [
{
name: "custom.local",
type: "SRV",
data: {
port: n.get("p"), // dynamic port
weight: 0,
priority: 10,
target: ip // local IP
}
}, {
name: "custom.local",
type: "A",
data: ip,
ttl: 300
}
]
});
}
});
EDIT: I can connect to my local server just fine, that wasn't an issue.
Quoting cfreak:
You can't put port numbers in DNS. DNS is only for looking up an IP by name. For your browser to see it by the name alone you need a proxy program in front of your service or you need to run the service itself on port 80. Port numbers really shouldn't be dynamic. You should specify it in the setup of your service.
That answers my question and offers next steps. Thanks!
UPDATE: Figured out what I was trying to do. Here's some code!
FOUND A SOLUTION, WOOP WOOP!
I'm using this module, but tweaked the source a bit (only because I have dynamic ports, because I feel like it).
/* jshint undef: true, unused: true, esversion: 6, node: true */
"use strict";
//
// G E T
// P A C K A G E S
import express from "express";
import http from "http";
import local from "./server/local";
const n = express();
n.get("/", (req, res) => {
res.send("Welcome home");
});
//
// L A U N C H
const server = http.createServer(n);
server.listen(0, () => {
const port = server.address().port;
local.add(port, "custom.local");
});
Hope this helps you as well, future Internet searcher! :D
Don't let negative folks on other SE sites bring you down. :virtual fist bump:

Babel causing Uncaught TypeError in transpiled ES5 Vue component code

Working on a small Node.js project that needs to send JSON objects over sockets. I discovered that JsonSocket (https://github.com/sebastianseilund/node-json-socket) served my needs and running the simple server/client demos provided by that author works great.
I am adapting the demo Client code (https://github.com/sebastianseilund/node-json-socket#simple-clientserver-example) to a Vue.js-Babel-Browserify project framework and placing the code in a .vue component file. Changes primarily involve passing data from an HTML text field (default text included in the binding data parameter) to the listening server via a socket connection, triggered by an HTML button. But I'm not getting beyond the button trigger at this point.
What I am getting is: Uncaught TypeError: _net2.default.Socket is not a constructor when sending the data over the socket with this transpiled code: var socket = new _jsonSocket2.default(new _net2.default.Socket());
Below is the original .vue code:
import net from 'net'
import JsonSocket from 'json-socket'
export default {
data () {
return {
header: 'Login',
messageStuff: '{ "cmd": "send", "what": { "this": "that" } }'
}
},
methods: {
submitMessage() {
var stuff = JSON.parse(this.messageStuff)
var port = 8069
var host = '192.168.11.5'
var socket = new JsonSocket(new net.Socket()) <-- *** source of the culprit! ***
socket.connect(port, host)
socket.on('connect', function () {
socket.sendMessage(stuff)
socket.on('message', function (message) {
console.log('Server replies...')
console.dir(message)
})
})
}
}
}
And here is the transpiled code of the relevant section of the script:
var _net = require('net');
var _net2 = _interopRequireDefault(_net);
var _jsonSocket = require('json-socket');
var _jsonSocket2 = _interopRequireDefault(_jsonSocket);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = {
data: function data() {
return {
header: 'Login',
messageStuff: '{ "cmd": "send", "what": { "this": "that" } }'
};
},
methods: {
submitMessage: function submitMessage() {
var stuff = JSON.parse(this.messageStuff);
var port = 8069;
var host = '192.168.11.5';
var socket = new _jsonSocket2.default(new _net2.default.Socket()); <-- *** the culprit! ***
socket.connect(port, host);
socket.on('connect', function () {
socket.sendMessage(stuff);
socket.on('message', function (message) {
console.log('Server says...');
console.dir(message);
});
});
}
}
};
})()
Not sure why Babel mangled the line (where the <--- mark is in the code in both windows).
Somehow I feel that this is related to Unexpected "Uncaught TypeError: XXX is not a constructor" errors with Babel and ES6 but in that case the solution revolved around adding access the default property in a require statement. I'm not sure how I could accomplish the same with import inside the vue component.
Any prodding in the right direction would be appreciated.

Socket io get is undefined

I'm using Sails JS and i tried to use socket IO librarie.
As it is explained in Sails JS documentation : http://sailsjs.org/#!documentation/sockets
I create a controller with this code:
index: function(req, res) {
var param = req.param('msg');
console.log("PARAM : ", param); // For display the message in terminal
// Send a JSON response
return res.json({
success: true,
message: param
});
},
And in my template (i use swig JS template engine) :
For include socket io library :
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
And in JavaScript code :
var socket = io.connect('http://localhost:1337');
socket.get('/echo', {
message: 'hi there!'
}, function(response) {
console.log ('response : ', response);
});
And i've this error : undefined is not a function on socket.get('/echo' ...
Thanks for your help !
I believe you need to add <script type="text/javascript" src="/app/sails.io.js"></script> as well. Since Socket.prototype.get is defined in this file.

Categories