Go 言語で初めてのライブラリ開発

この記事は Go4 Advent Calendar 2019 の 5 日目の記事です。

2019 年の締めに作りました。
どんなものを作ったのか、その際に何をやったのかまとめます。

1. 作ったライブラリ

遊戯王のカード情報(英語版)を取得できる APIYu-Gi-Oh! API by YGOPRODeck」のクライアントライブラリ「go-yugioh」を作りました。

README を英語で書いてバッチをつけるだけでそれっぽく見えます。

f:id:Doarakko:20191205032216p:plain

API 自体が英語版のみ(フランス語版は非公式)の対応のため日本語版のカード情報は取ってこれません。

使用例

  • カード名に dragon が含まれる光属性融合モンスターを取得
import (
    "fmt"

    "github.com/Doarakko/go-yugioh/yugioh"
)

func main() {
    client := yugioh.NewClient()
    cards, _, _ := client.Cards.List(
        &yugioh.CardsListOptions{
            KeyWord: "dragon", Type: "Fusion Monster", Attribute: "light"})

    for _, card := range cards {
        fmt.Printf("Name: %v\nType: %v\nRace: %v\nDescription:\n%v\n\n",
            card.Name, card.Type, card.Race, card.Description)
    }
}
Name: A-to-Z-Dragon Buster Cannon
Type: Fusion Monster
Race: Machine
Description:
"ABC-Dragon Buster" + "XYZ-Dragon Cannon"
Must be Special Summoned (from your Extra Deck) by banishing cards you control with the above original names, and cannot be Special Summoned by other ways. (You do not use "Polymerization".) During either player's turn, when your opponent activates a Spell/Trap Card, or monster effect: You can discard 1 card; negate the activation, and if you do, destroy that card. During either player's turn: You can banish this card, then target 1 each of your banished "ABC-Dragon Buster", and "XYZ-Dragon Cannon"; Special Summon them.

Name: ABC-Dragon Buster
Type: Fusion Monster
Race: Machine
Description:
"A-Assault Core" + "B-Buster Drake" + "C-Crush Wyvern"
Must first be Special Summoned (from your Extra Deck) by banishing the above cards you control and/or from your Graveyard. (You do not use "Polymerization".) Once per turn, during either player's turn: You can discard 1 card, then target 1 card on the field; banish it. During your opponent's turn: You can Tribute this card, then target 3 of your banished LIGHT Machine-Type Union monsters with different names; Special Summon them (this is a Quick Effect).
...
  • ランダムなカードを取得
    • API の仕様でパラメータ指定不可
func main() {
    client := yugioh.NewClient()

    card, _, _ := client.RandomCards.One()
    fmt.Printf("Name: %v\nType: %v\nRace: %v\nDescription:\n%v\n",
        card.Name, card.Type, card.Race, card.Description)
}
Name: Formula Synchron
Type: Synchro Tuner Monster
Race: Machine
Description:
1 Tuner + 1 non-Tuner monster
When this card is Synchro Summoned: You can draw 1 card. Once per Chain, during your opponent's Main Phase, you can: Immediately after this effect resolves, Synchro Summon using this card you control (this is a Quick Effect).
  • Amazoness に関連する魔法カードを取得
    • 対応しているカードグループ(AmazonessBlue-Eyes など)は client.Archetypes.List() から取得可能
func main() {
    client := yugioh.NewClient()
    cards, _, _ := client.Cards.List(
        &yugioh.CardsListOptions{Archetype: "Amazoness", Type: "Spell Card"})

    for _, card := range cards {
        fmt.Printf("Name: %v\nType: %v\nRace: %v\nDescription:\n%v\n\n",
            card.Name, card.Type, card.Race, card.Description)
    }
}
Name: Amazoness Call
Type: Spell Card
Race: Quick-Play
Description:
Take 1 "Amazoness" card from your Deck, except "Amazoness Call", and either add it to your hand or send it to the GY. During your Main Phase: You can banish this card from your GY, then target 1 "Amazoness" monster you control; this turn, that monster can attack all monsters your opponent controls, once each, also other monsters you control cannot attack. You can only activate 1 "Amazoness Call" per turn.

Name: Amazoness Fighting Spirit
Type: Spell Card
Race: Continuous
Description:
If an "Amazoness" monster attacks a monster with higher ATK, the attacking monster gains 1000 ATK during damage calculation only.
...

go-yugioh で作った CLI

実際にライブラリを使って遊戯王カードをドローするコマンド draw を作りました。

f:id:Doarakko:20191205032339p:plain

実行するとランダムな遊戯王カードがコマンドライン上に表示されます。
遊戯王カードをドローしたいときに使ってください。

クソアプリ 2 Advent Calendar 2019 で記事を書いてます。

遊戯王カードをドローするコマンドを作りました

2. 開発した経緯

2019 年新しく挑戦したこととして真っ先に思い浮かんだのが Go 言語でした。

せっかくなので最後に Go 言語の Advent Calendar の記事を書こうと思い、なんとなくライブラリ開発をすることにしました。

3. どんなライブラリを作るか

ライブラリを作ることを目的にスタートしたため、アイディアも何もない状態でした。
せっかく作るなら少しでも使われるものを作りたいということで、以下 2 つを条件にしました。

  1. 明確にユーザを定義できる

  2. 他の方が作っていない

そこで思いついたのが API のクライアントライブラリです。

  1. 明確にユーザを定義できる
    Go 言語でその API を使って開発を行う人

  2. 他の方が作っていない
    API はたくさんあるので、探せばクライアントライブラリが作られていないものが見つかるだろう

Go 言語初心者なので、参考にできるライブラリ(他のクライアントライブラリ)がたくさんあるというのも大きかったです。

4. やったこと

4.1 API 探し

以下 2 つを条件にしました。

  • Go 言語でクライアントライブラリが作成されていない
  • 特定の領域でメジャー
    • OK: カバディAPI の中で 1 番目に使われている
    • NG: サッカーの API の中で 2 番目に使われている

「<適当なキーワード> + API」 でググって、API を見つけたら GitHub でライブラリが作られていないか確認するというのを繰り返し行いました。

これは作られてないだろというものも結構ライブラリが作られていてびっくりしました。

4.2 ドキュメントを読みながら実際に API を使用

実際に公開されているすべてのエンドポイントを curl コマンドで叩いて確認しました。

4.3 他の Go 言語製のクライアントライブラリのソースを読む

主に参考にしたのが以下 2 つです。

URL に API のオプションをつける処理であったり、HTTP リクエストを送る処理はそのまま流用しました。

4.4 ライブラリ作成

API のドキュメントを見ながら構造体に落とし込みます。
多少フィール名を元の API から変えてます。

type Card struct {
    ID          int32   `json:"id,string"`
    Name        string  `json:"name"`
    Type        string  `json:"type"`
    Description string  `json:"desc"`
    Race        string  `json:"race"`
    Archetype   string  `json:"archetype"`
    Sets        []Set   `json:"card_sets"`
    Images      []Image `json:"card_images"`
...

type Prices struct {
    // Euro
    Cardmarket float32 `json:"cardmarket_price,string"`

    // Dollar
    Tcgplayer float32 `json:"tcgplayer_price,string"`
    Ebay      float32 `json:"ebay_price,string"`
    Amazon    float32 `json:"amazon_price,string"`
}

API のレスポンスの Key を構造体のフィールドの右に指定する(json:"name")と自動でパースしてくれるのはびっくりしました。
最高です。

int や float で使いたいもの(ID や値段)が文字列できたので、string とつけて対処(json:"id,string")しました。

Package json: func Marshal

今回の API は特にアカウント登録などなかったので、認証周りを気にする必要はありませんでした。

4.5 API 開発元 に Issue を立てる

この API ではパラメータにグループ(AmazonessBlue-Eyes など)を指定することで、そのグループに関連するカードのみを取得できます。
ただこのグループは遊戯王公式が一覧を公開している訳ではなく API 開発者が独自に行なっています。

[YGOPRODeck] Missing Archetypes - Master List #10

API 利用者からすると、対応しているグループが分からないのでパラメータの指定が難しいです。
そこで API から対応しているグループのリストを取得できるように開発者に頼むことにしました。

API 自体のソースは公開されていませんが、バグ報告用のリポジトリが公開されていたのでそちらから Issue を立てました。

[YGOPRODeck] New API: return archetype master #118

それが必要な理由と自分が作ったライブラリの紹介、API を作っていることに対するお礼を書きました。

Issue を立てた 5、6 時間後にはエンドポイントが新たに公開されてドキュメントも更新されたのでびっくりしました。
個人開発をずっと続けていたので、こうやって開発で誰かと関われたのは本当に嬉しかったです。

f:id:Doarakko:20191205032553p:plain

4.6 ライブラリに反映

新しく公開されたエンドポイントに合わせて、コード追加とドキュメント更新、サンプルプログラムの追加を行いました。

[go-yugioh] All card archetypes #2

4.7 GoDoc 登録・Go Report チェック

f:id:Doarakko:20191205032216p:plain

見た目それっぽくするためにつけました。
GoDoc はソースコードのコメントから自動で生成されます。

ライブラリのインポートパス(github.com/Doarakko/go-yugioh)を入力して、Go を押すと GoDoc に登録されます。

f:id:Doarakko:20191205032643p:plain

Go Report では、フォーマットやライセンスファイルがあるか、スペルミスがないかなどがチェックされてランクがつきます。

f:id:Doarakko:20191205032815p:plain

右上のバッジから取得した MarkDown 用のリンクを README に貼れば完了です。

4.8 ライブラリを使って CLI を開発

記事冒頭で紹介した、遊戯王カードをドローするコマンド draw を作りました。

f:id:Doarakko:20191205032830p:plain

私がこのライブラリ初の利用者です。

5. おわりに

クライアントライブラリなので開発自体に面白みはないですが、他の方のコードを読んだり、ライブラリを作って公開するまでの流れを経験できるのは初心者にちょうどいいんじゃないでしょうか。

今回は API 開発元に要望を送って、それが対応されて自分のライブラリに反映させるという貴重な体験もできました。

新しい職場で Go 言語を扱うことになったので、2020 年はより楽しい Go ライフを送れそうです。
ありがとうございました。

6. 参考資料