├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── examples ├── other.go └── server.go ├── go.mod └── linebot ├── client.go ├── event.go ├── imagemap.go ├── message.go └── template.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.7.1-alpine 2 | MAINTAINER Dongri Jin 3 | 4 | RUN apk --no-cache add git 5 | 6 | RUN go get github.com/kaneshin/lime 7 | 8 | ADD . /go/src/github.com/dongri/line-bot-sdk-go 9 | WORKDIR /go/src/github.com/dongri/line-bot-sdk-go/examples 10 | 11 | EXPOSE 3001 12 | 13 | CMD ["lime", "-bin=/tmp/examples", "-port=3001", "-app-port=3000"] 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dongri Jin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LINE Bot Messaging API SDK for Go (Golang) 2 | 3 | ## Start using it 4 | 5 | * Download and install it: 6 | 7 | ```go 8 | $ go get github.com/dongri/line-bot-sdk-go 9 | ``` 10 | 11 | ## Examples 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "log" 19 | "net/http" 20 | "os" 21 | 22 | "github.com/dongri/line-bot-sdk-go/linebot" 23 | ) 24 | 25 | var botClient *linebot.Client 26 | 27 | func main() { 28 | channelAccessToken := os.Getenv("LINE_CHANNEL_ACCESSTOKEN") 29 | channelSecret := os.Getenv("LINE_CHANNEL_SECRET") 30 | 31 | botClient = linebot.NewClient(channelAccessToken) 32 | botClient.SetChannelSecret(channelSecret) 33 | 34 | // EventHandler 35 | var myEvent linebot.EventHandler = NewEventHandler() 36 | botClient.SetEventHandler(myEvent) 37 | 38 | http.Handle("/callback", linebot.Middleware(http.HandlerFunc(callbackHandler))) 39 | port := os.Getenv("PORT") 40 | addr := fmt.Sprintf(":%s", port) 41 | http.ListenAndServe(addr, nil) 42 | } 43 | 44 | func callbackHandler(w http.ResponseWriter, r *http.Request) { 45 | log.Print("=== callback ===") 46 | } 47 | 48 | // BotEventHandler ... 49 | type BotEventHandler struct{} 50 | 51 | // NewEventHandler ... 52 | func NewEventHandler() *BotEventHandler { 53 | return &BotEventHandler{} 54 | } 55 | 56 | // OnFollowEvent ... 57 | func (be *BotEventHandler) OnFollowEvent(source linebot.EventSource, replyToken string) { 58 | log.Print(source.UserID + "=== フォローされた ===") 59 | // source.UserID と Token を保存してnotifyで使える 60 | message := linebot.NewTextMessage("Hello!") 61 | result, err := botClient.ReplyMessage(replyToken, message) 62 | fmt.Println(result) 63 | fmt.Println(err) 64 | } 65 | 66 | // OnUnFollowEvent ... 67 | func (be *BotEventHandler) OnUnFollowEvent(source linebot.EventSource) { 68 | log.Print(source.UserID + "=== ブロックされた ===") 69 | } 70 | 71 | // OnJoinEvent ... 72 | func (be *BotEventHandler) OnJoinEvent(source linebot.EventSource, replyToken string) { 73 | message := linebot.NewTextMessage("Room, Group 招待ありがとう!") 74 | result, err := botClient.ReplyMessage(replyToken, message) 75 | fmt.Println(result) 76 | fmt.Println(err) 77 | } 78 | 79 | // OnLeaveEvent ... 80 | func (be *BotEventHandler) OnLeaveEvent(source linebot.EventSource) { 81 | log.Print("=== Groupから蹴られた ===") 82 | } 83 | 84 | // OnPostbackEvent ... 85 | func (be *BotEventHandler) OnPostbackEvent(source linebot.EventSource, replyToken, postbackData string) { 86 | message := linebot.NewTextMessage("「" + postbackData + "」を選択したね!") 87 | result, err := botClient.ReplyMessage(replyToken, message) 88 | fmt.Println(result) 89 | fmt.Println(err) 90 | } 91 | 92 | // OnBeaconEvent ... 93 | func (be *BotEventHandler) OnBeaconEvent(source linebot.EventSource, replyToken, beaconHwid, beaconYype string) { 94 | log.Print("=== Beacon Event ===") 95 | } 96 | 97 | // OnTextMessage ... 98 | func (be *BotEventHandler) OnTextMessage(source linebot.EventSource, replyToken, text string) { 99 | message := linebot.NewTextMessage(text) 100 | result, err := botClient.ReplyMessage(replyToken, message) 101 | fmt.Println(result) 102 | fmt.Println(err) 103 | } 104 | 105 | // OnImageMessage ... 106 | func (be *BotEventHandler) OnImageMessage(source linebot.EventSource, replyToken, id string) { 107 | originalContentURL := "https://play-lh.googleusercontent.com/74iMObG1vsR3Kfm82RjERFhf99QFMNIY211oMvN636_gULghbRBMjpVFTjOK36oxCbs" 108 | previewImageURL := "https://play-lh.googleusercontent.com/74iMObG1vsR3Kfm82RjERFhf99QFMNIY211oMvN636_gULghbRBMjpVFTjOK36oxCbs" 109 | message := linebot.NewImageMessage(originalContentURL, previewImageURL) 110 | result, err := botClient.ReplyMessage(replyToken, message) 111 | fmt.Println(result) 112 | fmt.Println(err) 113 | } 114 | 115 | // OnVideoMessage ... 116 | func (be *BotEventHandler) OnVideoMessage(source linebot.EventSource, replyToken, id string) { 117 | originalContentURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/video-original.mp4" 118 | previewImageURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/video-preview.png" 119 | message := linebot.NewVideoMessage(originalContentURL, previewImageURL) 120 | result, err := botClient.ReplyMessage(replyToken, message) 121 | fmt.Println(result) 122 | fmt.Println(err) 123 | } 124 | 125 | // OnAudioMessage ... 126 | func (be *BotEventHandler) OnAudioMessage(source linebot.EventSource, replyToken, id string) { 127 | originalContentURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/ok.m4a" 128 | duration := 1000 129 | message := linebot.NewAudioMessage(originalContentURL, duration) 130 | result, err := botClient.ReplyMessage(replyToken, message) 131 | fmt.Println(result) 132 | fmt.Println(err) 133 | } 134 | 135 | // OnLocationMessage ... 136 | func (be *BotEventHandler) OnLocationMessage(source linebot.EventSource, replyToken string, title, address string, latitude, longitude float64) { 137 | title = "Disney Resort" 138 | address = "〒279-0031 千葉県浦安市舞浜1−1" 139 | lat := 35.632211 140 | lon := 139.881234 141 | message := linebot.NewLocationMessage(title, address, lat, lon) 142 | result, err := botClient.ReplyMessage(replyToken, message) 143 | fmt.Println(result) 144 | fmt.Println(err) 145 | } 146 | 147 | // OnStickerMessage ... 148 | func (be *BotEventHandler) OnStickerMessage(source linebot.EventSource, replyToken, packageID, stickerID string) { 149 | message := linebot.NewStickerMessage("1", "1") 150 | result, err := botClient.ReplyMessage(replyToken, message) 151 | fmt.Println(result) 152 | fmt.Println(err) 153 | } 154 | 155 | // OnEvent ... 156 | func (be *BotEventHandler) OnEvent(event linebot.Event) { 157 | } 158 | ``` 159 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | bot: 2 | build: . 3 | dockerfile: Dockerfile 4 | environment: 5 | LINE_CHANNEL_ACCESSTOKEN: ${LINE_CHANNEL_ACCESSTOKEN} 6 | LINE_CHANNEL_SECRET: ${LINE_CHANNEL_SECRET} 7 | GO_ENV: production 8 | volumes: 9 | - .:/go/src/github.com/dongri/line-bot-sdk-go 10 | ports: 11 | - "50005:3001" 12 | -------------------------------------------------------------------------------- /examples/other.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "log" 7 | "math/rand" 8 | "net/http" 9 | "net/url" 10 | "strconv" 11 | "time" 12 | ) 13 | 14 | // DoRequest ... 15 | func DoRequest(req *http.Request, proxyURL *url.URL) map[string]interface{} { 16 | client := &http.Client{ 17 | Timeout: time.Duration(30 * time.Second), 18 | } 19 | res, err := client.Do(req) 20 | if err != nil { 21 | log.Print(err) 22 | } 23 | defer res.Body.Close() 24 | var result map[string]interface{} 25 | body, err := io.ReadAll(res.Body) 26 | if err != nil { 27 | log.Print(err) 28 | } 29 | if err := json.Unmarshal(body, &result); err != nil { 30 | log.Print(err) 31 | } 32 | return result 33 | } 34 | 35 | // GetImageFromWeb ... 36 | func GetImageFromWeb() string { 37 | rand.New(rand.NewSource(time.Now().UnixNano())) 38 | offset := strconv.Itoa(rand.Intn(1000)) 39 | 40 | //blogName := "mincang.tumblr.com" 41 | blogName := "kawaii-sexy-love.tumblr.com" 42 | 43 | resp, err := http.Get("http://api.tumblr.com/v2/blog/" + blogName + "/posts/photo?api_key=3bllCUqUSGV73R3O7BKWq5m9mIERjzMORIPnkxv90s3KsqOCH4&limit=1&offset=" + offset) 44 | if err != nil { 45 | panic(err) 46 | } 47 | defer resp.Body.Close() 48 | 49 | data := make(map[string]interface{}) 50 | body, _ := io.ReadAll(resp.Body) 51 | err = json.Unmarshal(body, &data) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | response := data["response"].(map[string]interface{}) 57 | posts := response["posts"].([]interface{}) 58 | post := posts[0].(map[string]interface{}) 59 | photos := post["photos"].([]interface{}) 60 | photo := photos[0].(map[string]interface{}) 61 | altSizes := photo["alt_sizes"].([]interface{}) 62 | firstSize := altSizes[0].(map[string]interface{}) 63 | imageURL := firstSize["url"].(string) 64 | return imageURL 65 | } 66 | -------------------------------------------------------------------------------- /examples/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/dongri/line-bot-sdk-go/linebot" 12 | ) 13 | 14 | var botClient *linebot.Client 15 | 16 | func main() { 17 | channelAccessToken := os.Getenv("LINE_CHANNEL_ACCESSTOKEN") 18 | channelSecret := os.Getenv("LINE_CHANNEL_SECRET") 19 | 20 | botClient = linebot.NewClient(channelAccessToken) 21 | botClient.SetChannelSecret(channelSecret) 22 | 23 | // EventHandler 24 | var myEvent linebot.EventHandler = NewEventHandler() 25 | botClient.SetEventHandler(myEvent) 26 | 27 | http.HandleFunc("/", indexHandler) 28 | http.Handle("/callback", linebot.Middleware(http.HandlerFunc(callbackHandler))) 29 | port := os.Getenv("PORT") 30 | if len(port) == 0 { 31 | port = "3000" 32 | } 33 | addr := fmt.Sprintf(":%s", port) 34 | http.ListenAndServe(addr, nil) 35 | } 36 | 37 | func indexHandler(w http.ResponseWriter, r *http.Request) { 38 | fmt.Fprintf(w, "LINE BOT SDK GO") 39 | } 40 | 41 | func callbackHandler(w http.ResponseWriter, r *http.Request) { 42 | log.Print("=== callback ===") 43 | } 44 | 45 | // BotEventHandler ... 46 | type BotEventHandler struct{} 47 | 48 | // NewEventHandler ... 49 | func NewEventHandler() *BotEventHandler { 50 | return &BotEventHandler{} 51 | } 52 | 53 | // OnFollowEvent ... 54 | func (be *BotEventHandler) OnFollowEvent(source linebot.EventSource, replyToken string) { 55 | log.Print(source.UserID + "=== フォローされた ===") 56 | // source.UserID と Token を保存してnotifyで使える 57 | message := linebot.NewTextMessage("Hello!") 58 | result, err := botClient.ReplyMessage(replyToken, message) 59 | fmt.Println(result) 60 | fmt.Println(err) 61 | } 62 | 63 | // OnUnFollowEvent ... 64 | func (be *BotEventHandler) OnUnFollowEvent(source linebot.EventSource) { 65 | log.Print(source.UserID + "=== ブロックされた ===") 66 | } 67 | 68 | // OnJoinEvent ... 69 | func (be *BotEventHandler) OnJoinEvent(source linebot.EventSource, replyToken string) { 70 | message := linebot.NewTextMessage("Room, Group 招待ありがとう!") 71 | result, err := botClient.ReplyMessage(replyToken, message) 72 | fmt.Println(result) 73 | fmt.Println(err) 74 | } 75 | 76 | // OnLeaveEvent ... 77 | func (be *BotEventHandler) OnLeaveEvent(source linebot.EventSource) { 78 | log.Print("=== Groupから蹴られた ===") 79 | } 80 | 81 | // OnPostbackEvent ... 82 | func (be *BotEventHandler) OnPostbackEvent(source linebot.EventSource, replyToken, postbackData string) { 83 | originalContentURL := postbackData 84 | message := linebot.NewImageMessage(originalContentURL, originalContentURL) 85 | result, err := botClient.ReplyMessage(replyToken, message) 86 | fmt.Println(result) 87 | fmt.Println(err) 88 | } 89 | 90 | // OnBeaconEvent ... 91 | func (be *BotEventHandler) OnBeaconEvent(source linebot.EventSource, replyToken, beaconHwid, beaconYype string) { 92 | log.Print("=== Beacon Event ===") 93 | } 94 | 95 | // OnTextMessage ... 96 | func (be *BotEventHandler) OnTextMessage(source linebot.EventSource, replyToken, text string) { 97 | if text == "Buttons" { 98 | templateLabel := "Go" 99 | templateText := "Hello, Golang!" 100 | thumbnailImageURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/gopher.png" 101 | actionLabel := "Go to golang.org" 102 | actionURI := "https://golang.org" 103 | template := linebot.NewButtonsTemplate( 104 | thumbnailImageURL, templateLabel, templateText, 105 | linebot.NewTemplateURIAction(actionLabel, actionURI), 106 | linebot.NewTemplatePostbackAction("Go大好き", "Go大好き(Postback)", ""), 107 | ) 108 | altText := "Go template" 109 | message := linebot.NewTemplateMessage(altText, template) 110 | result, err := botClient.ReplyMessage(replyToken, message) 111 | fmt.Println(result) 112 | fmt.Println(err) 113 | } else if text == "Confirm" { 114 | template := linebot.NewConfirmTemplate( 115 | "Do it?", 116 | linebot.NewTemplateMessageAction("Yes", "Yes!"), 117 | linebot.NewTemplateMessageAction("No", "No!"), 118 | ) 119 | altText := "Confirm template" 120 | message := linebot.NewTemplateMessage(altText, template) 121 | result, err := botClient.ReplyMessage(replyToken, message) 122 | fmt.Println(result) 123 | fmt.Println(err) 124 | } else if text == "Audio" { 125 | originalContentURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/ok.m4a" 126 | duration := 1000 127 | message := linebot.NewAudioMessage(originalContentURL, duration) 128 | result, err := botClient.ReplyMessage(replyToken, message) 129 | fmt.Println(result) 130 | fmt.Println(err) 131 | } else if text == "Carousel" { 132 | var columns []*linebot.CarouselColumn 133 | for i := 0; i < 5; i++ { 134 | originalContentURL := GetImageFromWeb() 135 | originalContentURL = strings.Replace(originalContentURL, "http://", "https://", -1) 136 | column := linebot.NewCarouselColumn( 137 | originalContentURL, "", strconv.Itoa(i), 138 | linebot.NewTemplatePostbackAction("好き!", originalContentURL, "好き!"), 139 | linebot.NewTemplateMessageAction("普通", "普通"), 140 | ) 141 | columns = append(columns, column) 142 | } 143 | template := linebot.NewCarouselTemplate(columns...) 144 | message := linebot.NewTemplateMessage("Sexy Girl", template) 145 | result, err := botClient.ReplyMessage(replyToken, message) 146 | fmt.Println(result) 147 | fmt.Println(err) 148 | } else if text == "girl" { 149 | originalContentURL := GetImageFromWeb() 150 | originalContentURL = strings.Replace(originalContentURL, "http://", "https://", -1) 151 | message := linebot.NewImageMessage(originalContentURL, originalContentURL) 152 | result, err := botClient.ReplyMessage(replyToken, message) 153 | fmt.Println(result) 154 | fmt.Println(err) 155 | } else { 156 | message := linebot.NewTextMessage(text) 157 | result, err := botClient.ReplyMessage(replyToken, message) 158 | fmt.Println(result) 159 | fmt.Println(err) 160 | } 161 | } 162 | 163 | // OnImageMessage ... 164 | func (be *BotEventHandler) OnImageMessage(source linebot.EventSource, replyToken, id string) { 165 | originalContentURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/gohper.jpg" 166 | previewImageURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/gohper.jpg" 167 | message := linebot.NewImageMessage(originalContentURL, previewImageURL) 168 | result, err := botClient.ReplyMessage(replyToken, message) 169 | fmt.Println(result) 170 | fmt.Println(err) 171 | } 172 | 173 | // OnVideoMessage ... 174 | func (be *BotEventHandler) OnVideoMessage(source linebot.EventSource, replyToken, id string) { 175 | originalContentURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/video-original.mp4" 176 | previewImageURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/video-preview.png" 177 | message := linebot.NewVideoMessage(originalContentURL, previewImageURL) 178 | result, err := botClient.ReplyMessage(replyToken, message) 179 | fmt.Println(result) 180 | fmt.Println(err) 181 | } 182 | 183 | // OnAudioMessage ... 184 | func (be *BotEventHandler) OnAudioMessage(source linebot.EventSource, replyToken, id string) { 185 | originalContentURL := "https://dl.dropboxusercontent.com/u/358152/linebot/resource/ok.m4a" 186 | duration := 1000 187 | message := linebot.NewAudioMessage(originalContentURL, duration) 188 | result, err := botClient.ReplyMessage(replyToken, message) 189 | fmt.Println(result) 190 | fmt.Println(err) 191 | } 192 | 193 | // OnLocationMessage ... 194 | func (be *BotEventHandler) OnLocationMessage(source linebot.EventSource, replyToken string, title, address string, latitude, longitude float64) { 195 | titleSample := "Disney Resort" 196 | addressSample := "〒279-0031 千葉県浦安市舞浜1−1" 197 | lat := 35.632211 198 | lon := 139.881234 199 | message := linebot.NewLocationMessage(titleSample, addressSample, lat, lon) 200 | result, err := botClient.ReplyMessage(replyToken, message) 201 | fmt.Println(result) 202 | fmt.Println(err) 203 | } 204 | 205 | // OnStickerMessage ... 206 | func (be *BotEventHandler) OnStickerMessage(source linebot.EventSource, replyToken, packageID, stickerID string) { 207 | message := linebot.NewStickerMessage("1", "1") 208 | result, err := botClient.ReplyMessage(replyToken, message) 209 | fmt.Println(result) 210 | fmt.Println(err) 211 | } 212 | 213 | // OnEvent ... 214 | func (be *BotEventHandler) OnEvent(event linebot.Event) { 215 | } 216 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dongri/line-bot-sdk-go 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /linebot/client.go: -------------------------------------------------------------------------------- 1 | package linebot 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "net/http" 10 | "strconv" 11 | "time" 12 | ) 13 | 14 | // API URLs 15 | const ( 16 | EndPoint = "https://api.line.me" 17 | PushMessage = "/v2/bot/message/push" 18 | ReplyMessage = "/v2/bot/message/reply" 19 | GetMessageContent = "/v2/bot/message/%s/content" 20 | LeaveGroup = "/v2/bot/group/%s/leave" 21 | LeaveRoom = "/v2/bot/room/%s/leave" 22 | GetProfile = "/v2/bot/profile/%s" 23 | ) 24 | 25 | // APISendResult ... 26 | type APISendResult struct { 27 | Message string `json:"message"` 28 | } 29 | 30 | // BasicResponse ... 31 | type BasicResponse struct { 32 | } 33 | 34 | // MessageContentResponse ... 35 | type MessageContentResponse struct { 36 | Content []byte 37 | ContentLength int64 38 | ContentType string 39 | } 40 | 41 | // UserProfileResponse ... 42 | type UserProfileResponse struct { 43 | UserID string `json:"userId"` 44 | DisplayName string `json:"displayName"` 45 | PictureURL string `json:"pictureUrl"` 46 | StatusMessage string `json:"statusMessage"` 47 | } 48 | 49 | // Client ... 50 | type Client struct { 51 | endPoint string 52 | channelAccessToken string 53 | } 54 | 55 | var eventHandler EventHandler 56 | var channelSecret string 57 | 58 | // NewClient ... 59 | func NewClient(channelAccessToken string) *Client { 60 | return &Client{ 61 | channelAccessToken: channelAccessToken, 62 | endPoint: EndPoint, 63 | } 64 | } 65 | 66 | // SetEventHandler ... 67 | func (c *Client) SetEventHandler(event EventHandler) { 68 | eventHandler = event 69 | } 70 | 71 | // SetChannelSecret ... 72 | func (c *Client) SetChannelSecret(secret string) { 73 | channelSecret = secret 74 | } 75 | 76 | func (c *Client) setHeader(req *http.Request) *http.Request { 77 | req.Header.Set("Content-Type", "application/json; charset=UTF-8") 78 | req.Header.Set("X-LINE-ChannelToken", c.channelAccessToken) 79 | req.Header.Set("Authorization", "Bearer "+c.channelAccessToken) 80 | req.Header.Set("User-Agent", "dongri/line-bot-sdk-go") 81 | return req 82 | } 83 | 84 | func (c *Client) do(req *http.Request) (*http.Response, []byte, error) { 85 | req = c.setHeader(req) 86 | client := &http.Client{ 87 | Timeout: time.Duration(30 * time.Second), 88 | } 89 | res, err := client.Do(req) 90 | if err != nil { 91 | return res, nil, err 92 | } 93 | defer res.Body.Close() 94 | 95 | if res.StatusCode < 200 || res.StatusCode >= 400 { 96 | body, readErr := io.ReadAll(res.Body) 97 | if readErr != nil { 98 | return res, nil, readErr 99 | } 100 | var result APISendResult 101 | if unmarshalErr := json.Unmarshal(body, &result); unmarshalErr != nil { 102 | return res, nil, unmarshalErr 103 | } 104 | fmt.Println(result) 105 | return res, nil, errors.New("server error status code: " + strconv.Itoa(res.StatusCode)) 106 | } 107 | body, err := io.ReadAll(res.Body) 108 | return res, body, err 109 | } 110 | 111 | // PushMessage ... 112 | func (c *Client) PushMessage(to string, messages ...Message) (*APISendResult, error) { 113 | pushMessage := struct { 114 | To string `json:"to"` 115 | Messages []Message `json:"messages"` 116 | }{ 117 | To: to, 118 | Messages: messages, 119 | } 120 | b, err := json.Marshal(pushMessage) 121 | if err != nil { 122 | return nil, err 123 | } 124 | req, err := http.NewRequest("POST", EndPoint+PushMessage, bytes.NewBuffer(b)) 125 | if err != nil { 126 | return nil, err 127 | } 128 | _, body, err := c.do(req) 129 | if err != nil { 130 | return nil, err 131 | } 132 | var result APISendResult 133 | if err := json.Unmarshal(body, &result); err != nil { 134 | return nil, err 135 | } 136 | return &result, nil 137 | } 138 | 139 | // ReplyMessage ... 140 | func (c *Client) ReplyMessage(replyToken string, messages ...Message) (*APISendResult, error) { 141 | replyMessage := struct { 142 | ReplyToken string `json:"replyToken"` 143 | Messages []Message `json:"messages"` 144 | }{ 145 | ReplyToken: replyToken, 146 | Messages: messages, 147 | } 148 | b, err := json.Marshal(replyMessage) 149 | if err != nil { 150 | return nil, err 151 | } 152 | req, err := http.NewRequest("POST", EndPoint+ReplyMessage, bytes.NewBuffer(b)) 153 | if err != nil { 154 | return nil, err 155 | } 156 | _, body, err := c.do(req) 157 | if err != nil { 158 | return nil, err 159 | } 160 | var result APISendResult 161 | if err := json.Unmarshal(body, &result); err != nil { 162 | return nil, err 163 | } 164 | return &result, nil 165 | } 166 | 167 | // GetMessageContent ... 168 | func (c *Client) GetMessageContent(messageID string) (*MessageContentResponse, error) { 169 | endpoint := fmt.Sprintf(EndPoint+GetMessageContent, messageID) 170 | req, err := http.NewRequest("GET", endpoint, nil) 171 | if err != nil { 172 | return nil, err 173 | } 174 | res, body, err := c.do(req) 175 | if err != nil { 176 | return nil, err 177 | } 178 | result := MessageContentResponse{ 179 | Content: body, 180 | ContentType: res.Header.Get("Content-Type"), 181 | ContentLength: res.ContentLength, 182 | } 183 | return &result, nil 184 | } 185 | 186 | // GetProfile ... 187 | func (c *Client) GetProfile(userID string) (*UserProfileResponse, error) { 188 | endpoint := fmt.Sprintf(EndPoint+GetProfile, userID) 189 | req, err := http.NewRequest("GET", endpoint, nil) 190 | if err != nil { 191 | return nil, err 192 | } 193 | _, body, err := c.do(req) 194 | if err != nil { 195 | return nil, err 196 | } 197 | result := UserProfileResponse{} 198 | if err := json.Unmarshal(body, &result); err != nil { 199 | return nil, err 200 | } 201 | return &result, nil 202 | } 203 | 204 | // LeaveGroup ... 205 | func (c *Client) LeaveGroup(groupID string) (*BasicResponse, error) { 206 | endpoint := fmt.Sprintf(EndPoint+LeaveGroup, groupID) 207 | req, err := http.NewRequest("POST", endpoint, nil) 208 | if err != nil { 209 | return nil, err 210 | } 211 | _, body, err := c.do(req) 212 | if err != nil { 213 | return nil, err 214 | } 215 | result := BasicResponse{} 216 | if err := json.Unmarshal(body, &result); err != nil { 217 | return nil, err 218 | } 219 | return &result, nil 220 | } 221 | 222 | // LeaveRoom ... 223 | func (c *Client) LeaveRoom(roomID string) (*BasicResponse, error) { 224 | endpoint := fmt.Sprintf(EndPoint+LeaveRoom, roomID) 225 | req, err := http.NewRequest("POST", endpoint, nil) 226 | if err != nil { 227 | return nil, err 228 | } 229 | _, body, err := c.do(req) 230 | if err != nil { 231 | return nil, err 232 | } 233 | result := BasicResponse{} 234 | if err := json.Unmarshal(body, &result); err != nil { 235 | return nil, err 236 | } 237 | return &result, nil 238 | } 239 | -------------------------------------------------------------------------------- /linebot/event.go: -------------------------------------------------------------------------------- 1 | package linebot 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha256" 6 | "encoding/base64" 7 | "encoding/json" 8 | "errors" 9 | "io" 10 | "log" 11 | "net/http" 12 | ) 13 | 14 | // Webhook ... 15 | type Webhook struct { 16 | Events []Event `json:"events"` 17 | } 18 | 19 | // Event ... 20 | type Event struct { 21 | ReplyToken string `json:"replyToken"` 22 | Type EventType `json:"type"` 23 | Timestamp int64 `json:"timestamp"` 24 | Source EventSource `json:"source"` 25 | Message struct { 26 | ID string `json:"id"` 27 | Type MessageType `json:"type"` 28 | Text string `json:"text"` 29 | Duration int `json:"duration"` 30 | Title string `json:"title"` 31 | Address string `json:"address"` 32 | Latitude float64 `json:"latitude"` 33 | Longitude float64 `json:"longitude"` 34 | PackageID string `json:"packageId"` 35 | StickerID string `json:"stickerId"` 36 | } `json:"message"` 37 | Postback `json:"postback"` 38 | Beacon `json:"beacon"` 39 | } 40 | 41 | // EventType ... 42 | type EventType string 43 | 44 | // EentTypes 45 | const ( 46 | EventTypeMessage EventType = "message" 47 | EventTypeFollow EventType = "follow" 48 | EventTypeUnfollow EventType = "unfollow" 49 | EventTypeJoin EventType = "join" 50 | EventTypeLeave EventType = "leave" 51 | EventTypePostback EventType = "postback" 52 | EventTypeBeacon EventType = "beacon" 53 | ) 54 | 55 | // EventSourceType ... 56 | type EventSourceType string 57 | 58 | // EventSourceType .... 59 | const ( 60 | EventSourceTypeUser EventSourceType = "user" 61 | EventSourceTypeGroup EventSourceType = "group" 62 | EventSourceTypeRoom EventSourceType = "room" 63 | ) 64 | 65 | // EventSource ... 66 | type EventSource struct { 67 | Type EventSourceType `json:"type"` 68 | UserID string `json:"userId"` 69 | GroupID string `json:"groupId"` 70 | RoomID string `json:"roomId"` 71 | } 72 | 73 | // Postback ... 74 | type Postback struct { 75 | Data string `json:"data"` 76 | } 77 | 78 | // Beacon ... 79 | type Beacon struct { 80 | Hwid string `json:"hwid"` 81 | Type string `json:"type"` 82 | } 83 | 84 | // Middleware ... 85 | func Middleware(next http.Handler) http.Handler { 86 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 87 | defer r.Body.Close() 88 | body, err := io.ReadAll(r.Body) 89 | if err != nil { 90 | log.Fatal(err) 91 | } 92 | if !validateSignature(r.Header.Get("x-line-signature"), body) { 93 | log.Fatal(errors.New("invalid signature")) 94 | } 95 | webhook := new(Webhook) 96 | if err = json.Unmarshal(body, webhook); err != nil { 97 | log.Fatal(err) 98 | } 99 | for _, event := range webhook.Events { 100 | eventHandler.OnEvent(event) 101 | switch event.Type { 102 | case EventTypeMessage: 103 | switch event.Message.Type { 104 | case MessageTypeText: 105 | eventHandler.OnTextMessage(event.Source, event.ReplyToken, event.Message.Text) 106 | case MessageTypeImage: 107 | eventHandler.OnImageMessage(event.Source, event.ReplyToken, event.Message.ID) 108 | case MessageTypeVideo: 109 | eventHandler.OnVideoMessage(event.Source, event.ReplyToken, event.Message.ID) 110 | case MessageTypeAudio: 111 | eventHandler.OnAudioMessage(event.Source, event.ReplyToken, event.Message.ID) 112 | case MessageTypeLocation: 113 | eventHandler.OnLocationMessage(event.Source, event.ReplyToken, event.Message.Title, event.Message.Address, event.Message.Latitude, event.Message.Longitude) 114 | case MessageTypeSticker: 115 | eventHandler.OnStickerMessage(event.Source, event.ReplyToken, event.Message.PackageID, event.Message.StickerID) 116 | } 117 | case EventTypeFollow: 118 | eventHandler.OnFollowEvent(event.Source, event.ReplyToken) 119 | case EventTypeUnfollow: 120 | eventHandler.OnUnFollowEvent(event.Source) 121 | case EventTypeJoin: 122 | eventHandler.OnJoinEvent(event.Source, event.ReplyToken) 123 | case EventTypeLeave: 124 | eventHandler.OnLeaveEvent(event.Source) 125 | case EventTypePostback: 126 | eventHandler.OnPostbackEvent(event.Source, event.ReplyToken, event.Postback.Data) 127 | case EventTypeBeacon: 128 | eventHandler.OnBeaconEvent(event.Source, event.ReplyToken, event.Beacon.Hwid, event.Beacon.Type) 129 | } 130 | } 131 | next.ServeHTTP(w, r) 132 | }) 133 | } 134 | 135 | func validateSignature(signature string, body []byte) bool { 136 | decoded, err := base64.StdEncoding.DecodeString(signature) 137 | if err != nil { 138 | return false 139 | } 140 | hash := hmac.New(sha256.New, []byte(channelSecret)) 141 | hash.Write(body) 142 | return hmac.Equal(decoded, hash.Sum(nil)) 143 | } 144 | 145 | // EventHandler ... 146 | type EventHandler interface { 147 | OnEvent(event Event) 148 | OnFollowEvent(source EventSource, replyToken string) 149 | OnUnFollowEvent(source EventSource) 150 | OnJoinEvent(source EventSource, replyToken string) 151 | OnLeaveEvent(source EventSource) 152 | OnPostbackEvent(source EventSource, replyToken, postbackData string) 153 | OnBeaconEvent(source EventSource, replyToken, beaconHwid string, beaconType string) 154 | OnTextMessage(source EventSource, replyToken, text string) 155 | OnImageMessage(source EventSource, replyToken, id string) 156 | OnVideoMessage(source EventSource, replyToken, id string) 157 | OnAudioMessage(source EventSource, replyToken, id string) 158 | OnLocationMessage(source EventSource, replyToken string, title, address string, latitude, longitude float64) 159 | OnStickerMessage(source EventSource, replyToken, packageID, stickerID string) 160 | } 161 | -------------------------------------------------------------------------------- /linebot/imagemap.go: -------------------------------------------------------------------------------- 1 | package linebot 2 | 3 | // ImagemapActionType ... 4 | type ImagemapActionType string 5 | 6 | // ImagemapActionType ... 7 | const ( 8 | ImagemapActionTypeURI ImagemapActionType = "uri" 9 | ImagemapActionTypeMessage ImagemapActionType = "message" 10 | ) 11 | 12 | // ImagemapAction ... 13 | type ImagemapAction interface { 14 | imagemapAction() 15 | } 16 | 17 | // ImagemapMessage ... 18 | type ImagemapMessage struct { 19 | Type MessageType `json:"type"` 20 | BaseURL string `json:"baseUrl"` 21 | AltText string `json:"altText"` 22 | BaseSize ImagemapBaseSize `json:"baseSize"` 23 | Actions []ImagemapAction `json:"actions"` 24 | } 25 | 26 | // ImagemapBaseSize ... 27 | type ImagemapBaseSize struct { 28 | Width int `json:"width"` 29 | Height int `json:"height"` 30 | } 31 | 32 | // ImagemapArea ... 33 | type ImagemapArea struct { 34 | X int `json:"x"` 35 | Y int `json:"y"` 36 | Width int `json:"width"` 37 | Height int `json:"height"` 38 | } 39 | 40 | // ImagemapURIAction ... 41 | type ImagemapURIAction struct { 42 | Type ImagemapActionType `json:"type"` 43 | LinkURI string `json:"linkUri"` 44 | Area ImagemapArea `json:"area"` 45 | } 46 | 47 | // ImagemapMessageAction ... 48 | type ImagemapMessageAction struct { 49 | Type ImagemapActionType `json:"type"` 50 | Text string `json:"text"` 51 | Area ImagemapArea `json:"area"` 52 | } 53 | 54 | func (*ImagemapURIAction) imagemapAction() {} 55 | func (*ImagemapMessageAction) imagemapAction() {} 56 | 57 | // NewImagemapMessage ... 58 | func NewImagemapMessage(baseURL, altText string, baseSizeWidth, baseSizeHeght int, actions ...ImagemapAction) *ImagemapMessage { 59 | m := new(ImagemapMessage) 60 | m.Type = MessageTypeImagemap 61 | m.BaseURL = baseURL 62 | m.AltText = altText 63 | size := new(ImagemapBaseSize) 64 | size.Width = baseSizeWidth 65 | size.Height = baseSizeHeght 66 | m.BaseSize = *size 67 | m.Actions = actions 68 | return m 69 | } 70 | 71 | // NewImagemapURIAction ... 72 | func NewImagemapURIAction(linkURI string, area ImagemapArea) *ImagemapURIAction { 73 | a := new(ImagemapURIAction) 74 | a.Type = ImagemapActionTypeURI 75 | a.LinkURI = linkURI 76 | a.Area = area 77 | return a 78 | } 79 | 80 | // NewImagemapMessageAction ... 81 | func NewImagemapMessageAction(text string, area ImagemapArea) *ImagemapMessageAction { 82 | a := new(ImagemapMessageAction) 83 | a.Type = ImagemapActionTypeMessage 84 | a.Text = text 85 | a.Area = area 86 | return a 87 | } 88 | 89 | // NewImagemapArea ... 90 | func NewImagemapArea(x, y, width, height int) *ImagemapArea { 91 | a := new(ImagemapArea) 92 | a.X = x 93 | a.Y = y 94 | a.Width = width 95 | a.Height = height 96 | return a 97 | } 98 | -------------------------------------------------------------------------------- /linebot/message.go: -------------------------------------------------------------------------------- 1 | package linebot 2 | 3 | // MessageType ... 4 | type MessageType string 5 | 6 | // MessageType ... 7 | const ( 8 | MessageTypeText MessageType = "text" 9 | MessageTypeImage MessageType = "image" 10 | MessageTypeVideo MessageType = "video" 11 | MessageTypeAudio MessageType = "audio" 12 | MessageTypeLocation MessageType = "location" 13 | MessageTypeSticker MessageType = "sticker" 14 | MessageTypeTemplate MessageType = "template" 15 | MessageTypeImagemap MessageType = "imagemap" 16 | ) 17 | 18 | // Message ... 19 | type Message interface { 20 | message() 21 | } 22 | 23 | // TextMessage ... 24 | type TextMessage struct { 25 | Type MessageType `json:"type"` 26 | Text string `json:"text"` 27 | } 28 | 29 | // ImageMessage ... 30 | type ImageMessage struct { 31 | Type MessageType `json:"type"` 32 | OriginalContentURL string `json:"originalContentUrl"` 33 | PreviewImageURL string `json:"previewImageUrl"` 34 | } 35 | 36 | // VideoMessage ... 37 | type VideoMessage struct { 38 | Type MessageType `json:"type"` 39 | OriginalContentURL string `json:"originalContentUrl"` 40 | PreviewImageURL string `json:"previewImageUrl"` 41 | } 42 | 43 | // AudioMessage ... 44 | type AudioMessage struct { 45 | Type MessageType `json:"type"` 46 | OriginalContentURL string `json:"originalContentUrl"` 47 | Duration int `json:"duration"` 48 | } 49 | 50 | // LocationMessage ... 51 | type LocationMessage struct { 52 | Type MessageType `json:"type"` 53 | Title string `json:"title"` 54 | Address string `json:"address"` 55 | Latitude float64 `json:"latitude"` 56 | Longitude float64 `json:"longitude"` 57 | } 58 | 59 | // StickerMessage ... 60 | type StickerMessage struct { 61 | Type MessageType `json:"type"` 62 | PackageID string `json:"packageId"` 63 | StickerID string `json:"stickerId"` 64 | } 65 | 66 | // Message interface 67 | func (*TextMessage) message() {} 68 | func (*ImageMessage) message() {} 69 | func (*VideoMessage) message() {} 70 | func (*AudioMessage) message() {} 71 | func (*LocationMessage) message() {} 72 | func (*StickerMessage) message() {} 73 | func (*TemplateMessage) message() {} 74 | func (*ImagemapMessage) message() {} 75 | 76 | // NewTextMessage ... 77 | func NewTextMessage(text string) *TextMessage { 78 | m := new(TextMessage) 79 | m.Type = MessageTypeText 80 | m.Text = text 81 | return m 82 | } 83 | 84 | // NewImageMessage ... 85 | func NewImageMessage(originalContentURL, previewImageURL string) *ImageMessage { 86 | m := new(ImageMessage) 87 | m.Type = MessageTypeImage 88 | m.OriginalContentURL = originalContentURL 89 | m.PreviewImageURL = previewImageURL 90 | return m 91 | } 92 | 93 | // NewVideoMessage ... 94 | func NewVideoMessage(originalContentURL, previewImageURL string) *VideoMessage { 95 | m := new(VideoMessage) 96 | m.Type = MessageTypeVideo 97 | m.OriginalContentURL = originalContentURL 98 | m.PreviewImageURL = previewImageURL 99 | return m 100 | } 101 | 102 | // NewAudioMessage ... 103 | func NewAudioMessage(originalContentURL string, duration int) *AudioMessage { 104 | m := new(AudioMessage) 105 | m.Type = MessageTypeAudio 106 | m.OriginalContentURL = originalContentURL 107 | m.Duration = duration 108 | return m 109 | } 110 | 111 | // NewLocationMessage ... 112 | func NewLocationMessage(title, address string, latitude, longitude float64) *LocationMessage { 113 | m := new(LocationMessage) 114 | m.Type = MessageTypeLocation 115 | m.Title = title 116 | m.Address = address 117 | m.Latitude = latitude 118 | m.Longitude = longitude 119 | return m 120 | } 121 | 122 | // NewStickerMessage ... 123 | func NewStickerMessage(packageID, stickerID string) *StickerMessage { 124 | m := new(StickerMessage) 125 | m.Type = MessageTypeSticker 126 | m.PackageID = packageID 127 | m.StickerID = stickerID 128 | return m 129 | } 130 | -------------------------------------------------------------------------------- /linebot/template.go: -------------------------------------------------------------------------------- 1 | package linebot 2 | 3 | // TemplateType ... 4 | type TemplateType string 5 | 6 | // TemplateType ... 7 | const ( 8 | TemplateTypeButtons TemplateType = "buttons" 9 | TemplateTypeConfirm TemplateType = "confirm" 10 | TemplateTypeCarousel TemplateType = "carousel" 11 | ) 12 | 13 | // TemplateActionType ... 14 | type TemplateActionType string 15 | 16 | // TemplateActionType ... 17 | const ( 18 | TemplateActionTypeURI TemplateActionType = "uri" 19 | TemplateActionTypeMessage TemplateActionType = "message" 20 | TemplateActionTypePostback TemplateActionType = "postback" 21 | ) 22 | 23 | // Template ... 24 | type Template interface { 25 | template() 26 | } 27 | 28 | // TemplateAction ... 29 | type TemplateAction interface { 30 | templateAction() 31 | } 32 | 33 | // TemplateMessage ... 34 | type TemplateMessage struct { 35 | Type MessageType `json:"type"` 36 | AltText string `json:"altText"` 37 | Template Template `json:"template"` 38 | } 39 | 40 | // ButtonsTemplate ... 41 | type ButtonsTemplate struct { 42 | Type TemplateType `json:"type"` 43 | ThumbnailImageURL string `json:"thumbnailImageUrl,omitempty"` 44 | Title string `json:"title,omitempty"` 45 | Text string `json:"text"` 46 | Actions []TemplateAction `json:"actions"` 47 | } 48 | 49 | // ConfirmTemplate ... 50 | type ConfirmTemplate struct { 51 | Type TemplateType `json:"type"` 52 | Text string `json:"text"` 53 | Actions []TemplateAction `json:"actions"` 54 | } 55 | 56 | // CarouselTemplate ... 57 | type CarouselTemplate struct { 58 | Type TemplateType `json:"type"` 59 | Columns []*CarouselColumn `json:"columns"` 60 | } 61 | 62 | // TemplateURIAction ... 63 | type TemplateURIAction struct { 64 | Type TemplateActionType `json:"type"` 65 | Label string `json:"label"` 66 | URI string `json:"uri"` 67 | } 68 | 69 | // TemplateMessageAction ... 70 | type TemplateMessageAction struct { 71 | Type TemplateActionType `json:"type"` 72 | Label string `json:"label"` 73 | Text string `json:"text"` 74 | } 75 | 76 | // TemplatePostbackAction ... 77 | type TemplatePostbackAction struct { 78 | Type TemplateActionType `json:"type"` 79 | Label string `json:"label"` 80 | Data string `json:"data"` 81 | Text string `json:"text,omitempty"` 82 | } 83 | 84 | // CarouselColumn ... 85 | type CarouselColumn struct { 86 | ThumbnailImageURL string `json:"thumbnailImageUrl,omitempty"` 87 | Title string `json:"title,omitempty"` 88 | Text string `json:"text"` 89 | Actions []TemplateAction `json:"actions"` 90 | } 91 | 92 | // Template interface 93 | func (*ConfirmTemplate) template() {} 94 | func (*ButtonsTemplate) template() {} 95 | func (*CarouselTemplate) template() {} 96 | 97 | // TemplateAction interface 98 | func (*TemplateURIAction) templateAction() {} 99 | func (*TemplateMessageAction) templateAction() {} 100 | func (*TemplatePostbackAction) templateAction() {} 101 | 102 | // NewTemplateMessage ... 103 | func NewTemplateMessage(altText string, template Template) *TemplateMessage { 104 | m := new(TemplateMessage) 105 | m.Type = MessageTypeTemplate 106 | m.AltText = altText 107 | m.Template = template 108 | return m 109 | } 110 | 111 | // NewConfirmTemplate ... 112 | func NewConfirmTemplate(text string, actions ...TemplateAction) *ConfirmTemplate { 113 | t := new(ConfirmTemplate) 114 | t.Type = TemplateTypeConfirm 115 | t.Text = text 116 | t.Actions = actions 117 | return t 118 | } 119 | 120 | // NewButtonsTemplate ... 121 | func NewButtonsTemplate(thumbnailImageURL, title, text string, actions ...TemplateAction) *ButtonsTemplate { 122 | t := new(ButtonsTemplate) 123 | t.Type = TemplateTypeButtons 124 | t.ThumbnailImageURL = thumbnailImageURL 125 | t.Title = title 126 | t.Text = text 127 | t.Actions = actions 128 | return t 129 | } 130 | 131 | // NewCarouselTemplate ... 132 | func NewCarouselTemplate(columns ...*CarouselColumn) *CarouselTemplate { 133 | t := new(CarouselTemplate) 134 | t.Type = TemplateTypeCarousel 135 | t.Columns = columns 136 | return t 137 | } 138 | 139 | // NewCarouselColumn ... 140 | func NewCarouselColumn(thumbnailImageURL, title, text string, actions ...TemplateAction) *CarouselColumn { 141 | c := new(CarouselColumn) 142 | c.ThumbnailImageURL = thumbnailImageURL 143 | c.Title = title 144 | c.Text = text 145 | c.Actions = actions 146 | return c 147 | } 148 | 149 | // NewTemplateURIAction ... 150 | func NewTemplateURIAction(label, uri string) *TemplateURIAction { 151 | a := new(TemplateURIAction) 152 | a.Type = TemplateActionTypeURI 153 | a.Label = label 154 | a.URI = uri 155 | return a 156 | } 157 | 158 | // NewTemplateMessageAction ... 159 | func NewTemplateMessageAction(label, text string) *TemplateMessageAction { 160 | a := new(TemplateMessageAction) 161 | a.Type = TemplateActionTypeMessage 162 | a.Label = label 163 | a.Text = text 164 | return a 165 | } 166 | 167 | // NewTemplatePostbackAction ... 168 | func NewTemplatePostbackAction(label, data, text string) *TemplatePostbackAction { 169 | a := new(TemplatePostbackAction) 170 | a.Type = TemplateActionTypePostback 171 | a.Label = label 172 | a.Data = data 173 | a.Text = text 174 | return a 175 | } 176 | --------------------------------------------------------------------------------