DEV Community

D
D

Posted on

Golang (heroku) で LINE Bot 作ってみる

追記 (2016/04/12)
Botの機能追加+ソースコードリファクタリングしてるうちにライブラリ化しました
https://github.com/dongri/line-bot-sdk-go
今のところ、text, sticker, image, video簡単に送れます。プロフィールも簡単に取得可能です。


まずコードから。メッセージを送るとそのメッセージをそのまま返すサンプル。下にちゃんとしたやつもあります。


package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "os"
    "time"
)

// ReceivedMessage ...
type ReceivedMessage struct {
    Result []Result `json:"result"`
}

// Result ...
type Result struct {
    ID          string   `json:"id"`
    From        string   `json:"from"`
    FromChannel int      `json:"fromChannel"`
    To          []string `json:"to"`
    ToChannel   int      `json:"toChannel"`
    EventType   string   `json:"eventType"`
    Content     Content  `json:"content"`
}

// SendMessage ..
type SendMessage struct {
    To        []string `json:"to"`
    ToChannel int      `json:"toChannel"`
    EventType string   `json:"eventType"`
    Content   Content  `json:"content"`
}

// Content ...
type Content struct {
    ID          string   `json:"id"`
    ContentType int      `json:"contentType"`
    From        string   `json:"from"`
    CreatedTime int      `json:"createdTime"`
    To          []string `json:"to"`
    ToType      int      `json:"toType"`
    Text        string   `json:"text"`
}

// const ...
const (
    EndPoint  = "https://trialbot-api.line.me"
    ToChannel = 1383378250
    EventType = "138311608800106203"
)

func main() {
    http.HandleFunc("/", helloHandler)
    http.HandleFunc("/callback", callbackHandler)
    port := os.Getenv("PORT")
    addr := fmt.Sprintf(":%s", port)
    http.ListenAndServe(addr, nil)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, LINE Bot")
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
    decoder := json.NewDecoder(r.Body)
    var m ReceivedMessage
    err := decoder.Decode(&m)
    if err != nil {
        log.Println(err)
    }
    apiURI := EndPoint + "/v1/events"
    for _, result := range m.Result {
        from := result.Content.From
        text := result.Content.Text
        content := new(Content)
        content.ContentType = result.Content.ContentType
        content.ToType = result.Content.ToType
        content.Text = text
        request(apiURI, "POST", []string{from}, *content)
    }
}

func request(endpointURL string, method string, to []string, content Content) {
    m := &SendMessage{}
    m.To = to
    m.ToChannel = ToChannel
    m.EventType = EventType
    m.Content = content
    b, err := json.Marshal(m)
    if err != nil {
        log.Print(err)
    }
    req, err := http.NewRequest(method, endpointURL, bytes.NewBuffer(b))
    if err != nil {
        log.Print(err)
    }
    req = setHeader(req)
    client := &http.Client{
        Transport: &http.Transport{Proxy: http.ProxyURL(getProxyURL())},
        Timeout:   time.Duration(30 * time.Second),
    }
    res, err := client.Do(req)
    if err != nil {
        log.Print(err)
    }
    defer res.Body.Close()

    var result map[string]interface{}
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Print(err)
    }
    if err := json.Unmarshal(body, &result); err != nil {
        log.Print(err)
    }
    log.Print(result)
}

func setHeader(req *http.Request) *http.Request {
    req.Header.Add("Content-Type", "application/json; charset=UTF-8")
    req.Header.Add("X-Line-ChannelID", os.Getenv("ChannelID"))
    req.Header.Add("X-Line-ChannelSecret", os.Getenv("ChannelSecret"))
    req.Header.Add("X-Line-Trusted-User-With-ACL", os.Getenv("MID"))
    return req
}

func getProxyURL() *url.URL {
    proxyURL, err := url.Parse(os.Getenv("ProxyURL"))
    if err != nil {
        log.Print(err)
    }
    return proxyURL
}
Enter fullscreen mode Exit fullscreen mode

やってるうちに詰まったところをまとめ

  1. callback URLはhttpsのみ
    今まで簡単なサンプルは自前のサーバーでやってて、当然https対応してない。しかしLINE Botのcallbackはhttpsにportを443に指定しないといけない。let's encryptでhttps化。しかしcallbackにリクエストが来ない。herokuでなんとかすると決める。

  2. 許可したipしかsendmessageできない
    herokuにデプロイしてログを見てるとipで弾かれてる。指定したipでしかpostできないみたい。bot管理ページにwhitelistがあってそこに自分のサーバーip追加しないといけないらしい。しかし、herokuはデプロイする度にipが変わってて使いものにならない。こちらの記事( http://qiita.com/yuya_takeyama/items/0660a59d13e2cd0b2516 )にもかいてあるようにproxyを使ってproxyのipを指定すれば解決できる。

  3. contentTypeで弾かれてる
    headerのcontent-typeかと思ったがメッセージcontentのcontentTypeをちゃんと指定しないといけない

  4. 先着1万と書いてあるのが怪しい
    1万枠外でもBotの登録できるんじゃないかと疑ってる。自分のアカウントは昨日早めに登録したので、使えるが、1アカウント1Botの制限のため今日嫁のアカウントで登録したけど、callbackが来ない。

とりあえず電車でも haskell

Screen Shot 2016-04-08 at 11.21.08 PM.png

Screenshot_20160408-233023.png

Top comments (0)