I am bulding a messenger bot with node js express.I am trying to split my index.js file into two files. Here is the code for msg.js which is the new file
'const
express = require('express'),
bodyParser = require('body-parser'),
request = require('request'),
PAGE_ACCESS_TOKEN ="",
app = express().use(bodyParser.json());
//functions
module.exports = {
//hangles messages
handleMessage:function (sender_psid, received_message) {
let response;
// Checks if the message contains text
if (received_message.text) {
// Create the payload for a basic text message, which
// will be added to the body of our request to the Send API
response = {
"text": `You sent the message: "${received_message.text}". Now send me an attachment!`
}
} else if (received_message.attachments) {
// Get the URL of the message attachment
let attachment_url = received_message.attachments[0].payload.url;
response = {
"attachment": {
"type": "template",
"payload": {
"template_type": "generic",
"elements": [{
"title": "Is this the right picture?",
"subtitle": "Tap a button to answer.",
"image_url": attachment_url,
"buttons": [
{
"type": "postback",
"title": "Yes!",
"payload": "yes",
},
{
"type": "postback",
"title": "No!",
"payload": "no",
}
],
}]
}
}
}
}
// Send the response message
module.exports.callSendAPI(sender_psid, response);
},
// Handles messaging_postbacks events
handlePostback:function (sender_psid, received_postback) {
let response;
// Get the payload for the postback
if (received_postback) {
let payload = received_postback.payload;
}
// Send the message to acknowledge the postback
module.exports.callSendAPI(sender_psid, response);
},
// Sends response messages via the Send API
callSendAPI:function (sender_psid, response) {
// Construct the message body
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
// Send the HTTP request to the Messenger Platform
request({
"uri": "https://graph.facebook.com/v2.6/me/messages",
"qs": { "access_token": PAGE_ACCESS_TOKEN },
"method": "POST",
"json": request_body
}, (err, res, body) => {
if (!err) {
console.log('message sent!')
} else {
console.error("Unable to send message:" + err);
}
});
}
};
I have the following code at the bottom of my index.js file.
//Imports functions from other files
let msg = require('./msg.js'),
handleMessage = msg.handleMessage(),
handlePostback = msg.handlePostback(),
callSendAPI = msg.callSendAPI();
I am getting the following error:
msg.js:14
if (received_message.text) {
^
TypeError: Cannot read property 'text' of undefined
The problem is this line:
if (received_message.text) {
When this gets called, received_message which is passed in is undefined, so when you try to get the text field from the received_message variable it will throw an error since received_message is undefined and thus would not have any fields you could call from it. Check to see if received_message is ever set properly before being passed into your handleMessage function.
Related
so far I have been able to establish Test email delivery on strapi but I am not having any luck with posting a request when I am using the postman. For example I created route
// api/email/config/routes.json
{
"routes": [
{
"method": "POST",
"path": "/emails",
"handler": "Email.send",
"config": {
"policies": []
}
}
]
}
and then rest of the email body here is an example:
// File /api/email/controllers/Email.js
'use strict'
/**
* Read the documentation () to implement custom controller functions
*/
module.exports = {
/**
* Sends an email to the recipient in the body of the request
*/
send: async (ctx) => {
const body = ctx.request.body
const sendTo = body.email
strapi.log.debug(`Trying to send an email to ${sendTo}`)
try {
const emailOptions = {
to: sendTo,
subject: 'This is a test',
html: `<h1>Welcome!</h1><p>This is a test HTML email.</p>`,
}
await strapi.plugins['email'].services.email.send(emailOptions)
strapi.log.debug(`Email sent to ${sendTo}`)
ctx.send({ message: 'Email sent' })
} catch (err) {
strapi.log.error(`Error sending email to ${sendTo}`, err)
ctx.send({ error: 'Error sending email' })
console.log('====================================');
console.log(err);
console.log('====================================');
}
},
}
after running the application I posted request http://localhost:1337/emails and tried sending this json body {"email": "validemail#gmail.com"} but I keep receiving Method Not Allowed I can't seem to find what did I do wrong. Thank you in advance!
Here is my code. How can I configure it to return a response for the Google Assistant integration? I would like to use this code within the Dialogflow in-line code editor or deploy it to firebase functions.
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function getProjects(agent){
const api = 'https://playhacker.com/wp-json/wp/v2';
const tag = agent.parameters.tag;
//let url = `${api}/posts?tags=${tag}&_embed`;
let getPosts = (tag, callback) => {
let url = `${api}/posts?tags=${tag}&_embed`;
request({url}, (err, res, body) => {
if (err) {
callback('Sorry, there was an error getting posts from our blog', err);
return;
} else {
let posts = JSON.parse(body);
if (posts.length === 0) {
callback(`It does not seem like there is any content available on this topic`);
return;
} else {
let formattedPosts = posts.map((post) => {
return {
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Here is a project we found for you"
}
},
{
"basicCard": {
"title": post.title.rendered,
"subtitle": "Project",
"formattedText": post.excerpt.rendered.replace(/<(?:.|\\n)*?>/gm, '').replace(/&[^\\s]*/, ''),
"image": {
"url": post._embedded['wp:featuredmedia'][0].media_details.sizes.listing.source_url,
"accessibilityText": "featured image"
},
"buttons": [
{
"title": "Read more",
"openUrlAction": {
"url": post.link
}
}
],
"imageDisplayOptions": "CROPPED"
}
}
]
}
}
}
};
});
formattedPosts.unshift({
type: 0,
platform: 'google',
speech: 'Sure, here are some helpful projects'
});
callback(undefined, formattedPosts);
return;
}
}
});
};
}
let intentMap = new Map();
intentMap.set('getProjects',getProjects);
agent.handleRequest(intentMap);
});
When I run it in Dialogflow in-line editor, I got this error:
{
insertId: "000001-ec48afd6-c286-47e4-8c75-43a2c6f1fc08"
labels: {…}
logName: "projects/apologetic-robot-fiajxe/logs/cloudfunctions.googleapis.com%2Fcloud-functions"
receiveTimestamp: "2020-05-03T06:38:00.862369842Z"
resource: {
labels: {…}
type: "cloud_function"
}
severity: "ERROR"
textPayload: "Error: No responses defined for platform: null
at V2Agent.sendResponses_ (/srv/node_modules/dialogflow-fulfillment/src/v2-agent.js:243:13)
at WebhookClient.send_ (/srv/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:505:17)
at promise.then (/srv/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:316:38)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)"
timestamp: "2020-05-03T06:37:59.833Z"
trace: "projects/apologetic-robot-fiajxe/traces/29d9241c46088463269b00ecf3558974"
}
The error Error: No responses defined means you haven't defined any response to be sent to dialogflow that can be shown back to the chatbot user.
For eg. when the codeflow reaches the function getProjects, there should be a response back to dialogflow before returning from the function, anything like
agent.add(`There seems to be some error`);
which will in turn show this string back to the user.
Additonally, in your above example, I cannot see your getPosts function being called anywhere.
I am working with serverless and NodeJS using the aws-sdk module, and I have been getting a 502 internal server error on the frontend of the app, previously it was shown as a cors error but so far it is the same issue in only one specific table when performing update or delete. Here is the Lambda function itself:
export async function createUserSegment(event, context, callback) {
const { data, timestamp, identityId } = extractRequestData(event)
console.log('data', JSON.stringify(data));
if (!verifyParamsExist(callback, ["Data", "IdentityId"], [data, identityId]) ||
!verifyParamsExist(callback, ["Name", "Pattern", "Type"], [data.name, data.type, data.pattern]) ||
!verifyValidParams(callback, [data.name, ...data.pattern], ["Name", "Pattern"], [{ value: data.type, enums: USER_SEGMENT_TYPES }], ["Type"])) {
console.log('no good params');
return false
}
let cleanedParams
let params
try {
cleanedParams = cleanUserSegment(data.pattern, data.type)
console.log('cleaned params', cleanedParams);
} catch (e) {
console.error(e)
callback(null, badRequest({ type: "InvalidRule", status: false, msg: e.message }));
return
}
params = {
TableName: process.env.USER_SEGMENT_TABLE,
Key: {
"cognitoUsername": fetchSubFromEvent(event),
"segmentName": data.name.trim(),
},
// Performs an insert (fails if already exists)
// - 'cognitoUsername': cognito username (NOT IDENTITY ID!) of the segment creator.
// - 'segmentName': user-provided name to identify the segment
// - 'createdAt': unix timestamp for when segment is created
// - 'pattern': user-supplied pattern that the segment refers to
// - 'segmentType': either VISITED, NOT_VISITED, GOAL, NOT_GOAL to indicate what our pattern is matching (URL visits or goal completion)
// - 'identityId': cognito identity id of the segment creator. necessary for querying ES
UpdateExpression: 'SET pattern = :pattern, segmentType = :segmentType, createdAt = :createdAt, identityId = :identityId',
ConditionExpression: 'attribute_not_exists(cognitoUsername)',
ExpressionAttributeValues: {
":pattern": cleanedParams.pattern,
":segmentType": cleanedParams.type,
":createdAt": timestamp,
":identityId": identityId,
},
ReturnValues: "ALL_NEW"
};
writeToDynamoAndCatchDuplicates(params, callback, transformUserSegment)
}
async function writeToDynamoAndCatchDuplicates(params, callback, transformFn){
try {
if(params.ConditionExpression){
console.log(params);
let result = await dynamoDbLib.call("update", params);
console.log('this is res',result);
result.Attributes = transformFn(result.Attributes)
callback(null, success({ status: true, result: result }));
} else {
await dynamoDbLib.call("update", params);
const paramsForAll = {
TableName: params.TableName,
KeyConditionExpression: "#cognitoUsername = :username",
ExpressionAttributeNames: {
"#cognitoUsername": "cognitoUsername",
},
ExpressionAttributeValues: {
":username": params.Key.cognitoUsername
},
}
try {
let result = await dynamoDbLib.call("query", paramsForAll);
result.Items = result.Items.map(transformFn)
console.log(result);
callback(null, success({ status: true, result: result}));
} catch (e) {
console.error(e)
callback(null, failure({ status: false }));
}
}
} catch (e) {
console.error(e)
if(e.code === 'ConditionalCheckFailedException'){
callback(null, badRequest({ type: "Duplicate", status: false }));
} else {
callback(null, failure({ status: false }));
}
}
}
The call function for dynamoDbLib is simply:
import AWS from "aws-sdk";
AWS.config.update({ region: "us-east-1" });
export function call(action, params) {
const dynamoDb = new AWS.DynamoDB.DocumentClient();
return dynamoDb[action](params).promise();
}
The request goes through the Lambda up until within writeToDynamoAndCatchDuplicates the call function is made. it gets sent to dynamo but it never returns, neither a timeout nor an error, it simply ends. In cloudwatch the last thing I see is the params log from writeToDynamoAndCatchDuplicates, no error logged. I hadn't changed the IAM policies on the role assigned to all Lambdas with the following permissions for the table aforementioned:
...},
{
"Action": [
"dynamodb:UpdateItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:DeleteItem"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:xxxxxxxxxxxxx:table/staging-userSegmentTable"
],
"Effect": "Allow"
},
{ ...
If anyone spots anything that might be a possible reason for the issue, it will be much appreciated.
Also here is the last log shown before the END message on LOG events:
2020-01-07T23:42:03.715Z 4fbf0957-c8be-4520-8277-7b9a9bda8b67 INFO { TableName: 'staging-userSegmentTable',
Key:
{ cognitoUsername: '*******************************',
segmentName: 'cssfs' },
UpdateExpression:
'SET pattern = :pattern, segmentType = :segmentType, createdAt = :createdAt, identityId = :identityId',
ConditionExpression: 'attribute_not_exists(cognitoUsername)',
ExpressionAttributeValues:
{ ':pattern': [ '/' ],
':segmentType': [ 'CONTAINS' ],
':createdAt': 1578440523709,
':identityId': '****************************************' },
ReturnValues: 'ALL_NEW' }
which corresponds to console.log(params) before sending to DynamoDB inside the if statement of the writeToDynamoAndCatchDuplicates function.
Does your IAM have Read and Write Access to that Database and security group connected to the instance? Is this the only table that seems to be the issue?
I had a similar issue ended up having to give my DB instance full read access.
I am trying to initiate a push notification from cloud code, but I always receive this error
{
"code":107,
"message":"Received an error with invalid JSON from Parse: Cannot POST /push\n"
}
I am running parse-server#2.2.21
Parse.Cloud.define("SendPushNotif", function(request, response){
var query = new Parse.Query(Parse.Installation);
query.equalTo('channels', ["Giants"]);
Parse.Push.send({
where: query,
data: {
"title": "Test",
"alert": "This is a test push notification"
}
}, { useMasterKey: true })
.then(function() {
response.success(response);
}, function(error) {
response.success(error);
});
});
I have this nodejs app and one of the calls I've made is an upsert call that upserts the request body to mongodb. If the request body exists in mongo, update it, else insert it. The insert portion works correctly, however, I am having trouble with the upsert portion. My request body is being generated by a C# app that I've written.
Ok, so when I update, I need to use the $addToSet and $each commands because I am adding elements into an array in the mongo document.
Here is an example request body (this is a POST), this is what's sent to my nodejs app:
{
"$addToSet": {
"Data": {
"$each": [
{"ID":"10","RandNum":"45"},
{"ID":"11","RandNum":"1"},
{"ID":"12","RandNum":"3"}
]
}
}
}
The document is formatted like this in mongo:
{
"Timestamp": "timestamp",
"Id": "12345",
"Header": {
"Name": "Name",
"Query": "SELECT * FROM test"
},
"Data": [
{
"ID": "1",
"RandNum": "34"
}, {
"ID": "4",
"RandNum": "23"
}
]
}
I am only interested in inserting into the "Data" array so I am using the $addToSet and $each operators.
This is my upsert call on the nodejs app, the actual function has a lot of code so I'll just put the upsert parts:
var db = require('../db.js');
var ind = require('./index.js');
var mongo = require('mongodb').MongoClient;
exports.upsert = function(req, res) {
var token = req.params.token;
var app = req.params.applicationId;
var dataObject = {
"Status": 1,
"DataObject": {},
"UserFriendlyErrorMessage": "",
"DeveloperErrorMessage": ""
};
// set response header
res.writeHead(200, {"Content-Type": "application/json"});
res.writeHead(200, {"Content-Length": Buffer.byteLength(req.body)});
var upsertDatabase = mongo.connect(ind.mongoLoc + databaseName, function(err, newDb) {
if(err)
throw err;
var upsertCollection = newDb.collection(collectionName);
if(newDb) {
if(req.query.query) {
// UPSERT
try {
var json = JSON.parse(req.query.query);
upsertCollection.update(json, req.body, {upsert: true, multi: false}, function(err, records) {
if(err)
throw err;
console.log("Data updated");
});
} catch(e) {
dataObject["DataObject"] = null;
dataObject["UserFriendlyErrorMessage"] = "Could not update";
dataObject["DeveloperErrorMessage"] = e;
res.end(JSON.stringify(dataObject, 0, 4));
console.log(e);
}
} else {
// INSERT
upsertCollection.insert(req.body, function(err, records) {
if(err)
throw err;
console.log("Data inserted");
});
}
dataObject["Status"] = 0;
dataObject["DataObject"] = null;
res.end(JSON.stringify(dataObject, 0, 4));
} else {
// could not connect to database
dataObject["DataObject"] = null;
dataObject["UserFriendlyErrorMessage"] = "Could not connect to database";
dataObject["DeveloperErrorMessage"] = "ERROR: Could not connect to database";
res.end(JSON.stringify(dataObject, 0, 4));
}
});
};
In this function, I am testing to see if the document exists and then upserting it if it does. That's the part I'm focusing on. The dataObject is what I'm returning to the user to show errors/successes.
When I post the data to this call, I get this error (I'm viewing the POST call in Fiddler):
HTTP/1.1 504 Fiddler - Receive Failure
Date: Fri, 28 Feb 2014 15:30:39 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Cache-Control: no-cache, must-revalidate
Timestamp: 10:30:39.535
[Fiddler] ReadResponse() failed: The server did not return a response for this request.Server returned 0 bytes
Content-Type is supposed to be application/json
I fixed it. It seems that the line
res.writeHead(200, {"Content-Length": Buffer.byteLength(req.body)});
was not allowing it to post the data. I tried removing it and the upsert worked.
However, I'm wondering why removing that did the trick. I thought that Content-Length was necessary because it specifies the length of the body.