[研討會心得] 2020/05/27 Chatbots 19 @online 與 2020 May LINE 平台更新整理報告

前言

大家好,我是 LINE Taiwan 的 Tech Evangelist - Evan Lin。這次很開心受到 chatbot 社群的邀請,參加了 “Chatbot meetup 聊天機器人小小聚 19 @Online” 的聚會活動,並且分享 LINE API 更新與個人開發的心得。在此也跟各位分享本次參與的心得,並且也希望透過社群分享的力量能夠讓聊天機器人的開發動能更加的盛大。

由於 Chatbots Meetup 本身屬於社群自主性的活動,裡面也有許多社群朋友所贊助的閃電秀。裡面的所有內容也是相當的難得與有趣。也希望能夠透過本篇文章讓大家稍微了解 Chatbots Meetup 社群閃電秀的魅力。

由於防疫的要求,這次是在線上參與這次的聚會活動。很感謝每一位參與的朋友帶來的問題,希望透過線上以及錄影的方式可以讓更多的朋友可以了解。

LINE Platform Update 202005 / 資深開發技術推廣工程師 Evan Lin

投影片

04/30: liff.getLineVersion() and liff.id added to LIFF v2

雖然之前 LIFF 有提供了 liff.isApiAvailable() 的功能來檢查該 API 是否能夠支援。 但是許多時候,開發者是需要取得使用者的 LINE App 版本來對於問題的復現與除錯之用。 這時候可以透過 LIFF 的 liff.getLineVersion() 可以取得目前執行這個 LIFF 的 LINE App 版本號碼。 當然如果使用者是使用 External Browser 來開啟 LIFF 的話這個就會回傳 null

05/08: LIFF Versioning policy and life cycle have been released

接下來這個部分是討論 LIFF 的 Versioning policy 之後將採取 Semantic Versioning 也就是針對版本號碼的管理,將依照以下原則: MAJOR.MINOR.PATCH 。至於 MAJORMINORPATCH 分別代表什麼意義,歡迎查看相關資料。

此外,這一次的公告也提出了。將版本停止支援( End-of-life) 之後,相關的 SDK 也會從 CDN 移除,也就不會有任何 API 可以使用。

最後,也是最重要的就是開發者們關心的 LIFF 支援的週期: LIFF v1 將支援到 2021 的九月

希望所有開發者能夠記住這個時間,儘早準備 migrate LIFF App 到 LIFF v2 。

05/12: Messaging API update for May 2020

Message character limit and media file usage conditions have been changed

又到了每個月的平台更新相關解說,五月份的平台更新。第一個部分的更新,是關於 Messaging API 有一些訊息的額度要增加了。 依序有相關的 Image Message, Video Message 跟 Text Message 的上限增加。 需要的人可以查看這個部分

Getting LINE emoji information from the text object of a webhook event

第二個部分的更新是新增了 LINE Emoji webhook 的支援,讓開發者可以收到使用者傳來的 LINE Emoji 訊息資料。 詳細的部分歡迎查看: 關於 LINE Emoji 的一些細節(以 Golang 為例)

Safely retrying a failed API request

許多開發者在發送 Broadcast 或是 Multicast 訊息的時候,可能因為訊息量過大沒有收到平台這邊回覆正確。 也有可能因為某些意外狀況平台回覆並非正常的狀況,造成開發者們無法確認使用者有沒有收到正確的訊息。 以往的狀況是透過重複發送的方式來確保使用者能夠收到訊息,但是如此一來有不少重複的使用者可能會收到重複的訊息,進而造成費用的重複計算。

這一次五月的更新,提供了 “Safely retrying” 機制。 可以讓開發者測試一下上次的訊息是否有正確的發送成功,並且也可以確保有無任何的使用者被漏發了。 相關的使用情境如下:

  • 上次不知道有無法送完成,呼叫 “Safely retrying” 可以重複發送同一則訊息。 有收過得不會收到重複訊息,沒收到的可以確保收到。
  • 上次發送發生了平台無法完成指令的意外,透過 “Safely retrying” 可以跟平台確認上次的狀況。 如果上次有完整發送完畢,也不會有重複計費的疑慮。

05/19 OGP tags are now available in the LIFF app

OGP (Open Graph Protocol) 原本就是一個給 SNS 或是瀏覽器作為擷取縮圖與相關資訊的協定。 但是之前透過 http://liff.line.me 的轉址原因,無法正確讓 LIFF 上面的 OGP 成功運作。 現在也已經正常的支援,不論是哪一種 LIFF 資訊都可以透過填寫正確的 OGP 資訊在其他 SNS (或是 LINE App) 上取得正確的資訊與縮圖。

之前透過 LIFF 進去某個頁面後,無法正確的取得某個子頁面的網址作為傳遞與分享。 現在可以透過 liff.permanentLink.createUrl() 的新支援的 API 來分享 LIFF 頁面給其他使用者。 也可以透過 liff.permanentLink.setExtraQueryParams() 來幫忙將使用者目前的環境參數用來組合相關的資訊,成為一整個可以分享的網址。

LINE Beacon 的介紹:

最後也跟開發者們分享,目前的 LINE Beacon 也已經正式開始營運。有任何開發上需求的開發者(或是業者),歡迎透過 LINE for Business 與經銷業務接洽。 主要的差別,除了可以使用 Beacon Banner 之外,也可以大量的部署與推廣。更有相關的平台可以讓你設定許多有用的訊息推廣。

使用 lotify + Swagger 建置可共用的 LINE Notify bot / LINE API Expert - NiJia

影片

投影片

第二位是 LINE API Expert 的 NiJia 帶來的 LINE Notify 套件 (lotify) 的說明。lotify (https://github.com/louis70109/lotify) 是一個 Python 開發的 LINE Notify SDK ,可以幫助開發者快速上手並且讓開發者除了發送之外,還可以產生網址跟換 Token 的功能都有支援。 並且還有以下的特點:

  • 快速部署 Heroku
  • Client library
  • Swagger 文件
  • Openapi generator
  • JS 使用範例

github: https://github.com/louis70109/lotify

如何在 LIFF 傳送隱藏資料給機器人 / 微程式股份有限公司 - 均民

影片

投影片

講者透過分享了透過 liff.sendMessage() 來發送一個 2x1 的透明圖片的方式來夾帶不同的資訊給 LINE Bot 的 Webhook 端。 透過這樣可以將資訊從 LIFF 前端帶給 LINE Bot 並且因為是發送訊息的方式,就可以有 replyToken 可以回覆給使用者。

相關的部落格文章: https://taichunmin.idv.tw/blog/2020-04-07-line-liff-send-hidden-data.html

閃電秀 - LINEBOT 學習地圖】 - #Jeffrey

影片

投影片

講者 Jeffrey 近期在社群分享了一系列的 LINE Bot 學習地圖的影片,透過手繪圖的分享方式來談談 LINE Bot 在分析了 LINE Bot 互動行為的種類,也分享了設計上應該要如何能夠解決使用者的需求。

很推薦任何人都可以聽,從 UX 與設計方面來對於 LINE Bot 有了更多更廣的了解。

活動小結

社群分享永遠是讓創意激盪的最佳方式,而 Chatbots Meetup 是一個很熱情與充滿創造力的社群組織。也希望有更多有創意的開發者願意加入 LINE Chatbot 的開發行列,更希望能熱情的參與社群的活動與一起來分享。

立即加入「LINE開發者官方社群」官方帳號,就能收到第一手Meetup活動,或與開發者計畫有關的最新消息的推播通知。▼

「LINE開發者官方社群」官方帳號ID:@line_tw_dev

關於「LINE開發社群計畫」

LINE今年年初在台灣啟動「LINE開發社群計畫」,將長期投入人力與資源在台灣舉辦對內對外、線上線下的開發者社群聚會、徵才日、開發者大會等,已經舉辦30場以上的活動。歡迎讀者們能夠持續回來察看最新的狀況。詳情請看:

[LINE][教學] 如何使用新 API 來有效地發送與接收 LINE Emoji

前言

LINE Emoji 是指在 LINE App 中可以使用的 LINE 表情集,其中有分為通用的(免費)與付費的表情集。 使用 LINE Emoji 在訊息當中可以讓使用者閱讀的時候更有感覺。 但是身為開發者該如何正確地發送與接受處理這些 LINE Emoji 呢?

這一篇文章將透過 Golang 的範例程式碼,指引該如何正確地發送與接受 LINE 表情集 (LINE Emoji) 。

投影片:

範例程式碼

https://github.com/kkdai/linebot-emoji

實際跑一個範例 (demo)

  • 加入這個官方帳號:

img

  • 隨便傳送一個表情符號,會看到聊天機器人用三種方式回覆你。 分別是:
    • 舊的 Emoji 傳遞方式來發送
    • 新的 Emoji 傳遞方式來發送
    • 加上使用者的 Emoji 並且透過新的 Emoji 來發送

新的 API 說明:

Use LINE emoji in messages (2020/April)

現在開始要在文字訊息裡面發送 LINE emoji 不需要自行做 unicode 轉換。可以直接在 API 中加上相關的 LINE emoji 編號就可以達成了,開發上變得更方便,也更有彈性。

可以參考新的 API : Text message 或是參考新的公告:[Updated] Messaging API update for April 2020

Getting LINE emoji information from the text object of a webhook event (2020/May)

在四月提供了新的發送 API 之後,五月的 Webhook 也提供了新的 Webhook 資訊可以讓聊天機器人有效的處理 LINE Emoji 。 透過 emojis 可以取得所有訊息中出現的 LINE Emoji 詳細資訊如下:

可以參考新的 API : Text message webhook 或是參考新的公告:Messaging API update for May 2020

使用 Golang 開發一個 LINE Emoji Echo Bot:

接下來的會使用 Golang ,根據 https://github.com/line/line-bot-sdk-go 提供的功能來開發 Echo Bot 。也就是一個會依照使用者講的文字來回覆的聊天機器人。 但是不同於一般 Echo Chatbot ,這個 Echo Bot 將會回傳使用者傳過來的 LINE Emoji ,所以需要具有以下幾個功能:

  • 擷取 Webhook text Message 中的 emojis 資訊 。
  • 將原先文字中的 (xxx) (作為表示表情符號的意思,舉例來說 (heart) 是愛心),替換成 $
  • 組合需要回覆使用者的文字與表情,需要注意的事情有兩件:
    • 將使用者回覆的文字加上 emoji 資訊,裡面需要注意,相關的 index 資訊需要調整。
    • 傳送 emoji 前要注意,是否是存在於可發送的表情清單(sendable LINE Emoji list)。 由於某一些表情包是需要付費的,只有免費且是 LINE 官方提供使用的可以透過聊天機器人來傳送。 詳情請看: LINE Sendable LINE Emoji List

接下來將透過原始碼的說明來解釋相關的流程:

擷取 Webhook text Message 中的 emojis 資訊 :

// Emoji type
type Emoji struct {
Index int `json:"index"`
Length int `json:"length,omitempty"`
ProductID string `json:"productId,omitempty"`
EmojiID string `json:"emojiId,omitempty"`
}
view raw emoji.go hosted with ❤ by GitHub

收到 text message 的 webhook 之後,會多了一個資料可以讀取 msg.Emojis 的 array structure 。 每一格資料可能是類似一下的資料格式:

根據以上收到的資料可以發現以下資訊:

  • text: 為 Hello, world! (love),其中 Emoji 也會轉成文字在原先的 text 之中為 (love)
  • emojis: 為出現的所有 Emoji ,並且提供起始的 indexlength 。其中 index 為 zero-based , length 包括了 ()

根據以上的資訊,如果你需要將表情符號去掉來做 Language Understanding (語意分析)的話。需要透過以下步驟:

  • Hello, world! (love) 參考 Emojis 資料將 (love)去除,並且重新組合為 Hello, world!

透過新的 LINE Emoji API 來發送表情符號:

//NewEmojiMsg This use linebot.AddEmoji function.
func NewEmojiMsg(msg *linebot.TextMessage) linebot.SendingMessage {
return linebot.NewTextMessage(
fmt.Sprintf("$%s 你好 \n , 這是新的傳送 Emoji 的方式。", msg.Text)).AddEmoji(
linebot.NewEmoji(0, "5ac1bfd5040ab15980c9b435", "086"))
}
view raw new_emoji.go hosted with ❤ by GitHub

根據以上的範例程式碼,可以知道要發送的資料格式如下:

  • text: "$%s 你好 \n , 這是新的傳送 Emoji 的方式。" 其中 $ 為你要傳 LINE Emoji 的位子,而 %s 是要跟使用者傳來的訊息組合成的訊息。
  • AddEmoji: 就是你要傳遞的 LINE Emoji ,其中有 ProductIDEmojiID 需要去 LINE Sendable LINE Emoji List 查詢。 本段範例為 熊大的範例 (ProdctID=5ac1bfd5040ab15980c9b435, EmojiID=086)

根據這樣的方式所完成的 Echo Bot 只能夠完整的傳遞使用者的文字,但是使用者傳送的 LINE Emoji 無法正確被顯示。 (會顯示為 (love) 或是 (heart) )。

完整回傳使用者傳來的 LINE Emoji 訊息

那麼要如何完整的回傳呢? 需要透過以下三個大步驟:

  • 讀取原先使用者傳來問自訊息,並且開始解析 LINE Emoji
  • 將原先有 (brown) 的訊息,替換成 $ 。作為之後需要傳送的時候使用。
  • 將原先使用者傳送的文字,夾帶成聊天機器人要傳送的文字之中。並且將 Emojis ˋ中所有的 index 做位置上的修改( index的位置因為機器人一些其他文字而改變)。

接下來,會稍微分享一下相關的轉換方式:

//ReplaceEmoji Replace original emoji `(brown)` to `$`
func ReplaceEmoji(oriMsg string, emojis []*linebot.Emoji) string {
//Process correct echo message.
var lastLength int
workMsg := oriMsg
for k, v := range emojis {
msgArray := []byte(workMsg)
index := v.Index - lastLength + k
workMsg = fmt.Sprintf("%s%s%s", string(msgArray[:index]), "$", string(msgArray[index+v.Length:]))
lastLength = lastLength + v.Length
}
return workMsg
}

這部分主要功用是將使用者傳來具有 LINE Emoji 的訊息,轉換為 $ 。 (e.g. Hello, world! (love) –> Hello, world! $) 。 裡面主要用到字串的拆解方式,也就是 string(msgArray[:index]), "$", string(msgArray[index+v.Length:] 來拆解文字並且重新組合。

撰寫相關文字轉換的測試案例:

因為可能會遇到的種類相當的多(e.g. hi, _(brown)_, yo (love) (love) ),所以為了怕可能遇到的問題。我們也準備好相當多的測試程式碼。在此先附上部分的,完整的測試範例建議到 https://github.com/kkdai/LineBot-emoji/blob/master/tool_test.go 查看。

func TestSingleReplaceEmoji(t *testing.T) {
test0 := "hello"
want0 := "hello"
emojis0 := []*linebot.Emoji{}
if ret := ReplaceEmoji(test0, emojis0); !strings.EqualFold(want0, ret) {
t.Errorf("[[zero]] Replaced failed, %s", ret)
}
test01 := "(telescope)"
want01 := "$"
emojis := []*linebot.Emoji{
&linebot.Emoji{
Index: 0,
Length: 11}}
if ret := ReplaceEmoji(test01, emojis); !strings.EqualFold(want01, ret) {
t.Errorf("[[single emopji without string]] Replaced failed, %s", ret)
}
test1 := "(telescope) hello"
want1 := "$ hello"
if ret := ReplaceEmoji(test1, emojis); !strings.EqualFold(want1, ret) {
t.Errorf("[[single]] Replaced failed, %s", ret)
}
// more detail on https://github.com/kkdai/LineBot-emoji/blob/master/tool_test.go#L22
}

本段測試程式碼,僅僅對於整段文字裡面出現一個或是沒有的測試案例。 透過撰寫足夠完善的測試案例,可以讓整個 chatbot 的穩定度更好,也不需要透過部署就能夠把一些預先可見的錯誤找出來。

檢查是否是“可傳送的表情服務”:

由於某一些表情包是需要付費的,只有免費且是 LINE 官方提供使用的可以透過聊天機器人來傳送。 詳情請看: LINE Sendable LINE Emoji List

//CheckProdEmojiID :Return an valid product and emoji ID.
//If Emoji product ID is not standard one, it will be replaced by standard brown emoji.
func CheckProdEmojiID(proID, emojiID string) (string, string) {
//All standard emoji product list from https://d.line-scdn.net/r/devcenter/sendable_line_emoji_list.pdf
emojiProductIDs := [...]string{"5ac1bfd5040ab15980c9b435",
"5ac1de17040ab15980c9b438",
"5ac21184040ab15980c9b43a",
// ..... more on https://github.com/kkdai/LineBot-emoji/blob/master/tool.go#L93
"5ac22e85040ab15980c9b44f"}
for _, v := range emojiProductIDs {
if strings.EqualFold(v, proID) {
return proID, emojiID
}
}
return "5ac1bfd5040ab15980c9b435", "086"
}

如果傳送非免費的 LINE Emoji 資料給伺服器,則會收到錯誤訊息。所以這段是透過整理出來的資料來做檢查。

完整的轉換與檢查流程:

//NewEmojiMsgWithEmoji This use linebot.AddEmoji function, also parse original emoji to replace it.
func NewEmojiMsgWithEmoji(msg *linebot.TextMessage) linebot.SendingMessage {
if len(msg.Emojis) > 0 {
//Replace original emoji `(brown)` to `$`
workMsg := ReplaceEmoji(msg.Text, msg.Emojis)
log.Println("Got all detail emoji:", msg.Emojis)
retObj := linebot.NewTextMessage(fmt.Sprintf("$%s 你好 \n , 這是新的傳送 Emoji 的方式,如果你有 emoji 這裡會替換。", workMsg)).AddEmoji(linebot.NewEmoji(0, "5ac1bfd5040ab15980c9b435", "086"))
var lastLength int
for k, v := range msg.Emojis {
log.Println("Got emoji detail:", v)
prodID, emojiID := CheckProdEmojiID(v.ProductID, v.EmojiID)
index := v.Index - lastLength + k
retObj = retObj.AddEmoji(linebot.NewEmoji(1+index, prodID, emojiID))
lastLength = lastLength + v.Length
}
return retObj
}
return linebot.NewTextMessage(fmt.Sprintf("$ 你好 \n %s, 這是新的傳送 Emoji 的方式,如果你有 emoji 這裡會替換。", msg.Text)).AddEmoji(linebot.NewEmoji(0, "5ac1bfd5040ab15980c9b435", "086"))
}

快速講解一下,相關應用程式碼:

  • (5) workMsg := ReplaceEmoji(msg.Text, msg.Emojis): 將收到的使用者文字取代為 $
  • (17)lastLength = lastLength + v.Length: 這邊值得分享的是計算轉換後的 $ 位址,需要透過原本 Emoji 的長度來計算出來。 這樣才能算出每一個表情符號取代文字 $ 應該出現的 index

總結與展望未來 (Summary and Future Work)

透過 LINE Emoji 的 API ,讓開發者在傳送表情符號的處理上變得更加的直覺與簡單。也讓聊天機器人跟使用者之之間的互動變得更沒有距離。 不過如果需要將使用者傳過來的文字(加上表情符號)回傳回去的話,就需要有許多額外的處理。

之後可能針對這些部分可以有以下的相關處理:

  • 自然語言前處理: 如果想要針對使用者回覆的文字來做語意分析(Language Understanding) 那麼勢必要定義對於 LINE Emoji 的處理方式。開發者可以選擇全部忽略,或是將其放入分析器中。
  • 針對表情符號來互動: 使用者會使用表情符號,都會是有相對意義的。某些狀況下,可以透過表情符號(emoji) 來判斷使用者的情緒。 這也是一門相當大的學問才是。

參考

[研討會心得][DSC] 20200507 使用 Go 來打造 LINE 聊天機器人

前言

大家好,我是 LINE Taiwan 的 Technology Evangelist - Evan Lin。 感謝 DSC (Google Developer Student Clubs) 的邀請,在 2020/05/07 線上給予同學們基本的課程。 內容將有 Golang 的基本介紹,並且透過打造一個簡單的 LINE Bot 來跟各位展示 Golang 的一些優點。

這次有來自於四間大學(中興大學,台北大學,臺北科技大學以及銘傳大學)的 DSC 社團同學 ,全程也開放網路直播,希望可以讓更多對於 LINE Bot 有興趣的初學者一個良好的教材。

Build LINE Bot with Go / 資深開發技術推廣工程師 Evan Lin

投影片

這一次分享的投影片,主軸分成三個:

為什麼我會學習 Golang ? 什麼是 Golang?

經過了十年在多媒體外商軟體公司的工作之後,為了學習新的 Web 程式語言進而嘗試了不少的程式語言之後,最後選擇了 Golang 來鑽研。並且為了深入的學習 Golang ,而啟動了 Project 52 來作為學習 Golang 的方式,也因此學習了不少的演算法與進階的資料格式的應用方式。

這裡也介紹了一些 Golang 的基本優點:

  • Gofmt / goimport: 幫助你可以讓你的程式碼風格一致並且避免遺漏某一些需要使用的套件。
  • Go test: 幫助你快速準備好測試的方式,撰寫測試程式碼再也不困難。並且可以相當快速的做好效能調校的工作。
  • GoDoc:做文件一直是讓開發人員很困擾的事情,不是因為開發人員不願意寫。往往是輸出到文件需要有一些相關的轉換,或是需要許多額外的資料在程式碼之中。 使用 GoDoc 只要開發者的程式碼已經寫好了,就可以直接在網路上找到(舉例: https://pkg.go.dev/github.com/line/line-bot-sdk-go/linebot 就是直接將 https://github.com/line/line-bot-sdk-go/linebot 轉換成文件的網站。
  • GoRoutine:做 multiple routine programming 在許多語言其實都有一些困難跟繁瑣。除了多線程模型本身就需要有相當多需要注意的東西之外,要建置這樣的所需的程式碼其實也很複雜。使用 goroutine 相當的輕量,並且相當的容易。開發者可以把心力全部放在如何處理多線程的相關控制上。

如何學習 Golang :

由於這次主要的聽眾都還是大學的學生(部分還是大一的學生),所以許多的操作範例都是直接在 Golang Playground 來進行操作。可以省去許多程式初學者經常遇到的環境設置的問題。

同時也建議初學者可以把出現問題的程式碼透過 Golang Playground 來,分享出來在各種社群媒體來詢問。這樣一來也容易察覺問題與解決問題。

同時也建議同學們可以透過 Golang Tour 來一步一步的學習相關的知識,每一頁都有程式碼可以使用,可以線上編輯與執行。也可以透過邊練習,邊了解的方式更快速能夠加深印象。

什麼是 LINE Bot ? 如何透過 Golang 快速打造出一個 LINE Bot ?

學習一個語言最好的方式就是馬上透過它來打造一個東西,不論是一個產品,一個服務甚至是一個小工具。 在這裡相當建議各位透過打造 LINE Bot 來作為學習 Golang 的方式。主要的理由如下:

  • 打造 LINE Bot 不需要高深的美術觀念,對於一些 UI 美感有困難的人。學習 LINE Bot 不僅僅可以更快入手,並且可以讓開發者更專注在程式本身的功能上。
  • 使用正確的平台與訊息發送方式, LINE Bot 的開發是不需要任何費用。 在影片中的範例,展示透過 Heroku 作為伺服器的平台。打造出來的 LINE Bot 因為僅僅是回覆使用者的問題,也不需要任何額外的費用產生。
  • LINE 是一個相當好的工具(或是服務)的發佈平台,如果開發者們開發出相當有用的工具,透過 LINE 可以快速的推薦給親朋好友。而且不需要類似上架 App 的繁瑣的審核過程,發布的流程也是相當的快速。

接下來開始講解如何快速打造一個 LINE Bot:

範例程式碼: https://github.com/kkdai/LineBotTemplate

1. 先去 LINE 官方網站申請機器人帳號 (LINE Bot )

img

  • 請先確認有在 LINE Developer Console 開啟帳號
  • 然後建立一個 Messaging API Channel
  • 在 “Basic Setting” 頁面,取得 Channel Secret
  • 在 “Messaging API” 頁面,去申請 Channel Access Token
  • 在 “Basic Setting” 頁面,將 LINE 官方帳號管理介面打開
  • 到回覆設定的選項中,選擇啟動 “webhook”

2. Deploy LINE Bot template

記得到 https://github.com/kkdai/LineBotTemplate 然後點選下方的 Deploy 按鈕,將基本的程式碼 Deploy 到你的 heroku 之中.

Deploy]

  • 輸入剛剛取得的 Channel Access TokenChannel Secret
  • 請記住你設定的 Heroku App ID ,稍後會使用到。

3. 回到 LINE Bot Dashboard 設定基本資料

到你的 “Basic account information” 來設定,以下一些資料需要填好:

  • Callback URL: https://{YOUR_HEROKU_SERVER_ID}.herokuapp.com/callback

好了… 加入你的機器人.開始跟他講話吧.

這份程式碼是最簡單的範例,設定好之後他只會重複你打的文字.更多的功能會放在另外一份.

影片教學

可以根據以下影片的教學來看如何在五分鐘之內部署自己的 LINE Bot

想要修改代碼嗎?參考以下的影片教學吧

還有問題嗎? 歡迎在 github 上面開啟 issue 詢問。

如何成為 Golang 的專家?

最後因為分享 Golang 與 Project 52 的原因,因為參與了不少的社群活動與研討會,變成了 Golang Taipei 的主辦人之一,透過持續的分享與社群的經營,我才有幸能夠到 LINE 從事 Developer Relations 的工作並且變成了 Go GDE 。 在此也跟大家分享快速地學習 Golang 的小秘訣。

培養技術寫作的習慣:

由於主要旁聽的聽眾都是學生,在此也相當鼓勵學生們在學習一門新的技術,或是程式語言,透過寫作可以快速讓你不斷的反思學習到知識,並且會想要更深入的去了解。 比如說,學習到一個新的語言功能,試著把它寫成一個 TIL (Today I Learn) 透過這個方式,不僅僅可以讓你加深印象,事後透過網路搜尋可以幫助到許多有著相同問題的人(甚至就是你自己)。

使用 Golang 打造任何東西

學習(程式)語言最好的方式,就是去使用它。所以在這裡也相當建議同學們能夠實際上手去打造任何的小工具。不論是一個 LINE Bot 還是一些日常的小工具。 透過動手去打造他,才能了解每個語言之間的奧妙,才能知道某一些需要注意的地方。

分享!分享!記得要分享!!!

經常有人問我,為何能夠經常在各種場合去分享。 我也誠實的告知,其實分享的途中我學習到的更多:

  • 因為需要分享,我會將我了解的事物變成投影片,變成講稿。
  • 因為需要分享,我需要重新思考要如何讓不懂的人能夠了解我的問題與解決方式。
  • 因為需要分享,在口語之間我發現我了解得更深入,並且更容易記住。

所以也建議每位同學,一定要抓住機會去分享。

相關鏈結:

活動小結

很開心跟學生們分享 Golang 與 LINE Bot 的結合,記得上一次講到相關的介紹內容的時候,已經是四年前的事情了。 所以許多的素材與原始碼都需要翻新。 並且也準備好一些新的影片教學,也真的希望想要學習打造 LINE Bot 的朋友們,可以更快速的入手,也希望有任何問題可以到 LINE 的官方討論區或是「LINE開發者官方社群」官方帳號來詢問。

立即加入「LINE開發者官方社群」官方帳號,就能收到第一手Meetup活動,或與開發者計畫有關的最新消息的推播通知。▼

「LINE開發者官方社群」官方帳號ID:@line_tw_dev

關於「LINE開發社群計畫」

LINE今年年初在台灣啟動「LINE開發社群計畫」,將長期投入人力與資源在台灣舉辦對內對外、線上線下的開發者社群聚會、徵才日、開發者大會等,已經舉辦30場以上的活動。歡迎讀者們能夠持續回來察看最新的狀況。詳情請看:

[心得] Go Developer Survey 2019 (2: Developer Tools)

前提

Golang 協會日前公布了去年的開發者調查結果。

Go Developer Survey 2019 Results

tl;pr (Too Long, Please Read) 我知道這個調查很長,你一定不會讀。我來幫你每天挑一個重點給你喔。

“Developer tools”

今天來討(ㄧ ㄣ \/)論(ㄓ ㄢ \)關於開發者工具的部分。

“你都在哪裡開發 Go?”

第一名: VS Code (16% -> 41 %)

不得不說~現在的 VS Code 真的是相當好用的 IDE(之一),低資源需求加上許多好用的外掛。

第二名: GoLand (15% -> 34%)

近期發表許多好用的功能,完美的整合讓 Go 開發環境整個完美而容易使用。 JetBrains 的 GoLand 真的相當好用,有喜歡的記得授權買下去!

第三名: Vim (28% -> 14%)

身為最好用 IDE? 的 Vim (誰說是 IDE? 明明就是 OS?) 佔有率竟然慢慢的下滑。 可能因為其他工具的整合性越來越高,對於 console mode 的整合也變得很方便。 許多也都有提供 vim mode (咦?) 讓 vim 的佔有率慢慢減少。

你是用哪套 IDE 呢? 歡迎各位來討論喔!!

[分享][翻譯] LINE Flex Message Template

(翻譯文章) LINE Flex Message Template

前提

本篇文章為翻譯文章,原文為 LINE Flex Message Template from LINE 泰國的 LAE (LINE API Expert) PamornT.

本文

LINE LINE Bot 朋友您好,根據 LINE Chat Bot的個人經驗,發現有一項重要任務是設計對用戶消息和非常流行的消息的回答 - 那就是透過 Flex Message 來發送回覆。

當嘗試與同時創建 LINE Chat Bot 的朋友交談時,他們也必須使用相同的 Flex,我們認為應該共享不同類型的 Flex Message 的樣板作為資源,以節省開發 LINE Chat Bot 的時間,提供開發人員使用和使用。

如果有人想分享 Flex Message,請在下面的鏈接中分享。原作者在這裡收集。謝謝參加的每個人都可以提供幫助。非常感謝你。

https://www.facebook.com/groups/LINEDEVTH/permalink/497494880914423/

請選擇您喜歡的一個,複製圖片下方的鏈接要點,以一起使用 ^^

車牌(泰國格式)

CR。Pamorn Trivorrarat

https://gist.github.com/…/440fb7788e8c501ad7115b7d7ed14a3b

{
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": "https://sv1.picz.in.th/images/2020/02/29/x8qxLa.jpg",
"aspectRatio": "341:148",
"size": "full"
}
],
"position": "absolute",
"height": "110px",
"width": "260px",
"offsetTop": "22px",
"offsetStart": "22px"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "3 กก xxxx",
"align": "center",
"size": "4xl",
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "กรุงเทพมหานคร",
"align": "center",
"size": "xl"
}
]
}
],
"borderWidth": "5px",
"borderColor": "#000000",
"cornerRadius": "10px",
"paddingAll": "5px"
}
],
"borderColor": "#000000"
}
}

收據

CR。Pamorn Trivorrarat

https://gist.github.com/…/40d8044af1fff88aac97dc74f85f5a4b

{
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": "https://sv1.picz.in.th/images/2020/02/29/x8qxLa.jpg",
"aspectRatio": "341:148",
"size": "full"
}
],
"position": "absolute",
"height": "110px",
"width": "260px",
"offsetTop": "22px",
"offsetStart": "22px"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "3 กก xxxx",
"align": "center",
"size": "4xl",
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "กรุงเทพมหานคร",
"align": "center",
"size": "xl"
}
]
}
],
"borderWidth": "5px",
"borderColor": "#000000",
"cornerRadius": "10px",
"paddingAll": "5px"
}
],
"borderColor": "#000000"
}
}

登機證

CR.Siratee Kittiwitchaowakul

https://gist.github.com/…/e9b33491edf45416cd124b8234051379

{
"type": "bubble",
"size": "giga",
"header": {
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "image",
"url": "https://www.shareicon.net/data/128x128/2016/09/02/824400_airport_512x512.png",
"flex": 0
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "SK",
"size": "xxl",
"color": "#FFFFFF",
"weight": "bold",
"flex": 0
},
{
"type": "text",
"text": "Airline",
"size": "xxl",
"flex": 1,
"color": "#FFFFFF"
}
],
"spacing": "sm"
},
{
"type": "text",
"text": "BOARDING PASS",
"color": "#FFFFFF",
"size": "xl"
},
{
"type": "text",
"text": "ECONOMY CLASS",
"color": "#FFFFFF",
"size": "xl",
"weight": "bold"
}
]
}
],
"spacing": "lg"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "NAME OF PASSENGER",
"size": "md"
},
{
"type": "text",
"text": "SIRATEE.KITTIWITCHAOWAKUL",
"size": "xxl",
"wrap": true,
"weight": "bold"
}
],
"spacing": "sm"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "FLIGHT"
},
{
"type": "text",
"text": "SK111",
"size": "xxl",
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "DATE"
},
{
"type": "text",
"text": "29FEB",
"size": "xxl",
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "SEAT"
},
{
"type": "text",
"text": "28S",
"size": "xxl",
"weight": "bold"
}
],
"flex": 1
}
]
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "GATE"
},
{
"type": "text",
"text": "A1",
"size": "xxl",
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "BOARDING TIME"
},
{
"type": "text",
"text": "00:10",
"size": "xxl",
"weight": "bold"
}
]
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "FROM - TO"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "BKK",
"size": "3xl",
"weight": "bold"
},
{
"type": "text",
"text": "BANGKOK",
"weight": "bold"
}
],
"paddingTop": "10px"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": "https://cdn.onlinewebfonts.com/svg/img_548146.png"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "KIX",
"size": "3xl",
"weight": "bold",
"align": "center"
},
{
"type": "text",
"text": "OSAKA",
"weight": "bold",
"align": "center"
}
],
"paddingTop": "10px"
}
]
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "ETKT 21720000000000000",
"align": "center"
},
{
"type": "text",
"text": "SK*K ABCD1234",
"align": "center"
},
{
"type": "image",
"url": "https://previews.123rf.com/images/mshch/mshch1209/mshch120900003/15165152-new-technology-barcode-called-pdf417-this-example-of-code-literally-translates-as-the-following-text.jpg",
"aspectMode": "fit",
"aspectRatio": "3:1",
"size": "full"
}
],
"paddingTop": "10px"
}
],
"spacing": "md"
},
"footer": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "GATE CLOSES 10 MINUTES BEFORE DEPARTURE",
"size": "lg",
"wrap": true,
"weight": "bold",
"align": "center",
"color": "#FFFFFF"
}
],
"backgroundColor": "#f74d63"
},
"styles": {
"header": {
"backgroundColor": "#069144"
}
}
}

油價

CR。Charoensin缺陷

https://gist.github.com/…/0689bc210a2a8f5ccb3a352e70f72583

{
"type": "carousel",
"contents": [
{
"type": "bubble",
"size": "kilo",
"header": {
"type": "box",
"layout": "horizontal",
"backgroundColor": "#00bce4",
"contents": [
{
"type": "image",
"url": "https://www.bangchak.co.th/img/logo.png",
"aspectRatio": "137:40",
"size": "md",
"align": "start"
},
{
"type": "text",
"text": "ราคาน้ำมันวันนี้",
"weight": "bold",
"size": "md",
"gravity": "center",
"align": "end",
"color": "#FFFFFF"
}
]
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"margin": "md",
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "text",
"text": "วันนี้",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end"
},
{
"type": "text",
"text": "พรุ่งนี้",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end"
}
]
},
{
"type": "separator",
"color": "#cccccc",
"margin": "md"
},
{
"margin": "md",
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "image",
"url": "https://www.bangchak.co.th/img/logo-oil/logo-Hi-diesel.png",
"aspectRatio": "120:48"
},
{
"type": "text",
"text": "25.49",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
},
{
"type": "text",
"text": "25.49",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
}
]
},
{
"margin": "md",
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "image",
"url": "https://www.bangchak.co.th/img/logo-oil/logo-E20s.png",
"aspectRatio": "120:48"
},
{
"type": "text",
"text": "22.34",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
},
{
"type": "text",
"text": "22.34",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
}
]
},
{
"margin": "md",
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "image",
"url": "https://www.bangchak.co.th/img/logo-oil/logo-gasohol-91.png",
"aspectRatio": "120:48"
},
{
"type": "text",
"text": "25.08",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
},
{
"type": "text",
"text": "25.08",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
}
]
},
{
"margin": "md",
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "image",
"url": "https://www.bangchak.co.th/img/logo-oil/logo-gasohol-95.png",
"aspectRatio": "120:48"
},
{
"type": "text",
"text": "25.35",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
},
{
"type": "text",
"text": "25.35",
"color": "#111111",
"weight": "bold",
"size": "sm",
"align": "end",
"gravity": "center"
}
]
},
{
"margin": "md",
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "หมายเหตุ: ราคาขายปลีก กทม. ที่ยังไม่รวมภาษีบำรุงท้องถิ่น กทม. *ราคามีผล ณ วันที่ 29 ก.พ. 63 เวลา 05.00 น.",
"weight": "bold",
"color": "#999999",
"size": "xxs",
"flex": 1,
"wrap": true
}
]
}
]
},
"footer": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Source : bangchak.co.th",
"color": "#999999",
"size": "xs",
"align": "end",
"flex": 1
}
],
"margin": "xs"
}
]
},
"styles": {
"header": {
"backgroundColor": "#6486E3"
},
"body": {
"backgroundColor": "#FEFEFE"
},
"footer": {
"separator": true
}
}
}
]
}
view raw oil.json hosted with ❤ by GitHub

餐廳排隊卡

CR.Siratee Kittiwitchaowakul

https://gist.github.com/sirateek/f7ad9562bbdc500d87cfa27768e83292

{
"type": "bubble",
"size": "giga",
"header": {
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "Q",
"color": "#FFFFFF",
"size": "xl",
"weight": "bold"
},
{
"type": "text",
"text": "Fiji Restaurants",
"color": "#FFFFFF",
"wrap": true
}
],
"paddingTop": "30px"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": "https://i.pinimg.com/564x/42/93/05/429305229f4b907a56b8a24a448830ed.jpg",
"size": "full",
"align": "center"
}
],
"cornerRadius": "200px"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "บัตรจองคิว",
"align": "end",
"size": "xl",
"color": "#FFFFFF",
"weight": "bold"
}
],
"paddingTop": "40px"
}
],
"spacing": "md"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "คิวที่",
"size": "3xl",
"align": "center",
"weight": "bold"
}
],
"paddingAll": "10px"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "A 10",
"align": "center",
"size": "5xl",
"weight": "bold"
}
],
"backgroundColor": "#f0bc11",
"cornerRadius": "20px"
},
{
"type": "text",
"text": "จำนวนคิวที่รอ: 999 คิว",
"wrap": true,
"align": "center"
},
{
"type": "text",
"text": "เวลารอโดยประมาณ: 3 ปี",
"align": "center"
}
],
"spacing": "md"
}
],
"borderWidth": "3px",
"borderColor": "#000000",
"cornerRadius": "20px",
"paddingAll": "20px"
},
{
"type": "text",
"text": "ระบบจะแจ้งเตือนก่อนถึงคิวของท่าน 5 คิว",
"wrap": true,
"align": "center",
"color": "#11b1f0"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "กรุณาเดินทางมาถึงที่หน้าร้านค้าก่อนถึงคิวของท่าน หากระบบเรียกคิวของท่าน 3 ครั้งแล้วท่านไม่อยู่ ทางร้านจะขอสงวนสิทธิในการข้ามคิวของท่าน",
"wrap": true,
"color": "#FFFFFF",
"align": "center"
}
],
"backgroundColor": "#FF0000",
"cornerRadius": "10px",
"paddingAll": "10px"
}
],
"paddingAll": "20px",
"spacing": "md"
},
"footer": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "button",
"action": {
"type": "message",
"label": "ยกเลิกคิว",
"text": "ยกเลิกคิว"
},
"style": "primary",
"color": "#f07911"
},
{
"type": "button",
"action": {
"type": "message",
"label": "สั่งอาหารก่อน",
"text": "สั่งอาหารก่อน"
},
"style": "primary",
"color": "#4c11f0"
}
],
"spacing": "lg"
}
]
},
"styles": {
"header": {
"backgroundColor": "#29914f"
},
"footer": {
"separator": true
}
}
}

追踪包裹運送狀態

CR。Pamorn Trivorrarat

https://gist.github.com/PamornT/086a249fad9b99a6d49db4b1b57ee116

{
"type": "bubble",
"size": "mega",
"header": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "FROM",
"color": "#ffffff66",
"size": "sm"
},
{
"type": "text",
"text": "Akihabara",
"color": "#ffffff",
"size": "xl",
"flex": 4,
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "TO",
"color": "#ffffff66",
"size": "sm"
},
{
"type": "text",
"text": "Shinjuku",
"color": "#ffffff",
"size": "xl",
"flex": 4,
"weight": "bold"
}
]
}
],
"paddingAll": "20px",
"backgroundColor": "#0367D3",
"spacing": "md",
"height": "154px",
"paddingTop": "22px"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Jul 19, 2019",
"size": "sm",
"gravity": "center",
"align": "end"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "📄"
}
],
"cornerRadius": "30px",
"height": "20px",
"width": "20px",
"offsetStart": "7px"
},
{
"type": "filler"
}
],
"flex": 0,
"backgroundColor": "#848484",
"width": "30px",
"height": "30px",
"cornerRadius": "30px"
},
{
"type": "text",
"text": "รับฝาก",
"gravity": "center",
"flex": 1,
"size": "sm"
}
],
"spacing": "lg",
"cornerRadius": "30px",
"margin": "xl"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "18:12:26",
"size": "xs",
"color": "#8c8c8c",
"align": "end"
}
],
"flex": 2
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
}
],
"width": "2px",
"backgroundColor": "#B7B7B7"
},
{
"type": "filler"
}
],
"flex": 1
}
],
"width": "30px"
},
{
"type": "text",
"text": "คต. กาดสวนแก้ว",
"gravity": "top",
"flex": 2,
"size": "xs",
"color": "#8c8c8c"
}
],
"spacing": "lg",
"height": "40px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Jul 20, 2019",
"size": "sm",
"gravity": "center",
"align": "end"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "🚚"
}
],
"cornerRadius": "30px",
"height": "20px",
"width": "20px",
"offsetStart": "7px"
},
{
"type": "filler"
}
],
"flex": 0,
"backgroundColor": "#A9D0F5",
"width": "30px",
"height": "30px",
"cornerRadius": "30px"
},
{
"type": "text",
"text": "กำลังขนส่ง",
"gravity": "center",
"flex": 1,
"size": "xs"
}
],
"cornerRadius": "30px",
"spacing": "lg"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "15:12:26",
"size": "xs",
"color": "#8c8c8c",
"align": "end"
}
],
"flex": 2
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
}
],
"width": "2px",
"backgroundColor": "#B7B7B7"
},
{
"type": "filler"
}
],
"flex": 1
}
],
"width": "30px"
},
{
"type": "text",
"text": "คต. กาดสวนแก้ว",
"gravity": "top",
"flex": 2,
"size": "xs",
"color": "#8c8c8c"
}
],
"spacing": "lg",
"height": "40px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Jul 20, 2019",
"size": "sm",
"gravity": "center",
"align": "end"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "🚚"
}
],
"cornerRadius": "30px",
"height": "20px",
"width": "20px",
"offsetStart": "7px"
},
{
"type": "filler"
}
],
"flex": 0,
"backgroundColor": "#A9D0F5",
"width": "30px",
"height": "30px",
"cornerRadius": "30px"
},
{
"type": "text",
"text": "กำลังขนส่ง",
"gravity": "center",
"flex": 1,
"size": "xs"
}
],
"cornerRadius": "30px",
"spacing": "lg"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "19:34:00",
"size": "xs",
"color": "#8c8c8c",
"align": "end"
}
],
"flex": 2
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
}
],
"width": "2px",
"backgroundColor": "#B7B7B7"
},
{
"type": "filler"
}
],
"flex": 1
}
],
"width": "30px"
},
{
"type": "text",
"text": "ศป. ลำพูน",
"gravity": "top",
"flex": 2,
"size": "xs",
"color": "#8c8c8c"
}
],
"spacing": "lg",
"height": "40px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Jul 22, 2019",
"size": "sm",
"gravity": "center",
"align": "end"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "🚚"
}
],
"cornerRadius": "30px",
"height": "20px",
"width": "20px",
"offsetStart": "7px"
},
{
"type": "filler"
}
],
"flex": 0,
"backgroundColor": "#A9D0F5",
"width": "30px",
"height": "30px",
"cornerRadius": "30px"
},
{
"type": "text",
"text": "กำลังขนส่ง",
"gravity": "center",
"flex": 1,
"size": "xs"
}
],
"cornerRadius": "30px",
"spacing": "lg"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "02:27:46",
"size": "xs",
"color": "#8c8c8c",
"align": "end"
}
],
"flex": 2
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
}
],
"width": "2px",
"backgroundColor": "#B7B7B7"
},
{
"type": "filler"
}
],
"flex": 1
}
],
"width": "30px"
},
{
"type": "text",
"text": "ศป. อยุธยา",
"gravity": "top",
"flex": 2,
"size": "xs",
"color": "#8c8c8c"
}
],
"spacing": "lg",
"height": "40px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Jul 22, 2019",
"size": "sm",
"gravity": "center",
"align": "end"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "📦"
}
],
"cornerRadius": "30px",
"height": "20px",
"width": "20px",
"offsetStart": "7px"
},
{
"type": "filler"
}
],
"flex": 0,
"backgroundColor": "#F6E3CE",
"width": "30px",
"height": "30px",
"cornerRadius": "30px"
},
{
"type": "text",
"text": "กำลังนำจ่าย",
"gravity": "center",
"flex": 1,
"size": "xs"
}
],
"cornerRadius": "30px",
"spacing": "lg"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "11:09:07",
"size": "xs",
"color": "#8c8c8c",
"align": "end"
}
],
"flex": 2
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
}
],
"width": "2px",
"backgroundColor": "#B7B7B7"
},
{
"type": "filler"
}
],
"flex": 1
}
],
"width": "30px"
},
{
"type": "text",
"text": "หันคา",
"gravity": "top",
"flex": 2,
"size": "xs",
"color": "#8c8c8c"
}
],
"spacing": "lg",
"height": "40px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Jul 22, 2019",
"size": "sm",
"gravity": "center",
"align": "end"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "✔️"
}
],
"cornerRadius": "30px",
"height": "20px",
"width": "20px",
"offsetStart": "7px"
},
{
"type": "filler"
}
],
"flex": 0,
"backgroundColor": "#CEF6CE",
"width": "30px",
"height": "30px",
"cornerRadius": "30px"
},
{
"type": "text",
"text": "นำจ่ายสำเร็จ",
"gravity": "center",
"flex": 1,
"size": "xs"
}
],
"cornerRadius": "30px",
"spacing": "lg"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "11:59:50",
"size": "xs",
"color": "#8c8c8c",
"align": "end"
}
],
"flex": 2
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "filler"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "filler"
}
],
"width": "2px"
},
{
"type": "filler"
}
],
"flex": 1
}
],
"width": "30px"
},
{
"type": "text",
"text": "หันคา",
"gravity": "top",
"flex": 2,
"size": "xs",
"color": "#8c8c8c"
}
],
"spacing": "lg",
"height": "20px"
}
]
}
}
view raw PostTrack.json hosted with ❤ by GitHub

足球比分表

CR.Sitthi Thiammekha)

https://medium.com/linedevth/worldcup-flex-864fb27db1a2


圖表

CR。Pamorn Trivorrarat

https://gist.github.com/PamornT/7990f0d5431ac92a4b404323058bdc4e

{
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "จำนวนผู้ติดเชื้อรายวัน",
"weight": "bold",
"size": "xl"
},
{
"type": "box",
"layout": "vertical",
"margin": "lg",
"spacing": "sm",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "7",
"align": "center",
"offsetTop": "173px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"height": "193px",
"margin": "lg",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "32",
"align": "center",
"offsetTop": "148px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "168px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "33",
"align": "center",
"offsetTop": "147px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "167px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "30",
"align": "center",
"offsetTop": "150px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "170px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "35",
"align": "center",
"offsetTop": "145px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "165px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "60",
"align": "center",
"offsetTop": "120px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "140px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "50",
"align": "center",
"offsetTop": "130px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "150px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "89",
"align": "center",
"offsetTop": "91px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "111px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "188",
"align": "center",
"offsetTop": "0px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "12px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "122",
"align": "center",
"offsetTop": "58px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "78px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "106",
"align": "center",
"offsetTop": "74px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "94px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#EEEEEE",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "107",
"align": "center",
"offsetTop": "73px",
"size": "xxs"
}
],
"backgroundColor": "#ffffff",
"flex": 1,
"height": "93px",
"width": "20px"
}
],
"backgroundColor": "#ff0000",
"height": "200px"
}
]
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "14",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "15",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "16",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "17",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "18",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "19",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "20",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "21",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "22",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "23",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "24",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
},
{
"type": "box",
"layout": "horizontal",
"spacing": "sm",
"contents": [
{
"type": "spacer"
}
],
"backgroundColor": "#FFFFFF",
"flex": 1,
"width": "2px"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "25",
"align": "center",
"size": "sm"
}
],
"backgroundColor": "#ffffff",
"height": "40px",
"width": "20px"
}
]
}
]
}
]
}
}
view raw flex_graph.json hosted with ❤ by GitHub

附註:任何想要分享,添加,分享到此文章的人,非常感謝你。 https://www.facebook.com/groups/LINEDEVTH/permalink/497494880914423/

[心得] Go Developer Survey 2019 (1: Pain points)

前提

Golang 協會日前公布了去年的開發者調查結果。

Go Developer Survey 2019 Results

tl;pr (Too Long, Please Read) 我知道這個調查很長,你一定不會讀。我來幫你每天挑一個重點給你喔。

“Pain points”

這邊調查,為什麼你不使用 Go 在你的專案裡面的原因。 這邊列出前三名,別且列一下 2016 調查報告跟 2019 的數字比對。 本來會以為是第一名的 Generic 結果並不是喔!!

  • 第一名: 目前專案是其他程式語言( 58% -> 56%) 蠻合理的~ 換一個語言改寫整個上線的產品或是專案。不是你走~就是老闆叫你走。 XD

  • 第二名: 我的團隊(老闆)(專案)喜歡其他程式語言 ( 45% -> 37%) 這個蠻有趣的,其實社群內經常聽到「老闆叫我不要用 Go 」「老闆喜歡其他語言」 這些都需要持續的導入。 wwww 不過數字變低是好事。

  • 第三名: Go 缺少一些我需要的語言特性 ( 16% -> 25%) 終於到了大家比較有爭議的點,也就是是否 Go Generic 讓大家在開發上覺得困擾。 不過比例有增高,可能是開發了一段時間後真的有相關的需求。

歡迎各位來討論喔….