I'm trying to send a push notification through Firebase/Node JS. I currently have this code running on Heroku.
var Firebase = require('firebase');
var request = require('request');
var express = require('express');
var FCM = require('fcm-node');
var app = express();
app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.get('/push', function(request, response) {
response.send("running");
});
app.listen(app.get('port'), function() {
console.log('Node app is running on port', app.get('port'));
});
// Planet Firebase API Key
var API_KEY = "AIz..."; // found at Project Settings > General > Web API Key
var fcm = new FCM(API_KEY);
var config = {
apiKey: "AIz...",
authDomain: "XXX.firebaseapp.com", // Authentication > Sign-In Method > OAuth Redirect Domains
databaseURL: "https://XXX.firebaseio.com/", // Database > Url at the top
storageBucket: "gs://XXX.appspot.com", // Storage > Url at the top
};
var firebase = Firebase.initializeApp(config);
var ref = firebase.database().ref();
function listenForNotificationRequests() {
var requests = ref.child('notifications');
requests.on('child_changed', function(requestSnapshot) {
var objectAdded = requestSnapshot.val();
var uid = receiver["uid"]
var notificationMessage = objectAdded["message"]
sendNotificationToUser(uid, notificationMessage);
}, function(error) {
console.error(error);
});
};
function sendNotificationToUser(receiverID, notificationMessage) {
var message = {
to: '/notifications/'+receiverID,
notification: {
title: "App Title",
body: notificationMessage,
badge: 1
}
};
fcm.send(message, function(err, response){
if (response) {
console.log("Successfully sent with response: ", response);
} else {
console.log("Something has gone wrong! Error: " + err);
}
});
}
// start listening
listenForNotificationRequests();
Whenever fcm.send() is called, I get back:
Something has gone wrong! Error: NotAuthorizedError
This leads be to believe Firebase isn't initializing correctly, but I've checked the links and keys multiple times. What have I done incorrectly?
In your code, I noticed this part (specially the comment):
// Planet Firebase API Key
var API_KEY = "AIz..."; // found at Project Settings > General > Web API Key
var fcm = new FCM(API_KEY);
You're probably receiving an 401 - Authentication Error.
When using FCM, you should always use the Server Key and not the Web API Key. This is also visible in the Firebase Console:
Project Settings > Cloud Messaging > Server Key.
See my answer here for an idea about the different keys.
Related
I am working on a speech-to-text web app using the IBM Watson Speech to text API. The API is fetched on the click of a button. But whenever I click the button. I get the above-mentioned error. I Have stored my API key and URL in a .env file.
I tried a lot but keep on getting this error. Please Help me out as I am new to all this.
I got server.js from the Watson Github Repo
Server.js
'use strict';
/* eslint-env node, es6 */
const env = require('dotenv');
env.config();
const express = require('express');
const app = express();
const AuthorizationV1 = require('watson-developer-cloud/authorization/v1');
const SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
const TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');
const vcapServices = require('vcap_services');
const cors = require('cors');
// allows environment properties to be set in a file named .env
// on bluemix, enable rate-limiting and force https
if (process.env.VCAP_SERVICES) {
// enable rate-limiting
const RateLimit = require('express-rate-limit');
app.enable('trust proxy'); // required to work properly behind Bluemix's reverse proxy
const limiter = new RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
delayMs: 0 // disable delaying - full speed until the max limit is reached
});
// apply to /api/*
app.use('/api/', limiter);
// force https - microphone access requires https in Chrome and possibly other browsers
// (*.mybluemix.net domains all have built-in https support)
const secure = require('express-secure-only');
app.use(secure());
}
app.use(express.static(__dirname + '/static'));
app.use(cors())
// token endpoints
// **Warning**: these endpoints should probably be guarded with additional authentication & authorization for production use
// speech to text token endpoint
var sttAuthService = new AuthorizationV1(
Object.assign(
{
iam_apikey: process.env.SPEECH_TO_TEXT_IAM_APIKEY, // if using an RC service
url: process.env.SPEECH_TO_TEXT_URL ? process.env.SPEECH_TO_TEXT_URL : SpeechToTextV1.URL
},
vcapServices.getCredentials('speech_to_text') // pulls credentials from environment in bluemix, otherwise returns {}
)
);
app.use('/api/speech-to-text/token', function(req, res) {
sttAuthService.getToken(function(err, token) {
if (err) {
console.log('Error retrieving token: ', err);
res.status(500).send('Error retrieving token');
return;
}
res.send(token);
});
});
const port = process.env.PORT || process.env.VCAP_APP_PORT || 3002;
app.listen(port, function() {
console.log('Example IBM Watson Speech JS SDK client app & token server live at http://localhost:%s/', port);
});
// Chrome requires https to access the user's microphone unless it's a localhost url so
// this sets up a basic server on port 3001 using an included self-signed certificate
// note: this is not suitable for production use
// however bluemix automatically adds https support at https://<myapp>.mybluemix.net
if (!process.env.VCAP_SERVICES) {
const fs = require('fs');
const https = require('https');
const HTTPS_PORT = 3001;
const options = {
key: fs.readFileSync(__dirname + '/keys/localhost.pem'),
cert: fs.readFileSync(__dirname + '/keys/localhost.cert')
};
https.createServer(options, app).listen(HTTPS_PORT, function() {
console.log('Secure server live at https://localhost:%s/', HTTPS_PORT);
});
}
App.js
import React, {Component} from 'react';
import 'tachyons';
//import WatsonSpeech from 'ibm-watson';
var recognizeMic = require('watson-speech/speech-to-text/recognize-microphone');
class App extends Component {
onListenClick = () => {
fetch('http://localhost:3002/api/speech-to-text/token')
.then(function(response) {
return response.text();
}).then(function (token) {
var stream = recognizeMic({
token: token, // use `access_token` as the parameter name if using an RC service
objectMode: true, // send objects instead of text
extractResults: true, // convert {results: [{alternatives:[...]}], result_index: 0} to {alternatives: [...], index: 0}
format: false // optional - performs basic formatting on the results such as capitals an periods
});
stream.on('data', function(data) {
console.log('error 1')
console.log(data);
});
stream.on('error', function(err) {
console.log('error 2')
console.log(err);
});
//document.querySelector('#stop').onclick = stream.stop.bind(stream);
}).catch(function(error) {
console.log('error 3')
console.log(error);
});
}
render() {
return(
<div>
<h2 className="tc"> Hello, and welcome to Watson Speech to text api</h2>
<button onClick={this.onListenClick}>Listen to Microphone</button>
</div>
);
}
}
export default App
Since the only code you show is fetching an authorisation token then I guess that that is what is throwing the authentication failure. I am not sure how old the code you are using is, but the mechanism you are using was used when the STT service credentials are userid / password. The mechanism became unreliable when IAM keys started to be used.
Your sample is still using watson-developer-cloud, but that has been superseded by ibm-watson. As migrating the code to ibm-watson will take a lot of rework, you can continue to use watson-developer-cloud.
If do you stick with watson-developer-cloud and you want to get hold of a token, with an IAM Key then use:
AuthIAMV1 = require('ibm-cloud-sdk-core/iam-token-manager/v1'),
...
tokenService = new AuthIAMV1.IamTokenManagerV1({iamApikey : apikey});
...
tokenService.getToken((err, res) => {
if (err) {
...
} else {
token = res;
...
}
});
I'm encountering a problem with the express routes. Here's my case:
I have a node js app with the following code in app.js
var express = require('express');
var app = express();
var path = require('path');
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended: false
}));
var cfenv = require('cfenv');
// request module provides a simple way to create HTTP requests in Node.js
var request = require('request');
var routes = require('./routes')(app);
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/public/index.html'));
});
var appEnv = cfenv.getAppEnv();
// compose for mysql code
var dbcontroller = require('./controller/compose-mysql-connection');
dbcontroller.databaseconnection();
const util = require('util');
// and so is assert
const assert = require('assert');
var mysql = require('mysql');
var appEnv = cfenv.getAppEnv();
// Within the application environment (appenv) there's a services object
var services = appEnv.services;
// The services object is a map named by service so we extract the one for Compose for MySQL
var mysql_services = services["compose-for-mysql"];
// This check ensures there is a services for MySQL databases
assert(!util.isUndefined(mysql_services), "Must be bound to compose-for-mysql services");
// We now take the first bound Compose for MySQL database service and extract it's credentials object
var credentials = mysql_services[0].credentials;
var connectionString = credentials.uri;
// set up a new connection using our config details
var connection = mysql.createConnection(credentials.uri);
//reading from the database
app.get("/read_fb_info", function(request, response) {
connection.query('SELECT * FROM fb_info_table ORDER BY name ASC', function (err, result) {
if (err) {
console.log(err);
response.status(500).send(err);
} else {
console.log(result);
response.send(result);
}
});
});
app.use(express.static(__dirname + '/public'));
// start server on the specified port and binding host
app.listen(appEnv.port, '0.0.0.0', function() {
// print a message when the server starts listening
console.log("server starting on " + appEnv.url);
});
Then, in the routes folder I have a file with other two routes I use into the application.
Once the index page is loaded I have two button:
<body>
<div class="container">
<h1>External API Usage</h1>
<h3>LinkedIn</h3>
<a href='/info/linkedin'>
<img src="/images/LinkedIn_image.png" class="img-rounded" alt="LinkedIn" width="150" height="150">
</a>
<h3>Facebook</h3>
<a href='/info/facebook'>
<img src="/images/Facebook_image.png" class="img-rounded" alt="Facebook" width="150" height="150">
</a>
</div>
To handle routes I created an index.js file in the routes folder which includes the following:
retrieveFacebookUserInfo = function() {
var deferred = Q.defer();
var propertiesObject_FB = { id:'id', name:'name', access_token:'access_token' };
request({url:'https://graph.facebook.com/', qs:propertiesObject_FB}, function(err, response, body) {
if(err) {
deferred.resolve(null);
}
else {
var fb_json = JSON.parse(body);
console.log("Get response: " + response.statusCode);
console.log(fb_json);
//storing information to db
dbcontroller.writingtodb();
deferred.resolve(fb_json);
}
});
return deferred.promise;
};
app.get('/info/facebook', function(req, res){
retrieveFacebookUserInfo().then(function(result){
res.render('facebook.ejs', {
title : 'Facebook information',
fb_obj: result
});
});
});
app.get('/info/linkedin', function(req, res){
retrieveLinkedInUserInfo().then(function(result){
res.render('linkedin.ejs', {
title : 'LinkedIn information',
headline_linkedin: result.headline
});
});
});
If I try to open the second one (/info/facebook) at first e then the first one (/info/linkedin) it doesn't load the page related of /info/linkedin route. It shows this message:
404 Not Found: Requested route ('linkedin-demo-app.eu-gb.mybluemix.net') does not exist.
Do you guys know what is this kind of problem? It seems like it doesn' recognize and find the route again.
Thanks in advance
You simply don't have route handler for these two paths. You need to create them like you did for your /read_fb_info path:
app.get("/info/linkedin", function(request, response) {
//do somenthing and send your response
});
app.get("/info/facebook", function(request, response) {
//do somenthing and send your response
});
My use case:
My case is that i'm making a bot for listening podcast in which user will make call to twilio number and bot will ask what type of podcast would you like to listen then record for 10 seconds
when recording finish, it say user to please wait while we are finding podcast
I want that recording in my webhook so i will figure out caller mood and find appropriate podcast mp3 file from my database and play to caller
Issue I'm Facing:
I'm getting empty body in all of my webhooks
My code:
var express = require("express");
var bodyParser = require("body-parser");
var VoiceResponse = require('twilio').twiml.VoiceResponse;
var app = express();
var port = (process.env.PORT || 4000);
app.use(bodyParser.json())
// helper to append a new "Say" verb with alice voice
function say(text, twimlRef) {
twimlRef.say({ voice: 'alice' }, text);
}
// respond with the current TwiML content
function respond(responseRef, twimlRef) {
responseRef.type('text/xml');
responseRef.send(twimlRef.toString());
}
app.post("/voice", function (request, response, next) {
console.log("request: ", request.body); //body is comming as empty object
var phone = request.body.From;
var input = request.body.RecordingUrl;
var twiml = new VoiceResponse();
console.log("phone, input: ", phone, input);
say('What type of podcast would you like to listen. Press any key to finish.', twiml);
twiml.record({
method: 'POST',
action: '/voice/transcribe',
transcribeCallback: '/voice/transcribe',
maxLength: 10
});
respond(response, twiml);
});
app.post("/voice/transcribe", function (request, response, next) {
console.log("request: ", request.body); //body is comming as empty object
var phone = request.body.From;
var input = request.body.RecordingUrl;
var twiml = new VoiceResponse();
var transcript = request.body.TranscriptionText;
console.log("transcribe text: ", transcript);
//here i will do some magic(Ai) to detect user mood and find an
//appropriate mp3 file from my database and send to twilio
var mp3Url = 'https://api.twilio.com/cowbell.mp3'
say('start playing.', twiml);
twiml.play(mp3Url);
respond(response, twiml);
});
app.listen(port, function () {
console.log('app is running on port', port);
});
API Test with postman:
added url as webhook on twilio:
Heroku Logs:
Twilio developer evangelist here.
You are using body-parser which is good. However, you are using the JSON parser. Twilio makes requests in the format of application/www-x-form-urlencoded so you should change:
app.use(bodyParser.json())
to
app.use(bodyParser.urlencoded({ extended: false }))
Then you should see the parsed body as part of the request.body object.
As an extra note, the transcribeCallback is sent asynchronously to the call. So returning TwiML in response to that request won't affect the call at all. You will need to modify the call in flight, by redirecting it to some new TwiML when you get the result of transcription. An example of updating a call with Node.js is below:
const accountSid = 'your_account_sid';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);
client.calls('CAe1644a7eed5088b159577c5802d8be38')
.update({
url: 'http://demo.twilio.com/docs/voice.xml',
method: 'POST',
})
.then((call) => console.log(call.to));
I want to trigger my bot with http request (for example just entering http://localhost:3978/api/messages/http) so after triggering it, it will send every user that is connected to this bot some message.
I have seen this topic: How to send message later in bot framework?
And this is what I have so far:
var restify = require('restify');
var builder = require('botbuilder');
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector);
bot.dialog('/',function (session) {
var reply = session.message; // address: reply.address
reply.text = 'Wake up!'
console.log(reply.text);
bot.send(reply);
});
// Create response function
function respond(req, res, next) {
res.send('hello ' + req.params.name);
bot.send(reply);
next();
}
server.get('/api/messages/:name', respond);
Unfortunately, it doesn't send any messages while I am acessing my http://localhost:3978/api/messages/http. I also tried to use
connector.send('message');
But it always throughs me "ERROR: ChatConnector: send - message is missing address or serviceUrl."
UPDATE:
I have announced a global var for the reply with
var globalreply;
bot.dialog('/',function (session) {
globalreply = session.message; // address: reply.address
globalreply.text = 'Wake up!'
console.log(globalreply.text);
bot.send(globalreply);
});
// Create response function
function respond(req, res, next) {
res.send('hello ' + req.params.name);
bot.beginDialog;
bot.send(globalreply);
next();
}
But now it throughs me an error:
TypeError: Cannot read property 'conversation' of undefined.
At my bot.send(globalreply); line.
Looking forward your help.
Best regards.
If you want to set up a normal HTTP API route, I suggest using the Restify API style routing, rather than the bot's /api/messages route handler.
For example:
function apiResponseHandler(req, res, next) {
// trigger botbuilder actions/dialogs here
next();
}
server.get('/hello/:name', apiResponseHandler);
I am trying to get a https loopback server up and running protected by OAuth. I am using the loopback gateway sample project as a reference. But for some reason I can't get the OAuth piece to work. What I mean is, even after adding in the OAuth bits and pieces, the APIs don't seem to be protected. I get a response back even if there is no token in my request. This is what my server.js looks like
var loopback = require('loopback');
var boot = require('loopback-boot');
var https = require('https');
var path = require('path');
var httpsRedirect = require('./middleware/https-redirect');
var site = require('./site');
var sslConfig = require('./ssl-config');
var options = {
key: sslConfig.privateKey,
cert: sslConfig.certificate
};
var app = module.exports = loopback();
// Set up the /favicon.ico
app.middleware('initial', loopback.favicon());
// request pre-processing middleware
app.middleware('initial', loopback.compress());
app.middleware('session', loopback.session({ saveUninitialized: true,
resave: true, secret: 'keyboard cat' }));
// -- Add your pre-processing middleware here --
// boot scripts mount components like REST API
boot(app, __dirname);
// Redirect http requests to https
var httpsPort = app.get('https-port');
app.middleware('routes', httpsRedirect({httpsPort: httpsPort}));
var oauth2 = require('loopback-component-oauth2')(
app, {
// Data source for oAuth2 metadata persistence
dataSource: app.dataSources.pg,
loginPage: '/login', // The login page url
loginPath: '/login' // The login processing url
});
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Set up login/logout forms
app.get('/login', site.loginForm);
app.get('/logout', site.logout);
app.get('/account', site.account);
app.get('/callback', site.callbackPage);
var auth = oauth2.authenticate({session: false, scope: 'demo'});
app.use(['/protected', '/api', '/me', '/_internal'], auth);
app.get('/me', function(req, res) {
// req.authInfo is set using the `info` argument supplied by
// `BearerStrategy`. It is typically used to indicate scope of the token,
// and used in access control checks. For illustrative purposes, this
// example simply returns the scope in the response.
res.json({ 'user_id': req.user.id, name: req.user.username,
accessToken: req.authInfo.accessToken });
});
signupTestUserAndApp();
//var rateLimiting = require('./middleware/rate-limiting');
//app.middleware('routes:after', rateLimiting({limit: 100, interval: 60000}));
//var proxy = require('./middleware/proxy');
//var proxyOptions = require('./middleware/proxy/config.json');
//app.middleware('routes:after', proxy(proxyOptions));
app.middleware('files',
loopback.static(path.join(__dirname, '../client/public')));
app.middleware('files', '/admin',
loopback.static(path.join(__dirname, '../client/admin')));
// Requests that get this far won't be handled
// by any middleware. Convert them into a 404 error
// that will be handled later down the chain.
app.middleware('final', loopback.urlNotFound());
// The ultimate error handler.
app.middleware('final', loopback.errorHandler());
app.start = function(httpOnly) {
if(httpOnly === undefined) {
httpOnly = process.env.HTTP;
}
server = https.createServer(options, app);
server.listen(app.get('port'), function() {
var baseUrl = (httpOnly? 'http://' : 'https://') + app.get('host') + ':' + app.get('port');
app.emit('started', baseUrl);
console.log('LoopBack server listening # %s%s', baseUrl, '/');
});
return server;};
// start the server if `$ node server.js`
if (require.main === module) {
app.start();
}
function signupTestUserAndApp() {
// Create a dummy user and client app
app.models.User.create({username: 'bob',
password: 'secret',
email: 'foo#bar.com'}, function(err, user) {
if (!err) {
console.log('User registered: username=%s password=%s',
user.username, 'secret');
}
// Hack to set the app id to a fixed value so that we don't have to change
// the client settings
app.models.Application.beforeSave = function(next) {
this.id = 123;
this.restApiKey = 'secret';
next();
};
app.models.Application.register(
user.username,
'demo-app',
{
publicKey: sslConfig.certificate
},
function(err, demo) {
if (err) {
console.error(err);
} else {
console.log('Client application registered: id=%s key=%s',
demo.id, demo.restApiKey);
}
}
);
});
}
I don't get any errors when the server starts up. Thoughts?
Got it figured. More information here https://github.com/strongloop/loopback-gateway/issues/17, but basically I had my rest-api middleware not configured right.