I am currently attempting to make an Alexa skill that will issue a "Find my iPhone" alert to my apple devices when I give alexa the correct prompts. I am quite new to developing for the alexa skill set and coding at that (especially in node.js). Here is my quote:
var phoneId = "I have my values here";
var ipadId = "I have my values here";
var macId = "I have my values here";
var deviceId = "";
var APP_ID = ''; //replace with "amzn1.echo-sdk-ams.app.[your-unique-value-here]";
var AlexaSkill = require('./AlexaSkill');
var alexaResponse;
//Import Apple.js
var Apple = require('./Apple');
var apple = new Apple();
var alertSuccess = "Alert sent to Kenny's phone";
var alertFailed = "Alert couldn't be sent to Kenny's phone. Good luck finding it.";
var FindDevice = function () {
AlexaSkill.call(this, APP_ID);
};
// Extend AlexaSkill
FindDevice.prototype = Object.create(AlexaSkill.prototype);
FindDevice.prototype.constructor = FindDevice;
FindDevice.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
console.log("Quote onSessionStarted requestId: " + sessionStartedRequest.requestId
+ ", sessionId: " + session.sessionId);
// any initialization logic goes here
};
FindDevice.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
console.log("Quote onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
getWelcomeResponse(response);
};
FindDevice.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
console.log("Quote onSessionEnded requestId: " + sessionEndedRequest.requestId
+ ", sessionId: " + session.sessionId);
// any cleanup logic goes here
};
FindDevice.prototype.intentHandlers = {
// register custom intent handlers
"FindDeviceIntent": function (intent, session, response) {
determineDevice(intent, session, response);
}
};
/**
* Returns the welcome response for when a user invokes this skill.
*/
function getWelcomeResponse(response) {
// If we wanted to initialize the session to have some attributes we could add those here.
var speechText = "Welcome to the Lost Device. Which device shall I find?";
var repromptText = "<speak>Please choose a category by saying, " +
"iPhone <break time=\"0.2s\" /> " +
"Mac <break time=\"0.2s\" /> " +
"iPad <break time=\"0.2s\" /></speak>";
var speechOutput = {
speech: speechText,
type: AlexaSkill.speechOutputType.PLAIN_TEXT
};
var repromptOutput = {
speech: repromptText,
type: AlexaSkill.speechOutputType.SSML
};
response.ask(speechOutput, repromptOutput);
}
function determineDevice(intent, session, response) {
var deviceSlot = intent.slots.Device;
if (deviceSlot == "iPhone") {
deviceId = phoneId;
pingDevice(deviceId);
} else if (deviceSlot == "iPad") {
deviceId = ipadId;
pingDevice(deviceId);
} else if (deviceSlot == "Mac") {
deviceId = macId;
pingDevice(deviceId);
} else {
var speechText = "None of those are valid devices. Please try again.";
speechOutput = {
speech: speechText,
type: AlexaSkill.speechOutputType.PLAIN_TEXT
};
response.tell(speechOutput);
}
}
function pingDevice(deviceId) {
apple.sendAlert(deviceId, 'Glad you found your phone.', function(success, result){
if(success){
console.log("Alert Sent Successfully");
var speechOutput = alertSuccess;
response.tell(speechOutput);
} else {
console.log("Alert Unsuccessful");
console.log(result);
var speechOutput = alertFailed;
response.tell(speechOutput);
}
});
}
// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
// Create an instance of the FindDevice skill.
var findDevice = new FindDevice();
findDevice.execute(event, context);
};
Here is the error from lambda:
{
"errorMessage": "Cannot read property 'PLAIN_TEXT' of undefined",
"errorType": "TypeError",
"stackTrace": [
"getWelcomeResponse (/var/task/index.js:87:42)",
"FindDevice.eventHandlers.onLaunch (/var/task/index.js:58:5)",
"FindDevice.LaunchRequest (/var/task/AlexaSkill.js:10:37)",
"FindDevice.AlexaSkill.execute (/var/task/AlexaSkill.js:91:24)",
"exports.handler (/var/task/index.js:137:16)"
]
}
I understand that there is an undefined object here, but for the life of me I can't figure out where the code is going wrong. I am trying to take the Slot from my intent and then change the device to ping based on the slot word used. Also because I am so new to this a lot of the coding is just being done by patching things together. I did find that when I removed the .PLAIN_TEXT lines all together the code ran in lambda, but then broke in the alexa skills test area. I get the hunch I don't understand how the slots from intents are passed, but I am having trouble finding material I can understand on that matter. Any help would be fantastic!
Within the determineDevice function, you're accessing the "Device" slot object directly, rather than the actual value passed in, therefore it will never match your pre-defined set of device names.
A slot object has a name and a value - if you take a look at the Service Request JSON in the Service Simulator within the Developer Console when you're testing your Alexa skill, you'll see something like the following:
{
"session": {
"sessionId": "SessionId.dd05eb31-ae83-4058-b6d5-df55fbe51040",
"application": {
"applicationId": "amzn1.ask.skill.61dc6132-1727-4e56-b194-5996b626cb5a"
},
"attributes": {
},
"user": {
"userId": "amzn1.ask.account.XXXXXXXXXXXX"
},
"new": false
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.6f083909-a831-495f-9b55-75be9f37a9d7",
"locale": "en-GB",
"timestamp": "2017-07-23T22:14:45Z",
"intent": {
"name": "AnswerIntent",
"slots": {
"person": {
"name": "person",
"value": "Fred"
}
}
}
},
"version": "1.0"
}
Note I have a slot called "person" in this case, but to get the value in the slot, you need to access the "value" property. In your example, you would change the first line of the determineDevice function to:
var deviceSlot = intent.slots.Device.value;
As an aside, I've found the Alexa-cookbook Github repository an essential resource for learning how to work with the Alexa SDK, there are examples in there for most scenarios.
Related
I have requirement to style (change the color of link instead blue) and change the text in auth card in Search message extension. I tried to pass all parameters. But nothing appear to impact. I also tried to generate an Adpative card with text having link. But link doesnt have styling option.
This is code , I am using right now :
return new MessagingExtensionResponse
{
ComposeExtension = new MessagingExtensionResult
{
Type = "auth",
SuggestedActions = new MessagingExtensionSuggestedAction
{
Actions = new List<CardAction>
{
new CardAction
{
Type = ActionTypes.OpenUrl,
Value = signInLink,
Title = "Sign in"
},
},
},
},
};
Adaptive Card Json Tried:
{
"type": "TextBlock",
"text": "Please [Sign in](https://adaptivecards.io)"
}
I tried o replace crad action as suggested below, but it didnt work.
`
private async Task GetAuthCard(ITurnContext turnContext, CancellationToken cancellationToken)
{
// Retrieve OAuth Sign in Link
string signInLink = await GetSignInLinkAsync(turnContext, cancellationToken).ConfigureAwait(false);
AuthCardModel auth = new();
auth.AdaptiveCardPath = Path.Combine(".", "Resources", "AuthCardTemplate.json");
auth.signinLink = signInLink;
Attachment adaptiveCard = CreateAdaptiveCardActivity(auth.AdaptiveCardPath, auth);
_logger.LogWarning($"[TeamsBot LogIn adaptive card] -> {JsonConvert.SerializeObject(adaptiveCard)}");
MessagingExtensionAttachment attachment = new MessagingExtensionAttachment
{
ContentType = ThumbnailCard.ContentType,
Content = adaptiveCard.Content,
};
return new MessagingExtensionResponse
{
ComposeExtension = new MessagingExtensionResult
{
Type = "auth",
AttachmentLayout = "list",
Attachments = new List<MessagingExtensionAttachment> { attachment },
},
};
}
`
AuthCardTemplate.json :
`
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "__ * * Sign in * * __"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.3"
}
`
Teams Does not support Card action button styling in cards. This is by design. Markdown is a simple way to format text that looks great on any device. It doesn’t do anything fancy like change the font size, color, or type — just the essentials.
Document-https://learn.microsoft.com/en-us/adaptive-cards/authoring-cards/text-features#markdown-commonmark-subset
I had a question regarding Teams Microsoft Bot framework. Whenever my bot sends an adaptive card, the top and the bottom of the photo continue to cut off. Inside the adaptive card is the hero card image, it seems I'm unable to resize it to make it fit. I've tried making the image smaller and larger to see if that would fix the issue. Below is a screenshot of the issue I am having.
I'm hoping someone has run into the same issue and if this is fixable or not. Thank you.
Image being used; https://imgur.com/a/hkcSkrJ
public async Task<SendResult> SendAsync(NotificationTeamsAttempt attempt)
{
try
{
if (string.IsNullOrWhiteSpace(attempt.ConversationId))
throw new Exception("Conversation Id is required.");
if (string.IsNullOrWhiteSpace(attempt.ServiceUrl))
throw new Exception("Service Url is required.");
using (var connector = new ConnectorClient(new Uri(attempt.ServiceUrl), _clientId, _clientSecret))
{
var activity = MessageFactory.Text("");
activity.Attachments.Add(attempt.Attachment());
activity.Summary = attempt.Summary();
var response = await connector.Conversations.SendToConversationAsync(attempt.ConversationId, activity);
return new SendResult
{
IsSuccess = true,
DispatchId = response.Id
};
}
}
catch (Exception exception)
{
return new SendResult
{
IsSuccess = false,
Exception = exception
};
}
}
public override Attachment Attachment()
{
var card = new ThumbnailCard
{
Title = "Post submitted for review by " + DraftAuthor,
Subtitle = DraftTitle,
Text = DraftDescription,
Images = new List<CardImage>(),
Buttons = new List<CardAction>()
};
if (!string.IsNullOrWhiteSpace(TeamsUrl))
{
card.Buttons.Add(new CardAction
{
Type = "openUrl",
Title = "Review in Teams",
Value = TeamsUrl.Replace("null", $"%22post%7C{DraftId}%7C{DraftId}%22")
});
}
if (!string.IsNullOrWhiteSpace(SPUrl))
{
card.Buttons.Add(new CardAction
{
Type = "openUrl",
Title = "Review in SharePoint",
Value = $"{SPUrl}?postId={DraftId}&sourceId={DraftId}"
});
}
return card.ToAttachment();
}
Please disregard the black lines I've added. But below you can see where the image is cropping off.
Image of the cropping.
Moving comment to answer -
We are using the below JSON and we get the perfect image without cropping -- { "type": "AdaptiveCard", "body": [ { "type": "TextBlock", "size": "Medium", "weight": "Bolder", "text": "card image test" }, { "type": "Container", "items": [ { "title": "Public test 1", "type": "Image", "url": "https://i.imgur.com/OiJNN03.jpeg" } ] } ], "$schema": "adaptivecards.io/schemas/adaptive-card.json", "version": "1.0" }
I am Trying to create a alexa Skill. in lambda function testing i am getting correct response. But in development account testing i am getting error like The requested skill did not provide a valid response.
Lambda Function:
exports.handler =function(event,context){
// TODO implement
var request = event.request;
if(request.type === "LaunchRequest"){
let options={};
options.SpeachText = "Welcome to Wishing Skill.whom you want to wish?"
options.repromptText = "whom you want to wish?"
options.endSession = false;
context.succeed(buildResponse(options));
} else if(request.type === "IntentRequest"){
let options={};
if(request.intent.name === "HelloIntent"){
let name=request.intent.slots.FirstName.value;
options.SpeachText="Hello "+name+". ";
options.SpeachText+=getWish();
options.endSession=false;
context.succeed(buildResponse(options));
}else{
context.fail("Unknown Intent");
}
}else if(request.type === "IntentRequest"){
let options={};
options.SpeachText="Thank you. ";
context.succeed(buildResponse(options));
}else{
context.fail("Unknown Intent Type");
}
};
function getWish(){
let date= new Date();
var hours= date.getUTCHours() - 8;
if(hours<0){
hours=hours+24;
}
if(hours<12){
return "Good Morning.";
}else if(hours<18){
return "Good afternoon.";
}else{
return "Good evening.";
}
};
function buildResponse(options){
var response= {
version: "1.0",
response: {
outputSpeech: {
type: "PlainText",
text: options.SpeachText
},
shouldEndSession: options.endSession
}
}
if(options.repromptText){
response.response.reprompt={
outputSpeech: {
type: "PlainText",
text: options.repromptText
}
}
}
return response;
};
In test response
Response
{
"version": "1.0",
"response": {
"outputSpeech": {
"type": "PlainText",
"text": "Hello chandu. Good evening."
},
"shouldEndSession": false
}
}
Function Logs
START RequestId: d05e0869-495f-471b-9c51-3693badd2690 Version: $LATEST
END RequestId: d05e0869-495f-471b-9c51-3693badd2690
REPORT RequestId: d05e0869-495f-471b-9c51-3693badd2690 Duration: 18.85 ms Billed Duration: 19 ms Memory Size: 128 MB Max Memory Used: 65 MB Init Duration: 126.07 ms
Request ID
d05e0869-495f-471b-9c51-3693badd2690
But when I am trying in developer account it was giving The requested skill did not provide a valid response
reference image
Please Help me with Some answer. Thankyou in Advance.
I have to ask you a question before giving my suggestions:
Why do you create the reprompt speech part outside the response to then add it?
I would simply add that part in the creation of the response. Also, I have added more elements in the response json in order to make it more attractive to the user. The card element offers visual response of the alexa device, if it has a screen.
function buildSpeechletResponseImage() {
return {
outputSpeech: {
type: "PlainText",
text: "Hello"
},
card: {
type: 'Standard',
text: "Hellooo",
image: {
"smallImageUrl": String("your-image-url.jpg"),
"largeImageUrl": String("your-image-url.jpg")
}
},
reprompt: {
outputSpeech: {
type: 'PlainText',
text: 'Session will end soon'
}
},
shouldEndSession: false
};
}
I have the courseWorkId, the courseId, and the student email. I have verified they're correct using the API explorer to get the data. I want to get the studentSubmissionId so I can patch the grade and assign a score. I am not swift with JSON and cannot seem to parse the value I get for the response below.
function getSubmissionId(stdEmail) {
gapi.client.classroom.courses.courseWork.studentSubmissions.list({
courseId: gCourseId,
courseWorkId: gCourseWorkId,
userId:'stdEmail'
}).then(function(response) {
// response.result...??
// how to I parse this??
});
}
Check online on how to access JSON Objects:
var myObj = { "name":"John",
"age":30,
"car":{
"brand": "Ferrari"
}
};
x = myObj.age
y = myObj["car"]["brand"];
console.log(x); // returns 30
console.log(y); // returns Ferrari
This worked for me to get the studentSubmissionId.
function getSubmissionId(stdEmail) {
var gStudentSubmissionID;
gapi.client.classroom.courses.courseWork.studentSubmissions.list({
courseId: gCourseId,
courseWorkId: gCourseWorkId,
userId: stdEmail
}).then(function(response) {
gStudentSubmissionID = response.result.studentSubmissions[0].id;
//console.log(response.result.studentSubmissions[0].id);
console.log('associatedWithDeveloper: '+response.result.studentSubmissions[0].associatedWithDeveloper);
});
}
According to the docs, I should use a post_poll function to add the missing id field in the response.
How do I add the post_poll function ?
Here's my error:
Results must be an array, got: object,
({"totalevents":83,"events":[{"eventid":10266033,"c)
- Got a result missing the "id" property (83)
Tried following this but it is not clear to me, I'm very new to Zapier-CLI
Update - adding code
This is the function that returns the data:
const listEvents = (z, bundle) => {
console.log('listing events.. ');
let client_id = bundle.inputData.client_id;
const requestOpts = {
url: `https://wccqa.on24.com/wcc/api/v2/client/${client_id}/event`
};
return z.request(requestOpts)
.then((response) => {
return z.JSON.parse(response.content);
});
};
The sample response is the following, with the distiction that I added the id param manually to avoid errors when zapier test|push:
{
"id": 9964513,
"eventid": 9964513,
"archivestart": "2017-09-21T10:30:00-07:00",
"archiveend": "2018-09-21T10:30:00-07:00",
"description": "Zapier Event Test",
"iseliteexpired": "N",
"displaytimezonecd": "America/Bogota",
"eventtype": "Live Webcam ",
"regrequired": true,
"clientid": 22921,
"liveend": "2017-09-21T10:00:00-07:00",
"createtimestamp": "2017-09-21T09:47:44-07:00",
"audienceurl": "https://localhost.on24.com/wcc/r/9964513/C49755A02229BD48E6010848D7C81EF8",
"lastmodified": "2017-09-21T09:47:44-07:00",
"livestart": "2017-09-21T08:45:00-07:00",
"goodafter": "2017-09-21T09:00:00-07:00",
"regnotificationrequired": true,
"isactive": true,
"localelanguagecd": "en"
}
The ACTUAL response from the endpoint the following which is used in the app created in the Web Builder App instead of CLI and works fine:
{
"events": [
{
"eventid": 9964513,
"archivestart": "2017-09-21T10:30:00-07:00",
"archiveend": "2018-09-21T10:30:00-07:00",
"description": "Zapier Event Test",
"iseliteexpired": "N",
"displaytimezonecd": "America/Bogota",
"eventtype": "Live Webcam ",
"regrequired": true,
"clientid": 22921,
"liveend": "2017-09-21T10:00:00-07:00",
"createtimestamp": "2017-09-21T09:47:44-07:00",
"audienceurl": "https://localhost.on24.com/wcc/r/9964513/C49755A02229BD48E6010848D7C81EF8",
"lastmodified": "2017-09-21T09:47:44-07:00",
"livestart": "2017-09-21T08:45:00-07:00",
"goodafter": "2017-09-21T09:00:00-07:00",
"regnotificationrequired": true,
"isactive": true,
"localelanguagecd": "en"
}
],
"totalevents": 1
}
I was thinking something along the line of the following, but how do I register this ?
const postPoll = (event,z,bundle) => {
if(event.key === 'events'){
var results = z.JSON.parse(bundle.request.data).results;
var events = results.events.map(function(event){
event.id = event.eventid;
return event;
});
bundle.request.data = events;
}
};
module.exports = postPoll;
Nice, so you're almost there! CLI apps don't have pre_ and post_ poll methods. Instead, you put any manipulation after the response comes in.
const listEvents = (z, bundle) => {
console.log('listing events.. ');
let client_id = bundle.inputData.client_id;
const requestOpts = {
url: `https://wccqa.on24.com/wcc/api/v2/client/${client_id}/event`
};
return z.request(requestOpts)
.then((response) => {
return z.JSON.parse(response.content);
})
.then(data => {
const events = data.events; // array of events
return events.map(function(e){ // returns array of objects with `id` defined
e.id = e.event_id
return e
})
})
};