PokeAPIでポケモンGET!

自転車にまたがるとマッハ自転車のBGMが頭の中で流れ出します。岡村です。

今回は

  • PokeAPI
  • GoogleAppsScript
  • CloudFunctions
  • スプレッドシート

の4つを使ってポケモンGET機能を作ってみたいと思います。

やりたいこと

GoogleAppsScript上で関数を実行すると
スプレッドシートにポケモンのデータを含む新しい行が挿入されること。

それではやってみましょう!

PokeAPI

PokeAPIとはポケモンに関するデータを取得することができる
オープンソースのAPIです。

https://pokeapi.co/

今回取得したいデータは

  • 図鑑ナンバー
  • 名前
  • 画像URL
  • 解説

です。
アクセスするエンドポイントは

https://pokeapi.co/api/v2/pokemon-species/{id or name}/

です。
https://pokeapi.co/api/v2/pokemon/{id or name}/の方が
いろいろな種類のデータが取れるのですが、
日本語解説がpokemon-speciesの方にしかなかったので。

スプレッドシート

GETしたポケモンを格納するための
pokemonsシートを作っておきます。

列の定義は以下です。

  • A列:図鑑ナンバー
  • B列:名前
  • C列:画像URL
  • D列:解説

CloudFunctions

PokeAPIにポケモンデータ取得のリクエストを行います。
GoogleAppsScriptから行ったところ、
IPアドレスがブロックされたのか、
1回目以降データが取れなくなってしまいました。

People not complying with the fair use policy will have their IP address permanently banned.

公式ドキュメントFair Use Policyより引用

ですので前回同様PokeAPIへのリクエスト部分のみ、
CloudFunctionsを使って実装します。

ソースコードは以下のようにしました。

const fetch = require('node-fetch');
const functions = require('@google-cloud/functions-framework');

const END_POINT_POKEMON = 'https://pokeapi.co/api/v2/pokemon-species/';

/**
 * PokeAPIにポケモン取得をリクエストします。
 * 
 * 取得できた場合は、jsonオブジェクトを返します。ステータスは200です。
 * エラーが発生した場合は、空のjsonオブジェクトを返します。ステータスは500です。
 *
 * @param {Object} request クライアントからのリクエストに関する機能を提供するオブジェクト。
 * @param {Object} response クラアントへのレスポンスに関する機能を提供する提供するオブジェクト。
 */
functions.http('getPokemon', async (request, response) => {

  let returnJson = new Object();

  try {
    const url = END_POINT_POKEMON + request.body['id'];
    console.log('url:' + url);

    let responseObject = new Object(); 

    try {
      responseObject = await fetch(url); 
      returnJson = await responseObject.json();
      response.status(200);

    } catch(error) {
      throw new Error('PokeAPIへのリクエスト中にエラーが発生しました。error.message:' + error.message);
    }

  } catch(error) {
      console.error('エラーが発生しました。error:');
      console.error(error.stack);

      response.status(500);
  }

  console.log('returnJson:');
  console.log(returnJson);

  response.send(returnJson);

  return;
});

GoogleAppsScript

ソースコードは以下のようにしました。(DDD学習中🔥)

main関数からPokemonServiceクラスgetNewPokemon関数を呼び出します。



function main() {

  try {
    new PokemonService().getNewPokemon();
    
  } catch(error) {
    const errorMessage = 'エラーが発生しました。error:' + error.stack;
    console.error(errorMessage);
  }
}

getNewPokemon関数からPokeApiClientServiceクラスのrequestToPokeApi関数を呼び出します。

/** ポケモン機能のサービスクラスです。*/ 
class PokemonService {
  
  /** コンストラクタ。*/ 
  constructor() {
  
    /** 全国図鑑ナンバー最小値。*/ 
    this.minId = 1; 

    /** 全国図鑑ナンバー最大値。*/ 
    this.maxId = 1025; 
  }

  /** 
   * 新しいポケモンをGETし、スプレッドシートに追加します。 
   */ 
  getNewPokemon() {

    const id = Math.floor(Math.random() * (this.maxId + 1 - this.minId)) + this.minId;
    console.log('GETするポケモンの図鑑ナンバー:' + id);

    const response = new PokeApiClientService().requestToPokeApi(id);
    const pokemon = new Pokemon(response);
    new PokemonRepository().insertPokemon(pokemon);
  }
}
/** PokeApiのAPIクライアントサービスクラスです。*/ 
class PokeApiClientService {

  /** 
   * CloudFunctions経由でPokeApiへポケモン取得のリクエストを行います。 
   * @param id 取得するポケモンの図鑑ナンバー。
   * @return レスポンス。
   */ 
  requestToPokeApi(id) {

    // ご自身のCloudFunctionsのトリガーとなるURLを入力してください!
    const endPointGetPokemon = 'example.com';

    try {
      const headers = {
        "Authorization": `Bearer ${ScriptApp.getIdentityToken()}`
      }

      const params = {
        "id": id
      }

      const options = {
        "method": "POST",
        "headers": headers,
        "contentType": "application/json",
        "payload": JSON.stringify(params),
      }
    
      return UrlFetchApp.fetch(endPointGetPokemon, options);

    } catch (error) {
      throw error;
    }
  }
}

endPointGetPokemonにはご自身のCloudFunctionsのトリガーとなるURLを入力してください!
getNewPokemon関数にはCloudFunctionsがPokeApiを叩いたレスポンスが返ってきます。

レスポンスをPokemonクラスのコンストラクタに渡します。

/** ポケモンのEntityクラスです。*/ 
class Pokemon {

  /** 
   * コンストラクタ。
   * @param PokeApiからのレスポンス。
   */ 
  constructor(responseFromPokeApi) {

    const responseObject = JSON.parse(responseFromPokeApi.getContentText());

    const id = responseObject['id'];
    const name = responseObject.names.find(name => name.language.name === "ja-Hrkt").name;

    // 日本語解説がない場合は英語解説
    let text = '';
    try {
      text = responseObject.flavor_text_entries.find(entry => {
        return entry.language.name === "ja-Hrkt";
      }).flavor_text;

    } catch {
      // undefined
      text = responseObject.flavor_text_entries.find(entry => {
        return entry.language.name === "en";
      }).flavor_text;
    }

    /** 全国図鑑ナンバー。 */ 
    this.id = id;

    /** ポケモン名。 */ 
    this.name = name;

    /** 画像URL。 */ 
    this.imgUrl = 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/'+ this.id +'.png';

    /** 解説。 */ 
    this.text = text;
  }
}

図鑑ナンバー、名前、画像URL、解説をフィールドに持つエンティティとします。
スカーレット・バイオレットのポケモンたちは
まだ日本語解説が実装されてなかったので
日本語解説がなかった場合は英語解説をレスポンスから取り出します。

PokemonインスタンスをgetNewPokemon関数へ返し、
PokemonRepositoryクラスinsertPokemon関数へ渡します。

/** ポケモンのRepositoryクラスです。*/ 
class PokemonRepository {

  /** コンストラクタ。*/ 
  constructor() {

    this.scriptProperties = PropertiesService.getScriptProperties();
    const activeSpreadSheet = SpreadsheetApp.getActiveSpreadsheet();

    // pokemonsシートを取得する
    this.pokemonsSheet = activeSpreadSheet.getSheetByName('pokemons');
  }

  /** 
   * ポケモンを挿入します。
   * @param pokemon ポケモン。
   */
  insertPokemon(pokemon) {
    const pokemonValues = Object.values(pokemon);
    this.pokemonsSheet.appendRow(pokemonValues);
  }
}

ポケモンデータをスプレッドシートの最終行へ挿入します。

では、ソースが書けましたら
実行!

実行結果

スプレッドシートにポケモンが追加されてますね!

あとがき

ポケモンはGETできましたでしょうか?
私はこの機能をdiscordのbotに組み込んで
みんなでポケモン図鑑を完成させるゲームにしました。
友人が今日の積み上げこと「きょうつみ」を投稿したら1ポイント。
5ポイントが溜まったらポケモンGET!
図鑑完成度を100%にしよう!★

ポケモンといえば最近?
推し声優がパモの声をやってるらしい!!
という不純な?理由で?
パモが好きかもしれない。かなり。

また何か作ったら共有します!

お問い合わせ

サービスに関するご相談やご質問などこちらからお問い合わせください。

03-55107260

受付時間 10:00〜17:00