├── .editorconfig ├── .eslintrc ├── .gitignore ├── ExampleInventory.js ├── ExamplePromises.js ├── ExampleStats.js ├── LICENSE.md ├── README.md ├── auth.js ├── example.js ├── exampleFort.js ├── exampleStats.js ├── logins.js ├── package.json ├── poke.io.js ├── pokego.js ├── pokemon.proto ├── pokemons.json └── tests └── poke.io ├── GetLocation.js ├── GetLocationCoords.js └── SetLocation.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [package.json] 11 | indent_style = space 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [ 4 | 1, 5 | 4, 6 | {"SwitchCase": 1} 7 | ], 8 | "quotes": [ 9 | 2, 10 | "single" 11 | ], 12 | "semi": [ 13 | 2, 14 | "always" 15 | ], 16 | "comma-dangle": [ 17 | 0, 18 | "always-multiline" 19 | ], 20 | "no-console": [ 21 | 0, 22 | ], 23 | "no-unused-vars": [ 24 | 1, 25 | ] 26 | }, 27 | "env": { 28 | "node": true, 29 | "es6": true 30 | }, 31 | "extends": "eslint:recommended" 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Istanbul coverage report 5 | coverage 6 | 7 | # My tests 8 | derek 9 | -------------------------------------------------------------------------------- /ExampleInventory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var username = process.env.PGO_USERNAME || 'username'; 4 | var password = process.env.PGO_PASSWORD || 'password'; 5 | var provider = process.env.PGO_PROVIDER || 'google'; /* google OR ptc */ 6 | 7 | var location = { 8 | type: 'name', 9 | name: process.env.PGO_LOCATION || 'Time Square' 10 | 11 | // type: 'coords', 12 | // coords: { 13 | // latitude: 40.758896, 14 | // longitude: -73.985130, 15 | // altitude: 10 16 | // } 17 | 18 | }; 19 | 20 | var Pokego = require('./pokego.js'); 21 | var util = require('util'); 22 | util.inspect(console, true); 23 | 24 | Pokego.init(username, password, location, provider).then((profile) => { 25 | return new Promise(function(resolve, reject) { 26 | Pokego.GetProfile().then((profile) => { 27 | return Pokego.formatPlayercard(profile); 28 | }).then((val) => { 29 | Pokego.GetInventory().then((profile) => { 30 | Pokego.displayInventory(profile); 31 | }); 32 | }).catch((val) => { 33 | console.log(val); 34 | }); 35 | }).then((a) => {console.log(a);}); 36 | }); -------------------------------------------------------------------------------- /ExamplePromises.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var username = process.env.PGO_USERNAME || 'username'; 4 | var password = process.env.PGO_PASSWORD || 'password'; 5 | var provider = process.env.PGO_PROVIDER || 'google'; /* google OR ptc */ 6 | var pokeBall = process.env.PGO_POKEBALL || 'POKE_BALL'; // POKE_BALL or GREAT_BALL or ULTRA_BALL 7 | 8 | var location = { 9 | type: 'name', 10 | name: process.env.PGO_LOCATION || 'Time Square' 11 | 12 | // type: 'coords', 13 | // coords: { 14 | // latitude: 40.758896, 15 | // longitude: -73.985130, 16 | // altitude: 10 17 | // } 18 | 19 | }; 20 | 21 | var Pokego = require('./pokego.js'); 22 | var util = require('util'); 23 | util.inspect(console, true); 24 | 25 | var shouldLoop = true; 26 | 27 | Pokego.init(username, password, location, provider).then((profile) => { 28 | setInterval(function() { 29 | return new Promise(function(resolve, reject) { 30 | if(shouldLoop) { 31 | Pokego.GetProfile().then((profile) => { 32 | return Pokego.formatPlayercard(profile); 33 | }) 34 | .then((val) => { 35 | Pokego.Heartbeat().then((heart) => { 36 | console.log('[o] pump...'); 37 | for (var i = heart.cells.length - 1; i >= 0; i--) { 38 | if(heart.cells[i].WildPokemon[0]) { 39 | shouldLoop = false; 40 | for (var x = heart.cells[i].WildPokemon.length - 1; x >= 0; x--) { 41 | var currentPokemon = heart.cells[i].WildPokemon[x]; 42 | var pokemon = Pokego.pokemonlist[parseInt(currentPokemon.pokemon.PokemonId)-1]; 43 | console.log('[+] There is a catchable ' + pokemon.name + ' - ' + parseInt(currentPokemon.TimeTillHiddenMs) / 1000 + ' seconds until hidden.'); 44 | Pokego.fireAndForgetCatch(currentPokemon, pokemon.name, x, pokeBall).then((data) => { 45 | if(data == 0) { 46 | shouldLoop = true; 47 | resolve(''); 48 | } 49 | }); 50 | } 51 | } 52 | } 53 | Pokego.changePosition(); 54 | }).catch((err) => {console.log(err); }); 55 | }); 56 | } else { 57 | resolve('[p] Looping stalled to complete execution of task..'); 58 | } 59 | }).then((a) => {console.log(a);}); 60 | }, 5000); 61 | }); -------------------------------------------------------------------------------- /ExampleStats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var username = process.env.PGO_USERNAME || 'username'; 4 | var password = process.env.PGO_PASSWORD || 'password'; 5 | var provider = process.env.PGO_PROVIDER || 'google'; /* google OR ptc */ 6 | 7 | var location = { 8 | type: 'name', 9 | name: process.env.PGO_LOCATION || 'Time Square' 10 | 11 | // type: 'coords', 12 | // coords: { 13 | // latitude: 40.758896, 14 | // longitude: -73.985130, 15 | // altitude: 10 16 | // } 17 | 18 | }; 19 | 20 | var Pokego = require('./pokego.js'); 21 | var util = require('util'); 22 | util.inspect(console, true); 23 | 24 | Pokego.init(username, password, location, provider).then((profile) => { 25 | Pokego.GetProfile().then((profile) => { 26 | return Pokego.formatPlayercard(profile); 27 | }).then((x) => { 28 | Pokego.getStats().then((stats) => { 29 | console.log(stats); 30 | }); 31 | }).catch((val) => { 32 | console.log(val); 33 | }); 34 | }); -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Arm4x 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PokeGo 2 | Pokemon GO api node.js library, still WIP
3 | Check `ExamplePromises.js`, `example.js`, and `exampleFort.js` for examples 4 | 5 | ## Installation & Usage: 6 | ```git 7 | $ git clone git@github.com:d-pollard/Pokemon-GO-node-api.git . 8 | ``` 9 | 10 | ```javascript 11 | var Pokeio = require('./poke.io.js'); // when using poke.io.js 12 | var Pokego = require('./pokego.js'); // when using pokego.js 13 | ``` 14 | Check [ExamplePromises.js](./ExamplePromises.js) for the result showed in the demo or check the documentation below. 15 | 16 | ## Demo: 17 | ![alt tag](http://cl.arm4x.net/poke2.png) 18 | 19 | ## Documentation: 20 | 21 | ### Pokeio.init(username, password, location, provider, callback) 22 | 23 | Initializes Pokeio with either pokemon trainer club credentials or google account. 24 | Accepts locations by name or coordinates 25 | 26 | **Parameters** 27 | * `username {String}` Your pokemon trainer club or google username 28 | * `password {String}` Your pokemon trainer club or google password 29 | * `location {Object}` location accepts a combination of type = 'name' & name or type = 'coords' & latitude, longitude, altitude 30 | * `type {String}` Must be one of ['name', 'coords'] 31 | * `name {String}` Address for lookup using the google maps api. 32 | * `coords {Object}` 33 | * `latitude {Number}` 34 | * `longitude {Number}` 35 | * `altitude {Number}` 36 | * `provider {String}` Must be one of ['ptc', 'google'] 37 | * `callback {Function(error)}` 38 | * `error {Error}` 39 | 40 | ### Pokeio.GetAccessToken(username, password, callback) 41 | 42 | Will save the access token to the Pokeio internal state. 43 | 44 | **Parameters** 45 | * `username {String}` Your pokemon trainer club username 46 | * `password {String}` Your pokemon trainer club password 47 | * `callback {Function(error, token)}` 48 | * `error {Error}` 49 | * `token {String}` 50 | 51 | ### Pokeio.GetApiEndpoint(callback) 52 | 53 | Will save the api endpoint to the Pokeio internal state. 54 | 55 | **Parameters** 56 | * `callback {Function(error, api_endpoint)}` 57 | * `error {Error}` 58 | * `api_endpoint {String}` 59 | 60 | ### Pokeio.GetProfile(callback) 61 | **Parameters** 62 | * `callback {Function(error, profile)}` 63 | * `error {Error}` 64 | * `profile {Object}` 65 | * `creation_time {Number}` 66 | * `username {String}` 67 | * `team {Number}` 68 | * `tutorial {Number/Boolean}` 69 | * `poke_storage {String}` 70 | * `item_storage {String}` 71 | * `daily_bonus {Object}` 72 | * `NextCollectTimestampMs {Number}` 73 | * `NextDefenderBonusCollectTimestampMs {Number}` 74 | * `currency {Object}` 75 | * `type {String}` 76 | * `amount {Number}` 77 | 78 | ### Pokeio.GetLocation(callback) 79 | Reads current latitude and longitude and returns a human readable address using the google maps api. 80 | 81 | **Parameters** 82 | * `callback {Function(error, formatted_address)}` 83 | * `error {Error}` 84 | * `formatted_address {String}` 85 | 86 | ### Pokeio.GetLocationCoords() 87 | **Returns** 88 | * `coordinates {Object}` 89 | * `latitude {Number}` 90 | * `longitude {Number}` 91 | * `altitude {Number}` 92 | 93 | ### Pokeio.SetLocation(location, callback) 94 | 95 | Will save cooridinates to the Pokeio internal state. 96 | Accepts raw coordinates or location name based on the type property. 97 | 98 | **Parameters** 99 | * `location {Object}` 100 | * `type {String}` One of ['name', 'coords'] 101 | * `name {String}` Address for lookup using the google maps api. 102 | * `latitude {Number}` 103 | * `longitude {Number}` 104 | * `altitude {Number}` 105 | * `callback {Function(error, coordinates)}` 106 | * `error {Error}` 107 | * `coordinates {Object}` 108 | * `latitude {Number}` 109 | * `longitude {Number}` 110 | * `altitude {Number}` 111 | 112 | ## Thanks to: 113 | Python demo: [tejado](https://github.com/tejado/pokemongo-api-demo)
114 | ES5 demo: [Armax](https://github.com/Armax/Pokemon-GO-node-api/)
115 | 116 | 117 | ## Contact me 118 | [@Derek](mailto:derek@heroesplay.com) 119 | Feel free to contact me for help or anything else 120 | -------------------------------------------------------------------------------- /auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var login_url = 'https://sso.pokemon.com/sso/login?service=https%3A%2F%2Fsso.pokemon.com%2Fsso%2Foauth2.0%2FcallbackAuthorize'; 4 | var login_oauth = 'https://sso.pokemon.com/sso/oauth2.0/accessToken'; 5 | 6 | // Google Parts 7 | var android_id = '9774d56d682e549c'; 8 | var oauth_service = 'audience:server:client_id:848232511240-7so421jotr2609rmqakceuu1luuq0ptb.apps.googleusercontent.com'; 9 | var app = 'com.nianticlabs.pokemongo'; 10 | var client_sig = '321187995bc7cdc2b5fc91b11a96e2baa8602c62'; 11 | 12 | module.exports = { 13 | PokemonClub: function (user, pass, self) { 14 | return new Promise(function(resolve, reject) { 15 | var options = { 16 | url: login_url, 17 | headers: { 18 | 'User-Agent': 'niantic' 19 | } 20 | }; 21 | 22 | self.request.get(options, function (err, response, body) { 23 | var data; 24 | 25 | if (err) { 26 | return reject(err); 27 | } 28 | try { 29 | data = JSON.parse(body); 30 | } catch(e) { 31 | return reject(e); 32 | } 33 | 34 | options = { 35 | url: login_url, 36 | form: { 37 | 'lt': data.lt, 38 | 'execution': data.execution, 39 | '_eventId': 'submit', 40 | 'username': user, 41 | 'password': pass 42 | }, 43 | headers: { 44 | 'User-Agent': 'niantic' 45 | } 46 | }; 47 | 48 | self.request.post(options, function (err, response, body) { 49 | //Parse body if any exists, callback with errors if any. 50 | if(err) { 51 | return reject(err); 52 | } 53 | 54 | if (body) { 55 | var parsedBody = JSON.parse(body); 56 | if (parsedBody.errors && parsedBody.errors.length !== 0) { 57 | return reject('Error logging in: ' + parsedBody.errors[0]); 58 | } 59 | } 60 | 61 | var ticket = response.headers['location'].split('ticket=')[1]; 62 | 63 | options = { 64 | url: login_oauth, 65 | form: { 66 | 'client_id': 'mobile-app_pokemon-go', 67 | 'redirect_uri': 'https://www.nianticlabs.com/pokemongo/error', 68 | 'client_secret': 'w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR', 69 | 'grant_type': 'refresh_token', 70 | 'code': ticket 71 | }, 72 | headers: { 73 | 'User-Agent': 'niantic' 74 | } 75 | }; 76 | 77 | self.request.post(options, function (err, response, body) { 78 | var token; 79 | 80 | if(err) { 81 | return reject(err); 82 | } 83 | 84 | // console.log(body); 85 | 86 | try { 87 | token = body.split('token=')[1]; 88 | token = token.split('&')[0]; 89 | } catch(e) { 90 | return reject('[x] Either the PTC servers are down OR your login information is wrong'); 91 | } 92 | 93 | var expiry = body.split('token=')[1].split('&expires=')[1]; 94 | 95 | if (!token) { 96 | return reject('Login failed'); 97 | } 98 | 99 | self.DebugPrint('[i] Session token: ' + token); 100 | return resolve([token, expiry]); 101 | }); 102 | 103 | }); 104 | 105 | }); 106 | }); 107 | }, 108 | GoogleAccount: function (user, pass, self) { 109 | return new Promise(function(resolve, reject) { 110 | self.google.login(user, pass, android_id, function (err, data) { 111 | if (data) { 112 | self.google.oauth(user, data.masterToken, data.androidId, oauth_service, app, client_sig, function (err, data) { 113 | if (err) { 114 | return reject(err); 115 | } 116 | return resolve([data.Auth, data.Expiry]); 117 | }); 118 | } 119 | else { 120 | return reject(err); 121 | } 122 | }); 123 | }); 124 | } 125 | }; 126 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var Pokeio = require('./poke.io.js'); 2 | var util = require('util'); 3 | util.inspect(console, true); 4 | 5 | var username = process.env.PGO_USERNAME || 'username'; 6 | var password = process.env.PGO_PASSWORD || 'password'; 7 | var provider = process.env.PGO_PROVIDER || 'google'; 8 | 9 | var location = { 10 | type: 'name', 11 | name: process.env.PGO_LOCATION || 'Time Square' 12 | 13 | // type: 'coords', 14 | // coords: { 15 | // latitude: 40.758896, 16 | // longitude: -73.985130, 17 | // altitude: 10 18 | // } 19 | 20 | }; 21 | 22 | Pokeio.init(username, password, location, provider, function(err) { 23 | 24 | console.log('[i] Current location: ' + Pokeio.playerInfo.locationName); 25 | 26 | console.log('[i] lat/long/alt: : ' + Pokeio.playerInfo.latitude + ' ' + Pokeio.playerInfo.longitude + ' ' + Pokeio.playerInfo.altitude); 27 | 28 | Pokeio.GetProfile(function(err, profile) { 29 | if (err) throw err; 30 | 31 | console.log('[i] Username: ' + profile.username); 32 | console.log('[i] Poke Storage: ' + profile.poke_storage); 33 | console.log('[i] Item Storage: ' + profile.item_storage); 34 | 35 | var poke = 0; 36 | if (profile.currency[0].amount) { 37 | poke = profile.currency[0].amount; 38 | } 39 | 40 | console.log('[i] Pokecoin: ' + poke); 41 | console.log('[i] Stardust: ' + profile.currency[1].amount); 42 | 43 | setInterval(function() { 44 | // This will let you know the heartbeat is pumping.. 45 | console.log('[o] pump...'); 46 | Pokeio.Heartbeat(function(a,hb) { 47 | if(a !== null) { 48 | console.log('There appeared to be an error...'); 49 | } else { 50 | for (var i = hb.cells.length - 1; i >= 0; i--) { 51 | 52 | if(hb.cells[i].WildPokemon[0]) { 53 | for (var x = hb.cells[i].WildPokemon.length - 1; x >= 0; x--) { 54 | var currentPokemon = hb.cells[i].WildPokemon[x]; 55 | var iPokedex = Pokeio.pokemonlist[parseInt(currentPokemon.pokemon.PokemonId)-1]; 56 | Pokeio.EncounterPokemon(currentPokemon, function(suc, dat) { 57 | console.log('Encountering pokemon ' + iPokedex.name + '...'); 58 | Pokeio.CatchPokemon(currentPokemon, 2, function(xsuc, xdat) { 59 | var status = ['Unexpected error', 'Successful catch', 'Catch Escape', 'Catch Flee', 'Missed Catch']; 60 | console.log(status[xdat.Status]); 61 | Pokeio.changePosition(); 62 | }); 63 | }); 64 | } 65 | } 66 | } 67 | // console.log(util.inspect(hb, showHidden=false, depth=10, colorize=true)); 68 | } 69 | }); 70 | }, 5000); 71 | 72 | }); 73 | 74 | }); -------------------------------------------------------------------------------- /exampleFort.js: -------------------------------------------------------------------------------- 1 | var Pokeio = require('./poke.io.js'); 2 | var util = require('util'); 3 | util.inspect(console, true); 4 | 5 | var username = process.env.PGO_USERNAME || 'username'; 6 | var password = process.env.PGO_PASSWORD || 'password'; 7 | var provider = process.env.PGO_PROVIDER || 'google'; 8 | 9 | var location = { 10 | type: 'name', 11 | name: process.env.PGO_LOCATION || 'Time Square' 12 | 13 | // type: 'coords', 14 | // coords: { 15 | // latitude: 40.758896, 16 | // longitude: -73.985130, 17 | // altitude: 10 18 | // } 19 | 20 | }; 21 | 22 | Pokeio.init(username, password, location, provider, function(err) { 23 | 24 | console.log('[i] Current location: ' + Pokeio.playerInfo.locationName); 25 | 26 | console.log('[i] lat/long/alt: : ' + Pokeio.playerInfo.latitude + ' ' + Pokeio.playerInfo.longitude + ' ' + Pokeio.playerInfo.altitude); 27 | 28 | Pokeio.GetProfile(function(err, profile) { 29 | if (err) throw err; 30 | 31 | console.log('[i] Username: ' + profile.username); 32 | console.log('[i] Poke Storage: ' + profile.poke_storage); 33 | console.log('[i] Item Storage: ' + profile.item_storage); 34 | 35 | var poke = 0; 36 | if (profile.currency[0].amount) { 37 | poke = profile.currency[0].amount; 38 | } 39 | 40 | console.log('[i] Pokecoin: ' + poke); 41 | console.log('[i] Stardust: ' + profile.currency[1].amount); 42 | 43 | var fortArr = []; 44 | 45 | setInterval(function() { 46 | // This lets you know the heartbeat is pumping 47 | console.log('[o] pump...'); 48 | Pokeio.Heartbeat(function(a,hb) { 49 | if(a !== null) { 50 | console.log('There appeared to be an error...'); 51 | } else { 52 | for (var i = hb.cells.length - 1; i >= 0; i--) { 53 | if(hb.cells[i].Fort) { 54 | var currentFortArr = hb.cells[i].Fort; 55 | for (var j = currentFortArr.length - 1; j >= 0; j--) { 56 | var currentFort = currentFortArr[j]; 57 | // console.log(currentFort); 58 | if(currentFort.FortType === 1) { 59 | // "Fort" is a pokestop 60 | if(fortArr.indexOf(currentFort.FortId) === -1) { 61 | fortArr.push(currentFort.FortId); 62 | Pokeio.GetFort(currentFort.FortId, currentFort.Latitude, currentFort.Longitude, function(a,b) { 63 | // NO_RESULT_SET = 0; SUCCESS = 1; OUT_OF_RANGE = 2; IN_COOLDOWN_PERIOD = 3; INVENTORY_FULL = 4; 64 | var resultSet = ['Unexpected Error','Successful collect','Out of range','Already collected','Inventory Full']; 65 | if(b.result === 2) { 66 | Pokeio.warpSpeed(currentFort.Latitude, currentFort.Longitude); 67 | Pokeio.Heartbeat(function(z,y) { 68 | Pokeio.GetFort(currentFort.FortId, currentFort.Latitude, currentFort.Longitude, function(a,b) { 69 | console.log('Stop status: ' + resultSet[b.result]); 70 | }); 71 | }); 72 | 73 | } else { 74 | console.log('Stop status: ' + resultSet[b.result]); 75 | } 76 | console.log(util.inspect(b, showHidden=false, depth=10, colorize=true)); 77 | }); 78 | } 79 | 80 | } else { 81 | // "Fort" is a gym 82 | if(fortArr.indexOf(currentFort.FortId) === -1) { 83 | fortArr.push(currentFort.FortId); 84 | console.log('.:Fort is a gym, pass:.'); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | }); 92 | }, 5000); 93 | 94 | }); 95 | 96 | }); -------------------------------------------------------------------------------- /exampleStats.js: -------------------------------------------------------------------------------- 1 | var Pokeio = require('./poke.io.js'); 2 | var util = require('util'); 3 | util.inspect(console, true); 4 | 5 | var username = process.env.PGO_USERNAME || 'username'; 6 | var password = process.env.PGO_PASSWORD || 'password'; 7 | var provider = process.env.PGO_PROVIDER || 'google'; 8 | 9 | var location = { 10 | type: 'name', 11 | name: process.env.PGO_LOCATION || 'Time Square' 12 | 13 | // type: 'coords', 14 | // coords: { 15 | // latitude: 40.758896, 16 | // longitude: -73.985130, 17 | // altitude: 10 18 | // } 19 | 20 | }; 21 | 22 | Pokeio.init(username, password, location, provider, function(err) { 23 | 24 | console.log('[i] Current location: ' + Pokeio.playerInfo.locationName); 25 | 26 | console.log('[i] lat/long/alt: : ' + Pokeio.playerInfo.latitude + ' ' + Pokeio.playerInfo.longitude + ' ' + Pokeio.playerInfo.altitude); 27 | 28 | Pokeio.GetProfile(function(err, profile) { 29 | if (err) throw err; 30 | 31 | console.log('[i] Username: ' + profile.username); 32 | console.log('[i] Poke Storage: ' + profile.poke_storage); 33 | console.log('[i] Item Storage: ' + profile.item_storage); 34 | 35 | var poke = 0; 36 | if (profile.currency[0].amount) { 37 | poke = profile.currency[0].amount; 38 | } 39 | 40 | console.log('[i] Pokecoin: ' + poke); 41 | console.log('[i] Stardust: ' + profile.currency[1].amount); 42 | 43 | Pokeio.GetStats(function(success, data) { 44 | if(success === null) { 45 | console.log(data); 46 | } else { 47 | console.log('[x] There appeared to be an error!'); 48 | } 49 | }); 50 | 51 | }); 52 | 53 | }); -------------------------------------------------------------------------------- /logins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var login_url = 'https://sso.pokemon.com/sso/login?service=https%3A%2F%2Fsso.pokemon.com%2Fsso%2Foauth2.0%2FcallbackAuthorize'; 4 | var login_oauth = 'https://sso.pokemon.com/sso/oauth2.0/accessToken'; 5 | 6 | // Google Parts 7 | var android_id = '9774d56d682e549c'; 8 | var oauth_service = 'audience:server:client_id:848232511240-7so421jotr2609rmqakceuu1luuq0ptb.apps.googleusercontent.com'; 9 | var app = 'com.nianticlabs.pokemongo'; 10 | var client_sig = '321187995bc7cdc2b5fc91b11a96e2baa8602c62'; 11 | 12 | module.exports = { 13 | PokemonClub: function (user, pass, self, callback) { 14 | var options = { 15 | url: login_url, 16 | headers: { 17 | 'User-Agent': 'niantic' 18 | } 19 | }; 20 | 21 | self.request.get(options, function (err, response, body) { 22 | var data; 23 | 24 | if (err) { 25 | return callback(err, null); 26 | } 27 | data = JSON.parse(body); 28 | 29 | options = { 30 | url: login_url, 31 | form: { 32 | 'lt': data.lt, 33 | 'execution': data.execution, 34 | '_eventId': 'submit', 35 | 'username': user, 36 | 'password': pass 37 | }, 38 | headers: { 39 | 'User-Agent': 'niantic' 40 | } 41 | }; 42 | 43 | self.request.post(options, function (err, response, body) { 44 | //Parse body if any exists, callback with errors if any. 45 | if(err) { 46 | return callback(err, null); 47 | } 48 | 49 | if (body) { 50 | var parsedBody = JSON.parse(body); 51 | if (parsedBody.errors && parsedBody.errors.length !== 0) { 52 | return callback(new Error('Error logging in: ' + parsedBody.errors[0]), null); 53 | } 54 | } 55 | 56 | var ticket = response.headers['location'].split('ticket=')[1]; 57 | 58 | options = { 59 | url: login_oauth, 60 | form: { 61 | 'client_id': 'mobile-app_pokemon-go', 62 | 'redirect_uri': 'https://www.nianticlabs.com/pokemongo/error', 63 | 'client_secret': 'w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR', 64 | 'grant_type': 'refresh_token', 65 | 'code': ticket 66 | }, 67 | headers: { 68 | 'User-Agent': 'niantic' 69 | } 70 | }; 71 | 72 | self.request.post(options, function (err, response, body) { 73 | var token; 74 | 75 | if(err) { 76 | return callback(err, null); 77 | } 78 | 79 | token = body.split('token=')[1]; 80 | token = token.split('&')[0]; 81 | 82 | var expiry = body.split('token=')[1].split('&expires=')[1]; 83 | 84 | if (!token) { 85 | return callback(new Error('Login failed'), null); 86 | } 87 | 88 | self.DebugPrint('[i] Session token: ' + token); 89 | callback(null, [token, expiry]); 90 | }); 91 | 92 | }); 93 | 94 | }); 95 | }, 96 | GoogleAccount: function (user, pass, self, callback) { 97 | self.google.login(user, pass, android_id, function (err, data) { 98 | if (data) { 99 | self.google.oauth(user, data.masterToken, data.androidId, oauth_service, app, client_sig, function (err, data) { 100 | if (err) { 101 | return callback(err, null); 102 | } 103 | callback(null, [data.Auth, data.Expiry]); 104 | }); 105 | } 106 | else { 107 | return callback(err, null); 108 | } 109 | }); 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pokemon-go-node-api", 3 | "version": "1.2.5", 4 | "description": "Pokemon GO node.js module", 5 | "author": "Derek M.F. P", 6 | "license": "MIT", 7 | "main": "poke.io.js", 8 | "dependencies": { 9 | "events": "^1.1.1", 10 | "geocoder": "^0.2.2", 11 | "gpsoauthnode": "^0.0.5", 12 | "istanbul": "^0.4.4", 13 | "protobufjs": "^5.0.1", 14 | "request": "^2.73.0", 15 | "s2geometry-node": "^1.3.0", 16 | "tape": "^4.6.0", 17 | "long": "^3.2.0", 18 | "node-pogo-signature": "https://github.com/SpencerSharkey/node-pogo-signature.git" 19 | }, 20 | "devDependencies": {}, 21 | "scripts": { 22 | "test": "istanbul cover node_modules/tape/bin/tape tests/**/*.js" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/d-pollard/Pokemon-GO-node-api" 27 | }, 28 | "keywords": [ 29 | "pokemon", 30 | "go", 31 | "api", 32 | "node", 33 | "pokemon-go", 34 | "niantic" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /poke.io.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const request = require('request'); 4 | const geocoder = require('geocoder'); 5 | const events = require('events'); 6 | const ProtoBuf = require('protobufjs'); 7 | const GoogleOAuth = require('gpsoauthnode'); 8 | const Long = require('long'); 9 | const ByteBuffer = require('bytebuffer'); 10 | const crypto = require('crypto'); 11 | const pogoSignature = require('node-pogo-signature'); 12 | 13 | const s2 = require('s2geometry-node'); 14 | const Logins = require('./logins'); 15 | const fs = require('fs'); 16 | const pokemonlist = JSON.parse(fs.readFileSync(__dirname + '/pokemons.json', 'utf8')); 17 | 18 | let builder = ProtoBuf.loadProtoFile('pokemon.proto'); 19 | if (builder === null) { 20 | builder = ProtoBuf.loadProtoFile(__dirname + '/pokemon.proto'); 21 | } 22 | 23 | const pokemonProto = builder.build(); 24 | const {RequestEnvelop, ResponseEnvelop} = pokemonProto; 25 | const Signature = pokemonProto.Signature; 26 | 27 | const EventEmitter = events.EventEmitter; 28 | 29 | const api_url = 'https://pgorelease.nianticlabs.com/plfe/rpc'; 30 | 31 | function GetCoords(self) { 32 | let {latitude, longitude} = self.playerInfo; 33 | return [latitude, longitude]; 34 | }; 35 | 36 | 37 | function getNeighbors(lat, lng) { 38 | var origin = new s2.S2CellId(new s2.S2LatLng(lat, lng)).parent(15); 39 | var walk = [origin.id()]; 40 | // 10 before and 10 after 41 | var next = origin.next(); 42 | var prev = origin.prev(); 43 | for (var i = 0; i < 10; i++) { 44 | // in range(10): 45 | walk.push(prev.id()); 46 | walk.push(next.id()); 47 | next = next.next(); 48 | prev = prev.prev(); 49 | } 50 | return walk; 51 | } 52 | 53 | function Pokeio() { 54 | var self = this; 55 | self.events = new EventEmitter(); 56 | self.j = request.jar(); 57 | self.request = request.defaults({jar: self.j}); 58 | 59 | self.google = new GoogleOAuth(); 60 | 61 | self.playerInfo = { 62 | accessToken: '', 63 | debug: true, 64 | latitude: 0, 65 | longitude: 0, 66 | altitude: 0, 67 | locationName: '', 68 | provider: '', 69 | apiEndpoint: '', 70 | tokenExpire: 0, 71 | initTime: 0 72 | }; 73 | 74 | self.myStats = false; 75 | 76 | self.DebugPrint = function (str) { 77 | if (self.playerInfo.debug === true) { 78 | //self.events.emit('debug',str) 79 | console.log(str); 80 | } 81 | }; 82 | 83 | self.pokemonlist = pokemonlist.pokemon; 84 | 85 | 86 | function api_req(api_endpoint, access_token, req, callback) { 87 | // Auth 88 | var authInfo = new RequestEnvelop.AuthInfo({ 89 | provider: self.playerInfo.provider, 90 | token: new RequestEnvelop.AuthInfo.JWT(access_token, 59) 91 | }); 92 | 93 | 94 | // console.log(req); 95 | 96 | var f_req = new RequestEnvelop({ 97 | unknown1: 2, 98 | rpc_id: 1469378659230941192, 99 | 100 | requests: req, 101 | 102 | latitude: self.playerInfo.latitude, 103 | longitude: self.playerInfo.longitude, 104 | altitude: self.playerInfo.altitude, 105 | 106 | unknown12: 989 107 | }); 108 | 109 | if (self.playerInfo.authTicket) { 110 | f_req.auth_ticket = self.playerInfo.authTicket; 111 | 112 | var lat = self.playerInfo.latitude, lng = self.playerInfo.longitude, alt = self.playerInfo.altitude; 113 | var authTicketEncoded = self.playerInfo.authTicket.encode().toBuffer(); 114 | 115 | var signature = new Signature({ 116 | location_hash1: pogoSignature.utils.hashLocation1(authTicketEncoded, lat, lng, alt).toNumber(), 117 | location_hash2: pogoSignature.utils.hashLocation2(lat, lng, alt).toNumber(), 118 | unk22: crypto.randomBytes(32), 119 | timestamp: new Date().getTime(), 120 | timestamp_since_start: (new Date().getTime() - self.playerInfo.initTime), 121 | }); 122 | 123 | if (!Array.isArray(req)) { 124 | req = [req]; 125 | } 126 | 127 | req.forEach(function(request) { 128 | var reqHash = pogoSignature.utils.hashRequest(authTicketEncoded, request.encode().toBuffer()).toString(); 129 | var hash = require('long').fromString(reqHash, true, 10); 130 | signature.request_hash.push(hash); 131 | }); 132 | 133 | var iv = crypto.randomBytes(32); 134 | 135 | pogoSignature.encrypt(signature.encode().toBuffer(), iv, function(err, signatureEnc) { 136 | f_req.unknown6 = new RequestEnvelop.Unknown6({ 137 | unknown1: 6, 138 | unknown2: new RequestEnvelop.Unknown6.Unknown2({ 139 | unknown1: signatureEnc 140 | }) 141 | }); 142 | compiledProtobuf(f_req); 143 | }); 144 | 145 | } else { 146 | f_req.auth = authInfo; 147 | compiledProtobuf(f_req); 148 | } 149 | 150 | function compiledProtobuf(protobuf) { 151 | //console.log(JSON.stringify(protobuf)) 152 | protobuf = f_req.encode().toBuffer(); 153 | 154 | var options = { 155 | url: api_endpoint, 156 | body: protobuf, 157 | encoding: null, 158 | headers: { 159 | 'User-Agent': 'Niantic App' 160 | } 161 | }; 162 | 163 | self.request.post(options, function (err, response, body) { 164 | if (err) { 165 | return callback(new Error('Error')); 166 | } 167 | 168 | if (response === undefined || body === undefined) { 169 | console.error('[!] RPC Server offline'); 170 | return callback(new Error('RPC Server offline')); 171 | } 172 | 173 | var f_ret; 174 | try { 175 | f_ret = ResponseEnvelop.decode(body); 176 | } catch (e) { 177 | if (e.decoded) { 178 | // Truncated 179 | console.warn(e); 180 | f_ret = e.decoded; // Decoded message with missing required fields 181 | } 182 | } 183 | 184 | if (f_ret) { 185 | if (f_ret.auth_ticket) { 186 | self.playerInfo.authTicket = f_ret.auth_ticket; 187 | } 188 | return callback(null, f_ret); 189 | } else { 190 | api_req(api_endpoint, access_token, req, callback); 191 | } 192 | }); 193 | } 194 | } 195 | 196 | self.init = function (username, password, location, provider, callback) { 197 | if (provider !== 'ptc' && provider !== 'google') { 198 | return callback(new Error('Invalid provider')); 199 | } 200 | 201 | self.playerInfo.initTime = new Date().getTime(); 202 | 203 | // set provider 204 | self.playerInfo.provider = provider; 205 | // Updating location 206 | self.SetLocation(location, function (err, loc) { 207 | if (err) { 208 | return callback(err); 209 | } 210 | // Getting access token 211 | self.GetAccessToken(username, password, function (err, token) { 212 | if (err) { 213 | return callback(err); 214 | } 215 | // Getting api endpoint 216 | self.GetApiEndpoint(function (err, api_endpoint) { 217 | if (err) { 218 | return callback(err); 219 | } 220 | callback(null); 221 | }); 222 | }); 223 | }); 224 | }; 225 | 226 | self.GetAccessToken = function (user, pass, callback) { 227 | self.DebugPrint('[i] Logging with user: ' + user); 228 | if (self.playerInfo.provider === 'ptc') { 229 | Logins.PokemonClub(user, pass, self, function (err, token) { 230 | if (err) { 231 | return callback(err); 232 | } 233 | 234 | self.playerInfo.accessToken = token; 235 | self.DebugPrint('[i] Received PTC access token!'); 236 | callback(null, token); 237 | }); 238 | } else { 239 | Logins.GoogleAccount(user, pass, self, function (err, token) { 240 | if (err) { 241 | return callback(err); 242 | } 243 | 244 | self.playerInfo.accessToken = token; 245 | self.DebugPrint('[i] Received Google access token!'); 246 | callback(null, token); 247 | }); 248 | } 249 | }; 250 | 251 | self.GetAccessToken = function (user, pass, callback) { 252 | self.DebugPrint('[i] Logging with user: ' + user); 253 | if (self.playerInfo.provider === 'ptc') { 254 | Logins.PokemonClub(user, pass, self, function (err, token) { 255 | if (err) { 256 | return callback(err); 257 | } 258 | 259 | self.playerInfo.accessToken = token[0]; 260 | self.playerInfo.tokenExpire = token[1]; 261 | self.DebugPrint('[i] Received PTC access token! {Expires: ' + token[1] + '}'); 262 | callback(null, token[0]); 263 | }); 264 | } else { 265 | Logins.GoogleAccount(user, pass, self, function (err, token) { 266 | if (err) { 267 | return callback(err); 268 | } 269 | 270 | self.playerInfo.accessToken = token[0]; 271 | self.playerInfo.tokenExpire = token[1]; 272 | self.DebugPrint('[i] Received Google access token! {Expires: ' + token[1] + '}'); 273 | callback(null, token[0]); 274 | }); 275 | } 276 | }; 277 | 278 | 279 | 280 | self.GetApiEndpoint = function (callback) { 281 | var req = [new RequestEnvelop.Requests(2), new RequestEnvelop.Requests(126), new RequestEnvelop.Requests(4), new RequestEnvelop.Requests(129), new RequestEnvelop.Requests(5)]; 282 | 283 | api_req(api_url, self.playerInfo.accessToken, req, function (err, f_ret) { 284 | if (err) { 285 | return callback(err); 286 | } 287 | var api_endpoint = 'https://' + f_ret.api_url + '/rpc'; 288 | self.playerInfo.apiEndpoint = api_endpoint; 289 | self.DebugPrint('[i] Received API Endpoint: ' + api_endpoint); 290 | return callback(null, api_endpoint); 291 | }); 292 | }; 293 | 294 | self.GetInventory = function(callback) { 295 | var req = new RequestEnvelop.Requests(4); 296 | 297 | api_req(self.playerInfo.apiEndpoint, self.playerInfo.accessToken, req, function(err, f_ret){ 298 | if(err){ 299 | return callback(err); 300 | } 301 | var inventory = ResponseEnvelop.GetInventoryResponse.decode(f_ret.payload[0]); 302 | return callback(null, inventory); 303 | }); 304 | }; 305 | 306 | self.GetProfile = function (callback) { 307 | var req = new RequestEnvelop.Requests(2); 308 | api_req(self.playerInfo.apiEndpoint, self.playerInfo.accessToken, req, function (err, f_ret) { 309 | if (err) { 310 | return callback(err); 311 | } 312 | 313 | var profile = ResponseEnvelop.ProfilePayload.decode(f_ret.payload[0]).profile 314 | 315 | if (profile.username) { 316 | self.DebugPrint('[i] Logged in!'); 317 | } 318 | callback(null, profile); 319 | }); 320 | }; 321 | 322 | self.GetStats = function(callback) { 323 | self.GetInventory(function(a,b) { 324 | if(a === null) { 325 | var inventory = b.inventory_delta.inventory_items; 326 | for (var i = inventory.length - 1; i >= 0; i--) { 327 | var x = inventory[i]; 328 | var stats = x.inventory_item_data.player_stats; 329 | if(stats !== null) { 330 | // console.log(stats); 331 | self.myStats = stats; 332 | callback(null, stats); 333 | return; 334 | } else { 335 | continue; 336 | } 337 | } 338 | } else { 339 | callback(a, null); 340 | } 341 | }); 342 | }; 343 | 344 | // IN DEVELPOMENT, YES WE KNOW IS NOT WORKING ATM 345 | self.Heartbeat = function (callback) { 346 | let {apiEndpoint, accessToken} = self.playerInfo; 347 | 348 | 349 | var nullbytes = new Array(21); 350 | nullbytes.fill(0); 351 | 352 | self.playerInfo.initTime = new Date().getTime(); 353 | 354 | // Generating walk data using s2 geometry 355 | var walk = getNeighbors(self.playerInfo.latitude, self.playerInfo.longitude).sort(function (a, b) { 356 | return a > b; 357 | }); 358 | 359 | // Creating MessageQuad for Requests type=106 360 | var walkData = new RequestEnvelop.MessageQuad({ 361 | 'f1': walk, 362 | 'f2': nullbytes, 363 | 'lat': self.playerInfo.latitude, 364 | 'long': self.playerInfo.longitude 365 | }); 366 | 367 | var req = [new RequestEnvelop.Requests(106, walkData.encode().toBuffer()), new RequestEnvelop.Requests(126), new RequestEnvelop.Requests(4, new RequestEnvelop.Unknown3(Date.now().toString()).encode().toBuffer()), new RequestEnvelop.Requests(129), new RequestEnvelop.Requests(5, new RequestEnvelop.Unknown3('54b359c97e46900f87211ef6e6dd0b7f2a3ea1f5').encode().toBuffer())]; 368 | 369 | api_req(apiEndpoint, accessToken, req, function (err, f_ret) { 370 | if (err) { 371 | return callback(err); 372 | } else if (!f_ret || !f_ret.payload || !f_ret.payload[0]) { 373 | return callback('No result'); 374 | } 375 | 376 | var heartbeat = ResponseEnvelop.HeartbeatPayload.decode(f_ret.payload[0]); 377 | callback(null, heartbeat); 378 | }); 379 | }; 380 | 381 | self.GetLocation = function (callback) { 382 | geocoder.reverseGeocode(...GetCoords(self), function (err, data) { 383 | if (data.status === 'ZERO_RESULTS') { 384 | return callback(new Error('location not found')); 385 | } 386 | 387 | callback(null, data.results[0].formatted_address); 388 | }); 389 | }; 390 | 391 | self.CatchPokemon = function (mapPokemon, pokeball, callback) { 392 | console.log('Attempting to catch now...'); 393 | let {apiEndpoint, accessToken} = self.playerInfo; 394 | var catchPokemon = new RequestEnvelop.CatchPokemonMessage({ 395 | 'encounter_id': mapPokemon.EncounterId, 396 | 'pokeball': pokeball, 397 | 'normalized_reticle_size': 1.950, 398 | 'spawnpoint_id': mapPokemon.SpawnPointId, 399 | 'hit_pokemon': true, 400 | 'spin_modifier': 1, 401 | 'normalized_hit_position': 1 402 | }); 403 | 404 | // console.log(catchPokemon); 405 | 406 | var req = new RequestEnvelop.Requests(103, catchPokemon.encode().toBuffer()); 407 | 408 | api_req(apiEndpoint, accessToken, req, function (err, f_ret) { 409 | if (err) { 410 | return callback(err); 411 | } 412 | else if (!f_ret || !f_ret.payload || !f_ret.payload[0]) { 413 | return callback('No result'); 414 | } 415 | 416 | var catchPokemonResponse = ResponseEnvelop.CatchPokemonResponse.decode(f_ret.payload[0]); 417 | callback(null, catchPokemonResponse) 418 | }); 419 | 420 | }; 421 | 422 | self.EncounterPokemon = function (catchablePokemon, callback) { 423 | // console.log(catchablePokemon); 424 | let {apiEndpoint, accessToken, latitude, longitude} = self.playerInfo; 425 | 426 | var encounterPokemon = new RequestEnvelop.EncounterMessage({ 427 | 'encounter_id': catchablePokemon.EncounterId, 428 | 'spawnpoint_id': catchablePokemon.SpawnPointId, 429 | 'player_latitude': latitude, 430 | 'player_longitude': longitude 431 | }); 432 | 433 | // console.log(encounterPokemon); 434 | 435 | var req = new RequestEnvelop.Requests(102, encounterPokemon.encode().toBuffer()); 436 | 437 | api_req(apiEndpoint, accessToken, req, function (err, f_ret) { 438 | if (err) { 439 | return callback(err); 440 | } 441 | else if (!f_ret || !f_ret.payload || !f_ret.payload[0]) { 442 | return callback('No result'); 443 | } 444 | 445 | var catchPokemonResponse = ResponseEnvelop.EncounterResponse.decode(f_ret.payload[0]); 446 | 447 | callback(null, catchPokemonResponse); 448 | }); 449 | 450 | }; 451 | 452 | self.GetLocationCoords = function () { 453 | let {latitude, longitude, altitude} = self.playerInfo; 454 | return {latitude, longitude, altitude}; 455 | }; 456 | 457 | self.SetLocation = function (location, callback) { 458 | if (location.type !== 'name' && location.type !== 'coords') { 459 | return callback(new Error('Invalid location type')); 460 | } 461 | 462 | if (location.type === 'name') { 463 | if (!location.name) { 464 | return callback(new Error('You should add a location name')); 465 | } 466 | var locationName = location.name; 467 | geocoder.geocode(locationName, function (err, data) { 468 | if (err || data.status === 'ZERO_RESULTS') { 469 | return callback(new Error('location not found')); 470 | } 471 | 472 | let {lat, lng} = data.results[0].geometry.location; 473 | 474 | self.playerInfo.latitude = lat; 475 | self.playerInfo.longitude = lng; 476 | self.playerInfo.locationName = locationName; 477 | 478 | callback(null, self.GetLocationCoords()); 479 | }); 480 | } else if (location.type === 'coords') { 481 | if (!location.coords) { 482 | return callback(new Error('Coords object missing')); 483 | } 484 | 485 | self.playerInfo.latitude = location.coords.latitude || self.playerInfo.latitude; 486 | self.playerInfo.longitude = location.coords.longitude || self.playerInfo.longitude; 487 | self.playerInfo.altitude = location.coords.altitude || self.playerInfo.altitude; 488 | 489 | geocoder.reverseGeocode(...GetCoords(self), function (err, data) { 490 | if (data.status !== 'ZERO_RESULTS' && data.results && data.results[0]) { 491 | self.playerInfo.locationName = data.results[0].formatted_address; 492 | } 493 | 494 | callback(null, self.GetLocationCoords()); 495 | }); 496 | } 497 | }; 498 | 499 | self.changePosition = function () { 500 | self.playerInfo.longitude = self.playerInfo.longitude + 0.000055; 501 | self.playerInfo.latitude = self.playerInfo.latitude + 0.000055; 502 | return true; 503 | }; 504 | 505 | self.hatchEggs = function(cb) { 506 | self.changePosition(); 507 | self.Heartbeat(cb); 508 | }; 509 | 510 | self.GetFort = function(fortid, fortlat, fortlong, callback) { 511 | var FortMessage = new RequestEnvelop.FortSearchMessage({ 512 | 'fort_id': fortid, 513 | 'player_latitude': fortlat, 514 | 'player_longitude': fortlong, 515 | 'fort_latitude': fortlat, 516 | 'fort_longitude': fortlong 517 | }); 518 | 519 | // console.log(FortMessage); 520 | 521 | var req = new RequestEnvelop.Requests(101, FortMessage.encode().toBuffer()); 522 | 523 | api_req(self.playerInfo.apiEndpoint, self.playerInfo.accessToken, req, function (err, f_ret) { 524 | if (err) { 525 | return callback(err); 526 | } else if (!f_ret || !f_ret.payload || !f_ret.payload[0]) { 527 | return callback('No result'); 528 | } 529 | 530 | var FortSearchResponse = ResponseEnvelop.FortSearchResponse.decode(f_ret.payload[0]); 531 | callback(null, FortSearchResponse); 532 | }); 533 | }; 534 | 535 | self.warpSpeed = function(lat,long) { 536 | self.playerInfo.latitude = lat; 537 | self.playerInfo.longitude = long; 538 | return true; 539 | }; 540 | } 541 | 542 | module.exports = new Pokeio(); 543 | module.exports.Pokeio = Pokeio; 544 | module.exports.Pokego = require('./pokego.js'); 545 | -------------------------------------------------------------------------------- /pokego.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const request = require('request'); 4 | const geocoder = require('geocoder'); 5 | const events = require('events'); 6 | const ProtoBuf = require('protobufjs'); 7 | const GoogleOAuth = require('gpsoauthnode'); 8 | const Long = require('long'); 9 | const ByteBuffer = require('bytebuffer'); 10 | 11 | const s2 = require('s2geometry-node'); 12 | const Logins = require('./auth'); 13 | const fs = require('fs'); 14 | const pokemonlist = JSON.parse(fs.readFileSync(__dirname + '/pokemons.json', 'utf8')); 15 | 16 | let builder = ProtoBuf.loadProtoFile('pokemon.proto'); 17 | if (builder === null) { 18 | builder = ProtoBuf.loadProtoFile(__dirname + '/pokemon.proto'); 19 | } 20 | 21 | const pokemonProto = builder.build(); 22 | const {RequestEnvelop, ResponseEnvelop} = pokemonProto; 23 | 24 | const EventEmitter = events.EventEmitter; 25 | 26 | const api_url = 'https://pgorelease.nianticlabs.com/plfe/rpc'; 27 | 28 | function GetCoords(self) { 29 | let {latitude, longitude} = self.playerInfo; 30 | return [latitude, longitude]; 31 | }; 32 | 33 | 34 | function getNeighbors(lat, lng) { 35 | var origin = new s2.S2CellId(new s2.S2LatLng(lat, lng)).parent(15); 36 | var walk = [origin.id()]; 37 | // 10 before and 10 after 38 | var next = origin.next(); 39 | var prev = origin.prev(); 40 | for (var i = 0; i < 10; i++) { 41 | // in range(10): 42 | walk.push(prev.id()); 43 | walk.push(next.id()); 44 | next = next.next(); 45 | prev = prev.prev(); 46 | } 47 | return walk; 48 | } 49 | 50 | function Pokego() { 51 | var self = this; 52 | self.events = new EventEmitter(); 53 | self.j = request.jar(); 54 | self.request = request.defaults({jar: self.j}); 55 | 56 | self.google = new GoogleOAuth(); 57 | 58 | self.playerInfo = { 59 | accessToken: '', 60 | debug: true, 61 | latitude: 0, 62 | longitude: 0, 63 | altitude: 0, 64 | locationName: '', 65 | provider: '', 66 | apiEndpoint: '', 67 | tokenExpire: 0 68 | }; 69 | 70 | self.myStats = false; 71 | 72 | self.DebugPrint = function (str) { 73 | if (self.playerInfo.debug === true) { 74 | //self.events.emit('debug',str) 75 | console.log(str); 76 | } 77 | }; 78 | 79 | self.pokemonlist = pokemonlist.pokemon; 80 | 81 | function api_req(api_endpoint, access_token, req) { 82 | // Auth 83 | var auth = new RequestEnvelop.AuthInfo({ 84 | provider: self.playerInfo.provider, 85 | token: new RequestEnvelop.AuthInfo.JWT(access_token, 59) 86 | }); 87 | 88 | var f_req = new RequestEnvelop({ 89 | unknown1: 2, 90 | rpc_id: 1469378659230941192, 91 | 92 | requests: req, 93 | 94 | latitude: self.playerInfo.latitude, 95 | longitude: self.playerInfo.longitude, 96 | altitude: self.playerInfo.altitude, 97 | 98 | auth: auth, 99 | unknown12: 989 100 | }); 101 | 102 | var protobuf = f_req.encode().toBuffer(); 103 | 104 | var options = { 105 | url: api_endpoint, 106 | body: protobuf, 107 | encoding: null, 108 | headers: { 109 | 'User-Agent': 'Niantic App' 110 | } 111 | }; 112 | 113 | return new Promise(function(resolve, reject) { 114 | 115 | self.request.post(options, function (err, response, body) { 116 | if (response === undefined || body === undefined) { 117 | console.error('[!] RPC Server offline'); 118 | return reject('Error'); 119 | } 120 | 121 | try { 122 | var f_ret = ResponseEnvelop.decode(body); 123 | } catch (e) { 124 | if (e.decoded) { // Truncated 125 | console.warn(e); 126 | f_ret = e.decoded; // Decoded message with missing required fields 127 | resolve(f_ret); 128 | } 129 | } 130 | if (f_ret) { 131 | return resolve(f_ret); 132 | } 133 | else { 134 | api_req(api_endpoint, access_token, req) 135 | } 136 | }); 137 | }); 138 | } 139 | 140 | 141 | self.init = function(username, password, location, provider) { 142 | console.log('..:: Initializing the API ::..'); 143 | return new Promise(function(resolve, reject) { 144 | if(provider !== 'ptc' && provider !== 'google') { 145 | return reject('Provider is not supported'); 146 | } else { 147 | self.playerInfo.provider = provider; 148 | self.SetLocation(location).then((val) => { 149 | self.GetAccessToken(username, password).then((val) => { 150 | self.GetApiEndpoint().then((val) => { 151 | return resolve(val); 152 | }) 153 | .catch((err) => { 154 | console.log(err); 155 | }); 156 | }) 157 | .catch((err) => { 158 | console.log(err); 159 | }); 160 | }) 161 | .catch((err) => { 162 | console.log(err); 163 | }); 164 | } 165 | }); 166 | }; 167 | 168 | self.GetAccessToken = function (user, pass, callback) { 169 | return new Promise(function(resolve, reject) { 170 | self.DebugPrint('[i] Logging with user: ' + user); 171 | if(self.playerInfo.provider === 'ptc') { 172 | Logins.PokemonClub(user, pass, self).then((token) => { 173 | self.playerInfo.accessToken = token[0]; 174 | self.playerInfo.tokenExpire = token[1]; 175 | self.DebugPrint('[i] Received PTC access token! { Expires: ' + token[1] + ' }'); 176 | return resolve(token[0]); 177 | }).catch((err) => { 178 | return reject('[x] There was an error logging in. Please try again.') 179 | }); 180 | } else { 181 | Logins.GoogleAccount(user, pass, self).then((token) => { 182 | self.playerInfo.accessToken = token[0]; 183 | self.playerInfo.tokenExpire = token[1]; 184 | self.DebugPrint('[i] Received Google access token! {Expires: ' + token[1] + '}'); 185 | return resolve(token[0]); 186 | }); 187 | } 188 | }); 189 | }; 190 | 191 | 192 | self.GetApiEndpoint = function () { 193 | return new Promise(function(resolve, reject) { 194 | var req = [ 195 | new RequestEnvelop.Requests(2), 196 | new RequestEnvelop.Requests(126), 197 | new RequestEnvelop.Requests(4), 198 | new RequestEnvelop.Requests(129), 199 | new RequestEnvelop.Requests(5) 200 | ]; 201 | api_req(api_url, self.playerInfo.accessToken, req).then((f_ret) => { 202 | var apiEndpoint = `https://${f_ret.api_url}/rpc`; 203 | if(apiEndpoint === 'https://null/rpc') { 204 | return reject('[x] There seams to be an issue. Please try again.') 205 | } 206 | self.playerInfo.apiEndpoint = apiEndpoint; 207 | self.DebugPrint('[i] Received API Endpoint: ' + apiEndpoint); 208 | return resolve(apiEndpoint); 209 | }); 210 | }); 211 | }; 212 | 213 | self.GetInventory = function() { 214 | return new Promise(function(resolve, reject) { 215 | var req = new RequestEnvelop.Requests(4); 216 | 217 | api_req(self.playerInfo.apiEndpoint, self.playerInfo.accessToken, req).then((f_ret) => { 218 | var inventory = ResponseEnvelop.GetInventoryResponse.decode(f_ret.payload[0]); 219 | return resolve(inventory); 220 | }).catch((val) => { 221 | console.log(val); 222 | }); 223 | }); 224 | }; 225 | 226 | self.displayInventory = function(data) { 227 | //console.log(data); 228 | var inventory = data.inventory_delta.inventory_items; 229 | // console.log(inventory.length); 230 | for (var i = inventory.length - 1; i >= 0; i--) { 231 | var x = inventory[i]; 232 | var pokemon = x.inventory_item_data.pokemon; 233 | var item = x.inventory_item_data.item; 234 | var candy = x.inventory_item_data.pokemon_family; 235 | var stats = x.inventory_item_data.player_stats; 236 | if(pokemon !== null && pokemon.pokemon_id !== null) { 237 | // item is a pokemon 238 | var pkmn = self.pokemonlist[parseInt(pokemon.pokemon_id)-1]; 239 | console.log(pkmn.name + ' -> ' + pokemon.cp + 'cp '); 240 | } else if(item !== null && item.item !== null) { 241 | // item is an actual item 242 | console.log(self.whatItem(item.item) + ' -> ' + (item.count || 0) + ' count'); 243 | } else if(candy !== null && candy.family_id !== null){ 244 | // item is a candy 245 | console.log(self.whatFamily(candy.family_id) + ' -> ' + (candy.candy || 0) + ' count'); 246 | } else if(pokemon !== null && pokemon.is_egg === true) { 247 | // item *might be* an egg 248 | console.log('An egg'); 249 | } else if(stats !== null) { 250 | console.log(stats); 251 | } else { 252 | // console.log(x); 253 | } 254 | } 255 | }; 256 | 257 | self.getStats = function() { 258 | return new Promise(function(resolve, reject) { 259 | self.GetInventory().then((data) => { 260 | var inventory = data.inventory_delta.inventory_items; 261 | for (var i = inventory.length - 1; i >= 0; i--) { 262 | var x = inventory[i]; 263 | var stats = x.inventory_item_data.player_stats; 264 | if(stats !== null) { 265 | // console.log(stats); 266 | self.myStats = stats; 267 | return resolve(stats); 268 | } else { 269 | continue; 270 | } 271 | } 272 | }); 273 | }); 274 | }; 275 | 276 | self.GetProfile = function () { 277 | return new Promise(function(resolve, reject) { 278 | var req = new RequestEnvelop.Requests(2); 279 | api_req(self.playerInfo.apiEndpoint, self.playerInfo.accessToken, req).then((f_ret) => { 280 | var profile = ResponseEnvelop.ProfilePayload.decode(f_ret.payload[0]).profile 281 | if (profile.username) { 282 | self.DebugPrint('[i] Player Profile'); 283 | } 284 | return resolve(profile); 285 | }).catch((err) => { 286 | console.log(err); 287 | }); 288 | }); 289 | }; 290 | 291 | // IN DEVELPOMENT, YES WE KNOW IS NOT WORKING ATM 292 | self.Heartbeat = function () { 293 | let { apiEndpoint, accessToken } = self.playerInfo; 294 | 295 | 296 | var nullbytes = new Array(21); 297 | nullbytes.fill(0); 298 | 299 | // Generating walk data using s2 geometry 300 | var walk = getNeighbors(self.playerInfo.latitude, self.playerInfo.longitude).sort(function (a, b) { 301 | return a > b; 302 | }); 303 | 304 | // Creating MessageQuad for Requests type=106 305 | var walkData = new RequestEnvelop.MessageQuad({ 306 | 'f1': walk, 307 | 'f2': nullbytes, 308 | 'lat': self.playerInfo.latitude, 309 | 'long': self.playerInfo.longitude 310 | }); 311 | 312 | var req = [new RequestEnvelop.Requests(106, walkData.encode().toBuffer()), new RequestEnvelop.Requests(126), new RequestEnvelop.Requests(4, new RequestEnvelop.Unknown3(Date.now().toString()).encode().toBuffer()), new RequestEnvelop.Requests(129), new RequestEnvelop.Requests(5, new RequestEnvelop.Unknown3('05daf51635c82611d1aac95c0b051d3ec088a930').encode().toBuffer())]; 313 | return new Promise(function(resolve, reject) { 314 | api_req(apiEndpoint, accessToken, req).then((data) => { 315 | if (!data || !data.payload || !data.payload[0]) { 316 | return reject('No data. API Failure.'); 317 | } 318 | var heartbeat = ResponseEnvelop.HeartbeatPayload.decode(data.payload[0]); 319 | return resolve(heartbeat); 320 | }).catch((err) => { 321 | console.log(err); 322 | }); 323 | }).catch((err) => { 324 | console.log(err); 325 | }); 326 | }; 327 | 328 | self.GetLocation = function () { 329 | geocoder.reverseGeocode(...GetCoords(self), function (err, data) { 330 | if (data.status === 'ZERO_RESULTS') { 331 | return callback(new Error('location not found')); 332 | } 333 | callback(null, data.results[0].formatted_address); 334 | }); 335 | }; 336 | 337 | self.fireAndForgetCatch = function(catchablePokemon, name, cnt, ball) { 338 | if(ball === 'POKE_BALL') { ball = 1; } else if(ball === 'GREAT_BALL') { ball = 2; } else { ball = 3; } 339 | return new Promise(function(resolve, reject) { 340 | self.EncounterPokemon(catchablePokemon).then((data) => { 341 | self.CatchPokemon(data.WildPokemon, ball).then((final) => { 342 | var status = ['Unexpected error', 'Successful catch', 'Catch Escape', 'Catch Flee', 'Missed Catch']; 343 | if(final.Status == null) { 344 | console.log('[x] Error: You have no more of that ball left to use!'); 345 | } else { 346 | console.log('[s] Catch status for ' + name + ': ' + status[parseInt(final.Status)]); 347 | } 348 | return resolve(cnt); 349 | }).catch((err) => { 350 | console.log(err); 351 | }); 352 | }).catch((err) => { 353 | console.log(err); 354 | }); 355 | }); 356 | }; 357 | 358 | self.CatchPokemon = function (mapPokemon, pokeball) { 359 | console.log('Attempting to catch now...'); 360 | // console.log(mapPokemon); 361 | let {apiEndpoint, accessToken} = self.playerInfo; 362 | var catchPokemon = new RequestEnvelop.CatchPokemonMessage({ 363 | 'encounter_id': mapPokemon.EncounterId, 364 | 'pokeball': pokeball, 365 | 'normalized_reticle_size': 1.950, 366 | 'spawnpoint_id': mapPokemon.SpawnPointId, 367 | 'hit_pokemon': true, 368 | 'spin_modifier': 1, 369 | 'normalized_hit_position': 1 370 | }); 371 | 372 | var req = new RequestEnvelop.Requests(103, catchPokemon.encode().toBuffer()); 373 | return new Promise(function(resolve, reject) { 374 | api_req(apiEndpoint, accessToken, req).then((data) => { 375 | if (!data || !data.payload || !data.payload[0]) { 376 | return reject(data); 377 | } 378 | var catchPokemonResponse = ResponseEnvelop.CatchPokemonResponse.decode(data.payload[0]); 379 | return resolve(catchPokemonResponse); 380 | }); 381 | }); 382 | }; 383 | 384 | self.EncounterPokemon = function (catchablePokemon) { 385 | // console.log(catchablePokemon); 386 | let {apiEndpoint, accessToken, latitude, longitude} = self.playerInfo; 387 | 388 | var encounterPokemon = new RequestEnvelop.EncounterMessage({ 389 | 'encounter_id': catchablePokemon.EncounterId, 390 | 'spawnpoint_id': catchablePokemon.SpawnPointId, 391 | 'player_latitude': latitude, 392 | 'player_longitude': longitude 393 | }); 394 | 395 | // console.log(encounterPokemon); 396 | 397 | var req = new RequestEnvelop.Requests(102, encounterPokemon.encode().toBuffer()); 398 | 399 | return new Promise(function(resolve, reject) { 400 | 401 | api_req(apiEndpoint, accessToken, req).then((data) => { 402 | if (!data || !data.payload || !data.payload[0]) { 403 | return reject(data); 404 | } 405 | var catchPokemonResponse = ResponseEnvelop.EncounterResponse.decode(data.payload[0]); 406 | // console.log(catchPokemonResponse); 407 | return resolve(catchPokemonResponse); 408 | }); 409 | }); 410 | }; 411 | 412 | self.GetLocationCoords = function () { 413 | let {latitude, longitude, altitude} = self.playerInfo; 414 | return {latitude, longitude, altitude}; 415 | }; 416 | 417 | self.setCoords = function(lt,ln) { 418 | self.playerInfo.latitude = lt; 419 | self.playerInfo.longitude = ln; 420 | return true; 421 | }; 422 | 423 | self.SetLocation = function (location) { 424 | return new Promise(function(resolve, reject) { 425 | if (location.type !== 'name' && location.type !== 'coords') { 426 | return reject('You need to add a location name OR coordinates. API Failure.'); 427 | } else { 428 | if (location.type === 'name') { 429 | if(location.name === 'name' || location.name === '') { 430 | return reject('You need to add a location name OR coordinates. API Failure.'); 431 | } else { 432 | let locationName = location.name; 433 | geocoder.geocode(locationName, function(err, data) { 434 | if(err || data.status === 'ZERO_RESULTS') { 435 | return reject('Location not found. API Failure.'); 436 | } else { 437 | let {lat, lng} = data.results[0].geometry.location; 438 | self.setCoords(lat, lng); 439 | self.playerInfo.locationName = locationName; 440 | 441 | return resolve(self.GetLocationCoords()); 442 | } 443 | }); 444 | } 445 | } else if(location.type === 'coords') { 446 | if(!location.coords) { 447 | return reject('Coordinates are missing. API Failure.'); 448 | } else { 449 | self.playerInfo.latitude = location.coords.latitude || self.playerInfo.latitude; 450 | self.playerInfo.longitude = location.coords.longitude || self.playerInfo.longitude; 451 | self.playerInfo.altitude = location.coords.altitude || self.playerInfo.altitude; 452 | 453 | geocoder.reverseGeocode(...GetCoords(self), function(err, data) { 454 | if (data.status !== 'ZERO_RESULTS' && data.results && data.results[0]) { 455 | self.playerInfo.locationName = data.results[0].formatted_address; 456 | } 457 | return resolve(self.GetLocationCoords()); 458 | }); 459 | } 460 | 461 | } else { 462 | return reject('No data passed. API failure.') 463 | } 464 | } 465 | }); 466 | }; 467 | 468 | self.changePosition = function () { 469 | self.playerInfo.longitude = self.playerInfo.longitude + 0.000055; 470 | self.playerInfo.latitude = self.playerInfo.latitude + 0.000055; 471 | return true; 472 | }; 473 | 474 | self.hatchEggs = function(cb) { 475 | self.changePosition(); 476 | self.Heartbeat(cb); 477 | }; 478 | 479 | self.GetFort = function(fortid, fortlat, fortlong, callback) { 480 | var FortMessage = new RequestEnvelop.FortSearchMessage({ 481 | 'fort_id': fortid, 482 | 'player_latitude': fortlat, 483 | 'player_longitude': fortlong, 484 | 'fort_latitude': fortlat, 485 | 'fort_longitude': fortlong 486 | }); 487 | 488 | var req = new RequestEnvelop.Requests(101, FortMessage.encode().toBuffer()); 489 | return new Promise(function(resolve, reject) { 490 | api_req(self.playerInfo.apiEndpoint, self.playerInfo.accessToken, req).then((err, f_ret) => { 491 | if (err) { 492 | return reject(err); 493 | } else if (!f_ret || !f_ret.payload || !f_ret.payload[0]) { 494 | return reject('No result'); 495 | } 496 | 497 | var FortSearchResponse = ResponseEnvelop.FortSearchResponse.decode(f_ret.payload[0]); 498 | return resolve(FortSearchResponse); 499 | }); 500 | }); 501 | }; 502 | 503 | self.warpSpeed = function(lat,long) { 504 | self.playerInfo.latitude = lat; 505 | self.playerInfo.longitude = long; 506 | return true; 507 | }; 508 | 509 | self.formatPlayercard = function(profile) { 510 | let team = ['Nuetral','Mystic','Valor','Instinct']; 511 | console.log('[o] -> Player: ' + profile.username); 512 | console.log('[o] -> Team: ' + team[profile.team]); 513 | console.log('[o] -> Created: ' + self.timeConverter(parseInt(profile.creation_time))); 514 | console.log('[o] -> Poke Storage: ' + profile.poke_storage); 515 | console.log('[o] -> Item Storage: ' + profile.item_storage); 516 | console.log('[o] -> Poke Coin: ' + profile.currency[0].amount); 517 | console.log('[o] -> Star Dust: ' + profile.currency[1].amount); 518 | console.log('[o] -> location lat:' + self.playerInfo.latitude + ' lng: ' + self.playerInfo.longitude); 519 | return true; 520 | }; 521 | 522 | 523 | 524 | self.timeConverter = function(x){ 525 | var a = new Date(x); 526 | var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; 527 | var year = a.getFullYear(); 528 | var month = months[a.getMonth()]; 529 | var date = a.getDate(); 530 | var hour = a.getHours(); 531 | var min = a.getMinutes(); 532 | var sec = a.getSeconds(); 533 | var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ; 534 | return time; 535 | } 536 | 537 | self.whatItem = function(x) { 538 | var itemsArr = { 1: 'ITEM_POKE_BALL', 2: 'ITEM_GREAT_BALL', 3: 'ITEM_ULTRA_BALL', 4: 'ITEM_MASTER_BALL', 101: 'ITEM_POTION', 102: 'ITEM_SUPER_POTION', 103: 'ITEM_HYPER_POTION', 104: 'ITEM_MAX_POTION', 201: 'ITEM_REVIVE', 301: 'ITEM_LUCKY_EGG', 401: 'ITEM_INCENSE_ORDINARY', 402: 'ITEM_INCENSE_SPICY', 403: 'ITEM_INCENSE_COOL', 404: 'ITEM_INCENSE_FLORAL', 501: 'ITEM_TROY_DISK', 602: 'ITEM_X_ATTACK', 603: 'ITEM_X_DEFENSE', 604: 'ITEM_X_MIRACLE', 701: 'ITEM_RAZZ_BERRY', 702: 'ITEM_BLUK_BERRY', 703: 'ITEM_NANAB_BERRY', 704: 'ITEM_WEPAR_BERRY', 705: 'ITEM_PINAP_BERRY', 801: 'ITEM_SPECIAL_CAMERA', 901: 'ITEM_INCUBATOR_BASIC_UNLIMITED', 902: 'ITEM_INCUBATOR_BASIC', 1001: 'ITEM_POKEMON_STORAGE_UPGRADE', 1002: 'ITEM_ITEM_STORAGE_UPGRADE'}; 539 | return itemsArr[x]; 540 | }; 541 | 542 | self.whatFamily = function(x) { 543 | var candyClass = {0: 'FAMILY_UNSET', 1: 'FAMILY_BULBASAUR', 4: 'FAMILY_CHARMANDER', 7: 'FAMILY_SQUIRTLE', 10: 'FAMILY_CATERPIE', 13: 'FAMILY_WEEDLE', 16: 'FAMILY_PIDGEY', 19: 'FAMILY_RATTATA', 21: 'FAMILY_SPEAROW', 23: 'FAMILY_EKANS', 25: 'FAMILY_PIKACHU', 27: 'FAMILY_SANDSHREW', 29: 'FAMILY_NIDORAN_FEMALE', 32: 'FAMILY_NIDORAN_MALE', 35: 'FAMILY_CLEFAIRY', 37: 'FAMILY_VULPIX', 39: 'FAMILY_JIGGLYPUFF', 41: 'FAMILY_ZUBAT', 43: 'FAMILY_ODDISH', 46: 'FAMILY_PARAS', 48: 'FAMILY_VENONAT', 50: 'FAMILY_DIGLETT', 52: 'FAMILY_MEOWTH', 54: 'FAMILY_PSYDUCK', 56: 'FAMILY_MANKEY', 58: 'FAMILY_GROWLITHE', 60: 'FAMILY_POLIWAG', 63: 'FAMILY_ABRA', 66: 'FAMILY_MACHOP', 69: 'FAMILY_BELLSPROUT', 72: 'FAMILY_TENTACOOL', 74: 'FAMILY_GEODUDE', 77: 'FAMILY_PONYTA', 79: 'FAMILY_SLOWPOKE', 81: 'FAMILY_MAGNEMITE', 83: 'FAMILY_FARFETCHD', 84: 'FAMILY_DODUO', 86: 'FAMILY_SEEL', 88: 'FAMILY_GRIMER', 90: 'FAMILY_SHELLDER', 92: 'FAMILY_GASTLY', 95: 'FAMILY_ONIX', 96: 'FAMILY_DROWZEE', 97: 'FAMILY_HYPNO', 98: 'FAMILY_KRABBY', 100: 'FAMILY_VOLTORB', 102: 'FAMILY_EXEGGCUTE', 104: 'FAMILY_CUBONE', 106: 'FAMILY_HITMONLEE', 107: 'FAMILY_HITMONCHAN', 108: 'FAMILY_LICKITUNG', 109: 'FAMILY_KOFFING', 110: 'FAMILY_DEREK', 111: 'FAMILY_RHYHORN', 113: 'FAMILY_CHANSEY', 114: 'FAMILY_TANGELA', 115: 'FAMILY_KANGASKHAN', 116: 'FAMILY_HORSEA', 118: 'FAMILY_GOLDEEN', 120: 'FAMILY_STARYU', 122: 'FAMILY_MR_MIME', 123: 'FAMILY_SCYTHER', 124: 'FAMILY_JYNX', 125: 'FAMILY_ELECTABUZZ', 126: 'FAMILY_MAGMAR', 127: 'FAMILY_PINSIR', 128: 'FAMILY_TAUROS', 129: 'FAMILY_MAGIKARP', 131: 'FAMILY_LAPRAS', 132: 'FAMILY_DITTO', 133: 'FAMILY_EEVEE', 137: 'FAMILY_PORYGON', 138: 'FAMILY_OMANYTE', 140: 'FAMILY_KABUTO', 142: 'FAMILY_AERODACTYL', 143: 'FAMILY_SNORLAX', 144: 'FAMILY_ARTICUNO', 145: 'FAMILY_ZAPDOS', 146: 'FAMILY_MOLTRES', 147: 'FAMILY_DRATINI', 150: 'FAMILY_MEWTWO', 151: 'FAMILY_MEW'}; 544 | return candyClass[x]; 545 | }; 546 | 547 | 548 | }; 549 | 550 | module.exports = new Pokego(); 551 | module.exports.Pokego = Pokego; 552 | -------------------------------------------------------------------------------- /pokemon.proto: -------------------------------------------------------------------------------- 1 | message RequestEnvelop { 2 | required int32 unknown1 = 1; 3 | optional int64 rpc_id = 3; 4 | repeated Requests requests = 4; 5 | optional Unknown6 unknown6 = 6; 6 | optional double latitude = 7; 7 | optional double longitude = 8; 8 | optional double altitude = 9; 9 | optional AuthInfo auth = 10; 10 | optional AuthTicket auth_ticket = 11; 11 | optional int64 unknown12 = 12; 12 | 13 | message Requests { 14 | required int32 type = 1; 15 | optional bytes message = 2; 16 | } 17 | 18 | message Unknown3 { 19 | required string unknown4 = 1; 20 | } 21 | 22 | message Unknown6 { 23 | required int32 unknown1 = 1; 24 | required Unknown2 unknown2 = 2; 25 | 26 | message Unknown2 { 27 | required bytes unknown1 = 1; 28 | } 29 | 30 | } 31 | 32 | message AuthTicket { 33 | optional bytes start = 1; 34 | optional uint64 expire_timestamp_ms = 2; 35 | optional bytes end = 3; 36 | } 37 | 38 | message AuthInfo { 39 | required string provider = 1; 40 | required JWT token = 2; 41 | 42 | message JWT { 43 | required string contents = 1; 44 | required int32 unknown13 = 2; 45 | } 46 | } 47 | 48 | message MessageQuad { 49 | repeated uint64 f1 = 1 [packed=true]; 50 | repeated int64 f2 = 2 [packed=true]; 51 | required double lat = 3; 52 | required double long = 4; 53 | } 54 | 55 | message CatchPokemonMessage { 56 | required fixed64 encounter_id = 1; 57 | required int32 pokeball = 2; 58 | required double normalized_reticle_size = 3; 59 | required string spawnpoint_id = 4; 60 | required bool hit_pokemon = 5; 61 | required double spin_modifier = 6; 62 | required double normalized_hit_position = 7; 63 | } 64 | 65 | message EvolvePokemonMessage { 66 | required fixed64 PokemonId = 1; 67 | } 68 | 69 | message TransferPokemonMessage { 70 | required fixed64 PokemonId = 1; 71 | } 72 | 73 | message LevelUpRewardsMessage { 74 | required int32 level = 1; 75 | } 76 | 77 | message UseItemEggIncubatorMessage { 78 | required string item_id = 1; 79 | required uint64 PokemonId = 2; 80 | } 81 | 82 | message GetHatchedEggsMessage { 83 | // nothin.. 84 | } 85 | message UseItemXpBoostMessage { 86 | required int32 item_id = 1; 87 | } 88 | 89 | message EncounterMessage { 90 | repeated fixed64 encounter_id = 1; 91 | required string spawnpoint_id = 2; 92 | required double player_latitude = 3; 93 | required double player_longitude = 4; 94 | } 95 | 96 | message FortSearchMessage { 97 | required string fort_id = 1; 98 | required double player_latitude = 2; 99 | required double player_longitude = 3; 100 | required double fort_latitude = 4; 101 | required double fort_longitude = 5; 102 | } 103 | 104 | message FortDetailsRequest 105 | { 106 | required string fort_id = 1; 107 | required double fort_latitude = 2; 108 | required double fort_longitude = 3; 109 | } 110 | 111 | //rpc id : 137 112 | message RecycleInventoryItemMessage { 113 | required int32 item_id = 1; 114 | required int32 count = 2; 115 | } 116 | 117 | message ReleasePokemonMessage { 118 | optional fixed64 pokemon_id = 1; 119 | repeated fixed64 pokemon_ids = 2; 120 | } 121 | } 122 | 123 | message ResponseEnvelop { 124 | optional int32 unknown1 = 1; 125 | optional int64 unknown2 = 2; 126 | optional string api_url = 3; 127 | optional Unknown6 unknown6 = 6; 128 | optional AuthTicket auth_ticket = 7; 129 | repeated bytes payload = 100; 130 | 131 | message Unknown6 { 132 | required int32 unknown1 = 1; 133 | required Unknown2 unknown2 = 2; 134 | 135 | message Unknown2 { 136 | optional uint64 unknown1 = 1; 137 | repeated bytes storeitems = 2; 138 | repeated bytes currencies = 3; 139 | optional string unknown4 = 4; 140 | } 141 | 142 | } 143 | 144 | message AuthTicket { 145 | optional bytes start = 1; 146 | optional uint64 expire_timestamp_ms = 2; 147 | optional bytes end = 3; 148 | } 149 | 150 | message HeartbeatPayload { 151 | repeated ClientMapCell cells = 1; 152 | } 153 | 154 | message ClientMapCell { 155 | required uint64 S2CellId = 1; 156 | required int64 AsOfTimeMs = 2; 157 | repeated PokemonFortProto Fort = 3; 158 | repeated ClientSpawnPointProto SpawnPoint = 4; 159 | repeated WildPokemonProto WildPokemon = 5; 160 | //unknown DeletedObject = 6; 161 | optional bool IsTruncatedList = 7; 162 | repeated PokemonSummaryFortProto FortSummary = 8; 163 | repeated ClientSpawnPointProto DecimatedSpawnPoint = 9; 164 | repeated MapPokemonProto MapPokemon = 10; 165 | repeated NearbyPokemonProto NearbyPokemon = 11; 166 | } 167 | 168 | message MapPokemonProto { 169 | required string SpawnPointId = 1; 170 | required fixed64 EncounterId = 2; 171 | required int32 PokedexTypeId = 3; 172 | required int64 ExpirationTimeMs = 4; 173 | optional double Latitude = 5; 174 | optional double Longitude = 6; 175 | } 176 | 177 | message PokemonFortProto { 178 | required string FortId = 1; 179 | required int64 LastModifiedMs = 2; 180 | required double Latitude = 3; 181 | required double Longitude = 4; 182 | optional int32 Team = 5; 183 | optional int32 GuardPokemonId = 6; 184 | optional int32 GuardPokemonLevel = 7; 185 | required bool Enabled = 8; 186 | // ENUM.Holoholo.Rpc.FortType FortType = 9; 187 | optional int32 FortType = 9; 188 | optional int64 GymPoints = 10; 189 | optional bool IsInBattle = 11; 190 | optional bytes ActiveFortModifier = 12; 191 | optional FortLureInfoProto LureInfo = 13; 192 | optional int64 CooldownCompleteMs = 14; 193 | // ENUM.Holoholo.Rpc.Sponsor.Types.FortSponsor.Sponsor Sponsor = 15; 194 | optional int32 Sponsor = 15; 195 | // ENUM.Holoholo.Rpc.RenderingType.Types.FortRenderingType.RenderingType RenderingType = 16; 196 | optional int32 RenderingType = 16; 197 | } 198 | 199 | //based on : https://github.com/AHAAAAAAA/PokemonGo-Map/blob/master/pokemon.proto 200 | message FortLureInfoProto { 201 | required string FortId = 1; 202 | required double unknown2 = 2; 203 | required int32 ActivePokemonId = 3; 204 | required int64 LureExpiresTimestampMs = 4; 205 | optional string DeployerPlayerCodename = 5; 206 | } 207 | 208 | message PokemonSummaryFortProto { 209 | required string FortSummaryId = 1; 210 | required int64 LastModifiedMs = 2; 211 | required double Latitude = 3; 212 | required double Longitude = 4; 213 | } 214 | 215 | message ClientSpawnPointProto { 216 | required double Latitude = 2; 217 | required double Longitude = 3; 218 | } 219 | 220 | message WildPokemonProto { 221 | optional fixed64 EncounterId = 1; 222 | optional int64 LastModifiedMs = 2; 223 | optional double Latitude = 3; 224 | optional double Longitude = 4; 225 | optional string SpawnPointId = 5; 226 | optional Pokemon pokemon = 7; 227 | optional int32 TimeTillHiddenMs = 11; 228 | 229 | message Pokemon { 230 | optional uint64 Id = 1; 231 | optional PokemonId PokemonId = 2; 232 | optional int32 cp = 3; 233 | } 234 | } 235 | 236 | message NearbyPokemonProto { 237 | optional PokemonId PokedexNumber = 1; 238 | optional float DistanceMeters = 2; 239 | optional fixed64 EncounterId = 3; 240 | } 241 | 242 | message ProfilePayload { 243 | repeated int32 unknown1 = 1; 244 | optional Profile profile = 2; 245 | } 246 | 247 | message Profile { 248 | required int64 creation_time = 1; 249 | optional string username = 2; 250 | optional int32 team = 5; 251 | optional bytes tutorial = 7; 252 | optional AvatarDetails avatar = 8; 253 | optional int32 poke_storage = 9; 254 | optional int32 item_storage = 10; 255 | optional DailyBonus daily_bonus = 11; 256 | optional bytes unknown12 = 12; 257 | optional bytes unknown13 = 13; 258 | repeated Currency currency = 14; 259 | 260 | message AvatarDetails { 261 | optional int32 skin = 2; 262 | optional int32 hair = 3; 263 | optional int32 tshirt = 4; 264 | optional int32 trousers = 5; 265 | optional int32 cap = 6; 266 | optional int32 boots = 7; 267 | optional int32 gender = 8; 268 | optional int32 eyes = 9; 269 | optional int32 backpack = 10; 270 | } 271 | 272 | message DailyBonus { 273 | optional int64 NextCollectTimestampMs = 1; 274 | optional int64 NextDefenderBonusCollectTimestampMs = 2; 275 | } 276 | 277 | message Currency { 278 | required string type = 1; 279 | optional int32 amount = 2; 280 | } 281 | } 282 | 283 | message GetInventoryResponse { 284 | optional bool success = 1; 285 | optional InventoryDelta inventory_delta = 2; 286 | } 287 | 288 | message InventoryDelta { 289 | optional int64 original_timestamp_ms = 1; 290 | optional int64 new_timestamp_ms = 2; 291 | repeated InventoryItem inventory_items = 3; 292 | } 293 | 294 | message InventoryItem { 295 | optional int64 modified_timestamp_ms = 1; 296 | optional int64 deleted_item_key = 2; 297 | optional InventoryItemData inventory_item_data = 3; 298 | } 299 | 300 | message InventoryItemData { 301 | optional PokemonData pokemon = 1; 302 | optional Item item = 2; 303 | optional PokedexEntry pokedex_entry = 3; 304 | optional PlayerStats player_stats = 4; 305 | optional PlayerCurrency player_currency = 5; 306 | optional PlayerCamera player_camera = 6; 307 | optional InventoryUpgrades inventory_upgrades = 7; 308 | optional AppliedItems applied_items = 8; 309 | optional EggIncubators egg_incubators = 9; 310 | optional PokemonFamily pokemon_family = 10; 311 | } 312 | 313 | message PokemonData { 314 | optional fixed64 id = 1; 315 | optional PokemonId pokemon_id = 2; 316 | optional int32 cp = 3; 317 | optional int32 stamina = 4; 318 | optional int32 stamina_max = 5; 319 | optional PokemonMove move_1 = 6; 320 | optional PokemonMove move_2 = 7; 321 | optional string deployed_fort_id = 8; 322 | optional string owner_name = 9; 323 | optional bool is_egg = 10; 324 | optional double egg_km_walked_target = 11; 325 | optional double egg_km_walked_start = 12; 326 | optional int32 origin = 14; 327 | optional float height_m = 15; 328 | optional float weight_kg = 16; 329 | optional int32 individual_attack = 17; 330 | optional int32 individual_defense = 18; 331 | optional int32 individual_stamina = 19; 332 | optional float cp_multiplier = 20; 333 | optional ItemId pokeball = 21; 334 | optional uint64 captured_cell_id = 22; 335 | optional int32 battles_attacked = 23; 336 | optional int32 battles_defended = 24; 337 | optional string egg_incubator_id = 25; 338 | optional uint64 creation_time_ms = 26; 339 | optional int32 num_upgrades = 27; 340 | optional float additional_cp_multiplier = 28; 341 | optional int32 favorite = 29; 342 | optional string nickname = 30; 343 | optional int32 from_fort = 31; 344 | } 345 | 346 | message Pokemon { 347 | optional int32 id = 1; 348 | optional PokemonId pokemon_type = 2; 349 | optional int32 cp = 3; 350 | optional int32 stamina = 4; 351 | optional int32 stamina_max = 5; 352 | optional PokemonMove move_1 = 6; 353 | optional PokemonMove move_2 = 7; 354 | optional int32 deployed_fort_id = 8; 355 | optional string owner_name = 9; 356 | optional bool is_egg = 10; 357 | optional int32 egg_km_walked_target = 11; 358 | optional int32 egg_km_walked_start = 12; 359 | optional int32 origin = 14; 360 | optional float height_m = 15; 361 | optional float weight_kg = 16; 362 | optional int32 individual_attack = 17; 363 | optional int32 individual_defense = 18; 364 | optional int32 individual_stamina = 19; 365 | optional int32 cp_multiplier = 20; 366 | optional int32 pokeball = 21; 367 | optional uint64 captured_cell_id = 22; 368 | optional int32 battles_attacked = 23; 369 | optional int32 battles_defended = 24; 370 | optional int32 egg_incubator_id = 25; 371 | optional uint64 creation_time_ms = 26; 372 | optional int32 num_upgrades = 27; 373 | optional int32 additional_cp_multiplier = 28; 374 | optional int32 favorite = 29; 375 | optional string nickname = 30; 376 | optional int32 from_fort = 31; 377 | } 378 | 379 | enum TeamColor { 380 | NEUTRAL = 0; 381 | BLUE = 1; 382 | RED = 2; 383 | YELLOW = 3; 384 | } 385 | 386 | enum PokemonMove { 387 | MOVE_UNSET = 0; 388 | THUNDER_SHOCK = 1; 389 | QUICK_ATTACK = 2; 390 | SCRATCH = 3; 391 | EMBER = 4; 392 | VINE_WHIP = 5; 393 | TACKLE = 6; 394 | RAZOR_LEAF = 7; 395 | TAKE_DOWN = 8; 396 | WATER_GUN = 9; 397 | BITE = 10; 398 | POUND = 11; 399 | DOUBLE_SLAP = 12; 400 | WRAP = 13; 401 | HYPER_BEAM = 14; 402 | LICK = 15; 403 | DARK_PULSE = 16; 404 | SMOG = 17; 405 | SLUDGE = 18; 406 | METAL_CLAW = 19; 407 | VICE_GRIP = 20; 408 | FLAME_WHEEL = 21; 409 | MEGAHORN = 22; 410 | WING_ATTACK = 23; 411 | FLAMETHROWER = 24; 412 | SUCKER_PUNCH = 25; 413 | DIG = 26; 414 | LOW_KICK = 27; 415 | CROSS_CHOP = 28; 416 | PSYCHO_CUT = 29; 417 | PSYBEAM = 30; 418 | EARTHQUAKE = 31; 419 | STONE_EDGE = 32; 420 | ICE_PUNCH = 33; 421 | HEART_STAMP = 34; 422 | DISCHARGE = 35; 423 | FLASH_CANNON = 36; 424 | PECK = 37; 425 | DRILL_PECK = 38; 426 | ICE_BEAM = 39; 427 | BLIZZARD = 40; 428 | AIR_SLASH = 41; 429 | HEAT_WAVE = 42; 430 | TWINEEDLE = 43; 431 | POISON_JAB = 44; 432 | AERIAL_ACE = 45; 433 | DRILL_RUN = 46; 434 | PETAL_BLIZZARD = 47; 435 | MEGA_DRAIN = 48; 436 | BUG_BUZZ = 49; 437 | POISON_FANG = 50; 438 | NIGHT_SLASH = 51; 439 | SLASH = 52; 440 | BUBBLE_BEAM = 53; 441 | SUBMISSION = 54; 442 | KARATE_CHOP = 55; 443 | LOW_SWEEP = 56; 444 | AQUA_JET = 57; 445 | AQUA_TAIL = 58; 446 | SEED_BOMB = 59; 447 | PSYSHOCK = 60; 448 | ROCK_THROW = 61; 449 | ANCIENT_POWER = 62; 450 | ROCK_TOMB = 63; 451 | ROCK_SLIDE = 64; 452 | POWER_GEM = 65; 453 | SHADOW_SNEAK = 66; 454 | SHADOW_PUNCH = 67; 455 | SHADOW_CLAW = 68; 456 | OMINOUS_WIND = 69; 457 | SHADOW_BALL = 70; 458 | BULLET_PUNCH = 71; 459 | MAGNET_BOMB = 72; 460 | STEEL_WING = 73; 461 | IRON_HEAD = 74; 462 | PARABOLIC_CHARGE = 75; 463 | SPARK = 76; 464 | THUNDER_PUNCH = 77; 465 | THUNDER = 78; 466 | THUNDERBOLT = 79; 467 | TWISTER = 80; 468 | DRAGON_BREATH = 81; 469 | DRAGON_PULSE = 82; 470 | DRAGON_CLAW = 83; 471 | DISARMING_VOICE = 84; 472 | DRAINING_KISS = 85; 473 | DAZZLING_GLEAM = 86; 474 | MOONBLAST = 87; 475 | PLAY_ROUGH = 88; 476 | CROSS_POISON = 89; 477 | SLUDGE_BOMB = 90; 478 | SLUDGE_WAVE = 91; 479 | GUNK_SHOT = 92; 480 | MUD_SHOT = 93; 481 | BONE_CLUB = 94; 482 | BULLDOZE = 95; 483 | MUD_BOMB = 96; 484 | FURY_CUTTER = 97; 485 | BUG_BITE = 98; 486 | SIGNAL_BEAM = 99; 487 | X_SCISSOR = 100; 488 | FLAME_CHARGE = 101; 489 | FLAME_BURST = 102; 490 | FIRE_BLAST = 103; 491 | BRINE = 104; 492 | WATER_PULSE = 105; 493 | SCALD = 106; 494 | HYDRO_PUMP = 107; 495 | PSYCHIC = 108; 496 | PSYSTRIKE = 109; 497 | ICE_SHARD = 110; 498 | ICY_WIND = 111; 499 | FROST_BREATH = 112; 500 | ABSORB = 113; 501 | GIGA_DRAIN = 114; 502 | FIRE_PUNCH = 115; 503 | SOLAR_BEAM = 116; 504 | LEAF_BLADE = 117; 505 | POWER_WHIP = 118; 506 | SPLASH = 119; 507 | ACID = 120; 508 | AIR_CUTTER = 121; 509 | HURRICANE = 122; 510 | BRICK_BREAK = 123; 511 | CUT = 124; 512 | SWIFT = 125; 513 | HORN_ATTACK = 126; 514 | STOMP = 127; 515 | HEADBUTT = 128; 516 | HYPER_FANG = 129; 517 | SLAM = 130; 518 | BODY_SLAM = 131; 519 | REST = 132; 520 | STRUGGLE = 133; 521 | SCALD_BLASTOISE = 134; 522 | HYDRO_PUMP_BLASTOISE = 135; 523 | WRAP_GREEN = 136; 524 | WRAP_PINK = 137; 525 | FURY_CUTTER_FAST = 200; 526 | BUG_BITE_FAST = 201; 527 | BITE_FAST = 202; 528 | SUCKER_PUNCH_FAST = 203; 529 | DRAGON_BREATH_FAST = 204; 530 | THUNDER_SHOCK_FAST = 205; 531 | SPARK_FAST = 206; 532 | LOW_KICK_FAST = 207; 533 | KARATE_CHOP_FAST = 208; 534 | EMBER_FAST = 209; 535 | WING_ATTACK_FAST = 210; 536 | PECK_FAST = 211; 537 | LICK_FAST = 212; 538 | SHADOW_CLAW_FAST = 213; 539 | VINE_WHIP_FAST = 214; 540 | RAZOR_LEAF_FAST = 215; 541 | MUD_SHOT_FAST = 216; 542 | ICE_SHARD_FAST = 217; 543 | FROST_BREATH_FAST = 218; 544 | QUICK_ATTACK_FAST = 219; 545 | SCRATCH_FAST = 220; 546 | TACKLE_FAST = 221; 547 | POUND_FAST = 222; 548 | CUT_FAST = 223; 549 | POISON_JAB_FAST = 224; 550 | ACID_FAST = 225; 551 | PSYCHO_CUT_FAST = 226; 552 | ROCK_THROW_FAST = 227; 553 | METAL_CLAW_FAST = 228; 554 | BULLET_PUNCH_FAST = 229; 555 | WATER_GUN_FAST = 230; 556 | SPLASH_FAST = 231; 557 | WATER_GUN_FAST_BLASTOISE = 232; 558 | MUD_SLAP_FAST = 233; 559 | ZEN_HEADBUTT_FAST = 234; 560 | CONFUSION_FAST = 235; 561 | POISON_STING_FAST = 236; 562 | BUBBLE_FAST = 237; 563 | FEINT_ATTACK_FAST = 238; 564 | STEEL_WING_FAST = 239; 565 | FIRE_FANG_FAST = 240; 566 | ROCK_SMASH_FAST = 241; 567 | } 568 | 569 | enum PokemonId { 570 | MISSINGNO = 0; 571 | BULBASAUR = 1; 572 | IVYSAUR = 2; 573 | VENUSAUR = 3; 574 | CHARMENDER = 4; 575 | CHARMELEON = 5; 576 | CHARIZARD = 6; 577 | SQUIRTLE = 7; 578 | WARTORTLE = 8; 579 | BLASTOISE = 9; 580 | CATERPIE = 10; 581 | METAPOD = 11; 582 | BUTTERFREE = 12; 583 | WEEDLE = 13; 584 | KAKUNA = 14; 585 | BEEDRILL = 15; 586 | PIDGEY = 16; 587 | PIDGEOTTO = 17; 588 | PIDGEOT = 18; 589 | RATTATA = 19; 590 | RATICATE = 20; 591 | SPEAROW = 21; 592 | FEAROW = 22; 593 | EKANS = 23; 594 | ARBOK = 24; 595 | PIKACHU = 25; 596 | RAICHU = 26; 597 | SANDSHREW = 27; 598 | SANDLASH = 28; 599 | NIDORAN_FEMALE = 29; 600 | NIDORINA = 30; 601 | NIDOQUEEN = 31; 602 | NIDORAN_MALE = 32; 603 | NIDORINO = 33; 604 | NIDOKING = 34; 605 | CLEFARY = 35; 606 | CLEFABLE = 36; 607 | VULPIX = 37; 608 | NINETALES = 38; 609 | JIGGLYPUFF = 39; 610 | WIGGLYTUFF = 40; 611 | ZUBAT = 41; 612 | GOLBAT = 42; 613 | ODDISH = 43; 614 | GLOOM = 44; 615 | VILEPLUME = 45; 616 | PARAS = 46; 617 | PARASECT = 47; 618 | VENONAT = 48; 619 | VENOMOTH = 49; 620 | DIGLETT = 50; 621 | DUGTRIO = 51; 622 | MEOWTH = 52; 623 | PERSIAN = 53; 624 | PSYDUCK = 54; 625 | GOLDUCK = 55; 626 | MANKEY = 56; 627 | PRIMEAPE = 57; 628 | GROWLITHE = 58; 629 | ARCANINE = 59; 630 | POLIWAG = 60; 631 | POLIWHIRL = 61; 632 | POLIWRATH = 62; 633 | ABRA = 63; 634 | KADABRA = 64; 635 | ALAKHAZAM = 65; 636 | MACHOP = 66; 637 | MACHOKE = 67; 638 | MACHAMP = 68; 639 | BELLSPROUT = 69; 640 | WEEPINBELL = 70; 641 | VICTREEBELL = 71; 642 | TENTACOOL = 72; 643 | TENTACRUEL = 73; 644 | GEODUGE = 74; 645 | GRAVELER = 75; 646 | GOLEM = 76; 647 | PONYTA = 77; 648 | RAPIDASH = 78; 649 | SLOWPOKE = 79; 650 | SLOWBRO = 80; 651 | MAGNEMITE = 81; 652 | MAGNETON = 82; 653 | FARFETCHD = 83; 654 | DODUO = 84; 655 | DODRIO = 85; 656 | SEEL = 86; 657 | DEWGONG = 87; 658 | GRIMER = 88; 659 | MUK = 89; 660 | SHELLDER = 90; 661 | CLOYSTER = 91; 662 | GASTLY = 92; 663 | HAUNTER = 93; 664 | GENGAR = 94; 665 | ONIX = 95; 666 | DROWZEE = 96; 667 | HYPNO = 97; 668 | KRABBY = 98; 669 | KINGLER = 99; 670 | VOLTORB = 100; 671 | ELECTRODE = 101; 672 | EXEGGCUTE = 102; 673 | EXEGGUTOR = 103; 674 | CUBONE = 104; 675 | MAROWAK = 105; 676 | HITMONLEE = 106; 677 | HITMONCHAN = 107; 678 | LICKITUNG = 108; 679 | KOFFING = 109; 680 | WEEZING = 110; 681 | RHYHORN = 111; 682 | RHYDON = 112; 683 | CHANSEY = 113; 684 | TANGELA = 114; 685 | KANGASKHAN = 115; 686 | HORSEA = 116; 687 | SEADRA = 117; 688 | GOLDEEN = 118; 689 | SEAKING = 119; 690 | STARYU = 120; 691 | STARMIE = 121; 692 | MR_MIME = 122; 693 | SCYTHER = 123; 694 | JYNX = 124; 695 | ELECTABUZZ = 125; 696 | MAGMAR = 126; 697 | PINSIR = 127; 698 | TAUROS = 128; 699 | MAGIKARP = 129; 700 | GYARADOS = 130; 701 | LAPRAS = 131; 702 | DITTO = 132; 703 | EEVEE = 133; 704 | VAPOREON = 134; 705 | JOLTEON = 135; 706 | FLAREON = 136; 707 | PORYGON = 137; 708 | OMANYTE = 138; 709 | OMASTAR = 139; 710 | KABUTO = 140; 711 | KABUTOPS = 141; 712 | AERODACTYL = 142; 713 | SNORLAX = 143; 714 | ARTICUNO = 144; 715 | ZAPDOS = 145; 716 | MOLTRES = 146; 717 | DRATINI = 147; 718 | DRAGONAIR = 148; 719 | DRAGONITE = 149; 720 | MEWTWO = 150; 721 | MEW = 151; 722 | } 723 | 724 | message Item { 725 | optional ItemId item_id = 1; 726 | optional int32 count = 2; 727 | optional bool unseen = 3; 728 | } 729 | 730 | enum ItemType { 731 | ITEM_TYPE_NONE = 0; 732 | ITEM_TYPE_POKEBALL = 1; 733 | ITEM_TYPE_POTION = 2; 734 | ITEM_TYPE_REVIVE = 3; 735 | ITEM_TYPE_MAP = 4; 736 | ITEM_TYPE_BATTLE = 5; 737 | ITEM_TYPE_FOOD = 6; 738 | ITEM_TYPE_CAMERA = 7; 739 | ITEM_TYPE_DISK = 8; 740 | ITEM_TYPE_INCUBATOR = 9; 741 | ITEM_TYPE_INCENSE = 10; 742 | ITEM_TYPE_XP_BOOST = 11; 743 | ITEM_TYPE_INVENTORY_UPGRADE = 12; 744 | } 745 | 746 | message PokedexEntry { 747 | optional int32 pokedex_entry_number = 1; 748 | optional int32 times_encountered = 2; 749 | optional int32 times_captured = 3; 750 | optional int32 evolution_stone_pieces = 4; 751 | optional int32 evolution_stones = 5; 752 | } 753 | 754 | message PlayerStats { 755 | optional int32 level = 1; 756 | optional int64 experience = 2; 757 | optional int64 prev_level_xp = 3; 758 | optional int64 next_level_xp = 4; 759 | optional float km_walked = 5; 760 | optional int32 pokemons_encountered = 6; 761 | optional int32 unique_pokedex_entries = 7; 762 | optional int32 pokemons_captured = 8; 763 | optional int32 evolutions = 9; 764 | optional int32 poke_stop_visits = 10; 765 | optional int32 pokeballs_thrown = 11; 766 | optional int32 eggs_hatched = 12; 767 | optional int32 big_magikarp_caught = 13; 768 | optional int32 battle_attack_won = 14; 769 | optional int32 battle_attack_total = 15; 770 | optional int32 battle_defended_won = 16; 771 | optional int32 battle_training_won = 17; 772 | optional int32 battle_training_total = 18; 773 | optional int32 prestige_raised_total = 19; 774 | optional int32 prestige_dropped_total = 20; 775 | optional int32 pokemon_deployed = 21; 776 | optional bytes pokemon_caught_by_type = 22; // TODO: repeated PokemonType ?? 777 | optional int32 small_rattata_caught = 23; 778 | } 779 | 780 | message PlayerCurrency { 781 | optional int32 gems = 1; 782 | } 783 | 784 | message PlayerCamera { 785 | optional bool is_default_camera = 1; 786 | } 787 | 788 | message InventoryUpgrades { 789 | repeated InventoryUpgrade inventory_upgrades = 1; 790 | } 791 | 792 | message InventoryUpgrade { 793 | optional ItemType item = 1; 794 | optional InventoryUpgradeType upgrade_type = 2; 795 | optional int32 additional_storage = 3; 796 | } 797 | 798 | enum InventoryUpgradeType { 799 | UPGRADE_UNSET = 0; 800 | INCREASE_ITEM_STORAGE = 1; 801 | INCREASE_POKEMON_STORAGE = 2; 802 | } 803 | 804 | message AppliedItems { 805 | optional AppliedItem item = 4; 806 | } 807 | 808 | message AppliedItem { 809 | optional ItemId item_type = 1; 810 | optional ItemType item_type_category = 2; 811 | optional int64 expire_ms = 3; 812 | optional int64 applied_ms = 4; 813 | } 814 | 815 | enum ItemId { 816 | ITEM_UNKNOWN = 0; 817 | ITEM_POKE_BALL = 1; 818 | ITEM_GREAT_BALL = 2; 819 | ITEM_ULTRA_BALL = 3; 820 | ITEM_MASTER_BALL = 4; 821 | ITEM_POTION = 101; 822 | ITEM_SUPER_POTION = 102; 823 | ITEM_HYPER_POTION = 103; 824 | ITEM_MAX_POTION = 104; 825 | ITEM_REVIVE = 201; 826 | ITEM_MAX_REVIVE = 202; 827 | ITEM_LUCKY_EGG = 301; 828 | ITEM_INCENSE_ORDINARY = 401; 829 | ITEM_INCENSE_SPICY = 402; 830 | ITEM_INCENSE_COOL = 403; 831 | ITEM_INCENSE_FLORAL = 404; 832 | ITEM_TROY_DISK = 501; 833 | ITEM_X_ATTACK = 602; 834 | ITEM_X_DEFENSE = 603; 835 | ITEM_X_MIRACLE = 604; 836 | ITEM_RAZZ_BERRY = 701; 837 | ITEM_BLUK_BERRY = 702; 838 | ITEM_NANAB_BERRY = 703; 839 | ITEM_WEPAR_BERRY = 704; 840 | ITEM_PINAP_BERRY = 705; 841 | ITEM_SPECIAL_CAMERA = 801; 842 | ITEM_INCUBATOR_BASIC_UNLIMITED = 901; 843 | ITEM_INCUBATOR_BASIC = 902; 844 | ITEM_POKEMON_STORAGE_UPGRADE = 1001; 845 | ITEM_ITEM_STORAGE_UPGRADE = 1002; 846 | } 847 | 848 | message EggIncubators { 849 | repeated EggIncubator egg_incubator = 1; 850 | } 851 | 852 | message EggIncubator { 853 | optional string item_id = 1; 854 | optional ItemType item_type = 2; 855 | optional EggIncubatorType incubator_type = 3; 856 | optional int32 uses_remaining = 4; 857 | optional int64 pokemon_id = 5; // TODO: Check if is PokemonType 858 | optional double start_km_walked = 6; 859 | optional double target_km_walked = 7; 860 | } 861 | 862 | enum EggIncubatorType { 863 | INCUBATOR_UNSET = 0; 864 | INCUBATOR_DISTANCE = 1; 865 | } 866 | 867 | message PokemonFamily { 868 | optional PokemonFamilyId family_id = 1; 869 | optional int32 candy = 2; 870 | } 871 | 872 | enum PokemonFamilyId { 873 | FAMILY_UNSET = 0; 874 | FAMILY_BULBASAUR = 1; 875 | FAMILY_CHARMANDER = 4; 876 | FAMILY_SQUIRTLE = 7; 877 | FAMILY_CATERPIE = 10; 878 | FAMILY_WEEDLE = 13; 879 | FAMILY_PIDGEY = 16; 880 | FAMILY_RATTATA = 19; 881 | FAMILY_SPEAROW = 21; 882 | FAMILY_EKANS = 23; 883 | FAMILY_PIKACHU = 25; 884 | FAMILY_SANDSHREW = 27; 885 | FAMILY_NIDORAN = 29; 886 | FAMILY_NIDORAN2 = 32; 887 | FAMILY_CLEFAIRY = 35; 888 | FAMILY_VULPIX = 37; 889 | FAMILY_JIGGLYPUFF = 39; 890 | FAMILY_ZUBAT = 41; 891 | FAMILY_ODDISH = 43; 892 | FAMILY_PARAS = 46; 893 | FAMILY_VENONAT = 48; 894 | FAMILY_DIGLETT = 50; 895 | FAMILY_MEOWTH = 52; 896 | FAMILY_PSYDUCK = 54; 897 | FAMILY_MANKEY = 56; 898 | FAMILY_GROWLITHE = 58; 899 | FAMILY_POLIWAG = 60; 900 | FAMILY_ABRA = 63; 901 | FAMILY_MACHOP = 66; 902 | FAMILY_BELLSPROUT = 69; 903 | FAMILY_TENTACOOL = 72; 904 | FAMILY_GEODUDE = 74; 905 | FAMILY_PONYTA = 77; 906 | FAMILY_SLOWPOKE = 79; 907 | FAMILY_MAGNEMITE = 81; 908 | FAMILY_FARFETCHD = 83; 909 | FAMILY_DODUO = 84; 910 | FAMILY_SEEL = 86; 911 | FAMILY_GRIMER = 88; 912 | FAMILY_SHELLDER = 90; 913 | FAMILY_GASTLY = 92; 914 | FAMILY_ONIX = 95; 915 | FAMILY_DROWZEE = 96; 916 | FAMILY_KRABBY = 98; 917 | FAMILY_VOLTORB = 100; 918 | FAMILY_EXEGGCUTE = 102; 919 | FAMILY_CUBONE = 104; 920 | FAMILY_HITMONLEE = 106; 921 | FAMILY_HITMONCHAN = 107; 922 | FAMILY_LICKITUNG = 108; 923 | FAMILY_KOFFING = 109; 924 | FAMILY_RHYHORN = 111; 925 | FAMILY_CHANSEY = 113; 926 | FAMILY_TANGELA = 114; 927 | FAMILY_KANGASKHAN = 115; 928 | FAMILY_HORSEA = 116; 929 | FAMILY_GOLDEEN = 118; 930 | FAMILY_STARYU = 120; 931 | FAMILY_MR_MIME = 122; 932 | FAMILY_SCYTHER = 123; 933 | FAMILY_JYNX = 124; 934 | FAMILY_ELECTABUZZ = 125; 935 | FAMILY_MAGMAR = 126; 936 | FAMILY_PINSIR = 127; 937 | FAMILY_TAUROS = 128; 938 | FAMILY_MAGIKARP = 129; 939 | FAMILY_LAPRAS = 131; 940 | FAMILY_DITTO = 132; 941 | FAMILY_EEVEE = 133; 942 | FAMILY_PORYGON = 137; 943 | FAMILY_OMANYTE = 138; 944 | FAMILY_KABUTO = 140; 945 | FAMILY_AERODACTYL = 142; 946 | FAMILY_SNORLAX = 143; 947 | FAMILY_ARTICUNO = 144; 948 | FAMILY_ZAPDOS = 145; 949 | FAMILY_MOLTRES = 146; 950 | FAMILY_DRATINI = 147; 951 | FAMILY_MEWTWO = 150; 952 | FAMILY_MEW = 151; 953 | } 954 | 955 | enum FortType { 956 | GYM = 0; 957 | CHECKPOINT = 1; 958 | } 959 | 960 | message CatchPokemonResponse { 961 | required CatchStatus Status = 1; 962 | optional double MissPercent = 2; 963 | //optional uint64 CapturedPokemonId = 3; 964 | //optional CaptureAward CaptureAward = 4; 965 | 966 | enum CatchStatus { 967 | CATCH_ERROR = 0; 968 | CATCH_SUCCESS = 1; 969 | CATCH_ESCAPE = 2; 970 | CATCH_FLEE = 3; 971 | CATCH_MISSED = 4; 972 | } 973 | } 974 | 975 | enum ActivityType { 976 | ACTIVITY_UNKNOWN = 0; 977 | ACTIVITY_CATCH_POKEMON = 1; 978 | ACTIVITY_CATCH_LEGEND_POKEMON = 2; 979 | ACTIVITY_FLEE_POKEMON = 3; 980 | ACTIVITY_DEFEAT_FORT = 4; 981 | ACTIVITY_EVOLVE_POKEMON = 5; 982 | ACTIVITY_HATCH_EGG = 6; 983 | ACTIVITY_WALK_KM = 7; 984 | ACTIVITY_POKEDEX_ENTRY_NEW = 8; 985 | ACTIVITY_CATCH_FIRST_THROW = 9; 986 | ACTIVITY_CATCH_NICE_THROW = 10; 987 | ACTIVITY_CATCH_GREAT_THROW = 11; 988 | ACTIVITY_CATCH_EXCELLENT_THROW = 12; 989 | ACTIVITY_CATCH_CURVEBALL = 13; 990 | ACTIVITY_CATCH_FIRST_CATCH_OF_DAY = 14; 991 | ACTIVITY_CATCH_MILESTONE = 15; 992 | ACTIVITY_TRAIN_POKEMON = 16; 993 | ACTIVITY_SEARCH_FORT = 17; 994 | ACTIVITY_RELEASE_POKEMON = 18; 995 | ACTIVITY_HATCH_EGG_SMALL_BONUS = 19; 996 | ACTIVITY_HATCH_EGG_MEDIUM_BONUS = 20; 997 | ACTIVITY_HATCH_EGG_LARGE_BONUS = 21; 998 | ACTIVITY_DEFEAT_GYM_DEFENDER = 22; 999 | ACTIVITY_DEFEAT_GYM_LEADER = 23; 1000 | } 1001 | 1002 | message CaptureAward { 1003 | repeated ActivityType ActivityType = 1 [packed=true]; 1004 | repeated int32 XP = 2 [packed=true]; 1005 | repeated int32 Candy = 3 [packed=true]; 1006 | repeated int32 Stardust = 4 [packed=true]; 1007 | } 1008 | 1009 | message CaptureProbability { 1010 | repeated ItemId PokeballType = 1; 1011 | repeated float CaptureProbability = 2; 1012 | optional float ReticleDifficultySize = 12; 1013 | } 1014 | 1015 | message EncounterResponse { 1016 | optional WildPokemonProto WildPokemon = 1; 1017 | optional Background EncounterBackground = 2; 1018 | optional Status EncounterStatus = 3; 1019 | //optional CaptureProbability EncounterCaptureProbability = 4; 1020 | 1021 | enum Background { 1022 | PARK = 0; 1023 | DESERT = 1; 1024 | } 1025 | 1026 | enum Status { 1027 | ENCOUNTER_ERROR = 0; 1028 | ENCOUNTER_SUCCESS = 1; 1029 | ENCOUNTER_NOT_FOUND = 2; 1030 | ENCOUNTER_CLOSED = 3; 1031 | ENCOUNTER_POKEMON_FLED = 4; 1032 | ENCOUNTER_NOT_IN_RANGE = 5; 1033 | ENCOUNTER_ALREADY_HAPPENED = 6; 1034 | POKEMON_INVENTORY_FULL = 7; 1035 | } 1036 | } 1037 | 1038 | message FortSearchResponse { 1039 | optional Result result = 1; 1040 | repeated ItemAward items_awarded = 2; 1041 | optional int32 gems_awarded = 3; 1042 | optional PokemonData pokemon_data_egg = 4; 1043 | optional int32 experience_awarded = 5; 1044 | optional int64 cooldown_complete_timestamp_ms = 6; 1045 | optional int32 chain_hack_sequence_number = 7; 1046 | 1047 | enum Result { 1048 | NO_RESULT_SET = 0; 1049 | SUCCESS = 1; 1050 | OUT_OF_RANGE = 2; 1051 | IN_COOLDOWN_PERIOD = 3; 1052 | INVENTORY_FULL = 4; 1053 | } 1054 | } 1055 | 1056 | message FortDetailsResponse { 1057 | optional string fort_id = 1; 1058 | optional TeamColor team_color = 2; 1059 | optional PokemonData pokemon_data = 3; 1060 | optional string name = 4; 1061 | repeated string image_urls = 5; 1062 | optional int32 fp = 6; 1063 | optional int32 stamina = 7; 1064 | optional int32 max_stamina = 8; 1065 | optional int32 type = 9; 1066 | optional double latitude = 10; 1067 | optional double longitude = 11; 1068 | optional string description = 12; 1069 | repeated FortModifier modifiers = 13; 1070 | } 1071 | 1072 | message FortModifier { 1073 | optional ItemId item_id = 1; 1074 | optional int64 expiration_timestamp_ms = 2; 1075 | optional string deployer_player_codename = 3; 1076 | } 1077 | 1078 | message RecycleInventoryItemResponse { 1079 | optional Result result = 1; 1080 | optional int32 new_count = 2; 1081 | 1082 | enum Result { 1083 | UNSET = 0; 1084 | SUCCESS = 1; 1085 | ERROR_NOT_ENOUGH_COPIES = 2; 1086 | ERROR_CANNOT_RECYCLE_INCUBATORS = 3; 1087 | } 1088 | } 1089 | 1090 | message ItemAward { 1091 | optional ItemId item_id = 1; 1092 | optional int32 item_count = 2; 1093 | } 1094 | 1095 | message ReleasePokemonResponse { 1096 | required ReleaseStatus Status = 1; 1097 | optional int32 CandyAwarded = 2; 1098 | enum ReleaseStatus { 1099 | UNSET = 0; 1100 | SUCCESS = 1; 1101 | POKEMON_DEPLOYED = 2; 1102 | FAILED = 3; 1103 | ERROR_POKEMON_IS_EGG = 4; 1104 | } 1105 | } 1106 | 1107 | message EvolvePokemonResponse 1108 | { 1109 | optional EvolvePokemonStatus Result = 1; 1110 | optional Pokemon EvolvedPokemon = 2; 1111 | optional int32 ExpAwarded = 3; 1112 | optional int32 CandyAwarded = 4; 1113 | 1114 | enum EvolvePokemonStatus { 1115 | POKEMON_EVOLVED_UNSET = 0; 1116 | POKEMON_EVOLVED_SUCCESS = 1; 1117 | FAILED_POKEMON_MISSING = 2; 1118 | FAILED_INSUFFICIENT_RESOURCES = 3; 1119 | FAILED_POKEMON_CANNOT_EVOLVE = 4; 1120 | FAILED_POKEMON_IS_DEPLOYED = 5; 1121 | } 1122 | } 1123 | 1124 | message TransferPokemonResponse { 1125 | optional int32 Status = 1; 1126 | optional int32 CandyAwarded = 2; 1127 | } 1128 | 1129 | message LevelUpRewardsResponse { 1130 | required RewardStatus Status = 1; 1131 | repeated ItemAward items_awarded = 2; 1132 | repeated ItemId items_unlocked = 4; 1133 | 1134 | enum RewardStatus { 1135 | UNSET = 0; 1136 | SUCCESS = 1; 1137 | AWARDED_ALREADY = 2; 1138 | } 1139 | } 1140 | 1141 | message UseItemEggIncubatorResponse { 1142 | required IncubatorStatus Status = 1; 1143 | required EggIncubator egg_incubator = 2; 1144 | 1145 | enum IncubatorStatus { 1146 | UNSET = 0; 1147 | SUCCESS = 1; 1148 | ERROR_INCUBATOR_NOT_FOUND = 2; 1149 | ERROR_POKEMON_EGG_NOT_FOUND = 3; 1150 | ERROR_POKEMON_ID_NOT_EGG = 4; 1151 | ERROR_INCUBATOR_ALREADY_IN_USE = 5; 1152 | ERROR_POKEMON_ALREADY_INCUBATING = 6; 1153 | ERROR_INCUBATOR_NO_USES_REMAINING = 7; 1154 | } 1155 | } 1156 | 1157 | message GetHatchedEggsResponse { 1158 | required bool success = 1; 1159 | repeated uint64 pokemon_id = 2 [packed=true]; 1160 | repeated int32 experience_awarded = 3; 1161 | repeated int32 candy_awarded = 4; 1162 | repeated int32 stardust_awarded = 5; 1163 | } 1164 | 1165 | message UseItemXpBoostResponse { 1166 | required XpBoostStatus Status = 1; 1167 | optional AppliedItems applied_items = 2; 1168 | 1169 | enum XpBoostStatus { 1170 | UNSET = 0; 1171 | SUCCESS = 1; 1172 | ERROR_INVALID_ITEM_TYPE = 2; 1173 | ERROR_XP_BOOST_ALREADY_ACTIVE = 3; 1174 | ERROR_NO_ITEMS_REMAINING = 4; 1175 | ERROR_LOCATION_UNSET = 5; 1176 | } 1177 | } 1178 | 1179 | } 1180 | 1181 | message Signature { 1182 | 1183 | message LocationFix { 1184 | optional string provider = 1; // "network", "gps", "fused", possibly others 1185 | optional uint64 timestamp_since_start = 2; // in ms 1186 | optional float latitude = 13; 1187 | optional float longitude = 14; 1188 | 1189 | // ??? shows up in struct, dunno where these go 1190 | // float device_speed; 1191 | // float device_course; 1192 | optional float horizontal_accuracy = 20; // iOS only? (range seems to be -1 to +1) 1193 | optional float altitude = 21; 1194 | optional float vertical_accuracy = 22; // iOS only? (range seems to be ~10-12) 1195 | optional uint64 provider_status = 26; // Usually 3 (possibly GPS status: 1 = no fix, 2 = acquiring/inaccurate, 3 = fix acquired) 1196 | optional uint32 floor = 27; // No idea what this is, seems to be optional 1197 | optional uint64 location_type = 28; // Always 1 (if there is data at all) 1198 | } 1199 | 1200 | // don't really care about this since we're not using it 1201 | message AndroidGpsInfo { 1202 | optional uint64 time_to_fix = 1; 1203 | repeated int32 satellites_prn = 2; 1204 | repeated float snr = 3; 1205 | repeated float azimuth = 4; 1206 | repeated float elevation = 5; 1207 | repeated bool has_almanac = 6; 1208 | repeated bool has_ephemeris = 7; 1209 | repeated bool used_in_fix = 8; 1210 | } 1211 | 1212 | message SensorInfo { 1213 | optional uint64 timestamp_snapshot = 1; // in ms 1214 | optional double magnetometer_x = 3; 1215 | optional double magnetometer_y = 4; 1216 | optional double magnetometer_z = 5; 1217 | optional double angle_normalized_x = 6; 1218 | optional double angle_normalized_y = 7; 1219 | optional double angle_normalized_z = 8; 1220 | optional double accel_raw_x = 10; 1221 | optional double accel_raw_y = 11; 1222 | optional double accel_raw_z = 12; 1223 | optional double gyroscope_raw_x = 13; 1224 | optional double gyroscope_raw_y = 14; 1225 | optional double gyroscope_raw_z = 15; 1226 | optional double accel_normalized_x = 16; 1227 | optional double accel_normalized_y = 17; 1228 | optional double accel_normalized_z = 18; 1229 | optional uint64 accelerometer_axes = 19; // Always 3 1230 | } 1231 | 1232 | message DeviceInfo { 1233 | optional string device_id = 1; // Hex string 1234 | optional string android_board_name = 2; 1235 | optional string android_bootloader = 3; 1236 | optional string device_brand = 4; // On Android: product.brand 1237 | optional string device_model = 5; // On Android: product.device 1238 | optional string device_model_identifier = 6; // Android only, build.display.id 1239 | optional string device_model_boot = 7; // On Android: boot.hardware 1240 | optional string hardware_manufacturer = 8; // On Android: product.manufacturer 1241 | optional string hardware_model = 9; // On Android: product.model 1242 | optional string firmware_brand = 10; // On Android: product.name, on iOS: "iPhone OS" 1243 | optional string firmware_tags = 12; // Android only, build.tags 1244 | optional string firmware_type = 13; // On Android: build.type, on iOS instead: iOS version 1245 | optional string firmware_fingerprint = 14; // Android only, build.fingerprint 1246 | } 1247 | 1248 | message ActivityStatus { 1249 | // all of these had 1 as their value 1250 | optional uint64 start_time_ms = 1; 1251 | optional bool unknown_status = 2; 1252 | optional bool walking = 3; 1253 | optional bool running = 4; 1254 | optional bool stationary = 5; 1255 | optional bool automotive = 6; 1256 | optional bool tilting = 7; 1257 | optional bool cycling = 8; 1258 | optional bytes status = 9; 1259 | } 1260 | 1261 | optional uint64 timestamp_since_start = 2; // in ms 1262 | repeated LocationFix location_fix = 4; 1263 | optional AndroidGpsInfo gps_info = 5; 1264 | optional SensorInfo sensor_info = 7; 1265 | optional DeviceInfo device_info = 8; 1266 | optional ActivityStatus activity_status = 9; 1267 | optional uint32 location_hash1 = 10; // Location1 hashed based on the auth_token - xxHash32 1268 | optional uint32 location_hash2 = 20; // Location2 hashed based on the auth_token - xxHash32 1269 | optional bytes unk22 = 22; // possibly replay check. Generation unknown but pointed to by 0001B8614 1270 | optional uint64 timestamp = 23; // epoch timestamp in ms 1271 | repeated uint64 request_hash = 24; // hashes of each request message in a hashArray - xxhash64 1272 | 1273 | // Addresses for the corresponding hash functions: 1274 | // xxHash32 00054D28 1275 | // xxhash64 000546C8 - Feeds into 00053D40 1276 | 1277 | } 1278 | -------------------------------------------------------------------------------- /pokemons.json: -------------------------------------------------------------------------------- 1 | { 2 | "thanks": "https://github.com/Biuni/PokemonGOPokedex", 3 | "pokemon":[ 4 | { 5 | "id":"1", 6 | "num":"001", 7 | "name":"Bulbasaur", 8 | "img":"http://www.serebii.net/pokemongo/pokemon/001.png", 9 | "type": "Grass / Poison", 10 | "height": "0.71 m", 11 | "weight": "6.9 kg", 12 | "candy": "25 Bulbasaur Candy", 13 | "egg": "2 km" 14 | }, 15 | { 16 | "id":"2", 17 | "num":"002", 18 | "name":"Ivysaur", 19 | "img":"http://www.serebii.net/pokemongo/pokemon/002.png", 20 | "type": "Grass / Poison", 21 | "height": "0.99 m", 22 | "weight": "13.0 kg", 23 | "candy": "100 Bulbasaur Candy", 24 | "egg": "Not in Eggs" 25 | }, 26 | { 27 | "id":"3", 28 | "num":"003", 29 | "name":"Venusaur", 30 | "img":"http://www.serebii.net/pokemongo/pokemon/003.png", 31 | "type": "Grass / Poison", 32 | "height": "2.01 m", 33 | "weight": "100.0 kg", 34 | "candy": "None", 35 | "egg": "Not in Eggs" 36 | }, 37 | { 38 | "id":"4", 39 | "num":"004", 40 | "name":"Charmander", 41 | "img":"http://www.serebii.net/pokemongo/pokemon/004.png", 42 | "type": "Fire", 43 | "height": "0.61 m", 44 | "weight": "8.5 kg", 45 | "candy": "25 Charmander Candy", 46 | "egg": "2 km" 47 | }, 48 | { 49 | "id":"5", 50 | "num":"005", 51 | "name":"Charmeleon", 52 | "img":"http://www.serebii.net/pokemongo/pokemon/005.png", 53 | "type": "Fire", 54 | "height": "1.09 m", 55 | "weight": "19.0 kg", 56 | "candy": "100 Charmander Candy", 57 | "egg": "Not in Eggs" 58 | }, 59 | { 60 | "id":"6", 61 | "num":"006", 62 | "name":"Charizard", 63 | "img":"http://www.serebii.net/pokemongo/pokemon/006.png", 64 | "type": "Fire / Flying", 65 | "height": "1.70 m", 66 | "weight": "90.5 kg", 67 | "candy": "None", 68 | "egg": "Not in Eggs" 69 | }, 70 | { 71 | "id":"7", 72 | "num":"007", 73 | "name":"Squirtle", 74 | "img":"http://www.serebii.net/pokemongo/pokemon/007.png", 75 | "type": "Water", 76 | "height": "0.51 m", 77 | "weight": "9.0 kg", 78 | "candy": "25 Squirtle Candy", 79 | "egg": "2 km" 80 | }, 81 | { 82 | "id":"8", 83 | "num":"008", 84 | "name":"Wartortle", 85 | "img":"http://www.serebii.net/pokemongo/pokemon/008.png", 86 | "type": "Water", 87 | "height": "0.99 m", 88 | "weight": "22.5 kg", 89 | "candy": "100 Squirtle Candy", 90 | "egg": "Not in Eggs" 91 | }, 92 | { 93 | "id":"9", 94 | "num":"009", 95 | "name":"Blastoise", 96 | "img":"http://www.serebii.net/pokemongo/pokemon/009.png", 97 | "type": "Water", 98 | "height": "1.60 m", 99 | "weight": "85.5 kg", 100 | "candy": "None", 101 | "egg": "Not in Eggs" 102 | }, 103 | { 104 | "id":"10", 105 | "num":"010", 106 | "name":"Caterpie", 107 | "img":"http://www.serebii.net/pokemongo/pokemon/010.png", 108 | "type": "Bug", 109 | "height": "0.30 m", 110 | "weight": "2.9 kg", 111 | "candy": "12 Caterpie Candy", 112 | "egg": "2 km" 113 | }, 114 | { 115 | "id":"11", 116 | "num":"011", 117 | "name":"Metapod", 118 | "img":"http://www.serebii.net/pokemongo/pokemon/011.png", 119 | "type": "Bug", 120 | "height": "0.71 m", 121 | "weight": "9.9 kg", 122 | "candy": "50 Caterpie Candy", 123 | "egg": "Not in Eggs" 124 | }, 125 | { 126 | "id":"12", 127 | "num":"012", 128 | "name":"Butterfree", 129 | "img":"http://www.serebii.net/pokemongo/pokemon/012.png", 130 | "type": "Bug / Flying", 131 | "height": "1.09 m", 132 | "weight": "32.0 kg", 133 | "candy": "None", 134 | "egg": "Not in Eggs" 135 | }, 136 | { 137 | "id":"13", 138 | "num":"013", 139 | "name":"Weedle", 140 | "img":"http://www.serebii.net/pokemongo/pokemon/013.png", 141 | "type": "Bug / Poison", 142 | "height": "0.30 m", 143 | "weight": "3.2 kg", 144 | "candy": "12 Weedle Candy", 145 | "egg": "2 km" 146 | }, 147 | { 148 | "id":"14", 149 | "num":"014", 150 | "name":"Kakuna", 151 | "img":"http://www.serebii.net/pokemongo/pokemon/014.png", 152 | "type": "Bug / Poison", 153 | "height": "0.61 m", 154 | "weight": "10.0 kg", 155 | "candy": "50 Weedle Candy", 156 | "egg": "Not in Eggs" 157 | }, 158 | { 159 | "id":"15", 160 | "num":"015", 161 | "name":"Beedrill", 162 | "img":"http://www.serebii.net/pokemongo/pokemon/015.png", 163 | "type": "Bug / Poison", 164 | "height": "0.99 m", 165 | "weight": "29.5 kg", 166 | "candy": "None", 167 | "egg": "Not in Eggs" 168 | }, 169 | { 170 | "id":"16", 171 | "num":"016", 172 | "name":"Pidgey", 173 | "img":"http://www.serebii.net/pokemongo/pokemon/016.png", 174 | "type": "Normal / Flying", 175 | "height": "0.30 m", 176 | "weight": "1.8 kg", 177 | "candy": "12 Pidgey Candy", 178 | "egg": "2 km" 179 | }, 180 | { 181 | "id":"17", 182 | "num":"017", 183 | "name":"Pidgeotto", 184 | "img":"http://www.serebii.net/pokemongo/pokemon/017.png", 185 | "type": "Normal / Flying", 186 | "height": "1.09 m", 187 | "weight": "30.0 kg", 188 | "candy": "50 Pidgey Candy", 189 | "egg": "Not in Eggs" 190 | }, 191 | { 192 | "id":"18", 193 | "num":"018", 194 | "name":"Pidgeot", 195 | "img":"http://www.serebii.net/pokemongo/pokemon/018.png", 196 | "type": "Normal / Flying", 197 | "height": "1.50 m", 198 | "weight": "39.5 kg", 199 | "candy": "None", 200 | "egg": "Not in Eggs" 201 | }, 202 | { 203 | "id":"19", 204 | "num":"019", 205 | "name":"Rattata", 206 | "img":"http://www.serebii.net/pokemongo/pokemon/019.png", 207 | "type": "Normal", 208 | "height": "0.30 m", 209 | "weight": "3.5 kg", 210 | "candy": "25 Rattata Candy", 211 | "egg": "2 km" 212 | }, 213 | { 214 | "id":"20", 215 | "num":"020", 216 | "name":"Raticate", 217 | "img":"http://www.serebii.net/pokemongo/pokemon/020.png", 218 | "type": "Normal", 219 | "height": "0.71 m", 220 | "weight": "18.5 kg", 221 | "candy": "None", 222 | "egg": "Not in Eggs" 223 | }, 224 | { 225 | "id":"21", 226 | "num":"021", 227 | "name":"Spearow", 228 | "img":"http://www.serebii.net/pokemongo/pokemon/021.png", 229 | "type": "Normal / Flying", 230 | "height": "0.30 m", 231 | "weight": "2.0 kg", 232 | "candy": "50 Spearow Candy", 233 | "egg": "2 km" 234 | }, 235 | { 236 | "id":"22", 237 | "num":"022", 238 | "name":"Fearow", 239 | "img":"http://www.serebii.net/pokemongo/pokemon/022.png", 240 | "type": "Normal / Flying", 241 | "height": "1.19 m", 242 | "weight": "38.0 kg", 243 | "candy": "None", 244 | "egg": "Not in Eggs" 245 | }, 246 | { 247 | "id":"23", 248 | "num":"023", 249 | "name":"Ekans", 250 | "img":"http://www.serebii.net/pokemongo/pokemon/023.png", 251 | "type": "Poison", 252 | "height": "2.01 m", 253 | "weight": "6.9 kg", 254 | "candy": "50 Ekans Candy", 255 | "egg": "5 km" 256 | }, 257 | { 258 | "id":"24", 259 | "num":"024", 260 | "name":"Arbok", 261 | "img":"http://www.serebii.net/pokemongo/pokemon/024.png", 262 | "type": "Poison", 263 | "height": "3.51 m", 264 | "weight": "65.0 kg", 265 | "candy": "None", 266 | "egg": "Not in Eggs" 267 | }, 268 | { 269 | "id":"25", 270 | "num":"025", 271 | "name":"Pikachu", 272 | "img":"http://www.serebii.net/pokemongo/pokemon/025.png", 273 | "type": "Electric", 274 | "height": "0.41 m", 275 | "weight": "6.0 kg", 276 | "candy": "50 Pikachu Candy", 277 | "egg": "2 km" 278 | }, 279 | { 280 | "id":"26", 281 | "num":"026", 282 | "name":"Raichu", 283 | "img":"http://www.serebii.net/pokemongo/pokemon/026.png", 284 | "type": "Electric", 285 | "height": "0.79 m", 286 | "weight": "30.0 kg", 287 | "candy": "None", 288 | "egg": "Not in Eggs" 289 | }, 290 | { 291 | "id":"27", 292 | "num":"027", 293 | "name":"Sandshrew", 294 | "img":"http://www.serebii.net/pokemongo/pokemon/027.png", 295 | "type": "Ground", 296 | "height": "0.61 m", 297 | "weight": "12.0 kg", 298 | "candy": "50 Sandshrew Candy", 299 | "egg": "5 km" 300 | }, 301 | { 302 | "id":"28", 303 | "num":"028", 304 | "name":"Sandslash", 305 | "img":"http://www.serebii.net/pokemongo/pokemon/028.png", 306 | "type": "Ground", 307 | "height": "0.99 m", 308 | "weight": "29.5 kg", 309 | "candy": "None", 310 | "egg": "Not in Eggs" 311 | }, 312 | { 313 | "id":"29", 314 | "num":"029", 315 | "name":"Nidoran ♀", 316 | "img":"http://www.serebii.net/pokemongo/pokemon/029.png", 317 | "type": "Poison", 318 | "height": "0.41 m", 319 | "weight": "7.0 kg", 320 | "candy": "25 Nidoran ♀ Candy", 321 | "egg": "5 km" 322 | }, 323 | { 324 | "id":"30", 325 | "num":"030", 326 | "name":"Nidorina", 327 | "img":"http://www.serebii.net/pokemongo/pokemon/030.png", 328 | "type": "Poison", 329 | "height": "0.79 m", 330 | "weight": "20.0 kg", 331 | "candy": "100 Nidoran ♀ Candy", 332 | "egg": "Not in Eggs" 333 | }, 334 | { 335 | "id":"31", 336 | "num":"031", 337 | "name":"Nidoqueen", 338 | "img":"http://www.serebii.net/pokemongo/pokemon/031.png", 339 | "type": "Poison / Ground", 340 | "height": "1.30 m", 341 | "weight": "60.0 kg", 342 | "candy": "None", 343 | "egg": "Not in Eggs" 344 | }, 345 | { 346 | "id":"32", 347 | "num":"032", 348 | "name":"Nidoran ♂", 349 | "img":"http://www.serebii.net/pokemongo/pokemon/032.png", 350 | "type": "Poison", 351 | "height": "0.51 m", 352 | "weight": "9.0 kg", 353 | "candy": "25 Nidoran♂ Candy", 354 | "egg": "5 km" 355 | }, 356 | { 357 | "id":"33", 358 | "num":"033", 359 | "name":"Nidorino", 360 | "img":"http://www.serebii.net/pokemongo/pokemon/033.png", 361 | "type": "Poison", 362 | "height": "0.89 m", 363 | "weight": "19.5 kg", 364 | "candy": "100 Nidoran ♂ Candy", 365 | "egg": "Not in Eggs" 366 | }, 367 | { 368 | "id":"34", 369 | "num":"034", 370 | "name":"Nidoking", 371 | "img":"http://www.serebii.net/pokemongo/pokemon/034.png", 372 | "type": "Poison / Ground", 373 | "height": "1.40 m", 374 | "weight": "62.0 kg", 375 | "candy": "None", 376 | "egg": "Not in Eggs" 377 | }, 378 | { 379 | "id":"35", 380 | "num":"035", 381 | "name":"Clefairy", 382 | "img":"http://www.serebii.net/pokemongo/pokemon/035.png", 383 | "type": "Normal", 384 | "height": "0.61 m", 385 | "weight": "7.5 kg", 386 | "candy": "50 Clefairy Candy", 387 | "egg": "2 km" 388 | }, 389 | { 390 | "id":"36", 391 | "num":"036", 392 | "name":"Clefable", 393 | "img":"http://www.serebii.net/pokemongo/pokemon/036.png", 394 | "type": "Normal", 395 | "height": "1.30 m", 396 | "weight": "40.0 kg", 397 | "candy": "None", 398 | "egg": "Not in Eggs" 399 | }, 400 | { 401 | "id":"37", 402 | "num":"037", 403 | "name":"Vulpix", 404 | "img":"http://www.serebii.net/pokemongo/pokemon/037.png", 405 | "type": "Fire", 406 | "height": "0.61 m", 407 | "weight": "9.9 kg", 408 | "candy": "50 Vulpix Candy", 409 | "egg": "5 km" 410 | }, 411 | { 412 | "id":"38", 413 | "num":"038", 414 | "name":"Ninetales", 415 | "img":"http://www.serebii.net/pokemongo/pokemon/038.png", 416 | "type": "Fire", 417 | "height": "1.09 m", 418 | "weight": "19.9 kg", 419 | "candy": "None", 420 | "egg": "Not in Eggs" 421 | }, 422 | { 423 | "id":"39", 424 | "num":"039", 425 | "name":"Jigglypuff", 426 | "img":"http://www.serebii.net/pokemongo/pokemon/039.png", 427 | "type": "Normal", 428 | "height": "0.51 m", 429 | "weight": "5.5 kg", 430 | "candy": "50 Jigglypuff Candy", 431 | "egg": "2 km" 432 | }, 433 | { 434 | "id":"40", 435 | "num":"040", 436 | "name":"Wigglytuff", 437 | "img":"http://www.serebii.net/pokemongo/pokemon/040.png", 438 | "type": "Normal", 439 | "height": "0.99 m", 440 | "weight": "12.0 kg", 441 | "candy": "None", 442 | "egg": "Not in Eggs" 443 | }, 444 | { 445 | "id":"41", 446 | "num":"041", 447 | "name":"Zubat", 448 | "img":"http://www.serebii.net/pokemongo/pokemon/041.png", 449 | "type": "Poison / Flying", 450 | "height": "0.79 m", 451 | "weight": "7.5 kg", 452 | "candy": "50 Zubat Candy", 453 | "egg": "2 km" 454 | }, 455 | { 456 | "id":"42", 457 | "num":"042", 458 | "name":"Golbat", 459 | "img":"http://www.serebii.net/pokemongo/pokemon/042.png", 460 | "type": "Poison / Flying", 461 | "height": "1.60 m", 462 | "weight": "55.0 kg", 463 | "candy": "None", 464 | "egg": "Not in Eggs" 465 | }, 466 | { 467 | "id":"43", 468 | "num":"043", 469 | "name":"Oddish", 470 | "img":"http://www.serebii.net/pokemongo/pokemon/043.png", 471 | "type": "Grass / Poison", 472 | "height": "0.51 m", 473 | "weight": "5.4 kg", 474 | "candy": "25 Oddish Candy", 475 | "egg": "5 km" 476 | }, 477 | { 478 | "id":"44", 479 | "num":"044", 480 | "name":"Gloom", 481 | "img":"http://www.serebii.net/pokemongo/pokemon/044.png", 482 | "type": "Grass / Poison", 483 | "height": "0.79 m", 484 | "weight": "8.6 kg", 485 | "candy": "100 Oddish Candy", 486 | "egg": "Not in Eggs" 487 | }, 488 | { 489 | "id":"45", 490 | "num":"045", 491 | "name":"Vileplume", 492 | "img":"http://www.serebii.net/pokemongo/pokemon/045.png", 493 | "type": "Grass / Poison", 494 | "height": "1.19 m", 495 | "weight": "18.6 kg", 496 | "candy": "None", 497 | "egg": "Not in Eggs" 498 | }, 499 | { 500 | "id":"46", 501 | "num":"046", 502 | "name":"Paras", 503 | "img":"http://www.serebii.net/pokemongo/pokemon/046.png", 504 | "type": "Bug / Grass", 505 | "height": "0.30 m", 506 | "weight": "5.4 kg", 507 | "candy": "50 Paras Candy", 508 | "egg": "5 km" 509 | }, 510 | { 511 | "id":"47", 512 | "num":"047", 513 | "name":"Parasect", 514 | "img":"http://www.serebii.net/pokemongo/pokemon/047.png", 515 | "type": "Bug / Grass", 516 | "height": "0.99 m", 517 | "weight": "29.5 kg", 518 | "candy": "None", 519 | "egg": "Not in Eggs" 520 | }, 521 | { 522 | "id":"48", 523 | "num":"048", 524 | "name":"Venonat", 525 | "img":"http://www.serebii.net/pokemongo/pokemon/048.png", 526 | "type": "Bug / Poison", 527 | "height": "0.99 m", 528 | "weight": "30.0 kg", 529 | "candy": "50 Venonat Candy", 530 | "egg": "5 km" 531 | }, 532 | { 533 | "id":"49", 534 | "num":"049", 535 | "name":"Venomoth", 536 | "img":"http://www.serebii.net/pokemongo/pokemon/049.png", 537 | "type": "Bug / Poison", 538 | "height": "1.50 m", 539 | "weight": "12.5 kg", 540 | "candy": "None", 541 | "egg": "Not in Eggs" 542 | }, 543 | { 544 | "id":"50", 545 | "num":"050", 546 | "name":"Diglett", 547 | "img":"http://www.serebii.net/pokemongo/pokemon/050.png", 548 | "type": "Ground", 549 | "height": "0.20 m", 550 | "weight": "0.8 kg", 551 | "candy": "50 Diglett Candy", 552 | "egg": "5 km" 553 | }, 554 | { 555 | "id":"51", 556 | "num":"051", 557 | "name":"Dugtrio", 558 | "img":"http://www.serebii.net/pokemongo/pokemon/051.png", 559 | "type": "Ground", 560 | "height": "0.71 m", 561 | "weight": "33.3 kg", 562 | "candy": "None", 563 | "egg": "Not in Eggs" 564 | }, 565 | { 566 | "id":"52", 567 | "num":"052", 568 | "name":"Meowth", 569 | "img":"http://www.serebii.net/pokemongo/pokemon/052.png", 570 | "type": "Normal", 571 | "height": "0.41 m", 572 | "weight": "4.2 kg", 573 | "candy": "50 Meowth Candy", 574 | "egg": "5 km" 575 | }, 576 | { 577 | "id":"53", 578 | "num":"053", 579 | "name":"Persian", 580 | "img":"http://www.serebii.net/pokemongo/pokemon/053.png", 581 | "type": "Normal", 582 | "height": "0.99 m", 583 | "weight": "32.0 kg", 584 | "candy": "None", 585 | "egg": "Not in Eggs" 586 | }, 587 | { 588 | "id":"54", 589 | "num":"054", 590 | "name":"Psyduck", 591 | "img":"http://www.serebii.net/pokemongo/pokemon/054.png", 592 | "type": "Water", 593 | "height": "0.79 m", 594 | "weight": "19.6 kg", 595 | "candy": "50 Psyduck Candy", 596 | "egg": "5 km" 597 | }, 598 | { 599 | "id":"55", 600 | "num":"055", 601 | "name":"Golduck", 602 | "img":"http://www.serebii.net/pokemongo/pokemon/055.png", 603 | "type": "Water", 604 | "height": "1.70 m", 605 | "weight": "76.6 kg", 606 | "candy": "None", 607 | "egg": "Not in Eggs" 608 | }, 609 | { 610 | "id":"56", 611 | "num":"056", 612 | "name":"Mankey", 613 | "img":"http://www.serebii.net/pokemongo/pokemon/056.png", 614 | "type": "Fighting", 615 | "height": "0.51 m", 616 | "weight": "28.0 kg", 617 | "candy": "50 Mankey Candy", 618 | "egg": "5 km" 619 | }, 620 | { 621 | "id":"57", 622 | "num":"057", 623 | "name":"Primeape", 624 | "img":"http://www.serebii.net/pokemongo/pokemon/057.png", 625 | "type": "Fighting", 626 | "height": "0.99 m", 627 | "weight": "32.0 kg", 628 | "candy": "None", 629 | "egg": "Not in Eggs" 630 | }, 631 | { 632 | "id":"58", 633 | "num":"058", 634 | "name":"Growlithe", 635 | "img":"http://www.serebii.net/pokemongo/pokemon/058.png", 636 | "type": "Fire", 637 | "height": "0.71 m", 638 | "weight": "19.0 kg", 639 | "candy": "50 Growlithe Candy", 640 | "egg": "5 km" 641 | }, 642 | { 643 | "id":"59", 644 | "num":"059", 645 | "name":"Arcanine", 646 | "img":"http://www.serebii.net/pokemongo/pokemon/059.png", 647 | "type": "Fire", 648 | "height": "1.91 m", 649 | "weight": "155.0 kg", 650 | "candy": "None", 651 | "egg": "Not in Eggs" 652 | }, 653 | { 654 | "id":"60", 655 | "num":"060", 656 | "name":"Poliwag", 657 | "img":"http://www.serebii.net/pokemongo/pokemon/060.png", 658 | "type": "Water", 659 | "height": "0.61 m", 660 | "weight": "12.4 kg", 661 | "candy": "25 Poliwag Candy", 662 | "egg": "5 km" 663 | }, 664 | { 665 | "id":"61", 666 | "num":"061", 667 | "name":"Poliwhirl", 668 | "img":"http://www.serebii.net/pokemongo/pokemon/061.png", 669 | "type": "Water", 670 | "height": "0.99 m", 671 | "weight": "20.0 kg", 672 | "candy": "100 Poliwag Candy", 673 | "egg": "Not in Eggs" 674 | }, 675 | { 676 | "id":"62", 677 | "num":"062", 678 | "name":"Poliwrath", 679 | "img":"http://www.serebii.net/pokemongo/pokemon/062.png", 680 | "type": "Water / Fighting", 681 | "height": "1.30 m", 682 | "weight": "54.0 kg", 683 | "candy": "None", 684 | "egg": "Not in Eggs" 685 | }, 686 | { 687 | "id":"63", 688 | "num":"063", 689 | "name":"Abra", 690 | "img":"http://www.serebii.net/pokemongo/pokemon/063.png", 691 | "type": "Psychic", 692 | "height": "0.89 m", 693 | "weight": "19.5 kg", 694 | "candy": "25 Abra Candy", 695 | "egg": "5 km" 696 | }, 697 | { 698 | "id":"64", 699 | "num":"064", 700 | "name":"Kadabra", 701 | "img":"http://www.serebii.net/pokemongo/pokemon/064.png", 702 | "type": "Psychic", 703 | "height": "1.30 m", 704 | "weight": "56.5 kg", 705 | "candy": "100 Abra Candy", 706 | "egg": "Not in Eggs" 707 | }, 708 | { 709 | "id":"65", 710 | "num":"065", 711 | "name":"Alakazam", 712 | "img":"http://www.serebii.net/pokemongo/pokemon/065.png", 713 | "type": "Psychic", 714 | "height": "1.50 m", 715 | "weight": "48.0 kg", 716 | "candy": "None", 717 | "egg": "Not in Eggs" 718 | }, 719 | { 720 | "id":"66", 721 | "num":"066", 722 | "name":"Machop", 723 | "img":"http://www.serebii.net/pokemongo/pokemon/066.png", 724 | "type": "Fighting", 725 | "height": "0.79 m", 726 | "weight": "19.5 kg", 727 | "candy": "25 Machop Candy", 728 | "egg": "5 km" 729 | }, 730 | { 731 | "id":"67", 732 | "num":"067", 733 | "name":"Machoke", 734 | "img":"http://www.serebii.net/pokemongo/pokemon/067.png", 735 | "type": "Fighting", 736 | "height": "1.50 m", 737 | "weight": "70.5 kg", 738 | "candy": "100 Machop Candy", 739 | "egg": "Not in Eggs" 740 | }, 741 | { 742 | "id":"68", 743 | "num":"068", 744 | "name":"Machamp", 745 | "img":"http://www.serebii.net/pokemongo/pokemon/068.png", 746 | "type": "Fighting", 747 | "height": "1.60 m", 748 | "weight": "130.0 kg", 749 | "candy": "None", 750 | "egg": "Not in Eggs" 751 | }, 752 | { 753 | "id":"69", 754 | "num":"069", 755 | "name":"Bellsprout", 756 | "img":"http://www.serebii.net/pokemongo/pokemon/069.png", 757 | "type": "Grass / Poison", 758 | "height": "0.71 m", 759 | "weight": "4.0 kg", 760 | "candy": "25 Bellsprout Candy", 761 | "egg": "5 km" 762 | }, 763 | { 764 | "id":"70", 765 | "num":"070", 766 | "name":"Weepinbell", 767 | "img":"http://www.serebii.net/pokemongo/pokemon/070.png", 768 | "type": "Grass / Poison", 769 | "height": "0.99 m", 770 | "weight": "6.4 kg", 771 | "candy": "100 Bellsprout Candy", 772 | "egg": "Not in Eggs" 773 | }, 774 | { 775 | "id":"71", 776 | "num":"071", 777 | "name":"Victreebel", 778 | "img":"http://www.serebii.net/pokemongo/pokemon/071.png", 779 | "type": "Grass / Poison", 780 | "height": "1.70 m", 781 | "weight": "15.5 kg", 782 | "candy": "None", 783 | "egg": "Not in Eggs" 784 | }, 785 | { 786 | "id":"72", 787 | "num":"072", 788 | "name":"Tentacool", 789 | "img":"http://www.serebii.net/pokemongo/pokemon/072.png", 790 | "type": "Water / Poison", 791 | "height": "0.89 m", 792 | "weight": "45.5 kg", 793 | "candy": "50 Tentacool Candy", 794 | "egg": "5 km" 795 | }, 796 | { 797 | "id":"73", 798 | "num":"073", 799 | "name":"Tentacruel", 800 | "img":"http://www.serebii.net/pokemongo/pokemon/073.png", 801 | "type": "Water / Poison", 802 | "height": "1.60 m", 803 | "weight": "55.0 kg", 804 | "candy": "None", 805 | "egg": "Not in Eggs" 806 | }, 807 | { 808 | "id":"74", 809 | "num":"074", 810 | "name":"Geodude", 811 | "img":"http://www.serebii.net/pokemongo/pokemon/074.png", 812 | "type": "Rock / Ground", 813 | "height": "0.41 m", 814 | "weight": "20.0 kg", 815 | "candy": "25 Geodude Candy", 816 | "egg": "2 km" 817 | }, 818 | { 819 | "id":"75", 820 | "num":"075", 821 | "name":"Graveler", 822 | "img":"http://www.serebii.net/pokemongo/pokemon/075.png", 823 | "type": "Rock / Ground", 824 | "height": "0.99 m", 825 | "weight": "105.0 kg", 826 | "candy": "100 Geodude Candy", 827 | "egg": "Not in Eggs" 828 | }, 829 | { 830 | "id":"76", 831 | "num":"076", 832 | "name":"Golem", 833 | "img":"http://www.serebii.net/pokemongo/pokemon/076.png", 834 | "type": "Rock / Ground", 835 | "height": "1.40 m", 836 | "weight": "300.0 kg", 837 | "candy": "None", 838 | "egg": "Not in Eggs" 839 | }, 840 | { 841 | "id":"77", 842 | "num":"077", 843 | "name":"Ponyta", 844 | "img":"http://www.serebii.net/pokemongo/pokemon/077.png", 845 | "type": "Fire", 846 | "height": "0.99 m", 847 | "weight": "30.0 kg", 848 | "candy": "50 Ponyta Candy", 849 | "egg": "5 km" 850 | }, 851 | { 852 | "id":"78", 853 | "num":"078", 854 | "name":"Rapidash", 855 | "img":"http://www.serebii.net/pokemongo/pokemon/078.png", 856 | "type": "Fire", 857 | "height": "1.70 m", 858 | "weight": "95.0 kg", 859 | "candy": "None", 860 | "egg": "Not in Eggs" 861 | }, 862 | { 863 | "id":"79", 864 | "num":"079", 865 | "name":"Slowpoke", 866 | "img":"http://www.serebii.net/pokemongo/pokemon/079.png", 867 | "type": "Water / Psychic", 868 | "height": "1.19 m", 869 | "weight": "36.0 kg", 870 | "candy": "50 Slowpoke Candy", 871 | "egg": "5 km" 872 | }, 873 | { 874 | "id":"80", 875 | "num":"080", 876 | "name":"Slowbro", 877 | "img":"http://www.serebii.net/pokemongo/pokemon/080.png", 878 | "type": "Water / Psychic", 879 | "height": "1.60 m", 880 | "weight": "78.5 kg", 881 | "candy": "None", 882 | "egg": "Not in Eggs" 883 | }, 884 | { 885 | "id":"81", 886 | "num":"081", 887 | "name":"Magnemite", 888 | "img":"http://www.serebii.net/pokemongo/pokemon/081.png", 889 | "type": "Electric", 890 | "height": "0.30 m", 891 | "weight": "6.0 kg", 892 | "candy": "50 Magnemite Candy", 893 | "egg": "5 km" 894 | }, 895 | { 896 | "id":"82", 897 | "num":"082", 898 | "name":"Magneton", 899 | "img":"http://www.serebii.net/pokemongo/pokemon/082.png", 900 | "type": "Electric", 901 | "height": "0.99 m", 902 | "weight": "60.0 kg", 903 | "candy": "None", 904 | "egg": "Not in Eggs" 905 | }, 906 | { 907 | "id":"83", 908 | "num":"083", 909 | "name":"Farfetch'd", 910 | "img":"http://www.serebii.net/pokemongo/pokemon/083.png", 911 | "type": "Normal / Flying", 912 | "height": "0.79 m", 913 | "weight": "15.0 kg", 914 | "candy": "None", 915 | "egg": "5 km" 916 | }, 917 | { 918 | "id":"84", 919 | "num":"084", 920 | "name":"Doduo", 921 | "img":"http://www.serebii.net/pokemongo/pokemon/084.png", 922 | "type": "Normal / Flying", 923 | "height": "1.40 m", 924 | "weight": "39.2 kg", 925 | "candy": "50 Doduo Candy", 926 | "egg": "5 km" 927 | }, 928 | { 929 | "id":"85", 930 | "num":"085", 931 | "name":"Dodrio", 932 | "img":"http://www.serebii.net/pokemongo/pokemon/085.png", 933 | "type": "Normal / Flying", 934 | "height": "1.80 m", 935 | "weight": "85.2 kg", 936 | "candy": "None", 937 | "egg": "Not in Eggs" 938 | }, 939 | { 940 | "id":"86", 941 | "num":"086", 942 | "name":"Seel", 943 | "img":"http://www.serebii.net/pokemongo/pokemon/086.png", 944 | "type": "Water", 945 | "height": "1.09 m", 946 | "weight": "90.0 kg", 947 | "candy": "50 Seel Candy", 948 | "egg": "5 km" 949 | }, 950 | { 951 | "id":"87", 952 | "num":"087", 953 | "name":"Dewgong", 954 | "img":"http://www.serebii.net/pokemongo/pokemon/087.png", 955 | "type": "Water / Ice", 956 | "height": "1.70 m", 957 | "weight": "120.0 kg", 958 | "candy": "None", 959 | "egg": "Not in Eggs" 960 | }, 961 | { 962 | "id":"88", 963 | "num":"088", 964 | "name":"Grimer", 965 | "img":"http://www.serebii.net/pokemongo/pokemon/088.png", 966 | "type": "Poison", 967 | "height": "0.89 m", 968 | "weight": "30.0 kg", 969 | "candy": "50 Grimer Candy", 970 | "egg": "5 km" 971 | }, 972 | { 973 | "id":"89", 974 | "num":"089", 975 | "name":"Muk", 976 | "img":"http://www.serebii.net/pokemongo/pokemon/089.png", 977 | "type": "Poison", 978 | "height": "1.19 m", 979 | "weight": "30.0 kg", 980 | "candy": "None", 981 | "egg": "Not in Eggs" 982 | }, 983 | { 984 | "id":"90", 985 | "num":"090", 986 | "name":"Shellder", 987 | "img":"http://www.serebii.net/pokemongo/pokemon/090.png", 988 | "type": "Water", 989 | "height": "0.30 m", 990 | "weight": "4.0 kg", 991 | "candy": "50 Shellder Candy", 992 | "egg": "5 km" 993 | }, 994 | { 995 | "id":"91", 996 | "num":"091", 997 | "name":"Cloyster", 998 | "img":"http://www.serebii.net/pokemongo/pokemon/091.png", 999 | "type": "Water / Ice", 1000 | "height": "1.50 m", 1001 | "weight": "132.5 kg", 1002 | "candy": "None", 1003 | "egg": "Not in Eggs" 1004 | }, 1005 | { 1006 | "id":"92", 1007 | "num":"092", 1008 | "name":"Gastly", 1009 | "img":"http://www.serebii.net/pokemongo/pokemon/092.png", 1010 | "type": "Ghost / Poison", 1011 | "height": "1.30 m", 1012 | "weight": "0.1 kg", 1013 | "candy": "25 Gastly Candy", 1014 | "egg": "5 km" 1015 | }, 1016 | { 1017 | "id":"93", 1018 | "num":"093", 1019 | "name":"Haunter", 1020 | "img":"http://www.serebii.net/pokemongo/pokemon/093.png", 1021 | "type": "Ghost / Poison", 1022 | "height": "1.60 m", 1023 | "weight": "0.1 kg", 1024 | "candy": "100 Gastly Candy", 1025 | "egg": "Not in Eggs" 1026 | }, 1027 | { 1028 | "id":"94", 1029 | "num":"094", 1030 | "name":"Gengar", 1031 | "img":"http://www.serebii.net/pokemongo/pokemon/094.png", 1032 | "type": "Ghost / Poison", 1033 | "height": "1.50 m", 1034 | "weight": "40.5 kg", 1035 | "candy": "None", 1036 | "egg": "Not in Eggs" 1037 | }, 1038 | { 1039 | "id":"95", 1040 | "num":"095", 1041 | "name":"Onix", 1042 | "img":"http://www.serebii.net/pokemongo/pokemon/095.png", 1043 | "type": "Rock / Ground", 1044 | "height": "8.79 m", 1045 | "weight": "210.0 kg", 1046 | "candy": "None", 1047 | "egg": "10 km" 1048 | }, 1049 | { 1050 | "id":"96", 1051 | "num":"096", 1052 | "name":"Drowzee", 1053 | "img":"http://www.serebii.net/pokemongo/pokemon/096.png", 1054 | "type": "Psychic", 1055 | "height": "0.99 m", 1056 | "weight": "32.4 kg", 1057 | "candy": "50 Drowzee Candy", 1058 | "egg": "5 km" 1059 | }, 1060 | { 1061 | "id":"97", 1062 | "num":"097", 1063 | "name":"Hypno", 1064 | "img":"http://www.serebii.net/pokemongo/pokemon/097.png", 1065 | "type": "Psychic", 1066 | "height": "1.60 m", 1067 | "weight": "75.6 kg", 1068 | "candy": "None", 1069 | "egg": "Not in Eggs" 1070 | }, 1071 | { 1072 | "id":"98", 1073 | "num":"098", 1074 | "name":"Krabby", 1075 | "img":"http://www.serebii.net/pokemongo/pokemon/098.png", 1076 | "type": "Water", 1077 | "height": "0.41 m", 1078 | "weight": "6.5 kg", 1079 | "candy": "50 Krabby Candy", 1080 | "egg": "5 km" 1081 | }, 1082 | { 1083 | "id":"99", 1084 | "num":"099", 1085 | "name":"Kingler", 1086 | "img":"http://www.serebii.net/pokemongo/pokemon/099.png", 1087 | "type": "Water", 1088 | "height": "1.30 m", 1089 | "weight": "60.0 kg", 1090 | "candy": "None", 1091 | "egg": "Not in Eggs" 1092 | }, 1093 | { 1094 | "id":"100", 1095 | "num":"100", 1096 | "name":"Voltorb", 1097 | "img":"http://www.serebii.net/pokemongo/pokemon/100.png", 1098 | "type": "Electric", 1099 | "height": "0.51 m", 1100 | "weight": "10.4 kg", 1101 | "candy": "50 Voltorb Candy", 1102 | "egg": "5 km" 1103 | }, 1104 | { 1105 | "id":"101", 1106 | "num":"101", 1107 | "name":"Electrode", 1108 | "img":"http://www.serebii.net/pokemongo/pokemon/101.png", 1109 | "type": "Electric", 1110 | "height": "1.19 m", 1111 | "weight": "66.6 kg", 1112 | "candy": "None", 1113 | "egg": "Not in Eggs" 1114 | }, 1115 | { 1116 | "id":"102", 1117 | "num":"102", 1118 | "name":"Exeggcute", 1119 | "img":"http://www.serebii.net/pokemongo/pokemon/102.png", 1120 | "type": "Grass / Psychic", 1121 | "height": "0.41 m", 1122 | "weight": "2.5 kg", 1123 | "candy": "50 Exeggcute Candy", 1124 | "egg": "5 km" 1125 | }, 1126 | { 1127 | "id":"103", 1128 | "num":"103", 1129 | "name":"Exeggutor", 1130 | "img":"http://www.serebii.net/pokemongo/pokemon/103.png", 1131 | "type": "Grass / Psychic", 1132 | "height": "2.01 m", 1133 | "weight": "120.0 kg", 1134 | "candy": "None", 1135 | "egg": "Not in Eggs" 1136 | }, 1137 | { 1138 | "id":"104", 1139 | "num":"104", 1140 | "name":"Cubone", 1141 | "img":"http://www.serebii.net/pokemongo/pokemon/104.png", 1142 | "type": "Ground", 1143 | "height": "0.41 m", 1144 | "weight": "6.5 kg", 1145 | "candy": "50 Cubone Candy", 1146 | "egg": "5 km" 1147 | }, 1148 | { 1149 | "id":"105", 1150 | "num":"105", 1151 | "name":"Marowak", 1152 | "img":"http://www.serebii.net/pokemongo/pokemon/105.png", 1153 | "type": "Ground", 1154 | "height": "0.99 m", 1155 | "weight": "45.0 kg", 1156 | "candy": "None", 1157 | "egg": "Not in Eggs" 1158 | }, 1159 | { 1160 | "id":"106", 1161 | "num":"106", 1162 | "name":"Hitmonlee", 1163 | "img":"http://www.serebii.net/pokemongo/pokemon/106.png", 1164 | "type": "Fighting", 1165 | "height": "1.50 m", 1166 | "weight": "49.8 kg", 1167 | "candy": "None", 1168 | "egg": "10 km" 1169 | }, 1170 | { 1171 | "id":"107", 1172 | "num":"107", 1173 | "name":"Hitmonchan", 1174 | "img":"http://www.serebii.net/pokemongo/pokemon/107.png", 1175 | "type": "Fighting", 1176 | "height": "1.40 m", 1177 | "weight": "50.2 kg", 1178 | "candy": "None", 1179 | "egg": "10 km" 1180 | }, 1181 | { 1182 | "id":"108", 1183 | "num":"108", 1184 | "name":"Lickitung", 1185 | "img":"http://www.serebii.net/pokemongo/pokemon/108.png", 1186 | "type": "Normal", 1187 | "height": "1.19 m", 1188 | "weight": "65.5 kg", 1189 | "candy": "None", 1190 | "egg": "5 km" 1191 | }, 1192 | { 1193 | "id":"109", 1194 | "num":"109", 1195 | "name":"Koffing", 1196 | "img":"http://www.serebii.net/pokemongo/pokemon/109.png", 1197 | "type": "Poison", 1198 | "height": "0.61 m", 1199 | "weight": "1.0 kg", 1200 | "candy": "50 Koffing Candy", 1201 | "egg": "5 km" 1202 | }, 1203 | { 1204 | "id":"110", 1205 | "num":"110", 1206 | "name":"Weezing", 1207 | "img":"http://www.serebii.net/pokemongo/pokemon/110.png", 1208 | "type": "Poison", 1209 | "height": "1.19 m", 1210 | "weight": "9.5 kg", 1211 | "candy": "None", 1212 | "egg": "Not in Eggs" 1213 | }, 1214 | { 1215 | "id":"111", 1216 | "num":"111", 1217 | "name":"Rhyhorn", 1218 | "img":"http://www.serebii.net/pokemongo/pokemon/111.png", 1219 | "type": "Ground / Rock", 1220 | "height": "0.99 m", 1221 | "weight": "115.0 kg", 1222 | "candy": "50 Rhyhorn Candy", 1223 | "egg": "5 km" 1224 | }, 1225 | { 1226 | "id":"112", 1227 | "num":"112", 1228 | "name":"Rhydon", 1229 | "img":"http://www.serebii.net/pokemongo/pokemon/112.png", 1230 | "type": "Ground / Rock", 1231 | "height": "1.91 m", 1232 | "weight": "120.0 kg", 1233 | "candy": "None", 1234 | "egg": "Not in Eggs" 1235 | }, 1236 | { 1237 | "id":"113", 1238 | "num":"113", 1239 | "name":"Chansey", 1240 | "img":"http://www.serebii.net/pokemongo/pokemon/113.png", 1241 | "type": "Normal", 1242 | "height": "1.09 m", 1243 | "weight": "34.6 kg", 1244 | "candy": "None", 1245 | "egg": "10 km" 1246 | }, 1247 | { 1248 | "id":"114", 1249 | "num":"114", 1250 | "name":"Tangela", 1251 | "img":"http://www.serebii.net/pokemongo/pokemon/114.png", 1252 | "type": "Grass", 1253 | "height": "0.99 m", 1254 | "weight": "35.0 kg", 1255 | "candy": "None", 1256 | "egg": "5 km" 1257 | }, 1258 | { 1259 | "id":"115", 1260 | "num":"115", 1261 | "name":"Kangaskhan", 1262 | "img":"http://www.serebii.net/pokemongo/pokemon/115.png", 1263 | "type": "Normal", 1264 | "height": "2.21 m", 1265 | "weight": "80.0 kg", 1266 | "candy": "None", 1267 | "egg": "5 km" 1268 | }, 1269 | { 1270 | "id":"116", 1271 | "num":"116", 1272 | "name":"Horsea", 1273 | "img":"http://www.serebii.net/pokemongo/pokemon/116.png", 1274 | "type": "Water", 1275 | "height": "0.41 m", 1276 | "weight": "8.0 kg", 1277 | "candy": "50 Horsea Candy", 1278 | "egg": "5 km" 1279 | }, 1280 | { 1281 | "id":"117", 1282 | "num":"117", 1283 | "name":"Seadra", 1284 | "img":"http://www.serebii.net/pokemongo/pokemon/117.png", 1285 | "type": "Water", 1286 | "height": "1.19 m", 1287 | "weight": "25.0 kg", 1288 | "candy": "None", 1289 | "egg": "Not in Eggs" 1290 | }, 1291 | { 1292 | "id":"118", 1293 | "num":"118", 1294 | "name":"Goldeen", 1295 | "img":"http://www.serebii.net/pokemongo/pokemon/118.png", 1296 | "type": "Water", 1297 | "height": "0.61 m", 1298 | "weight": "15.0 kg", 1299 | "candy": "50 Goldeen Candy", 1300 | "egg": "5 km" 1301 | }, 1302 | { 1303 | "id":"119", 1304 | "num":"119", 1305 | "name":"Seaking", 1306 | "img":"http://www.serebii.net/pokemongo/pokemon/119.png", 1307 | "type": "Water", 1308 | "height": "1.30 m", 1309 | "weight": "39.0 kg", 1310 | "candy": "None", 1311 | "egg": "Not in Eggs" 1312 | }, 1313 | { 1314 | "id":"120", 1315 | "num":"120", 1316 | "name":"Staryu", 1317 | "img":"http://www.serebii.net/pokemongo/pokemon/120.png", 1318 | "type": "Water", 1319 | "height": "0.79 m", 1320 | "weight": "34.5 kg", 1321 | "candy": "50 Staryu Candy", 1322 | "egg": "5 km" 1323 | }, 1324 | { 1325 | "id":"121", 1326 | "num":"121", 1327 | "name":"Starmie", 1328 | "img":"http://www.serebii.net/pokemongo/pokemon/121.png", 1329 | "type": "Water / Psychic", 1330 | "height": "1.09 m", 1331 | "weight": "80.0 kg", 1332 | "candy": "None", 1333 | "egg": "Not in Eggs" 1334 | }, 1335 | { 1336 | "id":"122", 1337 | "num":"122", 1338 | "name":"Mr. Mime", 1339 | "img":"http://www.serebii.net/pokemongo/pokemon/122.png", 1340 | "type": "Psychic", 1341 | "height": "1.30 m", 1342 | "weight": "54.5 kg", 1343 | "candy": "None", 1344 | "egg": "10 km" 1345 | }, 1346 | { 1347 | "id":"123", 1348 | "num":"123", 1349 | "name":"Scyther", 1350 | "img":"http://www.serebii.net/pokemongo/pokemon/123.png", 1351 | "type": "Bug / Flying", 1352 | "height": "1.50 m", 1353 | "weight": "56.0 kg", 1354 | "candy": "None", 1355 | "egg": "10 km" 1356 | }, 1357 | { 1358 | "id":"124", 1359 | "num":"124", 1360 | "name":"Jynx", 1361 | "img":"http://www.serebii.net/pokemongo/pokemon/124.png", 1362 | "type": "Ice / Psychic", 1363 | "height": "1.40 m", 1364 | "weight": "40.6 kg", 1365 | "candy": "None", 1366 | "egg": "10 km" 1367 | }, 1368 | { 1369 | "id":"125", 1370 | "num":"125", 1371 | "name":"Electabuzz", 1372 | "img":"http://www.serebii.net/pokemongo/pokemon/125.png", 1373 | "type": "Electric", 1374 | "height": "1.09 m", 1375 | "weight": "30.0 kg", 1376 | "candy": "None", 1377 | "egg": "10 km" 1378 | }, 1379 | { 1380 | "id":"126", 1381 | "num":"126", 1382 | "name":"Magmar", 1383 | "img":"http://www.serebii.net/pokemongo/pokemon/126.png", 1384 | "type": "Fire", 1385 | "height": "1.30 m", 1386 | "weight": "44.5 kg", 1387 | "candy": "None", 1388 | "egg": "10 km" 1389 | }, 1390 | { 1391 | "id":"127", 1392 | "num":"127", 1393 | "name":"Pinsir", 1394 | "img":"http://www.serebii.net/pokemongo/pokemon/127.png", 1395 | "type": "Bug", 1396 | "height": "1.50 m", 1397 | "weight": "55.0 kg", 1398 | "candy": "None", 1399 | "egg": "10 km" 1400 | }, 1401 | { 1402 | "id":"128", 1403 | "num":"128", 1404 | "name":"Tauros", 1405 | "img":"http://www.serebii.net/pokemongo/pokemon/128.png", 1406 | "type": "Normal", 1407 | "height": "1.40 m", 1408 | "weight": "88.4 kg", 1409 | "candy": "None", 1410 | "egg": "5 km" 1411 | }, 1412 | { 1413 | "id":"129", 1414 | "num":"129", 1415 | "name":"Magikarp", 1416 | "img":"http://www.serebii.net/pokemongo/pokemon/129.png", 1417 | "type": "Water", 1418 | "height": "0.89 m", 1419 | "weight": "10.0 kg", 1420 | "candy": "400 Magikarp Candy", 1421 | "egg": "2 km" 1422 | }, 1423 | { 1424 | "id":"130", 1425 | "num":"130", 1426 | "name":"Gyarados", 1427 | "img":"http://www.serebii.net/pokemongo/pokemon/130.png", 1428 | "type": "Water / Flying", 1429 | "height": "6.50 m", 1430 | "weight": "235.0 kg", 1431 | "candy": "None", 1432 | "egg": "Not in Eggs" 1433 | }, 1434 | { 1435 | "id":"131", 1436 | "num":"131", 1437 | "name":"Lapras", 1438 | "img":"http://www.serebii.net/pokemongo/pokemon/131.png", 1439 | "type": "Water / Ice", 1440 | "height": "2.49 m", 1441 | "weight": "220.0 kg", 1442 | "candy": "None", 1443 | "egg": "10 km" 1444 | }, 1445 | { 1446 | "id":"132", 1447 | "num":"132", 1448 | "name":"Ditto", 1449 | "img":"http://www.serebii.net/pokemongo/pokemon/132.png", 1450 | "type": "Normal", 1451 | "height": "0.30 m", 1452 | "weight": "4.0 kg", 1453 | "candy": "None", 1454 | "egg": "Not in Eggs" 1455 | }, 1456 | { 1457 | "id":"133", 1458 | "num":"133", 1459 | "name":"Eevee", 1460 | "img":"http://www.serebii.net/pokemongo/pokemon/133.png", 1461 | "type": "Normal", 1462 | "height": "0.30 m", 1463 | "weight": "6.5 kg", 1464 | "candy": "25 Eevee Candy (Evolution decided at random. Or, for first evolution, rename Eevee: Rainer => Vaporeon, Sparky => Jolteon, Pyro => Flareon)", 1465 | "egg": "10 km" 1466 | }, 1467 | { 1468 | "id":"134", 1469 | "num":"134", 1470 | "name":"Vaporeon", 1471 | "img":"http://www.serebii.net/pokemongo/pokemon/134.png", 1472 | "type": "Water", 1473 | "height": "0.99 m", 1474 | "weight": "29.0 kg", 1475 | "candy": "None", 1476 | "egg": "Not in Eggs" 1477 | }, 1478 | { 1479 | "id":"135", 1480 | "num":"135", 1481 | "name":"Jolteon", 1482 | "img":"http://www.serebii.net/pokemongo/pokemon/135.png", 1483 | "type": "Electric", 1484 | "height": "0.79 m", 1485 | "weight": "24.5 kg", 1486 | "candy": "None", 1487 | "egg": "Not in Eggs" 1488 | }, 1489 | { 1490 | "id":"136", 1491 | "num":"136", 1492 | "name":"Flareon", 1493 | "img":"http://www.serebii.net/pokemongo/pokemon/136.png", 1494 | "type": "Fire", 1495 | "height": "0.89 m", 1496 | "weight": "25.0 kg", 1497 | "candy": "None", 1498 | "egg": "Not in Eggs" 1499 | }, 1500 | { 1501 | "id":"137", 1502 | "num":"137", 1503 | "name":"Porygon", 1504 | "img":"http://www.serebii.net/pokemongo/pokemon/137.png", 1505 | "type": "Normal", 1506 | "height": "0.79 m", 1507 | "weight": "36.5 kg", 1508 | "candy": "None", 1509 | "egg": "5 km" 1510 | }, 1511 | { 1512 | "id":"138", 1513 | "num":"138", 1514 | "name":"Omanyte", 1515 | "img":"http://www.serebii.net/pokemongo/pokemon/138.png", 1516 | "type": "Rock / Water", 1517 | "height": "0.41 m", 1518 | "weight": "7.5 kg", 1519 | "candy": "50 Omanyte Candy", 1520 | "egg": "10 km" 1521 | }, 1522 | { 1523 | "id":"139", 1524 | "num":"139", 1525 | "name":"Omastar", 1526 | "img":"http://www.serebii.net/pokemongo/pokemon/139.png", 1527 | "type": "Rock / Water", 1528 | "height": "0.99 m", 1529 | "weight": "35.0 kg", 1530 | "candy": "None", 1531 | "egg": "Not in Eggs" 1532 | }, 1533 | { 1534 | "id":"140", 1535 | "num":"140", 1536 | "name":"Kabuto", 1537 | "img":"http://www.serebii.net/pokemongo/pokemon/140.png", 1538 | "type": "Rock / Water", 1539 | "height": "0.51 m", 1540 | "weight": "11.5 kg", 1541 | "candy": "50 Kabuto Candy", 1542 | "egg": "10 km" 1543 | }, 1544 | { 1545 | "id":"141", 1546 | "num":"141", 1547 | "name":"Kabutops", 1548 | "img":"http://www.serebii.net/pokemongo/pokemon/141.png", 1549 | "type": "Rock / Water", 1550 | "height": "1.30 m", 1551 | "weight": "40.5 kg", 1552 | "candy": "None", 1553 | "egg": "Not in Eggs" 1554 | }, 1555 | { 1556 | "id":"142", 1557 | "num":"142", 1558 | "name":"Aerodactyl", 1559 | "img":"http://www.serebii.net/pokemongo/pokemon/142.png", 1560 | "type": "Rock / Flying", 1561 | "height": "1.80 m", 1562 | "weight": "59.0 kg", 1563 | "candy": "None", 1564 | "egg": "10 km" 1565 | }, 1566 | { 1567 | "id":"143", 1568 | "num":"143", 1569 | "name":"Snorlax", 1570 | "img":"http://www.serebii.net/pokemongo/pokemon/143.png", 1571 | "type": "Normal", 1572 | "height": "2.11 m", 1573 | "weight": "460.0 kg", 1574 | "candy": "None", 1575 | "egg": "10 km" 1576 | }, 1577 | { 1578 | "id":"144", 1579 | "num":"144", 1580 | "name":"Articuno", 1581 | "img":"http://www.serebii.net/pokemongo/pokemon/144.png", 1582 | "type": "Ice / Flying", 1583 | "height": "1.70 m", 1584 | "weight": "55.4 kg", 1585 | "candy": "None", 1586 | "egg": "Not in Eggs" 1587 | }, 1588 | { 1589 | "id":"145", 1590 | "num":"145", 1591 | "name":"Zapdos", 1592 | "img":"http://www.serebii.net/pokemongo/pokemon/145.png", 1593 | "type": "Electric / Flying", 1594 | "height": "1.60 m", 1595 | "weight": "52.6 kg", 1596 | "candy": "None", 1597 | "egg": "Not in Eggs" 1598 | }, 1599 | { 1600 | "id":"146", 1601 | "num":"146", 1602 | "name":"Moltres", 1603 | "img":"http://www.serebii.net/pokemongo/pokemon/146.png", 1604 | "type": "Fire / Flying", 1605 | "height": "2.01 m", 1606 | "weight": "60.0 kg", 1607 | "candy": "None", 1608 | "egg": "Not in Eggs" 1609 | }, 1610 | { 1611 | "id":"147", 1612 | "num":"147", 1613 | "name":"Dratini", 1614 | "img":"http://www.serebii.net/pokemongo/pokemon/147.png", 1615 | "type": "Dragon", 1616 | "height": "1.80 m", 1617 | "weight": "3.3 kg", 1618 | "candy": "25 Dratini Candy", 1619 | "egg": "10 km" 1620 | }, 1621 | { 1622 | "id":"148", 1623 | "num":"148", 1624 | "name":"Dragonair", 1625 | "img":"http://www.serebii.net/pokemongo/pokemon/148.png", 1626 | "type": "Dragon", 1627 | "height": "3.99 m", 1628 | "weight": "16.5 kg", 1629 | "candy": "100 Dratini Candy", 1630 | "egg": "Not in Eggs" 1631 | }, 1632 | { 1633 | "id":"149", 1634 | "num":"149", 1635 | "name":"Dragonite", 1636 | "img":"http://www.serebii.net/pokemongo/pokemon/149.png", 1637 | "type": "Dragon / Flying", 1638 | "height": "2.21 m", 1639 | "weight": "210.0 kg", 1640 | "candy": "None", 1641 | "egg": "Not in Eggs" 1642 | }, 1643 | { 1644 | "id":"150", 1645 | "num":"150", 1646 | "name":"Mewtwo", 1647 | "img":"http://www.serebii.net/pokemongo/pokemon/150.png", 1648 | "type": "Psychic", 1649 | "height": "2.01 m", 1650 | "weight": "122.0 kg", 1651 | "candy": "None", 1652 | "egg": "Not in Eggs" 1653 | }, 1654 | { 1655 | "id":"151", 1656 | "num":"151", 1657 | "name":"Mew", 1658 | "img":"http://www.serebii.net/pokemongo/pokemon/151.png", 1659 | "type": "Psychic", 1660 | "height": "0.41 m", 1661 | "weight": "4.0 kg", 1662 | "candy": "None", 1663 | "egg": "Not in Eggs" 1664 | } 1665 | ] 1666 | } 1667 | -------------------------------------------------------------------------------- /tests/poke.io/GetLocation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | 5 | var Pokeio = require('../../poke.io.js'); 6 | var pokeio = new Pokeio.Pokeio(); 7 | 8 | var latitude = 40.4731191; 9 | var longitude = -77.31329079999999; 10 | 11 | pokeio.playerInfo.latitude = latitude; 12 | pokeio.playerInfo.longitude = longitude; 13 | 14 | test('poke.io.GetLocation', function (t) { 15 | t.plan(2); 16 | 17 | pokeio.GetLocation(function(err, address) { 18 | t.error(err, 'No error returned'); 19 | 20 | t.equal(address, '355 Lyons Rd, Millerstown, PA 17062, USA', 'Returned exprected address'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/poke.io/GetLocationCoords.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | 5 | var Pokeio = require('../../poke.io.js'); 6 | var pokeio = new Pokeio.Pokeio(); 7 | 8 | var latitude = 40.4731191; 9 | var longitude = -77.31329079999999; 10 | 11 | pokeio.playerInfo.latitude = latitude; 12 | pokeio.playerInfo.longitude = longitude; 13 | 14 | test('poke.io.GetLocationCoords', function (t) { 15 | t.plan(3); 16 | 17 | var coords = pokeio.GetLocationCoords(); 18 | 19 | t.equal(coords.latitude, latitude, 'Returned exprected latitude'); 20 | t.equal(coords.longitude, longitude, 'Returned exprected longitude'); 21 | t.equal(coords.altitude, 0, 'Returned exprected altitude'); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/poke.io/SetLocation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | 5 | var Pokeio = require('../../poke.io.js'); 6 | var pokeio = new Pokeio.Pokeio(); 7 | 8 | var latitude = 40.4731191; 9 | var longitude = -77.31329079999999; 10 | 11 | test('poke.io.SetLocation - fail to set type', function(t) { 12 | t.plan(2); 13 | 14 | var location = {}; 15 | 16 | pokeio.SetLocation(location, function(err, coords) { 17 | t.equal(err.message, 'Invalid location type', 'Returned expected error'); 18 | t.error(coords, 'Did not return coords, as expected'); 19 | }); 20 | }); 21 | 22 | test('poke.io.SetLocation - name', function(t) { 23 | t.plan(4); 24 | 25 | var location = { 26 | type: 'name', 27 | name: 'your mom' 28 | }; 29 | 30 | pokeio.SetLocation(location, function(err, coords) { 31 | t.error(err, 'No error returned'); 32 | 33 | t.equal(coords.latitude, latitude, 'Returned expected latitude'); 34 | t.equal(coords.longitude, longitude, 'Returned expected longitude'); 35 | t.equal(coords.altitude, 0, 'Returned expected altitude'); 36 | }); 37 | }); 38 | 39 | test('poke.io.SetLocation - fail to set location.name', function(t) { 40 | t.plan(2); 41 | 42 | var location = { 43 | type: 'name', 44 | }; 45 | 46 | pokeio.SetLocation(location, function(err, coords) { 47 | t.equal(err.message, 'You should add a location name', 'Returned expected error'); 48 | t.error(coords, 'Did not return coords, as expected'); 49 | }); 50 | }); 51 | 52 | test('poke.io.SetLocation - coords', function(t) { 53 | t.plan(4); 54 | 55 | var location = { 56 | type: 'coords', 57 | coords: { 58 | latitude: latitude, 59 | longitude: longitude, 60 | altitude: 0 61 | } 62 | }; 63 | 64 | pokeio.SetLocation(location, function(err, coords) { 65 | t.error(err, 'No error returned'); 66 | 67 | t.equal(coords.latitude, latitude, 'Returned expected latitude'); 68 | t.equal(coords.longitude, longitude, 'Returned expected longitude'); 69 | t.equal(coords.altitude, 0, 'Returned expected altitude'); 70 | }); 71 | }); 72 | 73 | test('poke.io.SetLocation - fail to set location.coords object', function(t) { 74 | t.plan(2); 75 | 76 | var location = { 77 | type: 'coords', 78 | }; 79 | 80 | pokeio.SetLocation(location, function(err, coords) { 81 | t.equal(err.message, 'Coords object missing', 'Returned expected error'); 82 | t.error(coords, 'Did not return coords, as expected'); 83 | }); 84 | }); 85 | --------------------------------------------------------------------------------