GolangでFacebook feedを取得するSDKを書いた
bot作りに精を出している今日このごろ、Facebookのfeedが更新されたら通知するbotを作ろうと思い、自分用のfeed取得SDKを作ったので使い方をまとめます。
今のところGetFeed
という関数しかないです。
Install
どちらも自作になってしまいますが、以下2つをgo get
します。
go get github.com/cipepser/bots go get github.com/cipepser/httpclient/sdk
アクセストークンの設定
Feedを取得するためには、アクセストークンが必要です。そのためにfacebook for developersでappを作成してください。
curlでFacebook API叩くまでがわりとめんどかったのでメモ - DRYな備忘録を参考にさせて頂きました。
appを作成するとアクセストークンがここから確認できます。
以下の黒く塗り潰している箇所のApp Token
です。
App Token
をコピーし、./token/facebook_api.json
に以下のように設定してください。
{ "access_token":"<YOUR APP TOKEN>" }
これで準備は完了です。
How to Use
URL
に取得したいfeedのURLを入力し、GetFeed(URL)
を実行するだけです。
package main import ( "github.com/cipepser/bots/facebook" ) func main() { URL := "https://graph.facebook.com/v2.11/<user name>/feed" f, err := facebook.GetFeed(URL) if err != nil { panic(err) } fmt.Println(f) }
結果(上記のf
)は、facebook
パッケージの中で定義している以下のFeed
型で返ってくるので、必要なものを利用できます。
type Feed struct { Data []struct { CreatedTime string `json:"created_time"` Message string `json:"message"` ID string `json:"id"` Story string `json:"story,omitempty"` } `json:"data"` Paging struct { Cursors struct { Before string `json:"before"` After string `json:"after"` } `json:"cursors"` Next string `json:"next"` } `json:"paging"` }
実装について
実装の中身はGithubにあげてあります。
access_token
を埋め込んで、HTTPでGETするだけなので、応用しやすいです。
References
GolangでLINE Notify
LINEのBOTをDeveloper Trialのときに遊んで以来、ご無沙汰だったのですが、改めてLINE Notifyを試したらものすごく手軽でした。 ぐぐってみるとherokuにデブロイする系の記事が多いのですが、HTTPでPOSTするだけで通知できるので、備忘がてら手順をまとめたいと思います。
アクセストークンの取得
LINE Notifyの画面からログインします。 ログイン後、マイページへ移動します。
下の方に「アクセストークンの発行(開発者向け)」という項目があるので、 「トークンを発行する」を押下します。
すると以下の画面が出てくるので、
を入力します。
トークン名は、通知されるときにメッセージの先頭に付与されるので短めがおすすめです。
また、グループなどに通知したい場合は、先にグループを作っておく必要があります。 今回は「テスト」を事前に作っておきました。
「発行する」を押下すると以下のようにアクセストークンが得られます。
これをコピーしておきましょう。なくしてしまった場合は、もう一度同じ手順で発行します。
LINE Notifyアカウントをトークンルームに追加
アクセストークンを発行すると以下のように通知が来ます。
指示通りトークルーム(今回は「テスト」グループ)にLINE Notifyのアカウント(トークン名「bot」ではなく、LINE Notifyなので注意)を招待してあげます。
これで準備完了です。
メッセージを送る
実装は以下です。msg
の内容を送信します。
package main import ( "fmt" "io/ioutil" "log" "net/http" "net/url" "strings" ) func main() { accessToken := <YOUR ACCESS TOKEN> msg := "テストメッセージ" URL := "https://notify-api.line.me/api/notify" u, err := url.ParseRequestURI(URL) if err != nil { log.Fatal(err) } c := &http.Client{} form := url.Values{} form.Add("message", msg) body := strings.NewReader(form.Encode()) req, err := http.NewRequest("POST", u.String(), body) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "Bearer "+accessToken) _, err := c.Do(req) if err != nil { log.Fatal(err) } }
実行すると以下のようにメッセージを受信できます。
References
Karabiner11.4.0で「ctrl+かな」を「ESC」にバインドする
Karabinerを11.4.0
にアップデートしたら、今までprivate.xml
に設定してきたkey bindingsが使えなくなってしまいました。公式のマニュアルやKarabiner-Elementsの設定項目をまとめましたを参考にしながら、「ctrl+かな」を「ESC」にバインドする設定をしたので残します。
前回の記事は、complex_modifications
をインポートしましたが、自分で設定するのが今回です。
環境
macOS High Sierra ver 10.13.2 JIS keyboard Karabiner-Elements-11.4.0
設定ファイル
設定ファイルがxml
からjson
に変わり、配置も~/.config/karabiner
に変わっています。
❯ tree . ├── assets │ └── complex_modifications │ └── hoge.json └── karabiner.json
karabiner.json
は、メインのconfigファイルで、変更があれば自動でリロードされるそうです。
assets/complex_modifications
は、その名の通りcomplex_modificationsを設定するディレクトリで、この中にjson
ファイルを格納します。
設定
assets/complex_modifications
配下にctrl-kana-to-esc.json
を作成し、以下の内容を記載します。
{ "title": "Change ctrl+KANA to ESC", "rules": [{ "description": "Change ctrl+KANA key to Escape key", "manipulators": [{ "type": "basic", "from": { "key_code": "japanese_kana", "modifiers": { "mandatory": ["control"] } }, "to": [{ "key_code": "escape" }] }] }] }
あとはこの設定を適用してあげれば完了です。
最後に
private.xml
よりも簡単に設定できるようになったように感じました。Preferences
のLog
タブでエラーが見れるのでデバッグも捗ります。
References
KarabinerのSimple Vi ModeをHigh Sierraでも使う
Karabinerが使えなくなると聞いていたので、ずっとHigh Sierraにアップデートするのを渋っていたのですが、ついにアップデートしました。渋っている間に対応が済んでおり、Simple Vi Modeも使えたので、設定方法をまとめます。
環境
macOS High Sierra ver 10.13.2 Karabiner-Elements-11.4.0
手順
Karabiner-Elementsを起動し、Complex Modifications
タブを選びます。
出てきた画面でAdd rule
を押下します。
上のほうにあるImport more rules from the Internet (open a web browser)
を押下します。
ブラウザが立ち上がるので、Emulation Modes
の項目のVi Mode (rev 4.2)
をImport
します。
Karabiner-Elementsに戻ってくるのでImport
を押下します。
最後にVi Mode [S as Trigger Key]
のEnable
で有効化して完了です。
簡単ですね。
References
Golangでx-www-form-urlencodedのリクエストを投げる
やりたいことはタイトルそのままです。
parameter=hoge
をhttpでPOSTする方法は以下です。
form := url.Values{} form.Add("paramter", "hoge") body := strings.NewReader(form.Encode()) req, err := http.NewRequest("POST", "https://example.com", body) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
References
Golangのnew()とmake()の違い
この記事は Go (その3) Advent Calendar 2017 の5日目の記事です。
本記事では、変数の宣言に用いられる組み込み関数new()
とmake()
の違いについてまとめます。
まとめ
さっそくですが、違いを表にまとめます。
new(T) | make(T) | |
---|---|---|
対象 | 任意の型 | slice, map, channelのみ |
初期化 | 初期化しない(ゼロ値になる) | 初期化する |
返り値 | *T | T |
対象と初期化について
new()
とmake()
で、初期化しない/するの違いは、
slice, map, channelが、内部にデータ構造を持つことからきています。
以下にruntime
パッケージで、それぞれ型が定義されている箇所を引用します。
一番理解しやすいのが、sliceです。array(実データ), len, capを初期化してあげる必要があるため、make()
が用意されています。
slice
type slice struct { array unsafe.Pointer len int cap int }
https://golang.org/src/runtime/slice.go#L11-15
map
type hmap struct { // Note: the format of the Hmap is encoded in ../../cmd/internal/gc/reflect.go and // ../reflect/type.go. Don't change this structure without also changing that code! count int // # live cells == size of map. Must be first (used by len() builtin) flags uint8 B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items) noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details hash0 uint32 // hash seed buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0. oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated) extra *mapextra // optional fields }
https://golang.org/src/runtime/hashmap.go#L106-120
channel
type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex }
https://golang.org/src/runtime/chan.go#L31-50
初期化について補足
Effective Goでは、new()
では初期化が行われない、つまりゼロ値となることについて、helpfulであると述べられています。
これはゼロ値自体が意味を持つ場合には、初期化しているのと同じだけの意味があるということです。
Effective Goで述べられている例ですが、sync.Mutex
では以下のようにゼロ値自身がunlockなstateを表現します。
// A Mutex is a mutual exclusion lock. // The zero value for a Mutex is an unlocked mutex. // // A Mutex must not be copied after first use. type Mutex struct { state int32 sema uint32 }
自身で型を定義する際には、NewMyType()
のようなコンストラクタを用意する場合が多いでしょう。このときにゼロ値自身に意味を持たせ、new()
と組み合わせることでよい設計となると思います。
最後に
自分自身もあまりnew()
とmake()
の違いを認識できていなかったので、調べてまとめることができてよかったです。あといつか読もう読もうと思って着手できていなかったruntime
パッケージを読むきっかけにもなったので、思いの外収穫が多かったです。Goはソースコードにドキュメントレベルでコメントが書いてあるので、読むだけでなるほどと思うことが多く、とても勉強になります。
Githubの履歴を確認したらちょうどGoを書き始めてから1年が経過しました。書けば書くほど好きになっていくので、まだまだ書きますよー!
あぁ。水色gopherくんぬいぐるみ欲しいなぁ。。。
References
go tool compileの使い方
少し試したものの、忘れそうなのでメモ書きとして残します。
サンプルコード
package main import "fmt" func main() { fmt.Println("hello world") }
オブジェクトファイルの生成
-S
が1つだと標準出力のみです。2つだと.o
ファイルが生成されます。
$ go tool compile -S -S main.go "".main STEXT size=120 args=0x0 locals=0x48 0x0000 00000 (main.go:5) TEXT "".main(SB), $72-0 (中略) 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 62 48 eH..%....H;a.vbH 0x0010 83 ec 48 48 89 6c 24 40 48 8d 6c 24 40 48 c7 44 ..HH.l$@H.l$@H.D 0x0020 24 30 00 00 00 00 48 c7 44 24 38 00 00 00 00 48 $0....H.D$8....H 0x0030 8d 05 00 00 00 00 48 89 44 24 30 48 8d 05 00 00 ......H.D$0H.... 0x0040 00 00 48 89 44 24 38 48 8d 44 24 30 48 89 04 24 ..H.D$8H.D$0H..$ 0x0050 48 c7 44 24 08 01 00 00 00 48 c7 44 24 10 01 00 H.D$.....H.D$... 0x0060 00 00 e8 00 00 00 00 48 8b 6c 24 40 48 83 c4 48 .......H.l$@H..H 0x0070 c3 e8 00 00 00 00 eb 88 ........ rel 5+4 t=16 TLS+0 rel 50+4 t=15 type.string+0 rel 62+4 t=15 "".statictmp_0+0 rel 99+4 t=8 fmt.Println+0 rel 114+4 t=8 runtime.morestack_noctxt+0 (中略) type..importpath.fmt. SRODATA dupok size=6 0x0000 00 00 03 66 6d 74 ...fmt gclocals·69c1753bd5f81501d95132d08af04464 SRODATA dupok size=8 0x0000 02 00 00 00 00 00 00 00 ........ gclocals·e226d4ae4a7cad8835311c6a4683c14f SRODATA dupok size=10 0x0000 02 00 00 00 02 00 00 00 00 03 .......... gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8 0x0000 01 00 00 00 00 00 00 00 ........
オブジェクトファイルから実行コードを生成
$ go tool link main.o $ ls a.out main.go main.o $ ./a.out hello world
オブジェクトファイルに定義されているシンボルの表示
$ go tool nm main.o U 44c T %22%22.init 517 B %22%22.initdone· 3b5 T %22%22.main 507 R %22%22.statictmp_0 U fmt.Println U fmt.init 6de R gclocals·33cdeccccebe80329f1fdbee7f5874cb 6cc R gclocals·69c1753bd5f81501d95132d08af04464 6d4 R gclocals·e226d4ae4a7cad8835311c6a4683c14f 4ea ? go.info.%22%22.init 4cd ? go.info.%22%22.main 507 ? go.range.%22%22.init 4ea ? go.range.%22%22.main 4c2 R go.string."hello world" U runtime.algarray 517 R runtime.gcbits.01 560 R runtime.gcbits.03 U runtime.morestack_noctxt U runtime.throwinit 646 R type.*[1]interface {} 5c3 R type.*[]interface {} 528 R type.*interface {} 6c6 R type..importpath.fmt. 633 R type..namedata.*[1]interface {}- 5b1 R type..namedata.*[]interface {}- 518 R type..namedata.*interface {}- 67e R type.[1]interface {} 5fb R type.[]interface {} 561 R type.interface {} U type.string
実行コードの逆アセンブリ
$ go tool objdump a.out (略)