I have written a bot in NODE JS which will continuously monitor the twitch users and when some user will get online, then it will send message to "group me" app that user is online.
It is working fine. My problem is that sometimes it send message that user is online but actually user is offline.
Any idea on how to solve this?
See screenshot
var users = ["Statelinejay", "Ad_914", "Houssam06" ];
var messages = ["notsent", "notsent", "notsent" ];
var Client = require('node-rest-client').Client;
var client = new Client();
var express = require('express');
var request = require('request');
var app = express();
app.set('port', (process.env.PORT || 5000));
function theCall(index) {
console.log('Processing ' + users[index]);
client.get('https://api.twitch.tv/kraken/streams/' + users[index] + '?client_id=xxxxxxxxxxxxxxxxxxxxxxx', function (data, response) {
if (data["stream"] === null) {
messages[index] = 'notsent';
console.log(users[index] + 'is offline');
} else
//// Start of sending message
var myJSONObject = {
"bot_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"text": " https://www.twitch.tv/" + users[index]
};
if (messages[index] === 'notsent') {
request.post({
url: " https://api.groupme.com/v3/bots/post",
method: "POST",
json: true, // <--Very important!!!
body: myJSONObject
},
function (error, response, body) {
console.log(error);
}
);
messages[index] = 'sent';
console.log('message sent');
}
//// End of sending message
console.log(users[index] + ' is online') ;
}
theCall((++index % users.length));
});
}
app.listen(app.get('port'), function() {
console.log('Node app is running on port', app.get('port'));
theCall(0);
});
Are you sure that data["stream"] is always null whenever the streamer is offline? Can it be undefined? Can you please try
if (!data["stream"])
instead of
if (data["stream"] === null)
?
If this doesn't work, can you console.log your data and response variables for the false online streamer, and update the question?
Related
Recently i created a node js and webrtc project that use http. But I notified that webrtc only works with https. So how can i transfer this http based node js file to https based one? Please help me.
Really i have no idea how to do this. So please help me to make it. What is need is to run this file over https. Not in http. As you can see, the below code use just http. As the webrtc need to run over https, i just need to make this file to run over https too
var hat = require('hat')
var http = require('http')
var nodeStatic = require('node-static')
var ws = require('ws')
var PORT = process.argv[2] || 4000
var httpServer = http.createServer()
var staticServer = new nodeStatic.Server('./public')
var wsServer = new ws.Server({ server: httpServer })
var peers = {}
var waitingId = null
var count = 0
httpServer.on('request', function (req, res) {
req.addListener('end', function () {
staticServer.serve(req, res)
}).resume()
})
wsServer.on('connection', onconnection)
function onconnection (peer) {
var send = peer.send
peer.send = function () {
try {
send.apply(peer, arguments)
} catch (err) {}
}
peer.id = hat()
peers[peer.id] = peer
peer.on('close', onclose.bind(peer))
peer.on('error', onclose.bind(peer))
peer.on('message', onmessage.bind(peer))
count += 1
broadcast(JSON.stringify({ type: 'count', data: count }))
}
function onclose () {
peers[this.id] = null
if (this.id === waitingId) {
waitingId = null
}
if (this.peerId) {
var peer = peers[this.peerId]
peer.peerId = null
peer.send(JSON.stringify({ type: 'end' }), onsend)
}
count -= 1
broadcast(JSON.stringify({ type: 'count', data: count }))
}
function onmessage (data) {
console.log('[' + this.id + ' receive] ' + data + '\n')
try {
var message = JSON.parse(data)
} catch (err) {
console.error('Discarding non-JSON message: ' + err)
return
}
if (message.type === 'peer') {
if (waitingId && waitingId !== this.id) {
var peer = peers[waitingId]
this.peerId = peer.id
peer.peerId = this.id
this.send(JSON.stringify({
type: 'peer',
data: {
initiator: true
}
}), onsend)
peer.send(JSON.stringify({
type: 'peer'
}), onsend)
waitingId = null
} else {
waitingId = this.id
}
} else if (message.type === 'signal') {
if (!this.peerId) return console.error('unexpected `signal` message')
var peer = peers[this.peerId]
peer.send(JSON.stringify({ type: 'signal', data: message.data }))
} else if (message.type === 'end') {
if (!this.peerId) return console.error('unexpected `end` message')
var peer = peers[this.peerId]
peer.peerId = null
this.peerId = null
peer.send(JSON.stringify({ type: 'end' }), onsend)
} else {
console.error('unknown message `type` ' + message.type)
}
}
function onsend (err) {
if (err) console.error(err.stack || err.message || err)
}
function broadcast (message) {
for (var id in peers) {
var peer = peers[id]
if (peer) {
peer.send(message)
}
}
}
httpServer.listen(PORT, function () {
console.log('Listening on port ' + PORT)
})
HTTPS requires a security certificate which matches the domain name. Both domain name and certificate for production usage can be purchased online and will have an expiration date and will need renewals.
The certificate comprises of two files cert.pem and key.pem.
For local development a self-signed untrusted certificate can be generated for localhost domain (via openssl command-line tool).
openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'
The original code can be updated from 'http' to 'https' and those two files need to be loaded and passed as options to https.createServer()
I also had to update the call to 'node-static' as it was not serving local files.
var hat = require('hat')
var https = require('https') // updated
const fs = require('fs');
var nodeStatic = require('node-static')
var ws = require('ws')
var PORT = process.argv[2] || 4000
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
var httpServer = https.createServer(options) // updated
var staticServer = new nodeStatic.Server('./public')
var wsServer = new ws.Server({ server: httpServer })
var peers = {}
var waitingId = null
var count = 0
httpServer.on('request', function (req, res) { // updated
staticServer.serve(req, res)
})
// the rest of the original code
// httpServer variable is now an HTTPS server instance
httpServer.listen(PORT, function () {
console.log('Listening on port ' + PORT)
})
Starting the server and visiting https://localhost:4000 will be prompted with untrusted certificate warning which you have to acknowledge.
Register a site domain for under $15.00/year (for .com) including whois protection.
Create a free Cloudflare account and setup your new Domain Name, configure the DNS to proxy and handle your IPs you plan to host with under such domain.
Generate through cloudflare SSL Key, and utilize the certs in your projects. As long as the DNS routes to say your home IP or server IP it'll maintain that HTTPS.
For testing purposes you can use fake key/cert but this will always show insecure until you follow the above steps.
In NodeJS to engage the HTTPS:
const HTTPS = require('https');
const FILESYSTEM = require('fs');
// CREATE HTTP APP HERE THEN CREATE HTTPS SERVER FOR THE APP
HTTPS.createServer({ key: FILESYSTEM.readFileSync(__dirname + '/certs/key.pem'), cert: FILESYSTEM.readFileSync(__dirname + '/certs/cert.pem') }, app).listen(443);
I've created a bot while using MS Bot Framework and deploy it to Azure.
After the deployment, the bot is returning HTTP 500 error when we're trying the '/api/messages' URL.
Here my app.js :
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const dialog_service_1 = require("./services/dialog-service");
const authentification_service_1 = require("./services/authentification-service");
const restify = require("restify");
const bot_service_1 = require("./services/bot-service");
const utilities_service_1 = require("./services/utilities-service");
require("dotenv").config();
let botService = new bot_service_1.BotService();
// let utilitiesService = new UtilitiesService(__dirname + '/assets/labels.json');
let dialogService = new dialog_service_1.DialogService(bot_service_1.BotService.bot);
let port = process.env.port || process.env.PORT || '3978';
const server = restify.createServer({
formatters: {
'text/html': function (req, res, body) {
return body.toString();
}
}
});
console.log('server created');
// change done for restify 5.X+ (mapParams should be specified # true)
server.use(restify.plugins.bodyParser({
mapParams: true
}));
console.log('trying to listening..');
server.listen(port, () => {
console.log('%s server listening to %s', server.name, server.url);
});
console.log('listening');
console.log('mounting styles folder...');
//add the build/styles folder to the restify server
server.get(/\/styles\/?.*/, restify.plugins.serveStatic({
directory: __dirname + '/assets'
}));
console.log('mounted');
console.log('mounting api/messages endpoint...');
// entry point of your bot
server.post("/api/messages", bot_service_1.BotService.bot.connector("*").listen());
console.log('mounted...');
console.log('mounting api/oauthcallback endpoint...');
//callback handling
server.post("/api/oauthcallback", (req, res, next) => {
let authorizationCode = req.params.code;
if (authorizationCode !== undefined) {
authentification_service_1.AuthentificationService.acquireTokenWithAuthorizationCode(authorizationCode).then((response) => {
let state = req.params.state;
if (state) {
let address = JSON.parse(state);
response.state = state;
bot_service_1.BotService.bot.beginDialog(address, "/oauth-success", response);
}
utilities_service_1.UtilitiesService.readFile(__dirname + '/assets/html/callback.html').then(body => {
res.send(200, body, { "Content-Length": Buffer.byteLength(body).toString(), "Content-Type": "text/html" });
res.end();
});
}).catch((errorMessage) => {
var body = "<html><body>" + errorMessage + "</body></html>";
res.send(200, body, { "Content-Length": Buffer.byteLength(body).toString(), "Content-Type": "text/html" });
res.end();
});
}
else {
var body = "<html><body>" + "unable to retrieve the authentication code" + "</body></html > ";
res.send(200, body, { "Content-Length": Buffer.byteLength(body).toString(), "Content-Type": "text/html" });
res.end();
}
});
console.log('mounted');
//# sourceMappingURL=app.js.map
I've added some logs to help me, all console.log() is reached. so it seems that the server is well started...
Thanks for your help.
I noticed you aren't initiating a bot and starting a dialog. When I try your code as-is I receive a 502 error. When I introduce code for the bot the error disappears and the bot responds, as expected.
Since I don't have access to all of your files, I had to remove the associated code calls. So, I can't say if the error you are receiving is related to any of that code.
I used connector.listen() in the server.post for 'api/messages'. Defining connector, as shown below, follows the basic setup found in the documentation for building a bot using Node.
Hope this helps.
Steve.
'use string';
const builder = require('botbuilder');
const restify = require('restify');
require('dotenv').config();
let port = process.env.port || process.env.PORT || '3978';
let server = restify.createServer({
formatters: {
'text/html': function (req, res, body) {
return body.toString();
}
}
});
// change done for restify 5.X+ (mapParams should be specified # true)
server.use(restify.plugins.bodyParser({
mapParams: true
}));
server.listen(port, () => {
console.log('%s server listening to %s', server.name, server.url);
});
// entry point of your bot
let connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword,
openIdMetadata: process.env.BotOpenIdMetadata
});
server.post('/api/messages', connector.listen());
//callback handling
server.post('/api/oauthcallback', (req, res, next) => {
var authorizationCode = req.params.code;
if (authorizationCode !== undefined) {
console.log('authorization code provided');
}
else {
console.log('authorization code not provided');
}
});
// inMemoryStorage should only be used for testing. It is not stable for a production environment
let inMemoryStorage = new builder.MemoryBotStorage();
let bot = new builder.UniversalBot(connector).set('storage', inMemoryStorage);
bot.dialog('/', [
function (session) {
session.send('Hi');
}
]);
I'll try to make this as simple as possible so i'm not having to post a ton of code. Heres what my app does right now:
User uploads an audio file from the browser
That file is processed on my server, this process takes some time and has about 8 or so steps to complete.
Once everything is finished, the user gets feedback in the browser that the process is complete.
What I want to add to this, is after every step in the process that is completed, send some data back to the server. For example: "Your file is uploaded", "Meta data processed", "image extracted" etc etc so the user gets incremental feedback about what is happening and I believe Server Sent Events can help me do this.
Currently, the file is POSTed to the server with app.post('/api/track', upload.single('track'), audio.process). audio.process is where all the magic happens and sends the data back to the browser with res.send(). Pretty typical.
While trying to get the events working, I have implemented this function
app.get('/stream', function(req, res) {
res.sseSetup()
for (var i = 0; i < 5; i++) {
res.sseSend({count: i})
}
})
and when the user uploads a file from the server I just make a call to this route and register all the necessary events with this function on the client side:
progress : () => {
if (!!window.EventSource) {
const source = new EventSource('/stream')
source.addEventListener('message', function(e) {
let data = JSON.parse(e.data)
console.log(e);
}, false)
source.addEventListener('open', function(e) {
console.log("Connected to /stream");
}, false)
source.addEventListener('error', function(e) {
if (e.target.readyState == EventSource.CLOSED) {
console.log("Disconnected from /stream");
} else if (e.target.readyState == EventSource.CONNECTING) {
console.log('Connecting to /stream');
}
}, false)
} else {
console.log("Your browser doesn't support SSE")
}
}
this works as expected, when I upload a track, i get a stream of events counting from 0-4. So thats great!
My Problem/Question: How do i send relevant messages from the audio.process route, to the /stream route so that the messages can be related to whats happening. audio.process has to be a POST, and /stream has to be a GET with the header 'Content-Type': 'text/event-stream'. It seems kind of weird to make GET requests from within audio.process but is this the best way?
Any and all advice/tips are appreciated! Let me know if you need any more info.
New Answer:
Just use socket.io, it's so much easier and better!
https://www.npmjs.com/package/socket.io#in-conjunction-with-express
basic setup:
const express = require('express');
const PORT = process.env.PORT || 5000;
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
// listen to socket connections
io.on('connection', function(socket){
// get that socket and listen to events
socket.on('chat message', function(msg){
// emit data from the server
io.emit('chat message', msg);
});
});
// Tip: add the `io` reference to the request object through a middleware like so:
app.use(function(request, response, next){
request.io = io;
next();
});
server.listen(PORT);
console.log(`Listening on port ${PORT}...`);
and in any route handler, you can use socket.io:
app.post('/post/:post_id/like/:user_id', function likePost(request, response) {
//...
request.io.emit('action', 'user liked your post');
})
client side:
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function () {
var socket = io();
$('form').submit(function(e){
e.preventDefault(); // prevents page reloading
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
});
</script>
full example: https://socket.io/get-started/chat/
Original Answer
Someone (user: https://stackoverflow.com/users/451634/benny-neugebauer | from this article: addEventListener on custom object) literally gave me a hint on how to implement this without any other package except express! I have it working!
First, import Node's EventEmitter:
const EventEmitter = require('events');
Then create an instance:
const Stream = new EventEmitter();
Then create a GET route for event streaming:
app.get('/stream', function(request, response){
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
Stream.on("push", function(event, data) {
response.write("event: " + String(event) + "\n" + "data: " + JSON.stringify(data) + "\n\n");
});
});
In this GET route, you are writing back that the request is 200 OK, content-type is text/event-stream, no cache, and to keep-alive.
You are also going to call the .on method of your EventEmitter instance, which takes 2 parameters: a string of the event to listen for and a function to handle that event(that function can take as much params as it is given)
Now.... all you have to do to send a server event is to call the .emit method of your EventEmitter instance:
Stream.emit("push", "test", { msg: "admit one" });
The first parameter is a string of the event you want to trigger (make sure that it is the same as the one in the GET route). Every subsequent parameter to the .emit method will be passed to the listener's callback!
That is it!
Since your instance was defined in a scope above your route definitions, you can call the .emit method from any other route:
app.get('/', function(request, response){
Stream.emit("push", "test", { msg: "admit one" });
response.render("welcome.html", {});
});
Thanks to how JavaScript scoping works, you can even pass that EventEmitter instance around to other function, even from other modules:
const someModule = require('./someModule');
app.get('/', function(request, response){
someModule.someMethod(request, Stream)
.then(obj => { return response.json({}) });
});
In someModule:
function someMethod(request, Stream) {
return new Promise((resolve, reject) => {
Stream.emit("push", "test", { data: 'some data' });
return resolve();
})
}
That easy! No other package needed!
Here is a link to Node's EventEmitter Class: https://nodejs.org/api/events.html#events_class_eventemitter
My example:
const EventEmitter = require('events');
const express = require('express');
const app = express();
const Stream = new EventEmitter(); // my event emitter instance
app.get('/stream', function(request, response){
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
Stream.on("push", function(event, data) {
response.write("event: " + String(event) + "\n" + "data: " + JSON.stringify(data) + "\n\n");
});
});
setInterval(function(){
Stream.emit("push", "test", { msg: "admit one" });
}, 10000)
UPDATE:
i created a module/file that is easier to use and doesn't cause memory leaks!
const Stream = function() {
var self = this;
// object literal of connections; IP addresses as the key
self.connections = {};
self.enable = function() {
return function(req, res, next) {
res.sseSetup = function() {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
})
}
res.sseSend = function(id, event, data) {
var stream = "id: " + String(id) + "\n" +
"event: " + String(event) + "\n" +
"data: " + JSON.stringify(data) +
"\n\n";
// console.log(id, event, data, stream);
res.write(stream);
}
next()
}
}
self.add = function(request, response) {
response.sseSetup();
var ip = String(request.ip);
self.connections[ip] = response;
}.bind(self);
self.push_sse = function(id, type, obj) {
Object.keys(self.connections).forEach(function(key){
self.connections[key].sseSend(id, type, obj);
});
}.bind(self);
}
/*
Usage:
---
const express = require('express');
const Stream = require('./express-eventstream');
const app = express();
const stream = new Stream();
app.use(stream.enable());
app.get('/stream', function(request, response) {
stream.add(request, response);
stream.push_sse(1, "opened", { msg: 'connection opened!' });
});
app.get('/test_route', function(request, response){
stream.push_sse(2, "new_event", { event: true });
return response.json({ msg: 'admit one' });
});
*/
module.exports = Stream;
Script located here - https://github.com/ryanwaite28/script-store/blob/master/js/express-eventstream.js
I'm currently learning JavaScript / NodeJS / electron, and I want to build a small presenter-app to remotely control powerpoint-presentations.
I've setup a server using electron like this:
const electron = require('electron');
const robot = require("robotjs");
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const {
app,
BrowserWindow
} = electron;
var mainWin = null;
var contentString;
app.on('ready', function() {
mainWin = new BrowserWindow({
width: 800,
height: 600
});
contentString = "";
// Remove Menu-Bar
mainWin.setMenu(null);
const port = 3000;
var app = express();
app.use(cors());
app.post('/remote/forward', function(request, response, next) {
var ip = getRemoteIP(request);
log(mainWin, "presenter - forward");
robot.keyTap("right");
});
app.post('/remote/backward', function(request, response, next) {
var ip = getRemoteIP(request);
log(mainWin, "presenter - backward");
robot.keyTap("left");
});
app.listen(port, function() {
log(mainWin, 'server listening on port ' + port);
});
});
function log(mainWin, text) {
contentString += getFormattedDate() + " " + text;
contentString += "<br />";
mainWin.loadURL("data:text/html;charset=utf-8," + encodeURI(contentString));
}
I call these with two js-functions:
function sendForwardRequest() {
$.ajax({
type: 'POST',
data: {
blob: {action:"forward"}
},
contentType: "application/json",
dataType: 'json',
url: 'http://192.168.2.110:3000/remote/forward',
success: function(data) {
console.log('success');
},
error: function(error) {
console.log("some error in fetching the notifications");
}
});
}
function sendBackwardRequest() {
$.ajax({
type: 'POST',
data: {
blob: {action:"backward"}
},
contentType: "application/json",
dataType: 'json',
url: 'http://192.168.2.110:3000/remote/backward',
success: function(data) {
console.log('success');
},
error: function(error) {
console.log("some error in fetching the notifications");
}
});
}
I'm sure that this solution is quite miserble, as I said, I'm currently learning this. My question now is: This works for exactly seven times. After that, I have to reload my clients browser. How can I fix this? Also, what would be a better solution for the requests? I'd like to have only one app.post()-method and use the given post-parameters instead. Last question: What could be a nicer method for the logging? I'd like to append content to my window instead of having to reload the whole string each time.
Thank you very much!
this is the minified version of your code. try and see if it still only fires 7 times
/* Server side */
// instead of two app.post functions, use this one
app.get('/remote/:key', function(request, response, next) {
var ip = getRemoteIP(request);
log(mainWin, "presenter - " + request.params.key);
robot.keyTap(request.params.key);
response.send(request.params.key + ' key pressed');
});
/* Client Side */
function sendKey(key) {
return $.get('http://192.168.2.110:3000/remote/' + key)
}
// to send right key
sendKey('right').done(function(response) { /*success*/ }).fail(function(error) { /*error*/ });
I'm new to socket.io, and I'm doing a simple API with NodeJS (express 4). I'm developing an action that is similar to the old "poke" action at facebook. A user send a poke to other user, and this one gets a notification on real time (this is the reason why I am using socket.io).
This is the code:
app.js
var port = 3000;
var app = module.exports = express();
var server = require('http').Server(app);
...
server.listen(port);
require('./config/socket-io')(app, server, secret);
socket-io.js
module.exports = function(app, server, secret) {
var clients = {};
console.log("initiating sockets...");
var sio = require('socket.io').listen(server, {'log level': 2});
sio.on('connection', function (socket) {
console.log("...new connection: "+socket.client.id);
clients[socket.id] = socket;
socket.emit('identification', { data : socket.client.id });
socket.on('newShoutOut', function(data) {
var receptor = data.idTo;
var emiter = socket.client.id;
console.log("...new shout out from " +emiter+ " to "+receptor);
sio.sockets.sockets[receptor].emit({ data : data.data, from : emiter });
});
socket.on('disconnect', function() {
console.log("..."+socket.client.id + " disconnected");
});
});
};
Here you can differentiate three states:
Connection: The server detects all the clients connection to the host:port. After that, the server sends to each client his ID. This works fine.
Send message: One client sends a notification to other client. For now, the server receives the notification from one client, but the "receiver" doesn't receive anything.
Disconnection: Doesn't matter in this case.
My question is, what is the way to send a message to a client directly knowing the ID? What I am doing wrong? I tried so many options to send a message directly to a specific client ID but didn't work...
EDIT
Frontend
var socket = io('http://localhost:3000');
var id = "";
socket.on('connection', function (data) {
console.log("connected!");
console.log(data);
});
socket.on('identification', function(data) {
id = data.data;
$("#socket_info h1").html("ID: "+id);
});
socket.on('newShoutOut', function(data) {
console.log("newShoutOut received!");
});
Ok, so I assume the shoutout is coming from a user? You will need to create the event on the clientside, such as:
var button = $('#button');
button.on('click', function() {
var msg = 'message',
userID = '123'; //get the ID who they are messaging
socket.emit('sendShoutOut', {msg: msg, id: userID});
});
Then you will need to receive that response on the server, and reply to the user in that function:
socket.on('sendShoutOut', function( data ) {
socket.sockets.sockets[data.id].emit('sendPrivateMsg', { data : data.msg, from : emiter });
});
Lastly, the reciever must be notified, so you will need to handle the response on the client:
socket.on('sendPrivateMsg', function( data ) {
alert(data);
});
Hope this helps.