I am trying to get an Alexa skill (JS/Lambda) to post a value to a REST server using HTTP.request. I am trying to hack together something simple that will get the job done. I think I am missing something obvious.
Ideal Skill Usage
I say, "Alexa, tell Posting Test five."
Alexa updates the value at the URL specified in code to 5.
Alexa says, "I have updated the value to five."
Problems
I have two problems:
Spoken vs. typed utterances. If I type my slot value in the Amazon Service Simulator ("five") the value is posted to my server, as it should be. However, if I speak the same utterance, even though Alexa recognizes the words correctly (confirmed by looking at cards in the app), the value is not posted, and she says, "I can't find the answer to the question."
Where and how to call output function. I think I need to add something like the two lines below, but depending on where I add it in my current code, Alexa either responds without updating the node, or doesn't do anything.
var text = 'I have updated the value to' + targetSlot;
output( text, context );
Invocation Name
posting test
Intent Schema
{
"intents": [ {
"intent": "writeTarget",
"slots": [ {
"name": "Target",
"type": "NUMBER"
} ]
}]
}
Sample Utterances
writeTarget {Target}
AlexaSkill.js and index.js
I am using the AlexaSkill.js file that can be found in each example here.
My index.js looks like this. URL, req.write string, etc., are replaced with ****.
exports.handler = function( event, context ) {
var APP_ID = undefined;
const http = require( 'http' );
var AlexaSkill = require('./AlexaSkill');
var options = {
host: '****.com',
path: '/****',
port: '****',
method: 'PUT'
};
callback = function(response) {
var str = ''
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
console.log(str);
});
};
var targetSlot = event.request.intent.slots.Target.value;
var req = http.request(options, callback);
req.write("****");
req.end();
};
function output( text, context ) {
var answer = {
outputSpeech: {
type: "PlainText",
text: text
},
card: {
type: "Simple",
title: "System Data",
content: text
},
shouldEndSession: true
};
context.succeed( { answer: answer } );
}
Current Usage: A
I type "five" in the Service Simulator.
Node updates but Alexa doesn't say anything.
Current Usage: B
I tell Alexa, "Tell Posting Test two."
Alexa says, "I can't find the answer to the question." Card confirms she heard me correctly.
Nothing gets updated.
Thanks in advance for any help.
Update: Logs
Updating post to add logs:
Error message
{
"errorMessage": "Process exited before completing request"
}
Log Output Error
TypeError: Cannot read property 'intent' of undefined
at exports.handler (/var/task/index.js:24:35)
Lambda Response
The response is invalid
Related
I am creating a project where I need to take a user input - pass it through a function and return the new value to the user - seems simple enough. I am new to async functions and have read everything I possibly can, and can't works out if there's a more fundamental issue I am missing. I will show the basic code, and then what I wish to achieve. I believe the issue, is that I am returning back the status of the function rather than the value, but just can't work it out.
Basic Code:
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
generateResponse('james', 'hello world'); // Takes a user's name & input and recieves a response from a python file.
event.reply('textRecieve', 'hello world'); // Sends 'Hello World' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = "";
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
return message; // attempting to return the 'Message' from the python file
});
} catch (error) {
console.log("You've f*cked it somewhere my friend");
console.log(error);
}
}
Python Script:
import sys
name = sys.argv[1]
text = sys.argv[2]
print(f'Python File: {name} Text: {text}')
sys.stdout.flush()
Returns: (as expected)
> Executing task: npm run start <
> electron-quick-start#1.0.0 start
> electron .
Python File: james Text: hello world
What I'd Like it to do:
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
message = generateResponse('james', 'hello world'); // Takes a user's name & input and recieves a response from a python file, retunring the message to the 'message' variable.
console.log(message);
event.reply('textRecieve', 'message would send here'); // Sends the 'Message' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = ""
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
return message; // attempting to return the 'Message' from the python file
});
} catch (error) {
console.log("You've f*cked it somewhere my friend")
console.log(error)
}
return content; // content needs to be message instead due to async nature it returns empty string
}
Returns:
> Executing task: npm run start <
> electron-quick-start#1.0.0 start
> electron .
Promise { '' }
Python File: james Text: hello world
TLDR; I would like to take the 'message' generated through 'generateResponse()' and pass it through to my 'event.reply()'. Instead, I am receiving what I believe to be the status of the Promise. Any help would be greatly appreciated. Thanks
You should resolve the promise first.
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
const message = await generateResponse('james', 'hello world');
console.log(message);
event.reply('textRecieve', 'message would send here'); // Sends the 'Message' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = ""
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
content = message;
});
} catch (error) {
console.log("You've f*cked it somewhere my friend")
console.log(error)
}
return content; // content needs to be message instead due to async nature it returns empty string
}
Okay, so there were a few problems here... but the main was node.js 'non-ability' to pass variables around when 'asynchronous'. with node.js being new to me, I can't lie and say I was confused. Hopefully, the following link to a great workaround/method and my working code will be able to help someone:
https://stackoverflow.com/a/23667087/10246221
Code:
ipcMain - nested within app.whenReady().
ipcMain.on('gpt3', (event, input) => {
gpt3Async(event, input, function(result) {
event.reply('textRecieve', result);
console.log('gpt3Async: '+ result);
})
})
Code:
Generic 'nested' Function - free-floating around 'main.js' or 'index.js'.
function gpt3Async(event, input, callback) {
console.log('input: ' + input)
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: ['elliott' ,input]});
testshell.on('message', function (message) {
callback(message);
});
}
Code: Python Script 'text_echo.py' - in my case within a 'python' subdirectory.
import sys
name = sys.argv[1]
text = sys.argv[2]
print(f'Python File: {name} Text: {text}')
#sys.stdout.flush()
sys.stdout.flush()
For anyone working on a project where you need input and output for python scripts, this will help you out. also make sure you turn on the following:
webPreferences: {
//preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
sandbox: false,
},
BUT!, please be aware of the security implications this will have on your code, More info is available here: https://stackoverflow.com/a/57507392 & https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content & much more so do some reading if this is an important project...
Okay, An explainer, or at least something that blew my mind as a beginner... . The way I finally understood it was through the example link:
https://stackoverflow.com/a/23667087/10246221
for some reason, it hadn't clicked with me that functions could be nested within functions like this, all in one line. For someone who is used to JS or node.js this may seem fundamental, but seeing as this is a first-time project to me, and maybe others - if still using python code. Hopefully, this may help!
ipcMain.on('gpt3', (event, input) => { gpt3Async(event, input, function(result) { event.reply('textRecieve', result); console.log('gpt3Async: '+ result);})})
So I'm using discord.js with this code here:
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 4,
data: {
content: "Getting Data..."
}
}
})
I would like to be able to edit this message afterwards, but everything I have seen requireds a message id, and I seem to be unable to get the message id off of this code.
You can edit an interaction response with this patch request (See: Followup Messages):
PATCH /webhooks/<application_id>/<interaction_token>/messages/#original
Basic example using the axios library:
axios.patch(`https://discord.com/api/v8/webhooks/${appId}/${interaction.token}/messages/#original`, { content: 'New content' });
The answer to this request will also contain the message-id.
Here is an example function that will edit the original message either as plain text or an embed object and returns the discord message object for further usage (e.g. add reply emojis etc.):
const editInteraction = async (client, interaction, response) => {
// Set the data as embed if reponse is an embed object else as content
const data = typeof response === 'object' ? { embeds: [ response ] } : { content: response };
// Get the channel object by channel id:
const channel = await client.channels.resolve(interaction.channel_id);
// Edit the original interaction response:
return axios
.patch(`https://discord.com/api/v8/webhooks/${appId}/${interaction.token}/messages/#original`, data)
.then((answer) => {
// Return the message object:
return channel.messages.fetch(answer.data.id)
})
};
Also instead of sending an initial message like "getting data..." you also can send an empty response of type 5. This is the build-in method and displays a little loading animation too :) See here (One advantage is that this way no "edited" appears.)
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 5,
},
})
I want to define the response structure of my requests in the simplest way, and the first thing that comes in my mind to do this is a middleware.
My endpoints are returning the response content correctly:
{{base_url}}/users returns a list of users:
{
[
{
"id": 44,
"name": "some name"
[...]
}
]
}
What I want to do (in all requests) is to add the fields status and data (or any other I'd like to add), like this:
{
"status": 200,
"data": [
{
"id": 44,
"name": "some name"
[...]
}
]
}
I've created a middleware that waits for the resolution but I'm not able to get the content nor add some property to it.
[...]
async handle ({request, response}, next) {
await next()
const content = response._lazyBody.content
content.status = response.response.statusCode
}
[...]
I know this will not work but I want something similar to this. I've looked in Adonis docs and forum, but no answers fit to my needs.
Any help will be welcome
You can extend Response By extending the core. The simplest way is to create a file inside start folder and name it hooks.js and copy and paste the content below inside it:
const { hooks } = use('#adonisjs/ignitor')
const Response = use('Adonis/Src/Response')
hooks.after.providersBooted(() => {
Response.macro('customJson', function (status, data) {
this.status(status).json({
status,
data
})
})
})
this piece of code extends the Response module and add customJson method to it which takes two arguments, status and data, and send them back to the client.
And here you can see how to use it:
Route.get('/users', async ({ response }) => {
let status = ''// whatever you want
let data = ''// whatever you want
return response.customJson(status, data)
})
I have a very basic feathers service which stores data in mongoose using the feathers-mongoose package. The issue is with the get functionality. My model is as follows:
module.exports = function (app) {
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const messages = new Schema({
message: { type: String, required: true }
}, {
timestamps: true
});
return mongooseClient.model('messages', messages);
};
When the a user runs a GET command :
curl http://localhost:3030/messages/test
I have the following requirements
This essentially tries to convert test to ObjectID. What i would
like it to do is to run a query against the message attribute
{message : "test"} , i am not sure how i can achieve this. There is
not enough documentation for to understand to write or change this
in the hooks. Can some one please help
I want to return a custom error code (http) when a row is not found or does not match some of my criterias. How can i achive this?
Thanks
In a Feathers before hook you can set context.result in which case the original database call will be skipped. So the flow is
In a before get hook, try to find the message by name
If it exists set context.result to what was found
Otherwise do nothing which will return the original get by id
This is how it looks:
async context => {
const messages = context.service.find({
...context.params,
query: {
$limit: 1,
name: context.id
}
});
if (messages.total > 0) {
context.result = messages.data[0];
}
return context;
}
How to create custom errors and set the error code is documented in the Errors API.
I am trying to work on a custom jasmine reporter and get a list of all the failed specs in the specDone function:
specDone: function(result) {
if(result.status == 'failed') {
failedExpectations.push(result.fullName);
console.log(failedExpectations);
}
}
where failedExpectations will store an entire list of the failed specs and i need to access this in the afterLaunch function in the protractor config file. But due to the fact that the config file loads everytime a new spec runs it basically gets overwritten and scoping is such that I cannot access it in the afterLaunch function, that is where I am making the call to the slack api. Is there a way to achieve this?
This is what i have it based on : http://jasmine.github.io/2.1/custom_reporter.html
I think the best way is to post the results asynchronously after each spec (*or every "it" and "describe") using #slack/web-api. This way you don't have to worry about overwriting. Basically you "collect" all the results during the test run and send it before the next suite starts.
Keep in mind all of this should be done as a class.
First you prepare your you '#slack/web-api', so install it (https://www.npmjs.com/package/#slack/web-api).
npm i -D '#slack/web-api'
Then import it in your reporter:
import { WebClient } from '#slack/web-api';
And initialize it with your token. (https://slack.com/intl/en-pl/help/articles/215770388-Create-and-regenerate-API-tokens):
this.channel = yourSlackChannel;
this.slackApp = new WebClient(yourAuthToken);
Don't forget to invite your slack app to the channel.
Then prepare your result "interface" according to your needs and possibilities. For example:
this.results = {
title: '',
status: '',
color: '',
successTests: [],
fails: [],
};
Then prepare a method / function for posting your results:
postResultOnSlack = (res) => {
try {
this.slackApp.chat.postMessage({
text: `Suit name: ${res.title}`,
icon_emoji: ':clipboard:',
attachments: [
{
color: res.color,
fields: [
{
title: 'Successful tests:',
value: ` ${res.successTests}`,
short: false
},
{
title: 'Failed tests:',
value: ` ${res.fails}`,
short: false
},
]
}
],
channel: this.channel
});
console.log('Message posted!');
} catch (error) {
console.log(error);
}
When you got all of this ready it's time to "collect" your results.
So on every 'suitStart' remember to "clear" the results:
suiteStarted(result) {
this.results.title = result.fullName;
this.results.status = '';
this.results.color = '';
this.results.successTests = [];
this.results.fails = [];
}
Then collect success and failed tests:
onSpecDone(result) {
this.results.status = result.status
// here you can push result messages or whole stack or do both:
this.results.successTests.push(`${test.passedExpectations}`);
for(var i = 0; i < result.failedExpectations.length; i++) {
this.results.fails.push(test.failedExpectations[i].message);
}
// I'm not sure what is the type of status but I guess it's like this:
result.status==1 ? this.results.color = #DC143C : this.results.color = #048a04;
}
And finally send them:
suiteDone() {
this.postResultOnSlack(this.results);
}
NOTE: It is just a draft based on reporter of mine. I just wanted to show you the flow. I was looking at Jasmine custom reporter but this was based on WDIO custom reporter based on 'spec reporter'. They are all very similar but you probably have to adjust it. The main point is to collect the results during the test and send them after each part of test run.
*You can look up this explanation: https://webdriver.io/docs/customreporter.html
I highly recommend this framework, you can use it with Jasmine on top.