FactoryをつかってAlexaの処理をDRYに書く
APIを呼び出す系のスキルなどでは、権限確認などの処理をどのハンドラーにも書かないといけないということがあります。 この例では、アカウントリンクのアクセストークンの有無を確認し、存在しない場合にアカウントリンクを促す処理 […]
広告ここから
広告ここまで
目次
APIを呼び出す系のスキルなどでは、権限確認などの処理をどのハンドラーにも書かないといけないということがあります。
const ListMessageIntentHandler: RequestHandler = {
  canHandle(handlerInput) {
    return isMatchedIntent(handlerInput, 'ListMessageIntent');
  },
  async handle(handlerInput) {
    const token = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope)
    if (!token) {
      return handlerInput.responseBuilder
        .speak('Please link your Alexa account to AAA account.')
        .withLinkAccountCard()
        .getResponse();
    }
    const client = new SomeAPI({token})
    const items = await client.messages()
    const message = items.member.map(item => item.name).join(' and ')
    return handlerInput.responseBuilder
      .speak('This is your messages. ' + message)
      .reprompt('What is next?')
      .getResponse()
  },
};
const ListItemIntentHandler: RequestHandler = {
  canHandle(handlerInput) {
    return isMatchedIntent(handlerInput, 'ListItemIntent');
  },
  async handle(handlerInput) {
    const token = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope)
    if (!token) {
      return handlerInput.responseBuilder
        .speak('Please link your Alexa account to AAA account.')
        .withLinkAccountCard()
        .getResponse();
    }
    const client = new SomeAPI({token})
    const items = await client.list()
    const message = items.member.map(item => item.name).join(' and ')
    return handlerInput.responseBuilder
      .speak('This is your items. ' + message)
      .reprompt('What is next?')
      .getResponse()
  },
};この例では、アカウントリンクのアクセストークンの有無を確認し、存在しない場合にアカウントリンクを促す処理が重複しています。
インテントの数が増えると、これを毎回書くのが煩雑です。
そこでFactoryパターンで共通化してみましょう。
FactoryでRequestHandlerを作る
Factoryパターンで、必要なRequestHandlerを作ってもらう形にします。まずはFactoryを用意しましょう。
class SomeServiceRequestHandlerFactory {
  public static create(intentName: string, handle: (token: string, handlerInput: Alexa.HandlerInput) => Promise<Response> ): RequestHandler {
    return {
      canHandle(handlerInput: Alexa.HandlerInput) {
        return isMatchedIntent(handlerInput, intentName)
      },
      async handle(handlerInput) {
      const token = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope)
        if (!token) {
          return handlerInput.responseBuilder
            .speak('Please link your Alexa account to AAA account.')
            .withLinkAccountCard()
            .getResponse();
        }
        return handle(token, handlerInput)
      }
    }
  }
}あとは作りたいReqestHandlerをこのFactoryで作るだけです。
const ListMessageIntentHandler: RequestHandler =  SomeServiceRequestHandlerFactory.create(
  'ListItemIntent',
  async (token, handlerInput) => {
    const client = new SomeAPI({token})
    const items = await client.messages()
    const message = items.member.map(item => item.name).join(' and ')
    return handlerInput.responseBuilder
      .speak('This is your messages. ' + message)
      .reprompt('What is next?')
      .getResponse()
  },
};
const ListItemIntentHandler: RequestHandler = SomeServiceRequestHandlerFactory.create(
  'ListItemIntent',
  async (token, handlerInput) => {
    const client = new SomeAPI({token})
    const items = await client.list()
    const message = items.member.map(item => item.name).join(' and ')
    return handlerInput.responseBuilder
      .speak('This is your items. ' + message)
      .reprompt('What is next?')
      .getResponse()
  }
)かなり見通しがよくなりましたね。