AWS LambdaがHTTPSエンドポイント対応したのでCDKを使ってオウム返しのLINE botを作ってみる


何番煎じかわかりませんが、AWS LambdaとLine messaging apiを使ってLINE-botを作ります。

なぜこれをやろうかと思ったかというと、LambdaがHTTPS endpointを単体で作れるようになったからです!(https://aws.amazon.com/blogs/aws/announcing-aws-lambda-function-urls-built-in-https-endpoints-for-single-function-microservices/)

もともとLambdaはAlways freeで使えていましたがHTTPをトリガーとしてLambdaを実行する際に別途API Gatewayを用意しなければならず、そちらは12 months freeが終わったら従量課金となってしまい完全無料ではLambdaをHTTPをトリガーとしては使用できませんでした。

ということで、個人で完全無料を目指すならGCPのCloud Functionsを利用していたりと、選択肢が限られていたところに今回の発表があったので、嬉しくなってLambdaのHTTPSエンドポイントを使ってやろうと思い立ったということです。

前提条件

  • AWSアカウントがある
  • LINE Developersの準備ができている
  • AWS CLIインストール済み

環境

  • Windows10 10.0.19044のWSL2環境
  • Docker version 20.10.14
  • node: v16.14.2

準備

  • LINE Developersから適当にMessageing APIのchannelを作っておく
  • AWS CLIの設定をしておく

CDKのプロジェクトを作る

適当なディレクトリにCDKプロジェクトを作る。

mkdir lambda-line-bot
cd lambda-line-bot
npx cdk init --language typescript

オウム返しのLambdaを作る

Lambda用のディレクトリを適当に作って、今回はTypescriptでLambdaを作っていく。

mkdir -p lambda/testParrotFashion/src
cd lambda/testParrotFashion
npm install typescript --save-dev
npm install --save-dev @types/aws-lambda
npx tsc --init --rootDir src --outDir build --esModuleInterop --resolveJsonModule --lib esnext --module commonjs

src の下にオウム返しのコードを書く

index.ts

import * as Line from "@line/bot-sdk";
import { WebhookEvent } from "@line/bot-sdk";
import { APIGatewayEvent } from "aws-lambda";
const accessToken = process.env.ACCESS_TOKEN!;
const channelSecret = process.env.CHANNEL_SECRET!;

const config: Line.ClientConfig = {
  channelAccessToken: accessToken,
  channelSecret: channelSecret,
};

const client = new Line.Client(config);

exports.handler = async (event: APIGatewayEvent) => {
  const body: Line.WebhookRequestBody = JSON.parse(event.body!);
  try {
    await Promise.all(
      body.events.map(async (e: WebhookEvent) => {
        if (e.type !== "message" || e.message.type !== "text") {
          return null;
        }
        const message: Line.Message = { type: "text", text: e.message.text };
        await client.replyMessage(e.replyToken, message);
      })
    );
    return {
      statusCode: 200,
      body: JSON.stringify("SUCCESS"),
    };
  } catch (error) {
    return {
      statusCode: 400,
      body: "FAILED",
    };
  }
};

CDKを書く

作ったLambdaをデプロイするStackを定義する。

lambda-line-bot-stack.ts

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";

interface LambdaLineBotStackProps extends StackProps {
  channelAccessToken: string;
  channelSecret: string;
}
export class LambdaLineBotStack extends Stack {
  constructor(scope: Construct, id: string, props: LambdaLineBotStackProps) {
    super(scope, id, props);

    const lamdba = new NodejsFunction(this, "testParrotFashionFunction", {
      entry: "./lambda/testParrotFashion/src/index.ts",
      handler: "handler",
      environment: {
        ACCESS_TOKEN: props.channelAccessToken,
        CHANNEL_SECRET: props.channelSecret,
      },
    });
  }
}

lambda-line-bot.ts

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { LambdaLineBotStack } from "../lib/lambda-line-bot-stack";

const app = new cdk.App();
const accessToken = app.node.tryGetContext("ACCESS_TOKEN");
const channelSecret = app.node.tryGetContext("CHANNEL_SECRET");

new LambdaLineBotStack(app, "LambdaLineBotStack", {
  channelAccessToken: accessToken,
  channelSecret: channelSecret,

});

LINE Developersで事前に用意しておいたMessaging APIのChannel secretChannel access tokenを環境変数で定義しておく。

CHANNEL_SERCRET="XXXXXXXXXXXXXXXXXXXXXX"
ACCESS_TOKEN="YYYYYYYYYYYYYYYYYYYYYYYY"

あとはデプロイするだけ!

npx cdk deploy LambdaLineBotStack --context CHANNEL_SECRET=$CHANNEL_SECRET --context ACCESS_TOKEN=$ACCESS_TOKEN

まだaws-cdk-lambdaで function url は使えないみたいなので、デプロイしたLambdaのhttpsエンドポイントの設定をLambdaコンソールから設定していきましょう。

https://github.com/aws/aws-cdk/issues/19798

ここで払い出されたhttpsのエンドポイントをLINE Developersのwebhook URLに設定して…

ついでにいらない設定は消しておいて…

QRコードから友だち登録して完成!!

はいできました!

何番煎じかわかりませんが嬉しくてつい記事出ししてしまいました。まだまだ勉強中なので間違いあったら教えてほしいです。

あとから気付きましたが、S3はAlways Freeじゃなくて12 months freeでした…。
料金が気になったので一応確認しましたが、今の所コストはかかっていないようです。

テンプレートも1つだけなのでまだあまり気にしなくても良いと思いますが、完全無料を目指すなら気にしないといけないポイントですね。