diff --git a/platforms/platform-alexa/docs/README.md b/platforms/platform-alexa/docs/README.md index 6d81d50dd..4bf2c766d 100644 --- a/platforms/platform-alexa/docs/README.md +++ b/platforms/platform-alexa/docs/README.md @@ -631,6 +631,50 @@ This would result in the following output template: } ``` +### Custom Tasks + +You can handle [custom tasks](https://developer.amazon.com/en-US/docs/alexa/custom-skills/understand-tasks.html) with the following features: + +- `this.$alexa.task` property that offers helper methods and properties to access task related values +- `AlexaHandles.onTask()` for the [`@Handle` decorator](https://www.jovo.tech/docs/handle-decorators) to accept task requests + +Here is an example: + +```typescript +import { AlexaHandles } from '@jovotech/platform-alexa'; +// ... + +@Handle(AlexaHandles.onTask('Reminder', 1)) +reminderTask(): Promise { + const task = this.$alexa?.task.getTask(); + const taskName = this.$alexa?.task.name; + const taskVersion = this.$alexa?.task.version; + const input = this.$alexa?.task.input; + console.log(task, taskName, taskVersion, input); + + // ... +} +``` + +To learn more about all features of the `task` property, take a look at the [`AlexaTask.ts` file](https://github.com/jovotech/jovo-framework/blob/v4/latest/platforms/platform-alexa/src/AlexaTask.ts). + + +Under the hood, `onTask()` as part of [`AlexaHandles`](https://github.com/jovotech/jovo-framework/blob/v4/latest/platforms/platform-alexa/src/AlexaHandles.ts) looks like this with the parameters `taskName: string` and optional `taskVersion?: number | string`: + +```typescript +{ + types: [InputType.Launch], + if: (jovo: Jovo) => { + const task = jovo.$alexa?.task.getTask(); + if (!task) return false; + if (!jovo.$alexa?.task.hasTaskName(taskName)) return false; + if (taskVersion && !jovo.$alexa.task.isVersion(taskVersion)) return false; + return true; + }, +} +``` + + ### Name-free Interaction You can handle [name-free interactions](https://developer.amazon.com/docs/alexa/custom-skills/implement-canfulfillintentrequest-for-name-free-interaction.html) by responding to `CanFulfillIntentRequest` requests. To do this, you can use the following two helpers: diff --git a/platforms/platform-alexa/src/Alexa.ts b/platforms/platform-alexa/src/Alexa.ts index eb36866e4..3bfadfbeb 100644 --- a/platforms/platform-alexa/src/Alexa.ts +++ b/platforms/platform-alexa/src/Alexa.ts @@ -7,6 +7,7 @@ import { AlexaUser } from './AlexaUser'; import { AlexaEntity } from './interfaces'; import { AlexaIsp } from './AlexaIsp'; import { AlexaAudioPlayer } from './AlexaAudioPlayer'; +import { AlexaTask } from './AlexaTask'; export class Alexa extends Jovo< AlexaRequest, @@ -19,11 +20,13 @@ export class Alexa extends Jovo< $entities!: EntityMap; isp: AlexaIsp; audioPlayer: AlexaAudioPlayer; + task: AlexaTask; constructor($app: App, $handleRequest: HandleRequest, $platform: AlexaPlatform) { super($app, $handleRequest, $platform); this.isp = new AlexaIsp(this); this.audioPlayer = new AlexaAudioPlayer(this); + this.task = new AlexaTask(this); } getSkillId(): string | undefined { return ( diff --git a/platforms/platform-alexa/src/AlexaHandles.ts b/platforms/platform-alexa/src/AlexaHandles.ts index 23ee29ad0..74a579834 100644 --- a/platforms/platform-alexa/src/AlexaHandles.ts +++ b/platforms/platform-alexa/src/AlexaHandles.ts @@ -1,4 +1,4 @@ -import { EnumLike, HandleOptions, Jovo } from '@jovotech/framework'; +import { EnumLike, HandleOptions, InputType, Jovo } from '@jovotech/framework'; import { AlexaRequest } from './AlexaRequest'; import { PermissionStatus, PurchaseResultLike } from './interfaces'; @@ -93,4 +93,17 @@ export class AlexaHandles { platforms: ['alexa'], }; } + + static onTask(taskName: string, taskVersion?: number | string): HandleOptions { + return { + types: [InputType.Launch], + if: (jovo: Jovo) => { + const task = jovo.$alexa?.task.getTask(); + if (!task) return false; + if (!jovo.$alexa?.task.hasTaskName(taskName)) return false; + if (taskVersion && !jovo.$alexa.task.isVersion(taskVersion)) return false; + return true; + }, + }; + } } diff --git a/platforms/platform-alexa/src/AlexaTask.ts b/platforms/platform-alexa/src/AlexaTask.ts new file mode 100644 index 000000000..d53ab9de5 --- /dev/null +++ b/platforms/platform-alexa/src/AlexaTask.ts @@ -0,0 +1,41 @@ +import { Alexa } from './Alexa'; +import { Request } from './interfaces'; + +export class AlexaTask { + constructor(private alexa: Alexa) {} + + getTask(): Request['task'] { + return this.alexa.$request.request?.task; + } + + get version(): string | undefined { + return this.getTask()?.version; + } + + get name(): string | undefined { + return this.getTask()?.name; + } + + get input(): Record | undefined { + return this.getTask()?.input; + } + + /** + * Looks at the task name and checks if it ends with the provided taskName. Ignores skillId prefix + * @param taskName Task name without skill id + * @returns true, if task name comes after the skill id + */ + hasTaskName(taskName: string): boolean { + if (!this.name) return false; + return this.name.endsWith(`.${taskName}`); + } + + isVersion(version: number | string): boolean { + if (!this.version) return false; + return this.version === `${version}`; + } + + toJSON(): AlexaTask { + return { ...this, alexa: undefined }; + } +} diff --git a/platforms/platform-alexa/src/interfaces.ts b/platforms/platform-alexa/src/interfaces.ts index ad8797092..a3f189e68 100644 --- a/platforms/platform-alexa/src/interfaces.ts +++ b/platforms/platform-alexa/src/interfaces.ts @@ -222,6 +222,11 @@ export interface Request { listId?: string; listItemIds?: string[]; }; + task?: { + name: string; + version: string; + input: Record; + }; } // Defines a target for Alexa Conversations