20190714のGoに関する記事は3件です。

プログラミングマスコットのドット絵を書いた

UT.png

プログラム言語のマスコット、ロゴ

あなたはPCにステッカー貼ってますか?
利用言語を愛していますか?
言語のOSSにプルリクエストを送るためなら睡眠時間を削れますか?

そんなあなたに送るプログラムマスコット`sです。

きっかけはUTme!で自作Tシャツを作りたいと思っていた時に、プログラマーが目にするマスコットアニマル`sという記事を見たこと。

あと、三連休なのに予定がないからだZE:star:(2019/07/14)

Gopher (Go)

マスコットといえばgopherくん
ドット絵と寸胴ボディの親和性がGood。

goper.png
"gopher" by Renée French CC-BY-3.0

Duke (java)

Go言語のライバル?でもあるjavaのDukeさん。
マザー2のザコキャラ感がすんごい。

java.png
"Duke" by Sun Microsystems BSD

rustacean (rust)

rustのrustaceanさん。
前者2人を圧倒的速度で凌駕する高速カニさん。(強い)

rust.png

Moby Dock (Docker)

DockerのMoby Dock親方。
みんなを支える縁の下の力持ち。
docker.png

みんなを載せてるのでこんなイメージ。
無題の図形描画.jpg

android robot (android)

その名の通りアンドロイドのロボットでandroid robotさん。
ドット絵にしても変わった感が無い・・・

android.png
"android robot" by google.com CC BY 3.0

jenkins logo (jenkins)

怒り顔など、何かと改変されがちなジェンキンスのロゴ。
あなた、名が無きおじさんだったのね・・・

jenkins.png
"jenkins logo" by jenkins.io CC BY-SA 3.0

Tux (linux)

linuxのTuxさん。
可愛く見えて実は二段腹のおじさんペンギン。

linux.png
"Tux" by Larry Ewing、Simon Budig、Anja Gerwinski

GitLab logo (GitLab)

昔は怖かったGitLabのロゴ。
狐じゃないよ!たぬきだよ!

gitlub.png
"GitLab logo" by gitlab.com CC BY-NC-SA 4.0

Python logo (Python)

もはやマスコットではなくなってきた、Pythonのロゴマーク。
python.png
"python logo" by Python Software Foundation PSF license

postgreSQL logo (postgreSQL)

一番難しかった。ポスグレのぞうさん。
postgres.png
"PostgreSQL" by postgresql.org PostgreSQL license

MySQL logo (MySQL)

ポスグレ書くならこっちもね!MySQLのイルカさん。
笑顔がかわいい。
mysql.png

Slack logo (Slack)

みんなの雑談部屋。slackのロゴ。
新デザインにだんだん慣れてきた。
slack.png
※著作権に関する記載が見つからなかったので、問題あればコメントなどで指摘してください。:bow_tone1:

HTML5 logo (HTML5)

HTML5のロゴ。
筆者は20代半ばなので、5とそれ以前の違いがわかっていない。
html.png
"HTML5" by World Wide Web Consortium CC BY 3.0

Javascript logo (JavaScript)

HTMLやったならこっちもね。
JavaScriptさん。
js.png
※こちらも著作権に関する記載が見当たらなかったです。

UBUNTU logo (UBUNTU)

Macが買えず使っていたUBUNTUさん。
WSL2に期待中。
ubuntu.png

ReactJS logo (ReactJS)

個人的な思い入れ(業務で利用)だけで書いたReactJS。
複雑な構造をドット絵で書くのは無理あるよね。。。
react.png
"ReactJS logo" by facebook.com CC BY 4.0

最後に

こんな感じのTシャツを着ている人がいたら声をかけてください。きっと私です。
キャプチャ.PNG

著作権について

この記事に記載している画像はすべてSlackスタンプ、アイコンに流用していただいてかまいません。
ただし、全てのマスコット、ロゴは原案者がいらっしゃいます。
必ず著作権の確認、表示の上ご利用ください。
(octcat-githubは著作権縛りきつくて書いたけど載せれなかった。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goにデストラクタがないのをなんとかしたい

Goの真実

Goにはコンストラクタもデストラクタもない

ので、便宜上 コンストラクタに当たる関数は 構造体名の頭にNewのプリフィクスを入れた関数である

C言語だと init_hoge とするのが NewHogeになっただけだ

そして、そのNew関数は構造体のポインタを返す(Cでも呼び方わすれたが構造体をClassっぽく書くときに同じことをする)

結局C言語なのだ(念のため、C++ではない)

そしてデストラクタがないので、呼び出し側が終了関数を明示的に呼ばなければならない

Cだと delete_hoge とか release_hoge、teardown_hoge・・・ 呼び名が統一されてる覚えがない

Goにもデストラクタとして呼び名が統一されている気がしない

つまり、よく呼び忘れてメモリリークする!!

[https://play.golang.org/p/i6-vTaTVx9Z]

package main
import (
    "fmt"
)


type File struct{
  filename string
}

func NewFile(f string) *File{
  fmt.Printf("File{%s} new&open\n", f)    
  return &File{f}
}

func(s *File)Read(){
  fmt.Printf("File{%s} read\n", s.filename)    
}

func(s *File)Close(){
  fmt.Printf("File{%s} close\n", s.filename)    
}




func main() {
  fmt.Println("main start")    

  file := NewFile("test")
  file.Read()

  fmt.Println("main finish")    

}



main start
File{test} new&open
File{test} read
main finish

上記のコードはRAII原則にのっとり、New時にリソースをOpenしているが

残念なことにライブラリを使う人がClose()を呼び忘れたため、リソースリークしている

defer

いちおうGoにはdeferがあり、関数が終了した時に実行されるファイナライザを書くことができる

[https://play.golang.org/p/LPEm7oDSf2M]

...
  file := NewFile("test")
  defer file.Close()
  file.Read()
...

main start
File{test} new&open
File{test} read
main finish
File{test} close

これは便利だが、このdeferはライブラリを使う側の人間が忘れたらCloseしてくれない

構造体の方でdeferを入れたい

そう考えるかもしれないが、deferはあくまで、関数の終了時のファイナライザだ

コンストラクタにdeferを入れると当然コンストラクトした後にすぐCloseが走る

[https://play.golang.org/p/aav2psenFxF]


...
func NewFile(f string) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  defer file.Close()
  return &file
}
...

main start
File{test} new&open
File{test} close
File{test} read
main finish

runtime.SetFinalizer

見るからに悪手だが、ランタイムライブラリに、ガーベージコレクションが走った時に実行されるコールバックがある


  x:= "hoge"
  runtime.SetFinalizer(&x, func(x *string){fmt.Println("SetFinalizer")})

  // 上記だと即終了しGCが走らないので故意にGCを走らせ待機させる

と、上記では xがGCされる時に 次のラムダ式が呼ばれる。ラムダ式の引数はGCされるオブジェクトのポインタだ

[https://play.golang.org/p/QYkVhC1uQKP]

これを構造体に当てはめられないか?

コンストラクタでSetFinalizerする

コンストラクタで構造体を作成し、ポインタを使う側に返すが、このポインタにSetFinalizerを設定し

構造体がGCされるときにCloseしてみよう

念のためインスタンスを複数作る

[https://play.golang.org/p/LL3ejYYcowe]


package main
import (
    "fmt"
    "runtime"
    "time"
)


type File struct{
  filename string
}

func NewFile(f string) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  runtime.SetFinalizer(&file, func(f *File){f.Close()})

  return &file
}

func(s *File)Read(){
  fmt.Printf("File{%s} read\n", s.filename)    
}

func(s *File)Close(){
  fmt.Printf("File{%s} close\n", s.filename)    
}

func(s *File)Nop(){
  fmt.Printf("File{%s} nop\n", s.filename)    
}


func hoge(){
  file := NewFile("test")
  file.Read()
  file2 := NewFile("test2")
  file2.Read()
  file3 := NewFile("test3")
  file3.Nop()
}

func main() {
  fmt.Println("main start")    

  hoge()
  runtime.GC()
  time.Sleep(1 * time.Second)

  fmt.Println("main finish")     
}

main start
File{test} new&open
File{test} read
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} nop
File{test3} close
File{test2} close
File{test} close
main finish

綺麗に動いてしまった・・・

レシーバーにSetFinalizerする

先ほどはコンストラクタにSetFinalizerをしたので、たぶん確実にFinalizerが走るが

特定メソッドで、レシーバーに対してSetFinalizerしたらどうなるか?

たとえばReadメソッドでやってみる


func(s *File)Read(){
  runtime.SetFinalizer(s, func(f *File){f.Close()})
  fmt.Printf("File{%s} read\n", s.filename)    
}


main start
File{test} new&open
File{test} read
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} nop
File{test2} close
File{test} close
main finish

すばらしい、意図したとおりに動いた(file3はReadメソッドを読んでないのでFinalizeされてない)
使うシーンがあるか不明だが、例えばOpenメソッドを呼んだ時だけCloseのFinalizerを設定

という使い方も不可能ではなさそうだ

でもね

GCされるまでは開放されないので、もし長時間GCされずに存在していたら

その間ファイルを開きっぱなしだったり、ネットワークリソースを開放しない事になる

だから、SetFinalizerでデストラクターをするのは、よくない事は明確である・・

どこを間違えたかと思ったが、Goはオブジェクト指向言語ではないというのが答えだ

本当は関数型のように書くべきなんだろうか?

Callback にすべきではないか?

コンストラクタでdeferしても、コールバックで続きの処理を行えば

コンストラクタのコンテキスト内なので問題ないはずだ・・・

[https://play.golang.org/p/1ZBphVFMHKM]


  package main
import (
    "fmt"
    "runtime"
    "time"
)


type File struct{
  filename string
}

func NewFile(f string, cb func(file *File)*File) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  defer file.Close(nil)

  if(cb != nil){
    return cb(&file)
  }    
  return &file
}

func(s *File)Read(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} read\n", s.filename)    
  if(cb != nil){
    return cb(s)
  }    
  return s
}

func(s *File)Close(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} close\n", s.filename)
  if(cb != nil){
    return cb(s)
  }
  return s
}

func(s *File)Nop(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} nop\n", s.filename)    
  if(cb != nil){
    return cb(s)
  }
  return s
}


func hoge(){
  _ = NewFile("test", func(file *File)*File{
    file.Read(func(file *File)*File{
      fmt.Println("callback hell")
      return file          
    })
    return file
  })
}

func main() {
  fmt.Println("main start")    

  hoge()
  runtime.GC()
  time.Sleep(1 * time.Second)


  fmt.Println("main finish")    


}



main start
File{test} new&open
File{test} read
callback hell
File{test} close
main finish

やったぜ!!(やりたくない

もう少し実用的に

ReadやWriteの引数ちゃんと作って、もう少し実用的にします

エラーも返さないとね(エラー処理は省略

[https://play.golang.org/p/XbAWauNUNyZ]


package main

import (
    "fmt"
    "runtime"
    "time"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File) (*File, error)) (*File, error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    defer Close(&file, nil)

    if cb != nil {
        return cb(&file)
    }
    return &file, nil
}

func Read(file *File, len int, cb func(file *File, readed *string) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} read\n", file.filename)
    data := "readed"

    if cb != nil {
        return cb(file, &data)
    }
    return file, nil
}

func Write(file *File, data *string, cb func(file *File, written int) (*File, error)) (*File, error) {
    written := len(*data)
    fmt.Printf("File{%s} write {%s}  size={%d}\n", file.filename, *data, written)

    if cb != nil {
        return cb(file, written)
    }
    return file, nil
}

func Close(file *File, cb func(file *File) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} close\n", file.filename)
    if cb != nil {
        return cb(file)
    }
    return file, nil
}

func Nop(file *File, cb func(file *File) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} nop\n", file.filename)
    if cb != nil {
        return cb(file)
    }
    return file, nil
}

func hoge() {
    _, _ = NewFile("test", func(file *File) (*File, error) {
        Read(file, 5, func(file *File, readed *string) (*File, error) {
            fmt.Printf("READ{%s}\n", *readed)
            data :="hogehoge"
            Write(file, &data, func(file *File, written int) (*File, error) {
                fmt.Printf("WRITE [%d]bytes\n", written)
                return file, nil
            })

            return file, nil
        })
        return file, nil
    })

}

func main() {
    fmt.Println("main start")

    hoge()
    runtime.GC()
    time.Sleep(1 * time.Second)

    fmt.Println("main finish")

}


main start
File{test} new&open
File{test} read
READ{readed}
File{test} write {hogehoge}  size={8}
WRITE [8]bytes
File{test} close
main finish

ライブラリを使う側からはもうCloseの事考えなくてよくなりました

やったね!!

しかし、コールバック地獄、エラー処理の場所・・・もっと考えなければならない事が増えてしまいました

しかもGoはJavaScriptやC#のような、クロージャを簡単に書く書式もないので、まいかいfunc ()と大変

コールバック必要なのはNewだけだったんや!

別に非同期プログラミングのようなコールバックする必要はない

GoはGoroutineという素晴らしい仕組みがあるので、同期っぽく書けばいい

わざわざ人間に不親切なコールバック地獄にする必要がない

コールバックを使った理由はNew関数を抜けるときにdeferdでCloseし忘れを防ぎたい

処理が終わるまでNew関数を抜けないためだ

ReadやWriteまでコールバックする必要がない。Newだけでよかったんだ

[https://play.golang.org/p/WtixVskDI7R]


package main

import (
    "fmt"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File)error) ( error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    defer file.Close()
        err := cb(&file)

    return err
}

func (s *File) Read(){
    fmt.Printf("File{%s} read\n", s.filename)
}

func (s *File) Close() {
    fmt.Printf("File{%s} close\n", s.filename)
}

func (s *File) Nop() {
    fmt.Printf("File{%s} nop\n", s.filename)
}

func hoge() {
    _ = NewFile("test", func(file *File)error{
            file.Read()
        return nil
    })
}

func main() {
    fmt.Println("main start")

    hoge()

    fmt.Println("main finish")

}

main start
File{test} new&open
File{test} read
File{test} close
main finish

当然ちゃんとクローズされるし、コールバックは初回のNewだけなので、コードの見通しも悪くない

ただ、コールバックとして全てを中にいれなければいけない

オブジェクト指向脳だと自由に構造体を使い回したいと思うだろう

コールバックとオブジェクト指向のハイブリッド

Goにはメソッドのオーバーライドやデフォルト引数がない

可変長引数で処理できるが今回はそこは手抜きで、CallbackがnilだとClose管理はライブラリでは行わない

[https://play.golang.org/p/M6g-dqUz86P]


package main

import (
    "fmt"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File) error) (*File, error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    var err error = nil
    if cb != nil {
        defer file.Close()
        err = cb(&file)
    }

    return &file, err
}

func (s *File) Read() {
    fmt.Printf("File{%s} read\n", s.filename)
}

func (s *File) Close() {
    fmt.Printf("File{%s} close\n", s.filename)
}

func (s *File) Nop() {
    fmt.Printf("File{%s} nop\n", s.filename)
}

func hoge() {
    // クロージャで自動的にCloseする
    _,_ = NewFile("test", func(file *File) error {
        file.Read()
        return nil
    })

    // クローズは使う側が責任持つ
    file2, _ := NewFile("test2", nil)
    defer file2.Close()
    file2.Read()

    // 忘れると当然リークします
    file3, _ := NewFile("test3", nil)
//  defer file3.Close()
    file3.Read()
}

func main() {
    fmt.Println("main start")

    hoge()

    fmt.Println("main finish")

}


main start
File{test} new&open
File{test} read
File{test} close
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} read
File{test2} close
main finish

結局どれがいいのか?

Finalizerは動作はするが、GCが走るまで開放が行われないので実用的ではない

コールバック地獄はダメ!絶対!(おそらくGoはGoroutineで同期的に書く設計なので、promise/futureやasync/awaitのようなコールバック解決ライブラリはあまり出ない)

Newのコールバックは場合によっては使えると思う

でも基本は、ライブラリを使う側が責任もって後始末しなければいけないっぽい

ハイブリッドは良さそう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GolangでLINEBOTを作った話

はじめに

ある日、とてつもなくやることがなかった(ほんとはあった)のでなにかしようと考えていたら、知り合いに「LINEのBOTとかどう?」と言われたので衝動的に作りました。

動けばいいや精神でつくってます。
はじめに言います。Go言語はずっと学ぼうと思い続けて未だ何も学んでないです。
素人プログラムをお許しください

概要

いいAPIがないかと探していたら素晴らしいサービス様を見つけたのでこちらを使用(ありがとうございます)

前準備

Go言語のインストール

Mac

$ brew install go

Windows

ダウンロードページから落としましょう

Linux

ディストリビューションごとにapt-getとかしてあげてください

line-bot-sdkのダウンロード

GolangにはLINE公式のsdkが用意されているのでダウンロードします

$ go get github.com/line/line-bot-sdk-go/linebot

LINE公式アカウントの用意

ここからDevelopperアカウントを取得しましょう

早速コードを書く

なんの構想もなく書き始めました。どうも馬鹿です。

とりあえずAPIから受け取ったデータをパースする構造体を作ります。

都道府県別データのレスポンスは

<ekidata version="ekidata.jp pref api 1.0">
<pref>
    <code>23</code>
    <name>愛知県</name>
</pref>
<line>
    <line_cd>11411</line_cd>
    <line_name>JR中央本線(名古屋~塩尻)</line_name>
</line>
<line>
    <line_cd>11413</line_cd>
    <line_name>JR飯田線(豊橋~天竜峡)</line_name>
</line>
...

といった具合なので、対応する構造体は

type Pref struct {
    Pref struct {
        Code int    `xml:"code"`
        Name string `xml:"name"`
    } `xml:"pref"`
    Lines []struct {
        Code int    `xml:"line_cd"`
        Name string `xml:"line_name"`
    } `xml:"line"`
}

こんな感じで良さそうです
この調子で他の構造体も定義してください。

肝となる通信をしてデータを受け取る関数です

func getData(endPoint []byte, code int) ([]byte, error) {
    url := make([]byte, 0, 10)
    url = mainURL
    url = append(url, endPoint...)
    url = append(url, strconv.Itoa(code)...)
    url = append(url, ".xml"...)

    res, err := http.Get(string(url))
    if err != nil {
        return nil, fmt.Errorf("%v", err)
    }

    defer res.Body.Close()

    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return nil, err
    }

    return body, nil
}

これと言って凝った部分もありませんね
BOTに止まられては困るのでエラーはFatalではなく戻り値として呼び出し元に返しています

構造体、データ受け取りの関数などをひとまとめにしたものをgithubにアップします
Heroku内だと相対パスでのインポートができないようなのでこうしていますが、もっといいやり方知ってる人がいたら教えてください。。。

main.goは公式のサンプルを書き換えました

main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
    "strings"

    "github.com/line/line-bot-sdk-go/linebot"

    "github.com/Homarechan/datas"
    "githuc.com/Homarechan/prefcodes"
)

var helpMessage = `コマンド一覧
路線一覧:[都道府県]
駅一覧:[駅番号]
駅情報:[駅番号]
所属路線一覧:[駅番号]
隣接駅:[路線番号]
使い方

例:
路線一覧: 大阪府
駅情報:1130224`

func main() {
    port := os.Getenv("PORT")

    if port == "" {
        log.Fatal("$PORT must be set")
    }

    bot, err := linebot.New(
        os.Getenv("CHANNEL_SECRET"),
        os.Getenv("CHANNEL_TOKEN"),
    )

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

    // Setup HTTP Server for receiving requests from LINE platform
    http.HandleFunc("/callback", func(w http.ResponseWriter, req *http.Request) {
        events, err := bot.ParseRequest(req)
        if err != nil {
            if err == linebot.ErrInvalidSignature {
                w.WriteHeader(400)
            } else {
                w.WriteHeader(500)
            }
            return
        }
        for _, event := range events {
            if event.Type == linebot.EventTypeMessage {
                switch message := event.Message.(type) {
                case *linebot.TextMessage:
                    if event.ReplyToken == "00000000000000000000000000000000" {
                        return
                    }
                    if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(parse(message.Text))).Do(); err != nil {
                        log.Print(err)
                    }
                }
            }
        }
    })
    // This is just sample code.
    // For actual use, you must support HTTPS by using `ListenAndServeTLS`, a reverse proxy or something else.
    if err := http.ListenAndServe(":"+os.Getenv("PORT"), nil); err != nil {
        log.Fatal(err)
    }
}

func parse(message string) string {
    if startsWith(message, "路線一覧:") {
        return fetchText(message[13:len(message)])
    } else if startsWith(message, "駅一覧:") {
        code, err := strconv.Atoi(message[10:len(message)])
        if err != nil {
            return "エラー:\n路線一覧で取得した路線番号を入力してください"
        }
        return getLineData(code)
    } else if startsWith(message, "駅情報:") {
        code, err := strconv.Atoi(message[10:len(message)])
        if err != nil {
            return "エラー:\n駅一覧で取得した駅番号を入力してください"
        }
        return getStationData(code)
    } else if startsWith(message, "所属路線一覧:") {
        code, err := strconv.Atoi(message[19:len(message)])
        if err != nil {
            return "エラー:\n駅一覧で取得した駅番号を入力してください"
        }
        return getGroupData(code)
    } else if startsWith(message, "隣接駅:") {
        code, err := strconv.Atoi(message[10:len(message)])
        if err != nil {
            return "エラー:\n駅一覧で取得した駅番号を入力してください"
        }
        return getJoinData(code)
    }
    return helpMessage
}

func startsWith(str string, text string) bool {
    return strings.HasPrefix(str, text)
}

func fetchText(message string) string {
    text := message
    if len(text)/3 == 2 {
        if text == "大阪" || text == "京都" {
            text += "府"
        } else if text == "東京" {
            text = "東京都"
        } else {
            text += "県"
        }
    } else if len(text)/3 == 3 {
        if text == "神奈川" || text == "和歌山" {
            text += "県"
        }
    }

    _, ok := prefcodes.NameToCode[text]
    if !(ok) {
        return "都道府県名が無効です"
    }
    return getPrefData(text)
}

func getPrefData(pref string) string {
    linesInterface, err := datas.GetPrefData(prefcodes.NameToCode[pref])
    if err != nil {
        return err.Error()
    }

    lines := linesInterface.Lines
    result := "[" + pref + "]\n"
    for _, line := range lines {
        result += "\n"
        result += strconv.Itoa(line.Code)
        result += ": "
        result += line.Name
    }
    return result
}

func getLineData(linecode int) string {
    stations, err := datas.GetLineData(linecode)
    if err != nil {
        return "エラー:\n路線一覧で取得した路線番号を入力してください"
    }

    result := "[" + stations.Line.Name + "]\n"
    for _, station := range stations.Stations {
        result += "\n"
        result += strconv.Itoa(station.Code)
        result += ": "
        result += station.Name
    }
    return result
}

func getStationData(stationcode int) string {
    station, err := datas.GetStationData(stationcode)
    if err != nil {
        return "エラー:\n駅一覧で取得した駅番号を入力してください"
    }

    result := "[" + station.Station.Name + "]\n\n"
    result += "駅コード: " + strconv.Itoa(station.Station.Code)
    result += "\n"
    result += "駅グループコード: " + strconv.Itoa(station.Station.GroupCode)
    result += "\n"
    result += "路線: " + station.Station.LineName + "(" + strconv.Itoa(station.Station.LineCode) + ")"
    result += "\n"
    result += "都道府県: " + prefcodes.CodeToName[station.Station.PrefCode]
    result += "\n"
    result += "緯度: " + fmt.Sprintf("%f", station.Station.Latitude)
    result += "\n"
    result += "経度: " + fmt.Sprintf("%f", station.Station.Longtitude)

    return result
}

func getGroupData(stationcode int) string {
    group, err := datas.GetGroupData(stationcode)
    if err != nil {
        return "エラー:\n駅一覧で取得した駅番号を入力してください"
    }

    result := "[" + group.Station.Name + "]\n"
    for _, line := range group.GroupStations {
        result += "\n"
        result += strconv.Itoa(line.LineCode)
        result += ": "
        result += line.LineName
        result += "\n名称: "
        result += line.Name
    }
    return result
}

func getJoinData(stationcode int) string {
    joins, err := datas.GetJoinData(stationcode)
    if err != nil {
        return "エラー:\n路線一覧で取得した路線番号を入力してください"
    }

    line, err := datas.GetLineData(stationcode)
    if err != nil {
        return err.Error()
    }

    result := "[" + line.Line.Name + "]\n\n"
    for _, join := range joins.StationJoins {
        result += "隣接駅1:"
        result += join.Name1
        result += "("
        result += strconv.Itoa(join.Code1)
        result += ")\n"
        result += "隣接駅2:"
        result += join.Name2
        result += "("
        result += strconv.Itoa(join.Code2)
        result += ")\n\n"
    }
    return result
}

Herokuの設定

貧乏な学生なのでサーバーを借りるお金はないのでHeroku様を使います

Herokuのアプリを起動した際に読み込まれるProcfileを作ります

$ echo "web: $(basename `pwd`)" > Procfile

パッケージ管理にはgovendorを使います

$ go get -u github.com/kardianos/govendor
$ govendor init
$ govendor fetch +out

gitのセットアップを行います

$ git init
$ git add .
$ git commit -m "first commit"

あとはHerokuにデプロイするだけです

$ heroku create
$ heroku config:add CHANNEL_SECRET="チャンネルシークレット"
$ heroku config:add CHANNEL_TOKEN="チャンネルトークン"
$ git push heroku master

最後に、作成したアプリのWebhookURLをlinebotの管理画面で登録すれば完成です

完成品

https://github.com/Homarechan/Go-Bot
989mooot.png
http://line.naver.jp/ti/p/@989mooot

最後に

Haskell書きたい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む