API Gateway を使わないで、無料で、グローバルIPアドレスをLambda関数に送る

published_with_changes 更新日: event_note 公開日:

labelamazon alexa labelBlynk labelIoT labelRaspberry pi

 AWSから請求書が来て、API Gateway が有料サービスであったことに気づきました。
AWS請求画面


無料で実現するため、Alexa 部屋の温度を教えて、第10版です。 自力では無理なので今回も、Google Gemini Logo に聞いて作ります。

これまでのIPアドレスを取得する方法は、2つとも有料のAPI Gateway を使う方法だったので、それを使わない方法を考えます。

  1. Raspberry Pi でグローバルIPを取得し、AWS Lambda に送信する手順でルータのIPアドレスを自動で取得することに成功しました。

    • この方法は ラズパイからのIPアドレス送信を5分おきに行いlambda関数を動かしていないと、IPアドレスを忘れてしまう欠点があります。


  1.  対策として、IPアドレスをDynamoDBに保存して、適時、呼び出す方法を考えました。

    • しかし、なぜか1秒おきだったり、1分おきだったりまちまちなのですが、ログが吐き出される現象があります。どうも、API Gatewayが悪さしているようでした。
    • 設定などを何度も見直しましたが、改善することはなく、結局はAPI Gateway を削除するしか、吐き出しを止める方法がありませんでした。
    • この現象により、約10日間で15,649 件のRequests が発生し、$0.54 が請求されることになりました。

専門知識がないので、生成AIが勧めてきたAPI Gatewayを使って構築したのが失敗でした。


API Gatewayを使わず、Lambda関数URL を使う

グローバルIPアドレスの取得はRaspberry Pi側で行い、取得したIPアドレスをLambda関数に送信することで、Lambda関数のIPアドレスではなく、ユーザーの実際のグローバルIPアドレスをDynamoDBに保存できます。

  • Raspberry Pi側でグローバルIPアドレスを取得する方法は、いくつかあります。
  • 例えば、http://checkip.amazonaws.com などの外部サイトにアクセスする方法や、ルーターのUPnP機能を利用する方法などがあります。
  • どちらの方法でも、Lambda関数の実行ロールにDynamoDBへの書き込み権限を付与する必要があります。
  • セキュリティ対策として、Lambda関数URLの場合は認証タイプを「AWS_IAM」にするか、Lambda関数内で認証処理を実装することを推奨します。

変更内容

  1. Raspberry Piのスクリプト変更:
    • Raspberry Piで動作しているPythonスクリプトを変更します。
    • API GatewayのエンドポイントにIPアドレスを送信していた部分を、Lambda関数URLにIPアドレスを送信するように変更します。
    • IPアドレスは、HTTPのPOSTリクエストの「body」に入れて送信します。

  1. Lambda関数URLの有効化:
    • AWSのLambdaコンソールで、変更したLambda関数の設定を開きます。
    • 「関数URL」という項目があるので、そこでLambda関数URLを有効にします。
    • 認証の設定は、今回は簡単に「認証なし」を選びます。ただし、セキュリティを考慮する場合は、「AWS IAM」を選択してください。
    • 有効にすると、Lambda関数URLが表示されます。これは、インターネットからLambda関数にアクセスするためのURLです。

  1. Lambda関数の変更:
    • 現在のLambda関数を少し変更します。
    • API Gatewayから送信されるイベントの形式ではなく、Lambda関数URLから送信されるイベントの形式に対応するように変更します。
    • 具体的には、Raspberry Piから送信されるIPアドレスを、イベントの「body」の部分から取得するように変更します。

  1. Alexaスキルの変更:
    • Alexaスキルは、変更する必要はありません。Lambda関数URLを使っても、これまで通りLambda関数を呼び出すことができます。

1. Raspberry Piのスクリプト変更


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

# Lambda関数URLに変更
LAMBDA_ENDPOINT = "YOUR_LAMBDA_FUNCTION_URL"  # Lambda関数URLを入力

# IAMユーザーの認証情報を設定
AWS_ACCESS_KEY_ID = "YOUR_AWS_ACCESS_KEY_ID"
AWS_SECRET_ACCESS_KEY = "YOUR_AWS_SECRET_ACCESS_KEY"
REGION = "ap-northeast-1"

auth = AWS4Auth(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, REGION, 'lambda')

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

def send_ip_to_lambda(ip):
    """取得したIPアドレスをLambda関数URLに送信"""
    try:
        payload = {'ip': ip}
        headers = {'Content-Type': 'application/json'}
        response = requests.post(LAMBDA_ENDPOINT, json=payload, headers=headers, auth=auth)
        print(f"{datetime.datetime.now()} - Sent IP: {ip}, Response: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"{datetime.datetime.now()} - Error sending IP to Lambda: {e}")

if __name__ == "__main__":
    ip = get_global_ip()
    if ip:
        send_ip_to_lambda(ip)
          

変更内容

1. LAMBDA_ENDPOINT の変更
  • API GatewayのエンドポイントからLambda関数URLに変更します。
  • YOUR_LAMBDA_FUNCTION_URL の部分を、実際のLambda関数URLに置き換えてください。
  • 以下は、IAMユーザーの認証情報の設定になります。(後で説明)2つの項目をIAM認証設定で得られた情報に置き換えてください。
    • YOUR_AWS_ACCESS_KEY_ID
    • YOUR_AWS_SECRET_ACCESS_KEY 
2. send_ip_to_lambda 関数の変更
  • headersからAPIキー(x-api-key)を削除します。Lambda関数URLではAPIキーは不要です。

2. Lambda関数URLの有効化

今回はセキュリティ強化のためIAM認証されたLambda関数URLを使います。
Lambda関数URLで「AWS IAM」認証を使用する場合は、IAMユーザーを作成し、ポリシーをアタッチする必要があります。

その手順を説明します。

2.1 Lambda関数URLの作成(AWS IAM認証)

  1. Lambdaコンソールを開きます。
    • AWSマネジメントコンソールにログインし、Lambdaのページを開きます。
  2. 対象のLambda関数を選択します。
    • readTemperature_local(例)関数を選択します。
  3. 「設定」タブを開きます。
  4. 左側のメニューから「関数URL」を選択します。
  5. 「関数URLを作成」ボタンをクリックします。
  6. 認証タイプで「AWS IAM」を選択します。
  7. 「保存」ボタンをクリックします。
これでLambda関数URLが作成され、URLが表示されます。
Lambdaコンソール画面


「設定」から「関数URL」
関数URL設定画面


「関数URLを作成」から「AWS IAM」を選択
認証タイプ選択画面

2.2 IAMポリシーの作成

Lambda関数URLで「AWS IAM」を選択した場合のIAM設定について説明します。

「AWS IAM」を選択すると、IAMユーザーまたはIAMロールにLambda関数URLへのアクセス権限を付与する必要があります。これにより、認証されたユーザーまたはロールのみがLambda関数を呼び出すことができます。

今回のケースでは、Raspberry PiからLambda関数URLを呼び出すために使用するIAMユーザーを作成することを推奨します。

IAMポリシーでは、どのAWSサービスへ、誰(ユーザ、ロール)がアクセスでき、どういった操作を許可するかを定義します。

  1. IAMコンソールを開きます。
  2. AWSマネジメントコンソールにログインし、IAMのページを開きます。
  3. 「ポリシー」を選択し、「ポリシーの作成」をクリックします。
  4. 「JSON」タブを選択し、以下のポリシーを貼り付けます。
    • リージョン、アカウントID、関数名を実際の値に置き換えます。
    • IAMポリシーはグローバルサービスであるため、特定のリージョンに限定されません。
{
      "Version": "2012-10-17",
      "Statement": [
          {
              "Effect": "Allow",
              "Action": "lambda:InvokeFunctionUrl",
              "Resource": "arn:aws:lambda:リージョン:アカウントID:function:関数名"
          }
      ]
  }
    
  1. 「次のステップ:タグ」をクリックします。
  2. 必要に応じてタグを追加し、「次のステップ:確認」をクリックします。
  3. ポリシー名を入力し、「ポリシーの作成」をクリックします。
    • IAMポリシーのポリシー名は、任意の名前を設定できます。ただし、ポリシーの内容を分かりやすく表す名前を付けることを推奨します。
    • 今回の場合、Lambda関数URLへのアクセス権限を付与するポリシーであるため、ポリシー名は LambdaFunctionUrlAccessPolicy にします。
IAMポリシー作成画面


2.3 IAMユーザーの作成とポリシーのアタッチ

Raspberry PiからLambda関数URLを呼び出す場合:
IAMユーザーを作成し、アクセスキーとシークレットアクセスキーを使用します。

  1. IAMコンソールを開きます。
    • AWSマネジメントコンソールにログインし、IAMのページを開きます。
  2. 「ユーザー」を選択し、「ユーザーを追加」をクリックします。
  3. ユーザー名を入力し、「次へ」をクリックします。
    • IAMユーザーのユーザー名は、任意の名前を設定できます。ただし、ユーザーの役割や目的を分かりやすく表す名前を付けることを推奨します。(例 ip-reporter-user)
  4. 「既存のポリシーを直接アタッチ」を選択し、作成したポリシー(LambdaFunctionUrlAccessPolicyなど)を検索してチェックボックスをオンにして、「次のステップ:タグ」をクリックします。
  5. 必要に応じてタグを追加し、「次のステップ:確認」をクリックします。
  6. ユーザー情報を確認し、「ユーザーの作成」をクリックします。
  7. アクセスキーIDシークレットアクセスキーを安全な場所に保存します。
  8. これらのキー情報を前述のRaspberry Piのスクリプトに書き込みます。

「ユーザー」から「ユーザーを追加」
IAMユーザー追加画面



ユーザー名を入力
IAMユーザー名入力画面

2.4 参考:Lambda関数URLの作成(認証なし)

IAM認証なしの場合は、IAMの設定は不要で簡単に関数URLを作成できます。

  1. Lambdaコンソールを開きます。
    • AWSマネジメントコンソールにログインし、Lambdaのページを開きます。
  2. 対象のLambda関数を選択します。
    • readTemperature_local関数を選択します。
  3. 「設定」タブを開きます。
  4. 左側のメニューから「関数URL」を選択します。
  5. 「関数URLを作成」ボタンをクリックします。
  6. 認証タイプで「認証なし」を選択します。
  7. 「保存」ボタンをクリックします。


3. Lambda関数の変更

変更内容

  1. IPアドレスの取得方法:
    • event['body']からJSON形式の文字列を取得し、json.loads()で辞書型に変換後、['ip']でIPアドレスを取得します。
  2. DynamoDBテーブル名の変更:
    • YourDynamoDBTableNameの部分を、実際のDynamoDBテーブル名に置き換えてください。
  3. DynamoDBのパーティションキーの変更:
    • idの値を、実際のDynamoDBのパーティションキーの値に変更してください。
  4. blynkAuthToken のURLエンコード:
    • 既報のLambda関数では出なかったエラーメッセージ TypeError [ERR_UNESCAPED_CHARACTERS]: Request path contains unescaped characters が発生しました。これは、HTTPリクエストのパスに問題があることが明確です。
    • blynkAuthTokenの値を encodeURIComponent() でエンコード して対策しました。

既報(DynamoDBに保存したIPアドレスをLambda関数で呼び出す)で作成したLambda関数を元にして、Lambda関数URLを使うための変更を加えたコードを示します。AWS上では新たに作成したコードで、元コードを上書きして使います。(バージョンアップデート)


'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 -----------------------

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 -----------------------

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 = encodeURIComponent('********************************');
            break;
        case '仏間':
            blynkAuthToken = encodeURIComponent('********************************');
            break;
        case '屋根裏':
            blynkAuthToken = encodeURIComponent('********************************');
            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: `/${encodeURIComponent(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.body && JSON.parse(event.body).ip) { // Lambda関数URLからのIPアドレス取得
                globalIP = JSON.parse(event.body).ip;
                if (!event.session) {
                    event.session = {};
                }
                if (!event.session.attributes) {
                    event.session.attributes = {};
                }
                event.session.attributes.globalIP = globalIP; // セッション属性に保存
                console.log(`IP from event body: ${globalIP}`);
                updateIPAddress(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: JSON.parse(event.body).ip // event.bodyからIPアドレスを取得
        }
    };

    dynamoDb.put(params).promise()
        .then(() => {
            console.log(`Successfully updated IP address in DynamoDB: ${JSON.parse(event.body).ip}`);
            if (!event.session) {
                event.session = {};
            }
            if (!event.session.attributes) {
                event.session.attributes = {};
            }
            event.session.attributes.globalIP = JSON.parse(event.body).ip;
            console.log(`Updated globalIP to: ${JSON.parse(event.body).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;
                if (!event.session) {
                    event.session = {};
                }
                if (!event.session.attributes) {
                    event.session.attributes = {};
                }
                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.request && event.request.type) { // event.requestの存在チェックを追加
        // Alexaからのリクエストの場合の処理
        if (event.session && event.session.new) {
            onSessionStarted({ requestId: event.request.requestId }, event.session);
        }
        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 {
        // Lambda URLからのリクエストの場合
        context.succeed(); // Lambda関数を正常終了
        console.log('Lambda URL request completed.');
    }
}

// ... (buildResponse, buildSpeechletResponseなどの関数は省略)
      

まとめ: IPアドレスの優先順位:

プログラムは、IPアドレスを取得する際に、以下の優先順位で処理を行います。

順位1. セッション属性:
  • まず、以前の会話で保存された情報(セッション属性)の中にIPアドレスがないかを確認します。
  • もしセッション属性にIPアドレスがあれば、それを最優先で使用します。

順位2. 受け取ったIPアドレス:
  • セッション属性にIPアドレスがない場合、次に直接送られてきたIPアドレス(受け取ったIPアドレス)を使用します。
  • このIPアドレスは、データベースのIPアドレスを更新するために使用されます。

順位3. データベース(DynamoDB):
  • 受け取ったIPアドレスが使用された後に、そのIPアドレスでデータベースのIPアドレスを更新します。
  • これは、次回のIPアドレス取得のために使用されます。

Powered by Blogger | Designed by QooQ

keyboard_double_arrow_down

keyboard_double_arrow_down