GolangでLine APIをいじくるよ

Mar 3, 2019   #golang  #programming  : GolangでLine APIを利用してみましたよ。

GolangでLine APIをいじくる

GolangLine APIを用いたボット作成をしてみます。

説明すること・しないこと

この記事ではLine Messaging APIを使って、どんなメッセージを送信できるかを説明します。

どうすればボット作成環境を構築するかなどは説明しません。参考リンクなど確認ください。

簡単な説明

下の図のLineBotServerの部分を作り込みます:

sequenceDiagram User->>Line: Some Action(s) Line->>LineBotServer: Make HTTP Request alt Bad Request Note over LineBotServer: Do nothing else Proper Request LineBotServer->>LineBotServer: Do something LineBotServer->>Line: Make HTTP Request, using REST API Line->>User: Show some message(s) as Reply end

Line Developerに登録する

お金をかけずにLINEのMessaging APIの投稿(push)と返信(replay)を試してみる。 | ポンコツエンジニアのごじゃっぺ開発日記。に書いてある通りに進めます。

とりあえず動作させてみます

送信できるメッセージの種類ごとにテストしてみます。

テキストメッセージ

“text”とメッセージ送信したら、”text”と返信するようにしてみました。

text message

画像メッセージ

“image”とメッセージ送信したら、画像を投稿するようにしてみました。

image message

スタンプメッセージ

“sticker”とメッセージ送信したら、スタンプを投稿するようにしてみました。送信できるステッカーはLine Botで送信できるSticker一覧 (PDF)から確認できます:

sticker message

位置情報メッセージ

“location”とメッセージ送信したら、地図情報を投稿するようにしてみました:

location message

テンプレートメッセージ

画像やボタンなどを組み合わせたメッセージです。

ボタンテンプレート

こんな感じのメッセージになります:

button-template message

確認テンプレート

確認を行うダイアログ?的なメッセージになります:

confirm message

カルーセルテンプレート

横方向にテンプレートメッセージを並べたメッセージです。こんな感じになります:

carousel message

Flexメッセージ

テンプレートメッセージのレイアウトを自由に調整できるみたいなのですが、サンプルしか触っていないため、ちょっとよくわかっていないです。こんな感じになりました:

flex message

もっとレイアウトにこったサンプルがあればいいのですが。。。

クイックレスポンスメッセージ

メッセージ送信と同時に、簡単な選択肢を提示して、選択してもらうことができるようです:

quick response message

位置情報の送信、カメラ起動、カメラロールから画像選択などもできるみたい。上の例だと、右端のリンクをクリックすると、位置情報の送信画面になります。

その他 (ボタンをクリックするとできること)

テンプレートメッセージやFlexメッセージでボタンを配置してできることは、以下のようです。

Postback action

Botのcallback URLに対して、データをPOSTリクエストで送信するようです。

Message action

選択したボタンに割り当てたメッセージをLine上で送信するみたいです。

URI action

選択したボタンに割り当てたURLをLineのブラウザで開くようです。

Datetime picker action

日付選択用の画面が出てきます。

date and time picker example

Camera action

クイックレスポンスの時にのみ利用できるアクションのようです。カメラが起動します。

Camera roll action

クイックレスポンスの時にのみ利用できるアクションのようです。カメラロールが開いて、画像を選択できるようです。

Location action

クイックレスポンスの時にのみ利用できるアクションのようです。位置情報の画面が出てくるようです。

コード

package main

import (
        "log"
        "net/http"
        "os"
        "time"

        "github.com/jinzhu/gorm"
        _ "github.com/jinzhu/gorm/dialects/mysql"
        "github.com/line/line-bot-sdk-go/linebot"
)

const (
        userStatusAvailable    string = "available"
        userStatusNotAvailable string = "not_available"
)

type user struct {
        Id         string `gorm:"primary_key"`
        IdType     string
        Timestamp  time.Time `gorm:"not null;type:datetime"`
        ReplyToken string
        Status     string
}

func gormConnect() *gorm.DB {
        DBMS := "mysql"
        USER := "root"
        PASS := "Holiday88"
        PROTOCOL := "tcp(192.168.10.200:3307)"
        DBNAME := "LineBot"

        CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?parseTime=true&loc=Asia%2FTokyo"
        db, err := gorm.Open(DBMS, CONNECT)

        if err != nil {
                log.Fatal(err.Error())
        }
        return db
}

func main() {
        bot, err := linebot.New(
                os.Getenv("CHANNEL_SECRET"),
                os.Getenv("CHANNEL_TOKEN"),
        )
        if err != nil {
                log.Fatal(err)
        }

        // Setup HTTP Server for receiving requests from LINE platform
        http.HandleFunc("/callback", func(w http.ResponseWriter, req *http.Request) {
                events, err := bot.ParseRequest(req)
                if err != nil {
                        if err == linebot.ErrInvalidSignature {
                                w.WriteHeader(400)
                        } else {
                                w.WriteHeader(500)
                        }
                        return
                }

                for _, event := range events {
                        switch event.Type {
                        case linebot.EventTypeFollow:
                                // db instance
                                db := gormConnect()
                                defer db.Close()

                                userData := user{Id: event.Source.UserID,
                                        IdType:     string(event.Source.Type),
                                        Timestamp:  event.Timestamp,
                                        ReplyToken: event.ReplyToken,
                                        Status:     userStatusAvailable,
                                }
                                err := db.Where(user{Id: event.Source.UserID}).Assign(&userData).FirstOrCreate(&user{})
                                log.Println(err)

                        case linebot.EventTypeUnfollow:
                                log.Println("Unfollow Event: " + event.Source.UserID)
                                log.Println(event)

                                // db instance
                                db := gormConnect()
                                defer db.Close()

                                userData := user{Id: event.Source.UserID,
                                        IdType:     string(event.Source.Type),
                                        Timestamp:  event.Timestamp,
                                        ReplyToken: event.ReplyToken,
                                        Status:     userStatusNotAvailable,
                                }

                                err := db.Where(user{Id: event.Source.UserID}).Assign(&userData).FirstOrCreate(&user{})
                                log.Println(err)

                        case linebot.EventTypeMessage:
                                log.Println(event)
                                switch message := event.Message.(type) {
                                case *linebot.TextMessage:
                                        switch message.Text {
                                        case "text":
                                                resp := linebot.NewTextMessage(message.Text)

                                                _, err := bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "sticker":
                                                resp := linebot.NewStickerMessage("3", "230")

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "location":
                                                resp := linebot.NewLocationMessage("現在地", "宮城県多賀城市", 38.297807, 141.031)

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "image":
                                                resp := linebot.NewImageMessage("https://farm5.staticflickr.com/4849/45718165635_328355a940_m.jpg", "https://farm5.staticflickr.com/4849/45718165635_328355a940_m.jpg")

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "buttontemplate":
                                                resp := linebot.NewTemplateMessage(
                                                        "this is a buttons template",
                                                        linebot.NewButtonsTemplate(
                                                                "https://farm5.staticflickr.com/4849/45718165635_328355a940_m.jpg",
                                                                "Menu",
                                                                "Please select",
                                                                linebot.NewPostbackAction("Buy", "action=buy&itemid=123", "", "displayText"),
                                                                linebot.NewPostbackAction("Buy", "action=buy&itemid=123", "text", ""),
                                                                linebot.NewURIAction("View detail", "http://example.com/page/123"),
                                                        ),
                                                )

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "datetimepicker":
                                                resp := linebot.NewTemplateMessage(
                                                        "this is a buttons template",
                                                        linebot.NewButtonsTemplate(
                                                                "https://farm5.staticflickr.com/4849/45718165635_328355a940_m.jpg",
                                                                "Menu",
                                                                "Please select a date,  time or datetime",
                                                                linebot.NewDatetimePickerAction("Date", "action=sel&only=date", "date", "2017-09-01", "2017-09-03", ""),
                                                                linebot.NewDatetimePickerAction("Time", "action=sel&only=time", "time", "", "23:59", "00:00"),
                                                                linebot.NewDatetimePickerAction("DateTime", "action=sel", "datetime", "2017-09-01T12:00", "", ""),
                                                        ),
                                                )

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "confirm":
                                                resp := linebot.NewTemplateMessage(
                                                        "this is a confirm template",
                                                        linebot.NewConfirmTemplate(
                                                                "Are you sure?",
                                                                linebot.NewMessageAction("Yes", "yes"),
                                                                linebot.NewMessageAction("No", "no"),
                                                        ),
                                                )

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "carousel":
                                                resp := linebot.NewTemplateMessage(
                                                        "this is a carousel template with imageAspectRatio,  imageSize and imageBackgroundColor",
                                                        linebot.NewCarouselTemplate(
                                                                linebot.NewCarouselColumn(
                                                                        "https://farm5.staticflickr.com/4849/45718165635_328355a940_m.jpg",
                                                                        "this is menu",
                                                                        "description",
                                                                        linebot.NewPostbackAction("Buy", "action=buy&itemid=111", "", ""),
                                                                        linebot.NewPostbackAction("Add to cart", "action=add&itemid=111", "", ""),
                                                                        linebot.NewURIAction("View detail", "http://example.com/page/111"),
                                                                ).WithImageOptions("#FFFFFF"),
                                                                linebot.NewCarouselColumn(
                                                                        "https://farm5.staticflickr.com/4849/45718165635_328355a940_m.jpg",
                                                                        "this is menu",
                                                                        "description",
                                                                        linebot.NewPostbackAction("Buy", "action=buy&itemid=111", "", ""),
                                                                        linebot.NewPostbackAction("Add to cart", "action=add&itemid=111", "", ""),
                                                                        linebot.NewURIAction("View detail", "http://example.com/page/111"),
                                                                ).WithImageOptions("#FFFFFF"),
                                                        ).WithImageOptions("rectangle", "cover"),
                                                )
                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "flex":
                                                resp := linebot.NewFlexMessage(
                                                        "this is a flex message",
                                                        &linebot.BubbleContainer{
                                                                Type: linebot.FlexContainerTypeBubble,
                                                                Body: &linebot.BoxComponent{
                                                                        Type:   linebot.FlexComponentTypeBox,
                                                                        Layout: linebot.FlexBoxLayoutTypeVertical,
                                                                        Contents: []linebot.FlexComponent{
                                                                                &linebot.TextComponent{
                                                                                        Type: linebot.FlexComponentTypeText,
                                                                                        Text: "hello",
                                                                                },
                                                                                &linebot.TextComponent{
                                                                                        Type: linebot.FlexComponentTypeText,
                                                                                        Text: "world",
                                                                                },
                                                                        },
                                                                },
                                                        },
                                                )

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        case "quickresponse":
                                                resp := linebot.NewTextMessage(
                                                        "Select your favorite food category or send me your location!",
                                                ).WithQuickReplies(
                                                        linebot.NewQuickReplyItems(
                                                                linebot.NewQuickReplyButton("https://example.com/sushi.png", linebot.NewMessageAction("Sushi", "Sushi")),
                                                                linebot.NewQuickReplyButton("https://example.com/tempura.png", linebot.NewMessageAction("Tempura", "Tempura")),
                                                                linebot.NewQuickReplyButton("", linebot.NewLocationAction("Send location")),
                                                        ),
                                                )

                                                _, err = bot.ReplyMessage(event.ReplyToken, resp).Do()
                                                if err != nil {
                                                        log.Print(err)
                                                }
                                        }
                                }
                        }
                }
        })

        if err := http.ListenAndServe(":"+os.Getenv("PORT"), nil); err != nil {
                log.Fatal(err)
        }
}

参考