8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オウム返しチャットボット LINE API+Lambda+Go言語(フレームワーク利用なし)

Last updated at Posted at 2019-05-05

これなに?

LINEで、メッセージをオウム返しして返答するだけのチャットボット(良くあるやつ)。
Lambda+Go言語で、フレームワーク(serverless)を利用しないサンプルコードが見つからなかったので投稿。

go.png

環境

Windows環境で開発・コンパイル(build)して、AWS LambdaのWeb画面でZIPファイルをuploadする、というスタイルで構築。

開発・コンパイル環境

  • Windows 10 64bit
  • go1.11.9.windows-amd64
    • 記事執筆時点の最新は1.12.4だけれど、後述の通りbuildがうまくいかなかったので、1.11.9を利用
    • フレームワークは特に利用していない
  • IDE:Visual Studio Code
    • 今回の記事に関係ないので、詳細割愛。

実行環境

  • LINE Messaging API
  • AWS Lambda(言語:Go 1.x)
  • Amazon API Gateway

コード

LINE Messaging APIの設定、Lambda・API Gateway・CloudWatchの詳細な設定は特にややこしいところがないので割愛。

  • Amazon API Gatewayから受け取ったイベントは、LINEのSDKを利用せずにパースしている。
    • SDKを使った方がよかったのかもしれないが、GO+Lambda環境用のちょうどよいサンプルが見つからず、SDKの使い方がよくわからなかったため、結局自力でJSONをパース(自動生成されたコードだけど)。
    • 本当はCHANNELSECRETとACCESSTOKENのチェックをしないとダメだけど、動作検証用なのでいったん割愛。
  • main()のreturnが、Amazon API Gateway経由でLINEに返る。ここは200 OKを返せばOK。
  • リプライメッセージはLINE SDKのlinebot.ReplyMessage()を利用している。
  • Lambdaの環境変数として、CHANNELSECRETとACCESSTOKENを設定している。
line.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/line/line-bot-sdk-go/linebot"
)

// API Gatewayから受け取ったevents.APIGatewayProxyRequestのBody(JSON)をパースする
// https://app.quicktype.io/ に、実際に受け取ってたJSONメッセージを張り付けて、コード自動生成。

// ▼▼▼ https://app.quicktype.io/で自動生成したコード:ここから ▼▼▼
func UnmarshalLineRequest(data []byte) (LineRequest, error) {
	var r LineRequest
	err := json.Unmarshal(data, &r)
	return r, err
}

func (r *LineRequest) Marshal() ([]byte, error) {
	return json.Marshal(r)
}

type LineRequest struct {
	Events      []Event `json:"events"`
	Destination string  `json:"destination"`
}

type Event struct {
	Type       string  `json:"type"`
	ReplyToken string  `json:"replyToken"`
	Source     Source  `json:"source"`
	Timestamp  int64   `json:"timestamp"`
	Message    Message `json:"message"`
}

type Message struct {
	Type string `json:"type"`
	ID   string `json:"id"`
	Text string `json:"text"`
}

type Source struct {
	UserID string `json:"userId"`
	Type   string `json:"type"`
}

// ▲▲▲ https://app.quicktype.io/で自動生成したコード:ここまで ▲▲▲

// Handler
// fmt.Printlnやlog.Fatalは、CloudWatchのログで確認可能
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

	// 受け取ったJSONメッセージをログに書き込む(デバッグ用)
	fmt.Println("*** body")
	fmt.Println(request.Body)

	// JSONデコード
	fmt.Println("*** JSON decode")
	myLineRequest, err := UnmarshalLineRequest([]byte(request.Body))
	if err != nil {
		log.Fatal(err)
	}

	// ボットの定義
	fmt.Println("*** linebot new")
	bot, err := linebot.New(
		os.Getenv("CHANNELSECRET"),
		os.Getenv("ACCESSTOKEN"),
	)
	if err != nil {
		log.Fatal(err)
	}

	// リプライ実施
	fmt.Println("*** reply")
	var tmpReplyMessage string
	tmpReplyMessage = "回答:" + myLineRequest.Events[0].Message.Text
	if _, err = bot.ReplyMessage(myLineRequest.Events[0].ReplyToken, linebot.NewTextMessage(tmpReplyMessage)).Do(); err != nil {
		log.Fatal(err)
	}

	// 終了
	fmt.Println("*** end")
	return events.APIGatewayProxyResponse{Body: request.Body, StatusCode: 200}, nil
}

func main() {
	lambda.Start(Handler)
}

buildコマンド (PowerShell)

> $env:GOOS="linux"
> go env GOOS
linux
> go build -o line01 line01.go
> build-lambda-zip.exe -o line01.zip line01

実行結果

ハマったポイント

Go言語もVisual Studio Codeも初めてだったので、かなり初歩的だけれども。。

①Go言語用のLambdaパッケージ(github.com/aws/aws-lambda-go/lambda)がbuild時に読み込まれない

事象

  • go1.12 (Windows)
  • $GOPATH = C:\users\45lb_plates\go
  • $GOROOT = C:\Go
  • go get -u github.com/aws/aws-lambda-go/lambdaは実行済み

という環境でbuildを実行すると、以下のエラーとなる。

> go build -o line line.go
line.go:4:8: cannot find package "github.com/aws/aws-lambda-go/lambda" in any of:
 C:\Go\src\github.com\aws\aws-lambda-go\lambda (from $GOROOT)
 C:\users\45lb_plates\go\src\github.com\aws\aws-lambda-go\lambda (from $GOPATH)
exit status 1
Process exiting with code: 1

解決策

go1.12を諦め、go1.11.9をインストールしなおしたらあっさり解決。

  • 実際にパッケージgithub.com/aws/aws-lambda-go/lambdaがインストールされていたのはC:\Users\45lb_plates\go\pkg\mod\github.com\aws\aws-lambda-go@v1.10.0\lambdaだった。$GOROOT\pkg\mod配下にインストールされるのは、go1.12からの仕様?らしい。
  • 一方、build時に読み込もうとしているフォルダは$GOROOT\src配下で、ここが不整合となっている。
  • go1.12で、どうやったらbuild時に$GOROOT\pkg\modを読み込ませるのかわからなかった。環境変数GO111MODULEonにしたりoffにしたりは試してみたが、改善せず。

②Windows環境でLinux用のクロスコンパイルができない

事象

コンパイル対象のOSをSETコマンドでWindowsからLinuxへ変更したいが、されない。

> set GOOS=linux
> go env GOOS
Windows

解決策

Visual Studio Codeのターミナルが「PowerShell」になっていたため、SETコマンドを受け付けてなかった。
SETコマンドはDOSプロンプトで環境変数を変更するコマンド。PowerShellでSETコマンドを打ってもエラーは出ないが、環境変数は変わらない。(Goのクロスコンパイルについて検索すると、みんなSETコマンドだったので全然気づかなかった。。)

PoweShell用の環境変数を変更するコマンドを改めて実行し、解決。

> $env:GOOS="linux"
> go env GOOS
linux

参考にした資料・利用したツール等

参考:Lambda受け取ったJSONメッセージを確認するには

LINE API GatewayからAWS API Gateway経由でLambda受け取ったJSONメッセージを確認する方法。
このJSONメッセージをquicktypeに張り付けると、JSONメッセージをパースするコードが自動生成される。便利!

コード
package main

import (
	"fmt"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    fmt.Println(request.Body)
    return events.APIGatewayProxyResponse{Body: request.Body, StatusCode: 200}, nil
}

func main() {
	lambda.Start(Handler)
}
実行結果例(受け取ったJSONメッセージ)
{
    "events": [
        {
            "type": "message",
            "replyToken": "704xxxx4024cxxxx9c86xxxxcdf0xxxx",
            "source": {
                "userId": "U8845xxxx971bxxxx6f98xxxxa428xxxx",
                "type": "user"
            },
            "timestamp": 1599999999999,
            "message": {
                "type": "text",
                "id": "9809999999999",
                "text": "てすと"
            }
        }
    ],
    "destination": "Uc082xxxxe925xxxx0a19xxxx5e31xxxx"
}

おわり。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?