Skip to content

Latest commit

 

History

History
441 lines (325 loc) · 19.5 KB

Useful.md

File metadata and controls

441 lines (325 loc) · 19.5 KB

Alexa Skills Kit Blog

Alexa Design Guide

Account Linking

Your system must support OAuth 2.0 and the authorization code grant type.

Understand Account Linking for Alexa Skills

How to Set Up Alexa Account Linking with Amazon Cognito User Pools to Create a Personalized Customer Experience

Amazon Cognito - Post Confirmation Lambda Trigger

Calling DynamoDB using AWS Cognito triggers

AWS SDK for JavaScript CognitoIdentityProvider

Validate and Use Access Tokens in Custom Skill Code

For a request that requires authentication, your code should do at least two checks:

  • Verify that the accessToken exists.
  • Verify that the token represents a valid user in your resource server.
exports.handler = async (event, context) => {
  let date = new Date();

  const tableName = process.env.TableName;
  const region = process.env.Region;

  console.log(`Table: ${tableName} Region: ${region}`);

  AWS.config.update({ region: "REGION" });

  // If the required parameters are present, proceed
  if (event.request.userAttributes.sub) {
    let ddbParams = {
      TableName: tableName,
      Item: {
        id: { S: event.request.userAttributes.sub },
        UserEmail: { S: event.request.userAttributes.email },
        given_name: { S: event.request.userAttributes.given_name },
        family_name: { S: event.request.userAttributes.family_name },
        address: { S: event.request.userAttributes.address },
        phone_number: { S: event.request.userAttributes.address },
        createdAt: { S: date.toISOString() },
      },
    };

    // Call DynamoDB
    try {
      await ddb.putItem(ddbParams).promise();
      console.log("Success");
    } catch (err) {
      console.log("Error", err);
    }
    console.log("Success: Everything executed correctly");
    context.done(null, event);
  } else {
    // Nothing to do, the user's email ID is unknown
    context.done(null, event);
  }
};

Reminders API

Remind Customers of Important Tasks or Events with the Reminders API

Integrate the Reminders API with Your Skill to Deepen Customer Engagement

Alexa Reminders Overview

Alexa Reminders API Reference

Skill Events in Alexa Skills

Reminders API

Integrate the Reminders API with Your Skill to Deepen Customer Engagement

Proactive Events API

pre-defined set of schemas that you can leverage to set up your skill service to use proactive events. Your skill can send proactive events information that conforms to a schema via the Skill Management API (SMAPI) as part of a skill manifest. Each schema has a predefined template that represents the text that Alexa reads back to the customer.

Proactive Events API Reference

Alexa Proactive Events API

Proactive Events Sender (beta)

Get started with Amazon Alexa Skills Proactive Events API

Get started with Amazon Alexa Skills Proactive Events API

Interface ProactiveSubscriptionChangedRequest

How to Add the ProactiveEvents API to Your Existing Alexa Skill

How to Send Media Event Notifications to Your Alexa Skill Customers with the ProactiveEvents API

Proactive Events API

import got from "got";
import {
  AlexaClientId,
  AlexaClientSecret,
  AlexaUserId,
  proactiveEventsApi,
} from "./config";

function getMediaContentAvailableSchema(userId: string, message: string) {
  let timestamp = new Date();
  let expiryTime = new Date();
  expiryTime.setMinutes(expiryTime.getMinutes() + 60);

  let referenceId = `ReferenceIdPairing-${new Date().getTime()}`;

  const mediaContentJson = {
    timestamp: timestamp.toISOString(),
    referenceId: referenceId,
    expiryTime: expiryTime.toISOString(),
    event: {
      name: "AMAZON.MediaContent.Available",
      payload: {
        availability: {
          startTime: timestamp,
          provider: {
            name: "localizedattribute:providerName",
          },
          method: "RELEASE",
        },
        content: {
          name: "localizedattribute:contentName",
          contentType: "GAME",
        },
      },
    },
    localizedAttributes: [
      {
        locale: "en-GB",
        providerName: "Provider Name",
        contentName: message,
      },
    ],
    relevantAudience: {
      type: "Unicast",
      payload: {
        user: userId,
      },
    },
  };

  return mediaContentJson;
}

const getToken = async () => {
  try {
    const response = await got.post("https://api.amazon.com/auth/o2/token", {
      json: true,
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: {
        grant_type: "client_credentials",
        client_id: AlexaClientId,
        client_secret: AlexaClientSecret,
        scope: "alexa::proactive_events",
      },
      form: true,
    });

    // {
    //     access_token: 'Atc|XXXXXXXXXXXXXXXXXXXXXXXXX',
    //     scope: 'alexa::proactive_events',
    //     token_type: 'bearer',
    //     expires_in: 3600
    // }
    const tokenRequestId = response.headers["x-amzn-requestid"];
    console.log(`Token requestId: ${tokenRequestId}`);
    const accessToken = response.body.access_token;
    console.log(`accessToken: ${accessToken}`);

    return accessToken;
  } catch (error) {
    return error.response.body;
  }
};

const sendEvent = async (token: string, userId: string, message: string) => {
  try {
    const response = await got.post(
      proactiveEventsApi.development.northAmerica.url,
      {
        json: true,
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: getMediaContentAvailableSchema(userId, message),
      }
    );

    if ([200, 202].includes(response.statusCode)) {
      console.log("successfully sent event");
      console.log(`requestId: ${response.headers["x-amzn-requestid"]}`);
    } else {
      console.log(`Error https response: ${response.statusCode}`);
      console.log(`requestId: ${response.headers["x-amzn-requestid"]}`);

      if ([403].includes(response.statusCode)) {
        console.log(`userId ${userId} may not have subscribed to this event.`);
      }
    }
  } catch (error) {
    return error.response.body;
  }
};

async function notify(userId: string, message: string) {
  const token = await getToken();
  const status = await sendEvent(token, userId, message);

  return status;
}

notify(AlexaUserId, "Your device has been paired").then((p) => console.log(p));

Things to check

  • the skill.json has the right event(s) listed as being published
  • the device locale matches one of the locales included in the message
  • the customer id (userId) is correct - it normally will change each time you disable/re-enable the skill

Alexa Skill Events

Skill Events in Alexa Skills Alexa Skill Events

  • AccountLinkedBody
  • AccountLinkedRequest
  • Permission
  • PermissionAcceptedRequest
  • PermissionBody
  • PermissionChangedRequest
  • ProactiveSubscriptionChangedBody
  • ProactiveSubscriptionChangedRequest
  • ProactiveSubscriptionEvent
  • SkillDisabledRequest

Settings API

Personalize Your Alexa Skill with Customer-Specific Time Zones and Measurements Using the Alexa Settings API

Using the Alexa Settings API to Look Up the Device Time Zone

UserVoice

Obtain Customer Settings Information with the Alexa Settings API

Skill Messaging API

Skill Messaging API Reference

The Skill Messaging API is used to send message requests to skills.

Service API

Calling Alexa Service APIs

Get the user current time from your Alexa Skill

const getCurrentDate = async (handlerInput) => {
  const serviceClientFactory = handlerInput.serviceClientFactory;
  const deviceId = handlerInput.requestEnvelope.context.System.device.deviceId;

  try {
    const upsServiceClient = serviceClientFactory.getUpsServiceClient();
    return (userTimeZone = await upsServiceClient.getSystemTimeZone(deviceId));
  } catch (error) {
    if (error.name !== "ServiceError") {
      return handlerInput.responseBuilder
        .speak("There was a problem connecting to the service.")
        .getResponse();
    }
    console.log("error", error.message);
  }
};
const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
	.addRequestHandlers(...)
    .withApiClient(new Alexa.DefaultApiClient())
	.withSkillId(...)
	.lambda();

When you use .standard() Skill Builder instead of the .custom() one, you _ don't need to_ include .withApiClient(new Alexa.DefaultApiClient()). The Standard includes it by default. Ref

Device Settings Demo

export const getUnixTimestampForTimeZone = (deviceTimeZone: string) => {
  dayjs.extend(utc);
  dayjs.extend(timezone);
  return dayjs().tz(deviceTimeZone).valueOf();
};

List of tz database time zones

Location Services

Enhance Your Customer Experience with Real-Time Location Services for Alexa Skills

Use Location Services for Alexa Skills

If context.Geolocation.locationServices.access is "ENABLED" and context.Geolocation.locationServices.status is "RUNNING", then the device is sharing location.

For stationary devices like the Amazon Echo, Alexa can provide you the device's physical address with the Device Address API. Stationary devices do not provide geo-coordinate data through location services.

New Location Services Permissions Inconsistency

const LOCATION_SERVICES_PERMISSION = ["alexa::devices:all:geolocation:read"];

const [permission] = LOCATION_SERVICES_PERMISSION;
if (!isPermissionGranted(handlerInput, permission)) {
  return responseBuilder
    .speak(
      "Location Services permission is required to use this feature. Please enable it in the Alexa app."
    )
    .withAskForPermissionsConsentCard(LOCATION_SERVICES_PERMISSION)
    .getResponse();
}

/**
 * @param {Object} handlerInput Alexa handlerInput object
 * @param {String} permission Alexa Skill permission to check for
 * @returns {Boolean} true if permission is granted. false if it is not
 * @description checks to see if the desired Skill permission is granted
 */
function isPermissionGranted(handlerInput, permission) {
  const {
    requestEnvelope: {
      context: {
        System: { user },
      },
    },
  } = handlerInput;
  return (
    user.permissions &&
    user.permissions.scopes &&
    user.permissions.scopes[permission] &&
    user.permissions.scopes[permission].status === "GRANTED"
  );
}

Location-aware Alexa Skills with HERE Location Services

New Permissions Card for Requesting Customer Consent

//determine if the device is capable of supporting location services
var isGeolocationSupported =
  context.System.device.supportedInterfaces.Geolocation;

var lat = geo.coordinate.latitudeInDegrees;
var lng = geo.coordinate.longitudeInDegrees;

Developing Location-Aware Alexa Skills

AMAZON.YesIntent and AMAZON.NoIntent Now Compatible with ASK Dialog Management Features

Using Yes/No Intents with Dialog Management

Intent Request History API

Intent Request History API

Collect all user utterance provided to your skill on a daily basis but your skill must have at least 10 unique users per locale in a day, in order for data to be available for that locale for that day

FallbackIntent

AMAZON.FallbackIntent in the Alexa Skills Kit (ASK) built-in library helps you handle unexpected utterances, or when a customer says something that doesn’t map to any intents in your skill aka “out-of-domain requests.”

The Built-In FallbackIntent, PhoneNumber Slot (Beta), and Ordinal Slot (Beta) Get Expanded Locale Support

There is no way to capture the utterance that triggered the FallbackIntent.

Use the New Fallback Intent to Respond Gracefully to Unexpected Customer Requests

Code First Alexa Skill Development With VS Code and the ASK SDK Controls Framework

5 Common Error Messages for Custom Alexa Skills and How to Troubleshoot Them

Alexa: "There was a problem with the requested skill's response." - response JSON your skill sent back was malformed
Alexa: "Unable to reach the requested skill" - Alexa is unable to hit your endpoint

Lambda functions and custom skills

You do not need to verify that requests are coming from the Alexa service yourself. Access to execute your function is controlled by permissions within AWS instead.

It is recommended that you also enable skill ID verification for the function and provide the skill ID (also called the application ID) for your skill. This means your function can be invoked only if the skill ID in the Alexa Skills Kit request matches the skill ID configured in the trigger.When using skill ID verification, you do not need to include code to verify that the request is intended for your service. Requests from unverified skills do not invoke your function.

Progressive Response

Progressive response is interstitial SSML content (including text-to-speech and short audio) that Alexa plays while waiting for your full skill response.

Extend a Built-in Intent with Sample Utterances

Extend a Built-in Intent with Sample Utterances

You can extend the standard built-in intents by providing additional sample utterances. This is useful for invoking the intents with skill-specific utterances.

You cannot extend any of the other built-in intents, such as WeatherForecast or LocalBusiness.

Available standard built-in intents for Alexa-enabled devices with a screen

AWS Certified Alexa Skill Builder – Specialty (AXS-C01) Exam Learning Path

Request and Response JSON Reference

Request and Response JSON Reference

Request Types Reference (LaunchRequest, CanFulfillIntentRequest, IntentRequest, SessionEndedRequest)

Delegate the Dialog to Alexa

Delegate the Dialog to Alexa