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

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

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

quick response message

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

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

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

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

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

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

date and time picker example

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

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

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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)
        }
}