DynamoDBに保存したIPアドレスをLambda関数で呼び出す

published_with_changes 更新日: event_note 公開日:

labelamazon alexa labelBlynk labelIoT labelRaspberry pi

Alexaスキル「アレクサ、部屋の温度を教えて」を実現のための第9歩になります。(最終章だと思います。)

Raspberry Pi でグローバルIPを取得し、AWS Lambda に送信する手順でルータのIPアドレスを自動で取得することに成功しました。しかし、問題があります。
    1つ目の問題は、Lambda関数がセッション終了後にIPアドレスを保持できず、スキルがエラーになることです。
  • 2つ目は、Lambda関数が常に動作していないと、Alexaスキルが正常に動作しないことです。 

本記事はこの解決策を作った記録です。 Google Gemini Logo を使いました。

Lamda関数はスキルを呼び出すと動きだします。常に動いていることはありません。自動で取得したルータのグローバルIPアドレスを使って、Blynk serverにリクエストする流れは、今までどうりです。


最新のIPアドレスを維持するメカニズム


現状の課題は、Raspberry PiからLambda関数に渡されたIPアドレスが、Lambda関数の実行終了とともに消失してしまうことです。このため、次のIPアドレスが送信されるまでの間、AlexaスキルからLambda関数を呼び出すとエラーが発生します。また、Lambda関数が常に実行されている必要があり、リソースの無駄が発生しています。

これらの課題を解決するためには、次の方法が考えられます。
  • IPアドレスを保管する場所(データベース)を設け、取得したIPアドレスをこれに格納する。
  • スキルが呼ばれ、Lambda関数がIPアドレスを必要なときに格納からIPアドレスを取り出す。
この時の格納場所としては、以下が考えられます。
  • Raspberry pi が取得したIPアドレスのログ記録
  • AWSのDynamoDBに最新のIPアドレスを格納

Lambda関数と同列に構築できるので、DynamoDBを使用することにします。

方法メリットデメリット
SSHで接続シンプルで直感的
リアルタイムアクセス可能
デバッグが容易
セキュリティリスク
(公開鍵、秘密鍵)
IPアドレスの変更に依存
スケーラビリティに制限
DynamoDBを使用永続的なデータ保存
高い可用性とスケーラビリティ自動化とスクリプト化が可能
コストが発生する場合がある
設定と管理が複雑
若干のラグタイム


手順:DynamoDBの利用

  1. Raspberry PiのPythonスクリプトの変更:DynamoDBへのIPアドレス保存
  2. Lambda関数の変更:DynamoDBから最新のIPアドレスを取得
    1. DynamoDBテーブルの作成:IPアドレスを保存するためのテーブルを作成
    2. Lambda Layerを作成する
      1. Node.js環境の構築:
      2. aws-sdkのインストール: Lambda関数のデプロイパッケージにaws-sdkを追加
      3. zipファイルの作成: aws-sdkのインクルード
      4. Layerの作成: aws-sdkを含むパッケージをLambdaにアップロード
      5. Lambda関数へのLayerの関連付け
      6. IAMロールの作成
    3. Lambda関数の作成

1. Pythonスクリプト(評価用)

元のPython スクリプトの report_ip.py を大幅に変更して、report_ip2.py を作ります。なお、ファイルのエンコーディングが UTF-8 以外の形式で保存されている場合は、このブログ記事:(2-5) エラーの修正 2に沿ってエンコードします。
  • IPアドレスをpayloadに含めてLambda関数に送信。
  • 送信日時とIPアドレス、レスポンスコードをログに出力するように変更。

※ コードは評価用としてwhileループと time.sleep で10分毎に出力させています。Lambda関数が完成して正規に運用するときはこのループを削除します。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import json
import datetime
import time

# AWS API Gateway のエンドポイントと API キー
LAMBDA_ENDPOINT = "https://your-api-gateway-url.amazonaws.com/prod/ip-report"
API_KEY = "YOUR_API_KEY"  # 実際の API キーに置き換えてください

def get_global_ip():
    """checkip.amazonaws.com からグローバル IP アドレスを取得する"""
    try:
        response = requests.get("https://checkip.amazonaws.com")
        return response.text.strip()
    except Exception as e:
        print(f"Error getting global IP: {e}")
        return None

def send_ip_to_lambda(ip):
    """取得した IP アドレスを Lambda に送信する"""
    try:
        payload = {
            'session': {
                'new': True,
                'sessionId': 'SessionId',
                'application': {
                    'applicationId': 'amzn1.ask.skill.[unique-value-here]'
                },
                'attributes': {},
                'user': {
                    'userId': 'amzn1.ask.account.[unique-value-here]'
                }
            },
            'request': {
                'type': 'LaunchRequest',
                'requestId': 'EdwRequestId',
                'locale': 'ja-JP'
            },
            'ip': ip # IPアドレスをpayloadに含める
        }
        headers = {'Content-Type': 'application/json', 'x-api-key': API_KEY}
        response = requests.post(LAMBDA_ENDPOINT, json=payload, headers=headers)
        log_message = f"{datetime.datetime.now()} - Sent IP: {ip}, Response: {response.status_code}"
        print(log_message) # ログを標準出力
    except Exception as e:
        log_message = f"{datetime.datetime.now()} - Error sending IP to Lambda: {e}"
        print(log_message) # ログを標準出力

if __name__ == "__main__":
    while True:# 評価用、繰り返し
        ip = get_global_ip()
        if ip:
            send_ip_to_lambda(ip)
        time.sleep(600)  # 評価用、10分ごとに実行  
    

このスクリプトは、Alexa スキルが Lambda 関数を呼び出す際に送信するイベントを模倣したペイロードを作成し、IP アドレスをそのペイロードに含めて Lambda 関数に送信するものです。

applicationId と userId は、Alexa スキルとユーザーを識別するための ID です。

applicationId

  • Alexa スキルを識別するための ID です。
  • Alexa Developer Console でスキルを作成した際に発行されます。
  • 通常は amzn1.ask.skill.[skillId] のような形式です。
  • [unique-value-here] の部分を実際のスキル ID に置き換えてください。

userId

  • Alexa ユーザーを識別するための ID です。
  • Amazon アカウントに紐づけられています。
  • 通常は amzn1.ask.account.[accountId] のような形式です。
  • [unique-value-here] の部分を実際のユーザーアカウント ID に置き換えてください。

注意点

  • payload は、Alexa スキルから Lambda 関数に送信されるデータの塊です。
  • applicationId と userId は、Alexa スキルとユーザーを識別するための ID です。
  • これらの ID は、必須ではありませんが、スキルの機能を拡張するために利用されます。

今回のスクリプトでは、IP アドレスを Lambda 関数に送信することが主な目的であるため、applicationId と userId を設定しなくても、スキルは問題なく動作します。

2. Lambda関数の変更

2.1 DynamoDBテーブルの作成:

DynamoDBを使用するには、IPアドレスを保存するためのテーブルを作成する必要があります。以下にDynamoDBのテーブルを設定し、IPアドレスを保存・取得するための具体的な手順を示します

  1. AWS Management Consoleにログインし、「DynamoDB」を選択します。
  2. 「テーブルを作成」をクリックします。
  3. テーブル名を「IPTable」とし、パーティションキーを「id」とします。パーティションキーのタイプは「strings」に設定します。
  4. テーブルの作成を完了します。


2.2 Lambda Layerを作成

Lambda Layerは、Lambda関数が利用できるライブラリや依存関係をまとめたものです。AWS SDKをLayerとして作成し、Lambda関数に関連付けることで、この問題を解決できます。

2.2.1 Node.js環境の構築

まず、Node.jsをインストールする必要があります。以下の手順に従って、Node.jsをインストールしてください。

  1. Node.jsのダウンロードとインストール:

    • Node.jsの公式ウェブサイトにアクセスし、最新のLTSバージョンをダウンロードします。

    • インストーラーを実行して、画面の指示に従ってインストールします。

  2. 環境変数の設定:

    • インストールが完了したら、コマンドプロンプトを再度開き、npmコマンドが認識されるか確認します。

PS C:\WINDOWS\system32> node -v
PS C:\WINDOWS\system32> npm -v

  1. エラー対応:
このようなエラーが出ることがあります。

PS C:\WINDOWS\system32> node -v
v22.14.0
PS C:\WINDOWS\system32> npm -v
npm : このシステムではスクリプトの実行が無効になっているため、ファイル C:\Program Files\nodejs\npm.ps1 を読み込むことが
できません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してくだ
さい。
発生場所 行:1 文字:1
+ npm -v
+ ~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess


スタートメニューからPowerShellを検索し、「管理者として実行」を選択して起動します。

実行ポリシーの変更: 以下のコマンドを実行して、現在の実行ポリシーを確認します。

PS C:\WINDOWS\system32> Get-ExecutionPolicy

実行ポリシーがRestricted(制限あり)になっている場合は、以下のコマンドを実行して実行ポリシーを変更します。

PS C:\WINDOWS\system32> Set-ExecutionPolicy RemoteSigned -Force
このコマンドを実行すると、ローカルで作成したスクリプトは実行可能になりますが、インターネットからダウンロードしたスクリプトは署名が必要になります。
PS C:\WINDOWS\system32> npm -v
の再実行:

実行ポリシーを変更したら、PowerShellを再起動するか、以下のコマンドを実行して環境を更新します。

PS C:\WINDOWS\system32> $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine");

その後、npm -vを再度実行してnpmのバージョンが表示されることを確認してください。
PS C:\WINDOWS\system32> npm -v
10.9.2

2.2.2 AWS SDKのインストール:

任意のディレクトリ(たとえば、lambda_function)に移動し、以下のコマンドを実行してAWS SDKをインストールします。
PS C:\WINDOWS\system32> mkdir lambda_function
PS C:\WINDOWS\system32> cd lambda_function
PS C:\WINDOWS\system32\lambda_function> mkdir node_modules
PS C:\WINDOWS\system32\lambda_function> npm install aws-sdk --save-dev

このコマンドを実行すると、node_modulesというディレクトリが作成され、AWS SDKがその中にインストールされます。

2.2.3 zipファイルの作成手順

  1. nodejsディレクトリの作成:
    node_modulesディレクトリと同じ階層に、nodejsという名前のディレクトリを作成します。
  1. node_modulesディレクトリの移動:
    node_modulesディレクトリをnodejsディレクトリの中に移動します。
  1. nodejsディレクトリの圧縮:
    nodejsディレクトリをzipファイルに圧縮します。
    コマンド例:
Compress-Archive -Path nodejs -DestinationPath aws-sdk-layer.zip

上記手順で作成したzipファイルの構成を示します。圧縮は \nodejs フォルダを圧縮し、楚々の構成は、\nodejs\node_modules\aws-sdk です。


2.2.4 Layerの作成:

  1. AWSコンソールにログインし、Lambda > Layers > Create layer に移動します。
  2. Layer nameに適当な名前(例: aws-sdk-layer)を入力します。
  3. Upload .zip fileを選択し、先ほど作成した nodejs ディレクトリを圧縮したzipファイルをアップロードします。
  4. Compatible runtimesで、Lambda関数で使用しているランタイム(例: nodejs2.2.x)を選択します。
  5. Create layerをクリックしてLayerを作成します。



2.2.5 レイヤーの関連付け方法

  1. Lambda > Functions に移動し、該当のLambda関数を選択します。
  2. 「コード」タブを選択します。
  3. 画面下部の「レイヤー」パネルまでスクロールします。
  4. 「レイヤーの追加」をクリックします。
  5. 「ARNを指定」のテキストボックスに、作成したレイヤーのARNを直接入力します。
  6. レイヤーのARNは、Lambda > Layers で確認できます。
  7. ARNの形式は arn:aws:lambda:<リージョン>:<アカウントID>:layer:<レイヤー名>:<バージョン> です。
  8. 「追加」をクリックします。
  9. 画面右上の「保存」をクリックして設定を保存します。


2.2.6 IAMロールの作成

ルートユーザーでLambda関数を使用する場合のIAMロールの設定手順は、以下の通りです。

Lambda関数に付与するIAMロールを作成します。このロールには、Lambda関数の実行に必要な権限(AWS SDKへのアクセス権限、DynamoDBへのアクセス権限など)を付与します。

  1. AWSコンソールにログインし、IAM > ロール > ロールを作成 に移動します。
  2. 「Lambda」を選択し、「次のステップ」をクリックします。
  3. 必要なポリシーを選択します。
  4. AWS SDKへのアクセス権限: AWSLambdaBasicExecutionRole
  5. DynamoDBへのアクセス権限: AmazonDynamoDBFullAccess (必要に応じて適切なポリシーを選択)
  6. ロール名を入力し、「ロールを作成」をクリックします。

3. Lambda関数の作成

既報で作成したLambda関数を元にして、DynamoDBを使うための変更を加えたコードを示します。新たに作成したコードで、元コードを上書きします。


'use strict';

const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
const tableName = 'IPTable'; // DynamoDBのテーブル名
const http = require('http');

// --------------- Helpers that build all of the responses -----------------------

// ... (buildSpeechletResponse, buildResponse関数は変更なし)
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: 'PlainText',
            text: output,
        },
        card: {
            type: 'Simple',
            title: `SessionSpeechlet - ${title}`,
            content: `SessionSpeechlet - ${output}`,
        },
        reprompt: {
            outputSpeech: {
                type: 'PlainText',
                text: repromptText,
            },
        },
        shouldEndSession,
    };
}

function buildResponse(sessionAttributes, speechletResponse) {
    return {
        version: '1.0',
        sessionAttributes,
        response: speechletResponse,
    };
}

// --------------- Functions that control the skill's behavior -----------------------

// ... (getWelcomeResponse, handleSessionEndRequest, createLocationAttributes関数は変更なし)
function getWelcomeResponse(callback) {
    const sessionAttributes = {};
    const cardTitle = 'Welcome';
    const speechOutput = "二階の温度を教えてなどと問いかけると、それを答えます。";
    const repromptText = "私はこの部屋を見てるわよっ!何が知りたいのっ!   ってかっ";
    const shouldEndSession = false;

    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

function handleSessionEndRequest(callback) {
    const cardTitle = 'Session Ended';
    const speechOutput = 'Thank you for trying the Alexa Skills Kit sample. Have a nice day!';
    const shouldEndSession = true;

    callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}

function createLocationAttributes(location) {
    return { location };
}


async function readTemperatureInSession(intent, session, callback) {
    const cardTitle = intent.name;
    const LocationSlot = intent.slots.Location;
    const MeasurementSlot = intent.slots.Measurement;
    let repromptText = '';
    let sessionAttributes = session.attributes || {};
    const shouldEndSession = false;
    let speechOutput = '';
    let body = '';
    const blynkport = '8080';
    let blynkAuthToken;
    let blynkPin;

    let location = LocationSlot.value || sessionAttributes.location || '二階';
    sessionAttributes.location = location;

    let ip;

    try {
        // DynamoDBからIPアドレスを取得
        const params = {
            TableName: tableName,
            Key: {
                id: 'globalIP' // パーティションキーは固定値
            }
        };
        const data = await dynamoDb.get(params).promise();
        const item = data.Item;

        if (!item || !item.ipAddress) {
            console.error('IPアドレスがDynamoDBに存在しません。');
            speechOutput = 'IPアドレスが取得できませんでした。';
            repromptText = 'もう一度お試しください。';
            callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
            return;
        }

        ip = item.ipAddress; // DynamoDBから取得したIPアドレス

    } catch (error) {
        console.error(`DynamoDBからIPアドレスを取得中にエラーが発生しました: ${error}`);
        speechOutput = 'IPアドレスの取得中にエラーが発生しました。';
        repromptText = 'もう一度お試しください。';
        callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
        return; // エラー発生時は処理を中断
    }


    switch (location) {
        case '二階':
            blynkAuthToken = '******wpbmLskRMBvSyi9Lw';
            break;
        case '仏間':
            blynkAuthToken = 'slWRpvQM++++++++BMKIRS7vi4ch2';
            break;
        case '屋根裏':
            blynkAuthToken = 'uwPwuHpaI=======CQ6R4268eEeI';
            break;
        default:
            speechOutput = '指定された場所が見つかりませんでした。';
            repromptText = '他の場所を指定してください。';
            callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
            return;
    }

    let measurement = MeasurementSlot.value || '温度';
    switch (measurement) {
        case '何度':
        case '気温':
        case '室温':
        case '温度':
            blynkPin = 'V0';
            break;
        case '湿気':
        case '湿度':
            blynkPin = 'V1';
            break;
        case '大気圧':
        case '気圧':
            blynkPin = 'V2';
            break;
        case '熱中度指数':
        case '熱中度':
        case '暑さ指数':
        case '暑さ度':
        case '暑さ':
            blynkPin = 'V4';
            break;
        default:
            speechOutput = '指定された測定項目が見つかりませんでした。';
            repromptText = '他の測定項目を指定してください。';
            callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
            return;
    }

    try {
        console.log(`Sending request to Blynk server with Auth Token: ${blynkAuthToken}, Pin: ${blynkPin}, IP: ${ip}`);

        const httpPromise = new Promise((resolve, reject) => {
            http.get({
                host: ip,
                path: `/${blynkAuthToken}/get/${blynkPin}`,
                port: blynkport,
            }, (response) => {
                response.on('data', (d) => {
                    body += d;
                });
                response.on('end', () => {
                    resolve('Done Sending');
                });
            }).on('error', (e) => {
                reject(e); // エラーをreject
                console.error(`Blynkサーバーへのリクエストエラー: ${e}`); // エラーログ
            });
        });

        await httpPromise;

        console.log(`Received response from Blynk server: ${body}`);

        let info;
        try {
            info = parseFloat(JSON.parse(body)); // パースを試みる
        } catch (parseError) {
            console.error(`JSONパースエラー: ${parseError}, body: ${body}`);
            info = NaN; // パースに失敗したらNaNを設定
        }

        if (isNaN(info)) {
            speechOutput = '温度の取得に失敗しました。';
            repromptText = 'もう一度お試しください。';
            callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
        } else {
            // ... (温度表示処理)
            speechOutput = location + 'の ' + measurement + 'は ';
            repromptText = speechOutput;
            switch (measurement) {
                default:
                case '何度':
                case '気温':
                case '室温':
                case '温度':
                    speechOutput += `${info.toFixed(1)}度 です。`;
                    repromptText += `${info.toFixed(1)}℃です。`;
                    break;
                case '湿気':
                case '湿度':
                    speechOutput += `${info.toFixed(1)}パーセント です。`;
                    repromptText += `${info.toFixed(1)}%です。`;
                    break;
                case '大気圧':
                case '気圧':
                    speechOutput += `${info.toFixed(0)}ヘクトパスカル です。`;
                    repromptText += `${info.toFixed(0)}hPaです。`;
                    break;
                case '熱中度指数':
                case '熱中度':
                case '暑さ指数':
                case '暑さ度':
                case '暑さ':
                    let annotation = '';
                    if (info >= 31) {
                        annotation = '危険! ブラックです。';
                    } else if (info >= 28) {
                        annotation = '厳重警戒! レッドです。';
                    } else if (info >= 25) {
                        annotation = '警戒! オレンジです。';
                    } else if (info >= 21) {
                        annotation = '注意です。';
                    }
                    speechOutput += `${info.toFixed(0)}度 です。${annotation}`;
                    repromptText += `${info.toFixed(0)}℃です。${annotation}`;
                    break;
            }
            callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
        }

    } catch (error) {
        console.error(`温度取得処理全体のエラー: ${error}`);
        speechOutput = '温度の取得中にエラーが発生しました。';
        repromptText = 'もう一度お試しください。';
        callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
    }
}


// ... (onSessionStarted, onLaunch, onIntent, onSessionEnded関数は変更なし)
function onSessionStarted(sessionStartedRequest, session) {
    console.log(`onSessionStarted requestId=${sessionStartedRequest.requestId}, sessionId=${session.sessionId}`);
}

function onLaunch(launchRequest, session, callback) {
    console.log(`onLaunch requestId=${launchRequest.requestId}, sessionId=${session.sessionId}`);
    getWelcomeResponse((sessionAttributes, speechletResponse) => {
        callback(sessionAttributes, speechletResponse);
    });
}

function onIntent(intentRequest, session, callback) {
    console.log(`onIntent requestId=${intentRequest.requestId}, sessionId=${session.sessionId}`);
    const intent = intentRequest.intent;
    const intentName = intentRequest.intent.name;

    if (intentName === 'GetMeasurement') {
        readTemperatureInSession(intent, session, callback);
    } else if (intentName === 'AMAZON.HelpIntent') {
        getWelcomeResponse((sessionAttributes, speechletResponse) => {
            callback(sessionAttributes, speechletResponse);
        });
    } else if (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent') {
        handleSessionEndRequest((sessionAttributes, speechletResponse) => {
            callback(sessionAttributes, speechletResponse);
        });
    } else {
        throw new Error('Invalid intent');
    }
}

function onSessionEnded(sessionEndedRequest, session) {
    console.log(`onSessionEnded requestId=${sessionEndedRequest.requestId}, sessionId=${session.sessionId}`);
    // callbackを呼び出す必要はありません。
    // context.succeed()も不要です。
}

exports.handler = (event, context) => {
    try {
        console.log('Event:', JSON.stringify(event));

        let globalIP;
        if (event.session && event.session.attributes && event.session.attributes.globalIP) {
            globalIP = event.session.attributes.globalIP;
            console.log(`Using globalIP from session attributes: ${globalIP}`);
            continueProcessing(event, context); // IPアドレス取得後に実行したい処理
        } else {
            console.log("globalIP not found in session attributes. Retrieving from DynamoDB or IP");
            // IPアドレスの取得処理 (DynamoDB or event.ip)
            if (event.ip) {
                globalIP = event.ip;
                event.session.attributes.globalIP = globalIP; // セッション属性に保存
                console.log(`IP from event: ${globalIP}`);
                continueProcessing(event, context); // IPアドレス取得後に実行したい処理
            } else {
                getIPAddressFromDynamoDB(event, context);
            }
        }

    } catch (e) {
        console.error(`Exception: ${e}`);
        context.fail(`Exception: ${e}`);
    }
};

function updateIPAddress(event, context) {
    const params = {
        TableName: tableName,
        Item: {
            id: 'globalIP',
            ipAddress: event.ip
        }
    };

    dynamoDb.put(params).promise()
        .then(() => {
            console.log(`Successfully updated IP address in DynamoDB: ${event.ip}`);
            event.session.attributes.globalIP = event.ip;
            console.log(`Updated globalIP to: ${event.ip}`);
            continueProcessing(event, context);
        })
        .catch(err => {
            console.error(`Error updating IP address in DynamoDB: ${err}`);
            context.fail(`Error updating IP address: ${err}`);
        });
}

function getIPAddressFromDynamoDB(event, context) {
    const params = {
        TableName: tableName,
        Key: {
            id: 'globalIP'
        }
    };

    dynamoDb.get(params).promise()
        .then(data => {
            if (data.Item) {
                const ipAddress = data.Item.ipAddress;
                event.session.attributes.globalIP = ipAddress;
                console.log(`Retrieved IP address from DynamoDB: ${ipAddress}`);
                continueProcessing(event, context);
            } else {
                console.error('IP address not found in DynamoDB.');
                context.fail('IP address not found.');
            }
        })
        .catch(err => {
            console.error(`Error retrieving IP address from DynamoDB: ${err}`);
            context.fail(`Error retrieving IP address: ${err}`);
        });
}

function continueProcessing(event, context) {
    if (event.session && event.session.new) {
        onSessionStarted({ requestId: event.request.requestId }, event.session);
    }
    if (event.request && event.request.type) {
        if (event.request.type === 'LaunchRequest') {
            onLaunch(event.request, event.session, (sessionAttributes, speechletResponse) => {
                context.succeed(buildResponse(sessionAttributes, speechletResponse));
                console.log('LaunchRequest succeeded');
            });
        } else if (event.request.type === 'IntentRequest') {
            onIntent(event.request, event.session, (sessionAttributes, speechletResponse) => {
                context.succeed(buildResponse(sessionAttributes, speechletResponse));
                console.log('IntentRequest succeeded');
            });
        } else if (event.request.type === 'SessionEndedRequest') {
            onSessionEnded(event.request, event.session);
            context.succeed();
            console.log('SessionEndedRequest succeeded');
        }
    } else {
        console.error('event.request が見つかりません。');
        context.fail('event.request が見つかりません。');
    }
}

// ... (onSessionStarted, onLaunch, onIntent, onSessionEnded, buildResponse, buildSpeechletResponseなどの関数は省略)

Powered by Blogger | Designed by QooQ

keyboard_double_arrow_down

keyboard_double_arrow_down