🦜

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

2022/04/09に公開

何番煎じかわかりませんが、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エンドポイントを使ってやろうと思い立ったということです。

2022/03/23追記
aws-cdk-lib v2.21.0からlambdaでfunction URLが使えるようになったので、CDKのコードにも反映させています。

前提条件

  • 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,
      },
    });
    lamdba.addFunctionUrl({
      authType: lambda.FunctionUrlAuthType.NONE,
    });
  }
}

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

2022/03/23追記
まだaws-cdk-lambdaで function url は使えないみたいなので、
デプロイしたLambdaのhttpsエンドポイントの設定をLambdaコンソールから設定していきましょう。
すでにFunction URLの設定をデプロイしたLambdaに対して設定している場合は、一度Function URLの設定を削除しておかないと下記のエラーが発生するので、注意しておきます。

Error message: FunctionUrlConfig exists for this Lambda function

デプロイしたLambdaのhttpsエンドポイントの設定をLambdaコンソールから確認していきましょう。

httpsエンドポイントが設定されているのを確認できましたね。ここで払い出されたhttpsのエンドポイントをLINE Developersのwebhook URLに設定して…

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

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

はいできました!

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

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

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

Discussion