All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.klerch.alexa.tellask.model.AlexaOutput Maven / Gradle / Ivy

Go to download

This SDK is an extension to the Alexa Skills SDK for Java. It provides a framework for handling speechlet requests with multi-variant utterances organized in YAML files that make it easy to create localized skills. This SDK also lets you build your skill in declarative style and avoids a lot of boilerplate code.

There is a newer version: 0.2.3
Show newest version
/**
 * Created by Kay Lerch (https://twitter.com/KayLerch)
 *
 * Contribute to https://github.com/KayLerch/alexa-skills-kit-tellask-java
 *
 * Attached license applies.
 * This source is licensed under GNU GENERAL PUBLIC LICENSE Version 3 as of 29 June 2007
 */
package io.klerch.alexa.tellask.model;

import com.amazon.speech.ui.Card;
import io.klerch.alexa.state.model.AlexaStateModel;
import io.klerch.alexa.tellask.schema.type.AlexaOutputFormat;
import io.klerch.alexa.tellask.util.resource.ResourceUtteranceReader;
import io.klerch.alexa.tellask.schema.UtteranceReader;
import org.apache.commons.lang3.Validate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

/**
 * The AlexaOutput provides all the information necessary to generate the speechlet response.
 */
public class AlexaOutput {
    private String intentName;
    private Boolean shouldEndSession;
    private Boolean shouldReprompt;
    private final List models;
    private final List slots;
    private final Card card;
    private final UtteranceReader utteranceReader;
    private final String locale;

    private AlexaOutput(final AlexaOutputBuilder builder) {
        this.intentName = builder.intentName;
        this.shouldEndSession = builder.shouldEndSession;
        this.shouldReprompt = builder.shouldReprompt;
        this.models = builder.models;
        this.card = builder.card;
        this.slots = builder.slots;
        this.utteranceReader = builder.utteranceReader;
        this.locale = builder.locale;
    }

    /**
     * name of the output intent.
     * @return name of the output intent
     */
    public String getIntentName() {
        return intentName;
    }

    /**
     * True if the session ends on giving the next speechlet response
     * @return True if the session ends on giving the next speechlet response
     */
    public Boolean shouldEndSession() {
        return shouldEndSession;
    }

    /**
     * True if the speechlet response should contain a reprompt whose texts
     * are also managed in the YAML utterances. If there is no reprompt text
     * specified for this intent in the YAML utterances no reprompt will be
     * given back to the user.
     * @return True if the speechlet response should contain a reprompt
     */
    public Boolean shouldReprompt() {
        return shouldReprompt;
    }

    /**
     * All the state models whose state will be saved automatically when
     * giving AlexaOutput to the speechlet.
     * @return state models whose state will be saved automatically
     */
    public List getModels() {
        return models;
    }

    /**
     * All the slots whose values will be replaced with the corresponding placeholders
     * in the YAML utterances.
     * @return All the slots whose values will be replaced with the corresponding placeholders
     * in the YAML utterances.
     */
    public List getSlots() {
        return slots;
    }

    /**
     * Optionally a card will be attached to the speechlet response.
     * @return The card which will be attached to the speechlet response.
     */
    public Card getCard() {
        return card;
    }

    /**
     * The utterance reader is an implementation of logic reading utterances
     * from a store - likely a file sitting somewhere. This value (if set)
     * will override the reader configuration you might have done in either
     * the Lambda stream handler or the servlet. This getter will not return
     * the reader given in aforementioned configuration but only the reader
     * you provided on construction of this AlexaOutput.
     * @return The utterance reader implementation which will override the reader
     * eventually set in the Lambda stream handler or servlet.
     */
    public UtteranceReader getUtteranceReader() {
        return utteranceReader;
    }

    /**
     * The locale comes in with the speechlet request and indicates the spoken
     * language in the context the user session runs in
     * @return the locale
     */
    public String getLocale() {
        return locale;
    }

    /**
     * Entry point for creating a new AlexaOutput with its builder. A "tell" output
     * will close a session after it has been returned to the user
     * @param intentName the name of the output intent which is associated with a set
     *                   of reply utterances to choose from in the YAML utterances
     * @return a builder for creating a new AlexaOutput
     */
    public static AlexaOutputBuilder tell(final String intentName) {
        return new AlexaOutputBuilder(true, intentName);
    }

    /**
     * Entry point for creating a new AlexaOutput with its builder. An "ask" output
     * will NOT close a session after it has been returned to the user
     * @param intentName the name of the output intent which is associated with a set
     *                   of reply utterances to choose from in the YAML utterances
     * @return a builder for creating a new AlexaOutput
     */
    public static AlexaOutputBuilder ask(final String intentName) {
        return new AlexaOutputBuilder(false, intentName);
    }

    /**
     * A builder to create AlexaOutput objects
     */
    public static class AlexaOutputBuilder {
        private final String intentName;
        private final Boolean shouldEndSession;
        private Boolean shouldReprompt = false;
        private List models = new ArrayList<>();
        private List slots = new ArrayList<>();
        private Card card;
        private UtteranceReader utteranceReader;
        private String locale;

        private Predicate notExists = (final AlexaStateModel model) ->
                !(this.models.stream().anyMatch(m -> m.getModel().equals(model)));

        private AlexaOutputBuilder(final Boolean shouldEndSession, final String intentName) {
            this.shouldEndSession = shouldEndSession;
            this.intentName = intentName;
        }

        /**
         * A slot provides a value to fill in a placeholder in the reply utterance.
         * When not giving this slot a specific format it will be returned as text.
         * @param slotName name of the slot equal to the placeholder-name in the reply utterance
         * @param slotValue value of the slot filling in the utterance placeholder
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder putSlot(final String slotName, final Object slotValue) {
            slots.add(new AlexaOutputSlot(slotName, slotValue));
            return this;
        }

        /**
         * A slot provides a value to fill in a placeholder in the reply utterance.
         * @param slotName name of the slot equal to the placeholder-name in the reply utterance
         * @param slotValue value of the slot filling in the utterance placeholder
         * @param slotFormat the format a value is applied with when filling it in the utterance placeholder
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder putSlot(final String slotName, final Object slotValue, final AlexaOutputFormat slotFormat) {
            slots.add(new AlexaOutputSlot(slotName, slotValue).formatAs(slotFormat));
            return this;
        }

        /**
         * A slot provides a value to fill in a placeholder in the reply utterance.
         * @param slot the slot object containg all the details like the slot name, value and format
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder putSlot(final AlexaOutputSlot slot) {
            slots.add(slot);
            return this;
        }

        /**
         * One to many state models whose AlexaStateSave-annotated fields will be saved
         * to the associated state handler. If this model does not bring a handler with it
         * the speechlet handler will automatically use the AlexaSessionStateHandler to save
         * state to the local session object.
         * @param models One to many state models whose AlexaStateSave-annotated fields will be saved
         *               to the associated state handler.
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder putState(final AlexaStateModel... models) {
            if (models == null) {
                return this;
            }
            return withDeduplicatedStateOf(Arrays.asList(models));
        }

        /**
         * One to many state models whose AlexaStateSave-annotated fields will be saved
         * to the associated state handler. If this model does not bring a handler with it
         * the speechlet handler will automatically use the AlexaSessionStateHandler to save
         * state to the local session object.
         * @param models One to many state models whose AlexaStateSave-annotated fields will be saved
         *               to the associated state handler.
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder putState(final Collection models) {
            return withDeduplicatedStateOf(models);
        }

        /**
         * Overrides the locale which comes in with the speechlet request.
         * @param locale The new locale to use when replying to the user
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder withLocale(final String locale) {
            this.locale = locale;
            return this;
        }

        private AlexaOutputBuilder withDeduplicatedStateOf(final Collection stateModels) {
            stateModels.stream().filter(notExists)
                    .map(AlexaIntentModel::new)
                    .forEach(models::add);
            return this;
        }

        /**
         * The card attached to the speechlet response.
         * @param card The card attached to the speechlet response.
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder withCard(final Card card) {
            this.card = card;
            return this;
        }

        /**
         * Set true if the speechlet response should contain a reprompt whose texts
         * are also managed in the YAML utterances. If there is no reprompt text
         * specified for this intent in the YAML utterances no reprompt will be
         * given back to the user. By default this value is set to false
         * @param shouldReprompt Set true if the speechlet response should contain a reprompt
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder withReprompt(final boolean shouldReprompt) {
            this.shouldReprompt = shouldReprompt;
            return this;
        }

        /**
         * The utterance reader is an implementation of logic reading utterances
         * from a store - likely a file sitting somewhere. By default AlexaOutput will
         * be associated with the ResourceUtteranceReader loading utterances from YAML
         * files in your /resources/{locale}/utterances.yml. If you override the reader
         * in here it might also override the reader configuration you already made in
         * the Lambda stream handler or in the servlet.
         * @param reader The utterance reader implementation of your desire
         * @return the AlexaOutput builder
         */
        public AlexaOutputBuilder withReader(final UtteranceReader reader) {
            this.utteranceReader = reader;
            return this;
        }

        /**
         * Builds the AlexaOutput. Be sure you provided at least a non-blank intent-name.
         * @return the AlexaOutput builder
         */
        public AlexaOutput build() {
            Validate.notBlank(intentName, "Intent name must not be blank.");
            return new AlexaOutput(this);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy