Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from rewe-digital-incubator/alexa_plugin
Browse files Browse the repository at this point in the history
Alexa plugin
  • Loading branch information
rekire authored Mar 20, 2019
2 parents 7ea42fa + 76fe5de commit 30ec25b
Show file tree
Hide file tree
Showing 31 changed files with 580 additions and 453 deletions.
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ write your own voice application fast.
## Top features

- Dialogflow V2 API reference implementation written in Kotlin
- Multiplatform support, one service for Alexa and Google Assistant
- Intercepter API for adding e.g. tracking
- Spring reference implementation
- SSML builder for creating rich responses
Expand Down Expand Up @@ -110,6 +111,57 @@ the spring-sample project as template for your own project. When you use our plu
injection. The best it that we use it also for discovering your intent handler automatically. For the fallback intent
you should use the annotation `@FallbackIntentHandler`, if it is missing you service won't start up.

## Adding Multiplatform Support

You can add the Alexa plugin to build a service which can serve Alexa and the Google Assistant at once. You just have
to change your IntentHandler to implement the MultiplatformIntentHandler.

class WelcomeIntentHandler : MultiPlatformIntentHandler {

override fun canHandleAlexa(input: HandlerInput) =
input.matches(Predicates.requestType(LaunchRequest::class.java))

override fun handleAlexa(input: HandlerInput): Optional<Response> {
return input.responseBuilder
.withSpeech("Welcome to Dialog!")
.build()
}

override fun canHandleDialogflowIntent(handler: DialogflowHandler): Boolean {
return handler.action?.equals("input.welcome") ?: false
}

override fun handleDialogflowIntent(handler: DialogflowHandler): DialogflowResponseBuilder {
return handler.responseBuilder.withText("Welcome to Dialog!")
}
}

If you're using Dialog with Spring you can add the `alexa-spring-plugin` which will automatically provide a Bean of
`IntentHandlerHolder`. This call contains the list of all Alexa `RequestHandler` which can be added to the
`CustomSkillBuilder` like in the following example.

@Configuration
class AlexaConfig {

@Bean
fun provideSkill(
intentHandlerHolder: IntentHandlerConfig.IntentHandlerHolder,
interceptorHolder: InterceptorConfig.InterceptorHolder
): Skill =
CustomSkillBuilder()
.addRequestHandlers(intentHandlerHolder.intentHandlers)
.apply {
interceptorHolder.requestInterceptors.forEach {
addRequestInterceptor(it)
}
interceptorHolder.responseInterceptors.forEach {
addResponseInterceptor(it)
}
}
.withApiClient(ApacheHttpApiClient.standard())
.build()
}

## License

The MIT license (MIT)
Expand Down Expand Up @@ -142,4 +194,4 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
[DialogflowIntentHandler]: docs/core/org.rewedigital.dialog.handler/-dialogflow-intent-handler/index.md
[intent-methods]: docs/core/org.rewedigital.dialog.handler/-dialogflow-intent-handler/index.md#Functions
[ResponseBuilder]: docs/core/org.rewedigital.dialog.handler/-dialogflow-response-builder/index.md
[RequestInterceptor]: docs/core/org.rewedigital.dialog.interceptors/-request-interceptor/index.md
[RequestInterceptor]: docs/core/org.rewedigital.dialog.interceptors/-request-interceptor/index.md
26 changes: 26 additions & 0 deletions alexa-plugin/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apply plugin: 'kotlin'
apply from: '../docu.gradle'

group 'org.rewedigital.voice.dialog'
version rootProject.ext.versions.alexa
description 'The Alexa plugin for Dialog to write voice applications for Dialogflow and Alexa.'

dependencies {
implementation project(":core")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.amazon.alexa:ask-sdk-core:2.11.2"
}

dokka {
externalDocumentationLink {
url = new URL("https://github.com/rewe-digital-incubator/${rootProject.name}/blob/master/docs/core/")
packageListUrl = java.nio.file.Paths.get("$rootDir/docs/core/package-list").toUri().toURL()
}
}

task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputFormat = 'javadoc'
outputDirectory = "$buildDir/javadoc"
}

apply from: '../publish.gradle'
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.rewedigital.dialog.alexa

import com.amazon.ask.dispatcher.request.handler.HandlerInput
import com.amazon.ask.dispatcher.request.handler.RequestHandler
import com.amazon.ask.model.Response
import org.rewedigital.dialog.handler.DialogflowIntentHandler
import java.util.*


interface MultiPlatformIntentHandler : DialogflowIntentHandler, RequestHandler {

override fun canHandle(input: HandlerInput) = canHandleAlexa(input)

override fun handle(input: HandlerInput) = handleAlexa(input)

fun canHandleAlexa(input: HandlerInput): Boolean

fun handleAlexa(input: HandlerInput): Optional<Response>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.rewedigital.dialog.alexa

import com.amazon.ask.dispatcher.request.handler.HandlerInput
import org.rewedigital.dialog.interceptors.RequestInterceptor


interface MultiPlatformRequestInterceptor : RequestInterceptor,
com.amazon.ask.dispatcher.request.interceptor.RequestInterceptor {

override fun process(input: HandlerInput) = onAlexaRequest(input)

fun onAlexaRequest(input: HandlerInput)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.rewedigital.dialog.alexa

import com.amazon.ask.dispatcher.request.handler.HandlerInput
import com.amazon.ask.model.Response
import org.rewedigital.dialog.interceptors.ResponseInterceptor
import java.util.*


interface MultiPlatformResponseInterceptor : ResponseInterceptor,
com.amazon.ask.dispatcher.request.interceptor.ResponseInterceptor {

override fun process(input: HandlerInput, response: Optional<Response>) = onAlexaResponse(input, response)

fun onAlexaResponse(input: HandlerInput, response: Optional<Response>)
}
34 changes: 34 additions & 0 deletions alexa-spring-plugin/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply from: '../docu.gradle'

group 'org.rewedigital.voice.dialog'
version rootProject.ext.versions.alexa
description 'The Alexa plugin for the Spring plugin of Dialog.'

buildscript {
repositories {
jcenter()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-allopen:${rootProject.ext.versions.kotlin}")
}
}

dependencies {
implementation project(':core')
implementation project(':alexa-plugin')
implementation project(':spring-plugin')

implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.springframework:spring-context:5.1.4.RELEASE'
implementation 'com.amazon.alexa:ask-sdk-core:2.11.2'
}

task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputFormat = 'javadoc'
outputDirectory = "$buildDir/javadoc"
}

apply from: '../publish.gradle'
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.rewedigital.dialog.alexa.spring

import com.amazon.ask.dispatcher.request.handler.RequestHandler
import org.rewedigital.dialog.spring.annotations.IntentHandler
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.lang.instrument.IllegalClassFormatException


@Configuration
class IntentHandlerConfig {

data class IntentHandlerHolder(val intentHandlers: List<RequestHandler>)

@Bean
fun provideIntentHandlerHolder(context: ApplicationContext): IntentHandlerHolder {

val allIntentHandler =
context
.getBeanNamesForAnnotation(IntentHandler::class.java)
.map {
context.getBean(it) as? RequestHandler
?: throw IllegalClassFormatException("Your IntentHandler must implement the interface MultiPlatformIntentHandler")
}

return IntentHandlerHolder(allIntentHandler)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.rewedigital.dialog.alexa.spring

import com.amazon.ask.dispatcher.request.interceptor.RequestInterceptor
import com.amazon.ask.dispatcher.request.interceptor.ResponseInterceptor
import org.rewedigital.dialog.spring.annotations.Interceptor
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration


@Configuration
class InterceptorConfig {

data class InterceptorHolder(
val requestInterceptors: List<RequestInterceptor>,
val responseInterceptors: List<ResponseInterceptor>
)

@Bean
fun provideInterceptorHolder(context: ApplicationContext): InterceptorHolder {

val requestInterceptors = mutableListOf<RequestInterceptor>()
val responseInterceptors = mutableListOf<ResponseInterceptor>()

context.getBeanNamesForAnnotation(Interceptor::class.java)
.forEach { beanName ->
context.getBean(beanName)
.also { bean ->
if (bean is RequestInterceptor) {
requestInterceptors.add(bean)
}
if (bean is ResponseInterceptor) {
responseInterceptors.add(bean)
}
}
}

return InterceptorHolder(requestInterceptors, responseInterceptors)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.rewedigital.dialog.alexa.spring.IntentHandlerConfig,org.rewedigital.dialog.alexa.spring.InterceptorConfig
14 changes: 8 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@ buildscript {
ext.versions = [:]
ext.versions.kotlin = '1.3.21'
ext.versions.dokka = '0.9.17'
ext.versions.core = '1.0'
ext.versions.ssml = '1.0'
ext.versions.ssml_plugin = '1.0'
ext.versions.spring_plugin = '1.0'
ext.versions.konversation_plugin = '1.0'
ext.versions.core = '1.0.1'
ext.versions.alexa = '1.0.1'
ext.versions.ssml = '1.0.1'
ext.versions.ssml_plugin = '1.0.1'
ext.versions.spring_plugin = '1.0.1'
ext.versions.konversation_plugin = '1.0.1'

repositories {
mavenLocal()
jcenter()
maven { url 'https://novoda.bintray.com/snapshots' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.novoda:bintray-release:0.9'
classpath 'com.novoda:bintray-release:SNAPSHOT-13'
}
}

Expand Down
76 changes: 4 additions & 72 deletions core/build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
apply plugin: 'kotlin'
apply plugin: 'maven-publish'
apply plugin: 'com.novoda.bintray-release'
apply from: '../docu.gradle'

group "org.rewedigital.voice"
version "${rootProject.ext.versions.core}"
group 'org.rewedigital.voice:dialog'
version rootProject.ext.versions.core
description 'Dialog is a Dialogflow v2 API implementation written in Kotlin. With some great optional extensions you can use to write your own voice applications fast.'

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
Expand All @@ -15,71 +14,4 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputDirectory = "$buildDir/javadoc"
}

task sourcesJar(type: Jar, dependsOn: classes) {
archiveClassifier = 'sources'
from sourceSets.main.allSource
}

task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
archiveClassifier = 'javadoc'
from "$buildDir/javadoc"
}

publishing.publications {
core(MavenPublication) {
groupId = "org.rewedigital.voice"
artifactId = "dialog"
version = "${rootProject.ext.versions.core}"
artifact sourcesJar {
archiveClassifier = 'sources'
}
artifact javadocJar {
archiveClassifier = 'javadoc'
}
from components.java
pom {
name = 'dialog'
description = 'Dialog is a Dialogflow v2 API implementation written in Kotlin. With some great optional extensions you can use to write your own voice application fast.'
url = 'https://github.com/rewe-digital-incubator/dialog'
licenses {
license {
name = 'MIT License'
url = 'https://opensource.org/licenses/MIT'
distribution = 'repo'
}
}
developers {
// TODO use the MAINTAINERS file
developer {
name = 'Volkmar Vogel'
email = '[email protected]'
}
developer {
name = 'René Kilczan'
email = '[email protected]'
}
}

scm {
connection = 'scm:git:git://github.com/rewe-digital-incubator/dialog.git'
developerConnection = 'scm:git:git://github.com/rewe-digital-incubator/dialog.git'
url = 'https://github.com/rewe-digital-incubator/dialog'
}
}
}
}

publish {
userOrg = 'rewe-digital'
groupId = 'org.rewedigital.voice'
artifactId = 'core'
repoName = 'dialog'
publishVersion = rootProject.ext.versions.core
desc = 'Dialog is a Dialogflow v2 API implementation written in Kotlin. With some great optional extensions you can use to write your own voice applications fast.'
website = 'https://github.com/rewe-digital-incubator/dialog'
licences = ['MIT']
bintrayUser = project.properties['bintray.user']
bintrayKey = project.properties['bintray.apikey']
dryRun = false
publications = ['core']
}
apply from: '../publish.gradle'
Loading

0 comments on commit 30ec25b

Please sign in to comment.