diff --git a/.gitignore b/.gitignore
index cb30b978fa..75bdb5ad83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,7 +35,6 @@ var/
.pydevproject
.settings/
-
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
@@ -98,19 +97,13 @@ ENV/
.idea/
# Personal load details
-config.json
src/
-info.json
-inventory.json
-pokedex.json
-web/catchable.json
-web/catchable-*.json
-web/location-*.json
-web/inventory-*.json
-web/location.json
-web/userdata.js
+web/
data/last-location*.json
data/catch-ignore.yml
-release_config.json
-web/userdata.js
-location.json
+
+#Multiple config
+configs/*
+!configs/config.json.example
+!configs/release_config.json.example
+!configs/config.json.pokemons.example
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 7930970692..612529e7ad 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
-[submodule "origin"]
+[submodule "web"]
path = web
url = https://github.com/OpenPoGo/OpenPoGoWeb.git
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 3a9d0b9443..bffb4ce251 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,4 +10,4 @@ addons:
install:
- pip install -r requirements.txt
- pip install pylint
-script: "python travis-pythoncheck.py"
+script: "python pylint-recursive.py"
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
new file mode 100644
index 0000000000..3b215b11c5
--- /dev/null
+++ b/CONTRIBUTORS.md
@@ -0,0 +1,33 @@
+## Contributors
+ * eggins [first pull request]
+ * crack00r
+ * ethervoid
+ * Bashin
+ * tstumm
+ * TheGoldenXY
+ * Reaver01
+ * rarshonsky
+ * earthchie
+ * haykuro
+ * 05-032
+ * sinistance
+ * CapCap
+ * mzupan
+ * gnekic(GeXx)
+ * Shoh
+ * luizperes
+ * brantje
+ * VirtualSatai
+ * dmateusp
+ * jtdroste
+ * msoedov
+ * Grace
+ * Calcyfer
+ * asaf400
+ * guyz
+ * DavidK1m
+ * budi-khoirudin
+ * riberod07
+ * th3w4y
+ * Leaklessgfy
+ * steffwiz
diff --git a/README.md b/README.md
index 77c4a53af8..b7a0480339 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,51 @@
-
-
-
-
-
+# PokemonGo-Bot
+PokemonGo bot is a project created by the [PokemonGoF](https://github.com/PokemonGoF) team.
+The project is currently setup in two different branches. `dev` and `master`.
+
+We use [Slack](https://slack.com) as a web chat. [Click here to join the chat!](https://pokemongo-bot.herokuapp.com)
+
+## Features
+- [x] GPS Location configuration
+- [x] Search Pokestops
+- [x] Catch Pokemon
+- [x] Determine which pokeball to use (uses Razz Berry if the catch percentage is low!)
+- [x] Exchange Pokemon as per configuration
+- [x] Evolve Pokemon as per configuration
+- [x] Auto switch mode (Inventory Checks - switches between catch/farming items)
+- [x] Limit the step to farm specific area for pokestops
+- [x] Rudimentary IV Functionality filter
+- [x] Ignore certain pokemon filter
+- [ ] Standalone Desktop Application
+- [ ] Hatch eggs
+- [ ] Incubate eggs
+- [ ] Use candy
+- [ ] Fight Gym
+
+## Wiki
+All information on [Getting Started](https://github.com/PokemonGoF/PokemonGo-Bot/wiki/Getting-Started) is available in the [Wiki](https://github.com/PokemonGoF/PokemonGo-Bot/wiki/)!
+To ensure that all updates are documented - [@eggins](https://github.com/eggins) will keep the Wiki updated with the latest information on installing, updating and configuring the bot.
+
+## Credits
+- [tejado](https://github.com/tejado) many thanks for the API
+- [Mila432](https://github.com/Mila432/Pokemon_Go_API) for the login secrets
+- [elliottcarlson](https://github.com/elliottcarlson) for the Google Auth PR
+- [AeonLucid](https://github.com/AeonLucid/POGOProtos) for improved protos
+- [AHAAAAAAA](https://github.com/AHAAAAAAA/PokemonGo-Map) for parts of the s2sphere stuff
+
+
+## Donation
+
+Bitcoin Address: 1PJMCx9NNQRasQYaa4MMff9yyNFffhHgLu
-
+
-# PokemonGo-Bot
-The Pokemon Go Bot, baking with community.
-## Project Chat
-We use [Slack](https://slack.com) as a web chat. [Click here to join the chat!](https://pokemongo-bot.herokuapp.com)
-## Breaking Changes
-You need modify config.json (config.json.example for example) then pokecli.py --config config.json
-Please clean up your old clone if you have issue, and following the [install instruction](https://github.com/PokemonGoF/PokemonGo-Bot#installation).
-
-## About dev/master Branch
-Dev branch has most up to date feature and even everyone handle the part well, still, will have broken changes. Your test contribute and PR for fix are warm welcome.
-Master branch is the stable branch.
-No PR on master branch to keep things easier.
+
+## OLD README BELOW. STILL UPDATING THIS.
+
## Table of Contents
-- [Project Chat](#project-chat)
-- [Features](#features)
-- [TODO List](#todo-list)
- __Installation__
- [Requirements](#requirements)
- [Mac](#installation-mac)
@@ -37,33 +58,6 @@ No PR on master branch to keep things easier.
- [Credits](#credits)
- [Donation](#donation)
-## Features
- * Search Fort (Spin Pokestop)
- * Catch Pokemon
- * Release low cp pokemon
- * Walking as you
- * Limit the step to farm specific area for pokestops
- * Use the ball you have to catch, don't if you don't have
- * Rudimentary IV Functionality filter
- * Auto switch mode(Full of item then catch, no ball useable then farm)
- * Ignore certain pokemon filter
- * Use superior ball types when necessary
- * When out of normal pokeballs, use the next type of ball unless there are less than 10 of that type, in which case switch to farm mode
- * Drop items when bag is full (In Testing, Document contribute needed)
- * Pokemon catch filter (In Testing, Document contribute needed)
- * Google Map API key setup (Readme update needed)
- * Show all objects on map (In Testing)
- * Evolve pokemons (Code in, Need input, In Testing)
-
-## TODO List
-
-- [ ] Standalone Desktop APP
-- [ ] Pokemon transfer filter ?? This already done, right?
-- [ ] Hatch eggs
-- [ ] Incubate eggs
-- [ ] Use candy
-- [ ] Fight Gym
-
## Installation
### Requirements (click each one for install guide)
@@ -82,10 +76,10 @@ No PR on master branch to keep things easier.
- Linux: `apt-get install python-protobuf`
### Note on branch
-Please keep in mind that master is not always up to date whereas 'dev' is. In the installation note below change `master` to `dev` if you want to get the latest version.
+Please keep in mind that master is not always up-to-date whereas 'dev' is. In the installation note below change `master` to `dev` if you want to get and use the latest version.
### Installation Linux
-(change master to dev for the newer version)
+(change master to dev for the latest version)
```
$ git clone -b master https://github.com/PokemonGoF/PokemonGo-Bot
@@ -96,7 +90,7 @@ $ git submodule update
```
### Installation Mac
-(change master to dev for the newer version)
+(change master to dev for the latest version)
```
$ git clone -b master https://github.com/PokemonGoF/PokemonGo-Bot
@@ -109,7 +103,7 @@ $ git submodule update
```
### Installation Windows
-(change master to dev for the newer version)
+(change master to dev for the latest version)
On Windows, you will need to install PyYaml through the installer and not through requirements.txt.
@@ -128,7 +122,7 @@ $ pip install PyYAML-3.11-cp27-cp27m-win32.whl
// (replace PyYAML-3.11-cp27-cp27m-win32.whl with PyYAML-3.11-cp27-cp27m-win_amd64.whl
```
-After this, just do :
+After this, just do:
```
$ git clone -b master https://github.com/PokemonGoF/PokemonGo-Bot
@@ -164,20 +158,20 @@ This project uses Google Maps. There's one map coupled with the project, but as
6. After the code done, will update here how to replace.
### Python possible bug
-If you encounter problems with the module `ssl` and it function `_create_unverified_context`. Just comment it. (Solution available in Python 2.7.11)
-To do it follow instruction below :
+If you encounter problems with the module `ssl` and it's function `_create_unverified_context`, just comment it. (Solution available in Python 2.7.11)
+In order to comment out the function and the module, please follow the instructions below:
- edit `pokecli.py`
- put `#` before `if` (line 43) and `ssl` (line 44)
- save it
-Please keep in mind that this fix is necessary only if your python version don't have the `_create_unverified_context` argument in ssl module.
+Please keep in mind that this fix is only necessary if your python version don't have the `_create_unverified_context` argument in the ssl module.
## Update
To update your project do: `git pull` in the project folder
-## Usage (up to date)
- 1/ copy `config.json.example` to `config.json` and `release_config.json.example` to `release_config.json`.
- 2/ Edit `config.json` and replace `auth_service`, `username`, `password`, `location` and `gmapkey` with your parameters (others keys are optional, check `Advance Configuration` below)
+## Usage (up-to-date)
+ 1/ copy `config.json.example` to `config.json` and `release_config.json.example` to `release_config.json`.
+ 2/ Edit `config.json` and replace `auth_service`, `username`, `password`, `location` and `gmapkey` with your parameters (other keys are optional, check `Advance Configuration` below)
## Advance Configuration
- `max_steps` :
@@ -189,7 +183,27 @@ To update your project do: `git pull` in the project folder
- `location_cache` :
- `distance_unit` :
- `item_filter` :
-- `evolve_all` : Set to true to evolve pokemon if possible
+- `evolve_all` : Set to true to evolve pokemons if possible
+
+## Catch Configuration
+Default configuration will capture all Pokemon.
+```"any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"}```
+You can override the global configuration with Pokemon-specific options, such as:
+```"Pidgey": {"catch_above_cp": 0, "catch_above_iv": 0.8", "logic": "and"}```
+to only capture Pidgey with a good roll.
+Additionally, you can specify always_capture and never_capture flags. For example:
+```"Pidgey": {"never_capture": true}```
+will stop catching Pidgey entirely.
+
+## Release Configuration
+Default configuration will not release any Pokemon.
+```"any": {"release_below_cp": 0, "release_below_iv": 0, "logic": "or"}```
+You can override the global configuration with Pokemon-specific options, such as:
+```"Pidgey": {"release_below_cp": 0, "release_below_iv": 0.8", "logic": "or"}```
+to only release Pidgey with bad rolls.
+Additionally, you can specify always_release and never_release flags. For example:
+```"Pidgey": {"always_release": true}```
+will release all Pidgey caught.
### Evolve All Configuration
By setting the `evolve_all` attribute in config.json, you can instruct the bot to automatically
@@ -238,7 +252,7 @@ sudo apt-get install nginx
#### 2. Check the webserver
Check if the webserver is running by using your browser and entering the IP address of your local machine/server.
-On a local machine this would be http://127.0.0.1. On AWS this is your public DNS if you havent configured an elastic IP.
+On a local machine this would be http://127.0.0.1. On AWS this is your public DNS if you haven't configured an elastic IP.
#### 3. Change Base Directory of the Webserver
```
@@ -254,7 +268,7 @@ Comment out following line: ```root /var/www/html;``` and change it to the web f
### What's IV ?
Here's the [introduction](http://bulbapedia.bulbagarden.net/wiki/Individual_values)
-### Does it run automatally?
+### Does it run automatically?
Not yet, still need a trainer to train the script param. But we are very close to.
### Set GEO Location
It works, use -l "xx.yyyy,zz.ttttt" to set lat long for location. -- diordache
@@ -264,7 +278,7 @@ Try to generate an [app password](!https://support.google.com/accounts/answer/18
```
-p ""
```
-This error is mostly occurs for those who using 2 factor authentication but either way for the purpose of security would be nice to have a separate password for the bot app.
+This error mostly occurs for those who are using 2 factor authentication, but either way, for the purpose of security it would be nice to have a separate password for the bot app.
### FLEE
@@ -282,7 +296,7 @@ Create the following filter
```
./data/catch-ignore.yml
```
-Its a yaml file with a list of names so make it look like
+It's a yaml file with a list of names so make it look like
```
ignore:
- Pidgey
@@ -305,52 +319,3 @@ If using multiple usernames format like this:
```var users = ["username1","username2"];```
---------
-## Contributors (Don't forget add yours here when you create PR)
- * eggins -- The first pull request :)
- * crack00r
- * ethervoid
- * Bashin
- * tstumm
- * TheGoldenXY
- * Reaver01
- * rarshonsky
- * earthchie
- * haykuro
- * 05-032
- * sinistance
- * CapCap
- * mzupan
- * gnekic(GeXx)
- * Shoh
- * luizperes
- * brantje
- * VirtualSatai
- * dmateusp
- * jtdroste
- * msoedov
- * Grace
- * Calcyfer
- * asaf400
- * guyz
- * DavidK1m
- * budi-khoirudin
- * riberod07
- * th3w4y
- * Leaklessgfy
-
--------
-## Credits
-- [tejado](https://github.com/tejado) many thanks for the API
-- [Mila432](https://github.com/Mila432/Pokemon_Go_API) for the login secrets
-- [elliottcarlson](https://github.com/elliottcarlson) for the Google Auth PR
-- [AeonLucid](https://github.com/AeonLucid/POGOProtos) for improved protos
-- [AHAAAAAAA](https://github.com/AHAAAAAAA/PokemonGo-Map) for parts of the s2sphere stuff
-
-
-## Donation
-
-Bitcoin Address: 1PJMCx9NNQRasQYaa4MMff9yyNFffhHgLu
-
-
-
-
diff --git a/config.json.example b/config.json.example
deleted file mode 100644
index 3245708e6d..0000000000
--- a/config.json.example
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "auth_service": "google",
- "username": "YOURACCOUNT@gmail.com",
- "password": "YOURPASSWORD",
- "location": "SOME LOCATION",
- "gmapkey": "AGMAPAPIKEY",
- "max_steps": 5,
- "mode": "all",
- "walk": 4.16,
- "debug": false,
- "test": false,
- "initial_transfer": 0,
- "location_cache": true,
- "distance_unit": "km",
- "item_filter": "101,102,103,104",
- "evolve_all": "NONE"
-}
diff --git a/configs/config.json.example b/configs/config.json.example
new file mode 100644
index 0000000000..11665d4413
--- /dev/null
+++ b/configs/config.json.example
@@ -0,0 +1,27 @@
+{
+ "auth_service": "google",
+ "username": "YOUR_USERNAME",
+ "password": "YOUR_PASSWORD",
+ "location": "SOME_LOCATION",
+ "gmapkey": "GOOGLE_MAPS_API_KEY",
+ "max_steps": 5,
+ "mode": "all",
+ "walk": 4.16,
+ "debug": false,
+ "test": false,
+ "initial_transfer": 0,
+ "location_cache": true,
+ "distance_unit": "km",
+ "item_filter": "",
+ "evolve_all": "NONE",
+ "use_lucky_egg": false,
+ "evolve_captured": false,
+ "catch": {
+ "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"},
+ // "Rattata": { "always_catch" : true }
+ },
+ "release": {
+ "any": {"release_below_cp": 0, "release_below_iv": 0, "logic": "or"},
+ // "Rattata": { "always_release" : true }
+ }
+}
\ No newline at end of file
diff --git a/configs/config.json.pokemons.example b/configs/config.json.pokemons.example
new file mode 100644
index 0000000000..c919d15540
--- /dev/null
+++ b/configs/config.json.pokemons.example
@@ -0,0 +1,267 @@
+{
+ "auth_service": "google",
+ "username": "YOUR_USERNAME",
+ "password": "YOUR_PASSWORD",
+ "location": "SOME_LOCATION",
+ "gmapkey": "GOOGLE_MAPS_API_KEY",
+ "max_steps": 5,
+ "mode": "all",
+ "walk": 4.16,
+ "debug": false,
+ "test": false,
+ "initial_transfer": 0,
+ "location_cache": true,
+ "distance_unit": "km",
+ "item_filter": "",
+ "evolve_all": "NONE",
+ "use_lucky_egg": false,
+ "evolve_captured": false,
+ "catch": {
+ "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or" },
+
+ "// Pokemons with example": { "always_catch": true },
+ "// Gets filtered with release parameters": {},
+
+ "// Ledgendary pokemons (Goes under S-Tier)": {},
+ "Lapras": { "always_catch": true },
+ "Moltres": { "always_catch": true },
+ "Zapdos": { "always_catch": true },
+ "Articuno": { "always_catch": true },
+
+ "// S-Tier pokemons (if pokemon can be evolved into tier, list the representative)": {},
+ "Mewtwo": { "always_catch": true },
+ "Dragonite": { "always_catch": true },
+ "Snorlax": { "always_catch": true },
+ "// Mew evolves to Mewtwo": {},
+ "Mew": { "always_catch": true },
+ "Arcanine": { "always_catch": true },
+ "Vaporeon": { "always_catch": true },
+ "Gyarados": { "always_catch": true },
+ "Exeggutor": { "always_catch": true },
+ "Muk": { "always_catch": true },
+ "Weezing": { "always_catch": true },
+ "Flareon": { "always_catch": true },
+
+ "// Growlithe evolves to Arcanine": {},
+ "Growlithe": { "catch_above_cp": 465, "catch_above_iv": 0.8, "cp_iv_logic": "and" },
+ "// Dragonair evolves to Dragonite": {},
+ "Dragonair": { "catch_above_cp": 0, "catch_above_iv": 0.8, "cp_iv_logic": "and" },
+ "// Munchlax evolves to Snorlax": {},
+ "Munchlax": { "catch_above_cp": 0, "catch_above_iv": 0.8, "cp_iv_logic": "and" },
+ "// Grimer evolves to Muk": {},
+ "Grimer": { "catch_above_cp": 448, "catch_above_iv": 0.8, "cp_iv_logic": "and" },
+
+ "// Magikarp evolves to Gyarados": {},
+ "Magikarp": { "catch_above_cp": 91, "catch_above_iv": 0.9, "cp_iv_logic": "and" },
+ "// Exeggcute evolves to Exeggutor": {},
+ "Exeggcute": { "catch_above_cp": 384, "catch_above_iv": 0.8, "cp_iv_logic": "and" },
+ "// Eevee evolves to many versions, like Vaporeon, Flareon": {},
+ "Eevee": { "catch_above_cp": 376, "catch_above_iv": 0.8, "cp_iv_logic": "and"},
+
+ "// A-Tier pokemons": {},
+ "Slowbro": { "always_catch": true },
+ "Victreebel": { "always_catch": true },
+ "Machamp": { "always_catch": true },
+ "Poliwrath": { "always_catch": true },
+ "Clefable": { "always_catch": true },
+ "Nidoking": { "always_catch": true },
+ "Venusaur": { "always_catch": true },
+ "Charizard": { "always_catch": true },
+ "Golduck": { "always_catch": true },
+ "Nidoqueen": { "always_catch": true },
+ "Vileplume": { "always_catch": true },
+ "Blastoise": { "always_catch": true },
+ "Omastar": { "always_catch": true },
+ "Aerodactyl": { "always_catch": true },
+ "Golem": { "always_catch": true },
+ "Wigglytuff": { "always_catch": true },
+ "Dewgong": { "always_catch": true },
+ "Ninetales": { "always_catch": true },
+ "Magmar": { "always_catch": true },
+ "Kabutops": { "always_catch": true },
+ "Electabuzz": { "always_catch": true },
+ "Starmie": { "always_catch": true },
+ "Jolteon": { "always_catch": true },
+ "Rapidash": { "always_catch": true },
+ "Pinsir": { "always_catch": true },
+ "Scyther": { "always_catch": true },
+ "Tentacruel": { "always_catch": true },
+ "Gengar": { "always_catch": true },
+ "Hypno": { "always_catch": true },
+ "Pidgeot": { "always_catch": true },
+ "Rhydon": { "always_catch": true },
+ "Seaking": { "always_catch": true },
+ "Kangaskhan": { "always_catch": true }
+ },
+ "release": {
+ "any": {"release_under_cp": 0, "release_under_iv": 0, "logic": "or" },
+
+ "// Ledgendary pokemons (Goes under S-Tier)": {},
+ "Lapras": { "release_under_cp": 1041, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Moltres": { "release_under_cp": 1132, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Zapdos": { "release_under_cp": 1087, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Articuno": { "release_under_cp": 1039, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+
+ "// S-Tier pokemons (if pokemon can be evolved into tier, list the representative)": {},
+ "Mewtwo": { "release_under_cp": 1447, "release_under_iv": 0.8, "cp_iv_logic": "and"},
+ "Dragonite": { "release_under_cp": 1221, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Snorlax": { "release_under_cp": 1087, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Mew evolves to Mewtwo": {},
+ "Mew": { "release_under_cp": 1152, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Arcanine": { "release_under_cp": 1041, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Vaporeon": { "release_under_cp": 984, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Gyarados": { "release_under_cp": 938, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Exeggutor": { "release_under_cp": 1032, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Muk": { "release_under_cp": 909, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Weezing": { "release_under_cp": 784, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Flareon": { "release_under_cp": 924, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+
+ "// Growlithe evolves to Arcanine": {},
+ "Growlithe": { "release_under_cp": 465, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Dragonair evolves to Dragonite": {},
+ "Dragonair": { "release_under_cp": 609, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Munchlax evolves to Snorlax": {},
+ "Munchlax": { "release_under_cp": 1221, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Grimer evolves to Muk": {},
+ "Grimer": { "release_under_cp": 448, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Magikarp evolves to Gyarados": {},
+ "Magikarp": { "release_under_cp": 91, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Exeggcute evolves to Exeggutor": {},
+ "Exeggcute": { "release_under_cp": 384, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "// Eevee evolves to many versions, like Vaporeon, Flareon": {},
+ "Eevee": { "release_under_cp": 376, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+
+ "// A-Tier pokemons": {},
+ "Slowbro": { "release_under_cp": 907, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Victreebel": { "release_under_cp": 883, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Machamp": { "release_under_cp": 907, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Poliwrath": { "release_under_cp": 876, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Clefable": { "release_under_cp": 837, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Nidoking": { "release_under_cp": 864, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Venusaur": { "release_under_cp": 902, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Charizard": { "release_under_cp": 909, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Golduck": { "release_under_cp": 832, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Nidoqueen": { "release_under_cp": 868, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Vileplume": { "release_under_cp": 871, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Blastoise": { "release_under_cp": 888, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Omastar": { "release_under_cp": 780, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Aerodactyl": { "release_under_cp": 756, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Golem": { "release_under_cp": 804, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Wigglytuff": { "release_under_cp": 760, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Dewgong": { "release_under_cp": 748, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Ninetales": { "release_under_cp": 763, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Magmar": { "release_under_cp": 792, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Kabutops": { "release_under_cp": 744, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Electabuzz": { "release_under_cp": 739, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Starmie": { "release_under_cp": 763, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Jolteon": { "release_under_cp": 746, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Rapidash": { "release_under_cp": 768, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Pinsir": { "release_under_cp": 741, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Scyther": { "release_under_cp": 724, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Tentacruel": { "release_under_cp": 775, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Gengar": { "release_under_cp": 724, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Hypno": { "release_under_cp": 763, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Pidgeot": { "release_under_cp": 729, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Rhydon": { "release_under_cp": 782, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Seaking": { "release_under_cp": 712, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Kangaskhan": { "release_under_cp": 712, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+
+ "// Koffing evolves to Weezing (A-Tier)": {},
+ "Koffing": { "release_under_cp": 403, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+
+ "// Below is B-tier and lower pokemons": {},
+ "Magikarp": { "release_under_cp": 91, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Caterpie": { "release_under_cp": 156, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Weedle": { "release_under_cp": 156, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Diglett": { "release_under_cp": 158, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Metapod": { "release_under_cp": 168, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Kakuna": { "release_under_cp": 170, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Rattata": { "release_under_cp": 204, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Abra": { "release_under_cp": 208, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Zubat": { "release_under_cp": 225, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Chansey": { "release_under_cp": 235, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Pidgey": { "release_under_cp": 237, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Spearow": { "release_under_cp": 240, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Meowth": { "release_under_cp": 264, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Krabby": { "release_under_cp": 276, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Sandshrew": { "release_under_cp": 278, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Poliwag": { "release_under_cp": 278, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Horsea": { "release_under_cp": 278, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Gastly": { "release_under_cp": 280, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Ekans": { "release_under_cp": 288, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Shellder": { "release_under_cp": 288, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Vulpix": { "release_under_cp": 290, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Voltorb": { "release_under_cp": 292, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Geodude": { "release_under_cp": 297, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Doduo": { "release_under_cp": 297, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Onix": { "release_under_cp": 300, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Mankey": { "release_under_cp": 307, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Pikachu": { "release_under_cp": 309, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Magnemite": { "release_under_cp": 312, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Tentacool": { "release_under_cp": 316, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Paras": { "release_under_cp": 319, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Jigglypuff": { "release_under_cp": 321, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Ditto": { "release_under_cp": 321, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Staryu": { "release_under_cp": 326, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Charmander": { "release_under_cp": 333, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Goldeen": { "release_under_cp": 336, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Squirtle": { "release_under_cp": 352, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Cubone": { "release_under_cp": 352, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Venonat": { "release_under_cp": 360, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Bulbasaur": { "release_under_cp": 374, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Drowzee": { "release_under_cp": 374, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Machop": { "release_under_cp": 381, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Psyduck": { "release_under_cp": 386, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Seel": { "release_under_cp": 386, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Kabuto": { "release_under_cp": 386, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Bellsprout": { "release_under_cp": 391, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Omanyte": { "release_under_cp": 391, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Kadabra": { "release_under_cp": 396, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Oddish": { "release_under_cp": 400, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Dugtrio": { "release_under_cp": 408, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Rhyhorn": { "release_under_cp": 412, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Clefairy": { "release_under_cp": 420, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Slowpoke": { "release_under_cp": 424, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Pidgeotto": { "release_under_cp": 427, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Farfetch'd": { "release_under_cp": 441, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Poliwhirl": { "release_under_cp": 468, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Nidorino": { "release_under_cp": 480, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Haunter": { "release_under_cp": 482, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Nidorina": { "release_under_cp": 489, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Graveler": { "release_under_cp": 501, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Beedrill": { "release_under_cp": 504, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Raticate": { "release_under_cp": 504, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Butterfree": { "release_under_cp": 508, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Hitmonlee": { "release_under_cp": 520, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Ponyta": { "release_under_cp": 530, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Hitmonchan": { "release_under_cp": 530, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Charmeleon": { "release_under_cp": 544, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Wartortle": { "release_under_cp": 552, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Persian": { "release_under_cp": 568, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Lickitung": { "release_under_cp": 568, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Ivysaur": { "release_under_cp": 571, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Electrode": { "release_under_cp": 576, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Marowak": { "release_under_cp": 578, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Gloom": { "release_under_cp": 590, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Porygon": { "release_under_cp": 590, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Seadra": { "release_under_cp": 597, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Jynx": { "release_under_cp": 600, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Weepinbell": { "release_under_cp": 602, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Tangela": { "release_under_cp": 607, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Fearow": { "release_under_cp": 609, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Parasect": { "release_under_cp": 609, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Machoke": { "release_under_cp": 614, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Arbok": { "release_under_cp": 616, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Sandslash": { "release_under_cp": 631, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Alakazam": { "release_under_cp": 633, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Kingler": { "release_under_cp": 636, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Dodrio": { "release_under_cp": 640, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Tauros": { "release_under_cp": 643, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Primeape": { "release_under_cp": 650, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Magneton": { "release_under_cp": 657, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Venomoth": { "release_under_cp": 660, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Golbat": { "release_under_cp": 672, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Raichu": { "release_under_cp": 708, "release_under_iv": 0.8, "cp_iv_logic": "and" },
+ "Cloyster": { "release_under_cp": 717, "release_under_iv": 0.8, "cp_iv_logic": "and"}
+ }
+}
\ No newline at end of file
diff --git a/pokecli.py b/pokecli.py
index 07cf746c06..9a22a9f819 100755
--- a/pokecli.py
+++ b/pokecli.py
@@ -25,29 +25,25 @@
Author: tjado
"""
-import os
-import re
-import json
import argparse
-import time
+import codecs
+import json
+import logging
+import os
import ssl
import sys
-import codecs
from getpass import getpass
-import logging
-import requests
-from pokemongo_bot import logger
+
from pokemongo_bot import PokemonGoBot
-from pokemongo_bot.cell_workers.utils import print_green, print_yellow, print_red
+from pokemongo_bot import logger
if sys.version_info >= (2, 7, 9):
ssl._create_default_https_context = ssl._create_unverified_context
-
def init_config():
parser = argparse.ArgumentParser()
- config_file = "config.json"
- release_config_json = "release_config.json"
+ config_file = "configs/config.json"
+ web_dir = "web"
# If config file exists, load variables from json
load = {}
@@ -59,8 +55,12 @@ def init_config():
with open(config_arg) as data:
load.update(json.load(data))
elif os.path.isfile(config_file):
+ logger.log('No config argument specified, checking for /configs/config.json', 'yellow')
with open(config_file) as data:
load.update(json.load(data))
+ else:
+ logger.log('Error: No /configs/config.json or specified config', 'red')
+
# Read passed in Arguments
required = lambda x: not x in load
@@ -129,8 +129,8 @@ def init_config():
"-if",
"--item_filter",
help=
- "Pass a list of unwanted items to recycle when collected at a Pokestop (e.g, \"101,102,103,104\" to recycle potions when collected)",
- type=str,
+ "Pass a list of unwanted items to recycle when collected at a Pokestop (e.g, SYNTAX FOR CONFIG.JSON : [\"101\",\"102\",\"103\",\"104\"] to recycle potions when collected, SYNTAX FOR CONSOLE ARGUMENT : \"101\",\"102\",\"103\",\"104\")",
+ type=list,
default=[])
parser.add_argument("-ev",
@@ -144,6 +144,11 @@ def init_config():
help="(Ad-hoc mode) Bot will attempt to evolve all the pokemons captured!",
type=bool,
default=False)
+ parser.add_argument("-le",
+ "--use_lucky_egg",
+ help="Uses lucky egg when using evolve_all",
+ type=bool,
+ default=False)
config = parser.parse_args()
if not config.username and 'username' not in load:
@@ -155,6 +160,8 @@ def init_config():
for key in config.__dict__:
if key in load:
config.__dict__[key] = load[key]
+ config.catch = load['catch']
+ config.release = load['release']
if config.auth_service not in ['ptc', 'google']:
logging.error("Invalid Auth service specified! ('ptc' or 'google')")
@@ -164,56 +171,47 @@ def init_config():
parser.error("Needs either --use-location-cache or --location.")
return None
- if config.item_filter:
- config.item_filter = [str(item_id) for item_id in config.item_filter.split(',')]
+ # When config.item_filter looks like "101,102,103" needs to be converted to ["101","102","103"]
+ if isinstance(config.item_filter, basestring):
+ config.item_filter= config.item_filter.split(",")
- config.release_config = {}
- if os.path.isfile(release_config_json):
- with open(release_config_json) as data:
- config.release_config.update(json.load(data))
-
- web_index = 'web/index.html'
- if config.gmapkey and os.path.isfile(web_index):
- find_url = 'https:\/\/maps.googleapis.com\/maps\/api\/js\?key=\S*'
- replace_url = "https://maps.googleapis.com/maps/api/js?key=%s&callback=initMap\""
- #Someone make this pretty! (Efficient)
- with open(web_index, "r+") as sources: # r+ is read + write
- lines = sources.readlines()
- for line in lines:
- sources.write(re.sub(r"%s" % find_url, replace_url % config.gmapkey, line))
+ # create web dir if not exists
+ try:
+ os.makedirs(web_dir)
+ except OSError:
+ if not os.path.isdir(web_dir):
+ raise
if config.evolve_all:
config.evolve_all = [str(pokemon_name) for pokemon_name in config.evolve_all.split(',')]
return config
-
def main():
+
+ logger.log('PokemonGO Bot v1.0', 'green')
# log settings
# log format
#logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(module)10s] [%(levelname)5s] %(message)s')
-
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)
config = init_config()
if not config:
return
-
- logger.log('[x] PokemonGO Bot v1.0', 'green')
- logger.log('[x] Configuration initialized', 'yellow')
+ logger.log('Configuration initialized', 'yellow')
try:
bot = PokemonGoBot(config)
bot.start()
- logger.log('[x] Starting PokemonGo Bot....', 'green')
+ logger.log('Starting PokemonGo Bot....', 'green')
while True:
bot.take_step()
except KeyboardInterrupt:
- logger.log('[x] Exiting PokemonGo Bot', 'red')
+ logger.log('Exiting PokemonGo Bot', 'red')
# TODO Add number of pokemon catched, pokestops visited, highest CP
# pokemon catched, etc.
diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py
index d6df1a38d7..8a864e37b3 100644
--- a/pokemongo_bot/__init__.py
+++ b/pokemongo_bot/__init__.py
@@ -1,24 +1,23 @@
# -*- coding: utf-8 -*-
-import os
-import logging
-import googlemaps
+import datetime
import json
+import logging
import random
-import threading
-import datetime
-import sys
-import yaml
-import logger
import re
+import sys
+import time
+
+from geopy.geocoders import GoogleV3
from pgoapi import PGoApi
+from pgoapi.utilities import f2i
+
+import logger
from cell_workers import PokemonCatchWorker, SeenFortWorker, MoveToFortWorker, InitialTransferWorker, EvolveAllWorker
-from cell_workers.utils import distance
+from cell_workers.utils import distance, get_cellid, encode
from human_behaviour import sleep
-from stepper import Stepper
-from geopy.geocoders import GoogleV3
-from math import radians, sqrt, sin, cos, atan2
from item_list import Item
+from spiral_navigator import SpiralNavigator
class PokemonGoBot(object):
@@ -30,26 +29,127 @@ def __init__(self, config):
def start(self):
self._setup_logging()
self._setup_api()
- self.stepper = Stepper(self)
+ self.navigator = SpiralNavigator(self)
random.seed()
def take_step(self):
- self.stepper.take_step()
+ location = self.navigator.take_step()
+ cells = self.find_close_cells(*location)
+
+ for cell in cells:
+ self.work_on_cell(cell, location)
+
+ def update_web_location(self, cells=[], lat=None, lng=None, alt=None):
+ # we can call the function with no arguments and still get the position and map_cells
+ if lat == None:
+ lat = self.position[0]
+ if lng == None:
+ lng = self.position[1]
+ if alt == None:
+ alt = self.position[2]
+
+ if cells == []:
+ cellid = get_cellid(lat, lng)
+ timestamp = [0, ] * len(cellid)
+ self.api.get_map_objects(
+ latitude=f2i(lat),
+ longitude=f2i(lng),
+ since_timestamp_ms=timestamp,
+ cell_id=cellid
+ )
+ response_dict = self.api.call()
+ map_objects = response_dict.get('responses', {}).get('GET_MAP_OBJECTS', {})
+ status = map_objects.get('status', None)
+ cells = map_objects['map_cells']
+
+ user_web_location = 'web/location-%s.json' % (self.config.username)
+ # should check if file exists first but os is not imported here
+ # alt is unused atm but makes using *location easier
+ with open(user_web_location,'w') as outfile:
+ json.dump(
+ {'lat': lat,
+ 'lng': lng,
+ 'alt': alt,
+ 'cells': cells
+ }, outfile)
+
+ user_data_lastlocation = 'data/last-location-%s.json' % (self.config.username)
+ with open(user_data_lastlocation, 'w') as outfile:
+ outfile.truncate()
+ json.dump({'lat': lat, 'lng': lng}, outfile)
+
+ def find_close_cells(self, lat, lng):
+ cellid = get_cellid(lat, lng)
+ timestamp = [0, ] * len(cellid)
+
+ self.api.get_map_objects(
+ latitude=f2i(lat),
+ longitude=f2i(lng),
+ since_timestamp_ms=timestamp,
+ cell_id=cellid
+ )
+ response_dict = self.api.call()
+ map_objects = response_dict.get('responses', {}).get('GET_MAP_OBJECTS', {})
+ status = map_objects.get('status', None)
+
+ map_cells = []
+ if status and status == 1:
+ map_cells = map_objects['map_cells']
+ position = (lat, lng, 0)
+ map_cells.sort(
+ key=lambda x: distance(
+ lat,
+ lng,
+ x['forts'][0]['latitude'],
+ x['forts'][0]['longitude']) if x.get('forts', []) else 1e6
+ )
+ self.update_web_location(map_cells,lat,lng)
+ return map_cells
+
+ def work_on_cell(self, cell, position):
+ # Check if session token has expired
+ self.check_session(position)
- def work_on_cell(self, cell, position, include_fort_on_path):
if self.config.evolve_all:
- # Run evolve all once. Flip the bit.
- print('[#] Attempting to evolve all pokemons ...')
- worker = EvolveAllWorker(self)
- worker.work()
+ # Will skip evolving if user wants to use an egg and there is none
+ skip_evolves = False
+
+ # Pop lucky egg before evolving to maximize xp gain
+ use_lucky_egg = self.config.use_lucky_egg
+ lucky_egg_count = self.item_inventory_count(Item.ITEM_LUCKY_EGG.value)
+
+ if use_lucky_egg and lucky_egg_count > 0:
+ logger.log('Using lucky egg ... you have {}'
+ .format(lucky_egg_count))
+ response_dict_lucky_egg = self.use_lucky_egg()
+ if response_dict_lucky_egg and 'responses' in response_dict_lucky_egg and \
+ 'USE_ITEM_XP_BOOST' in response_dict_lucky_egg['responses'] and \
+ 'result' in response_dict_lucky_egg['responses']['USE_ITEM_XP_BOOST']:
+ result = response_dict_lucky_egg['responses']['USE_ITEM_XP_BOOST']['result']
+ if result is 1: # Request success
+ logger.log('Successfully used lucky egg... ({} left!)'
+ .format(lucky_egg_count-1), 'green')
+ else:
+ logger.log('Failed to use lucky egg!', 'red')
+ skip_evolves = True
+ elif use_lucky_egg: #lucky_egg_count is 0
+ # Skipping evolve so they aren't wasted
+ logger.log('No lucky eggs... skipping evolve!', 'yellow')
+ skip_evolves = True
+
+ if not skip_evolves:
+ # Run evolve all once.
+ logger.log('Attempting to evolve all pokemons ...', 'cyan')
+ worker = EvolveAllWorker(self)
+ worker.work()
+
+ # Flip the bit.
self.config.evolve_all = []
- self._filter_ignored_pokemons(cell)
-
if (self.config.mode == "all" or self.config.mode ==
"poke") and 'catchable_pokemons' in cell and len(cell[
'catchable_pokemons']) > 0:
- logger.log('[#] Something rustles nearby!')
+ logger.log('Something rustles nearby!')
# Sort all by distance from current pos- eventually this should
# build graph & A* it
cell['catchable_pokemons'].sort(
@@ -57,15 +157,14 @@ def work_on_cell(self, cell, position, include_fort_on_path):
lambda x: distance(self.position[0], self.position[1], x['latitude'], x['longitude']))
user_web_catchable = 'web/catchable-%s.json' % (self.config.username)
- if os.path.isfile(user_web_catchable): # only write to file if it exists
- for pokemon in cell['catchable_pokemons']:
- with open(user_web_catchable, 'w') as outfile:
- json.dump(pokemon, outfile)
+ for pokemon in cell['catchable_pokemons']:
+ with open(user_web_catchable, 'w') as outfile:
+ json.dump(pokemon, outfile)
- if self.catch_pokemon(pokemon) == PokemonCatchWorker.NO_POKEBALLS:
- break
- with open(user_web_catchable, 'w') as outfile:
- json.dump({}, outfile)
+ if self.catch_pokemon(pokemon) == PokemonCatchWorker.NO_POKEBALLS:
+ break
+ with open(user_web_catchable, 'w') as outfile:
+ json.dump({}, outfile)
if (self.config.mode == "all" or self.config.mode == "poke"
) and 'wild_pokemons' in cell and len(cell['wild_pokemons']) > 0:
@@ -78,18 +177,19 @@ def work_on_cell(self, cell, position, include_fort_on_path):
if self.catch_pokemon(pokemon) == PokemonCatchWorker.NO_POKEBALLS:
break
if (self.config.mode == "all" or
- self.config.mode == "farm") and include_fort_on_path:
+ self.config.mode == "farm"):
if 'forts' in cell:
# Only include those with a lat/long
forts = [fort
for fort in cell['forts']
if 'latitude' in fort and 'type' in fort]
- gyms = [gym for gym in cell['forts'] if 'gym_points' in gym]
+ gyms = [gym for gym in cell['forts'] if 'gym_points' in gym]
# Sort all by distance from current pos- eventually this should
# build graph & A* it
forts.sort(key=lambda x: distance(self.position[
0], self.position[1], x['latitude'], x['longitude']))
+
for fort in forts:
worker = MoveToFortWorker(fort, self)
worker.work()
@@ -117,27 +217,43 @@ def _setup_logging(self):
logging.getLogger("pgoapi").setLevel(logging.ERROR)
logging.getLogger("rpc_api").setLevel(logging.ERROR)
+ def check_session(self, position):
+ # Check session expiry
+ if self.api._auth_provider and self.api._auth_provider._ticket_expire:
+ remaining_time = self.api._auth_provider._ticket_expire/1000 - time.time()
+
+ if remaining_time < 60:
+ logger.log("Session stale, re-logging in", 'yellow')
+ self.position = position
+ self.login()
+
+
+ def login(self):
+ logger.log('Attempting login to Pokemon Go.', 'white')
+ self.api._auth_token = None
+ self.api._auth_provider = None
+ self.api._api_endpoint = None
+ lat, lng = self.position[0:2]
+ self.api.set_position(lat, lng, 0)
+
+ while not self.api.login(self.config.auth_service,
+ str(self.config.username),
+ str(self.config.password)):
+
+ logger.log('[X] Login Error, server busy', 'red')
+ logger.log('[X] Waiting 10 seconds to try again', 'red')
+ time.sleep(10)
+
+ logger.log('Login to Pokemon Go successful.', 'green')
+
def _setup_api(self):
# instantiate pgoapi
self.api = PGoApi()
- # check if the release_config file exists
- try:
- with open('release_config.json') as file:
- pass
- except:
- # the file does not exist, warn the user and exit.
- logger.log('[#] IMPORTANT: Rename and configure release_config.json.example for your Pokemon release logic first!', 'red')
- exit(0)
-
# provide player position on the earth
self._set_starting_position()
- if not self.api.login(self.config.auth_service,
- str(self.config.username),
- str(self.config.password)):
- logger.log('Login Error, server busy', 'red')
- exit(0)
+ self.login()
# chain subrequests (methods) into one RPC call
@@ -155,6 +271,7 @@ def _setup_api(self):
# @@@ TODO: Convert this to d/m/Y H:M:S
creation_date = datetime.datetime.fromtimestamp(
player['creation_timestamp_ms'] / 1e3)
+ creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S")
pokecoins = '0'
stardust = '0'
@@ -164,28 +281,28 @@ def _setup_api(self):
pokecoins = player['currencies'][0]['amount']
if 'amount' in player['currencies'][1]:
stardust = player['currencies'][1]['amount']
-
- logger.log('[#] Username: {username}'.format(**player))
- logger.log('[#] Acccount Creation: {}'.format(creation_date))
- logger.log('[#] Bag Storage: {}/{}'.format(
- self.get_inventory_count('item'), player['max_item_storage']))
- logger.log('[#] Pokemon Storage: {}/{}'.format(
- self.get_inventory_count('pokemon'), player[
- 'max_pokemon_storage']))
- logger.log('[#] Stardust: {}'.format(stardust))
- logger.log('[#] Pokecoins: {}'.format(pokecoins))
- logger.log('[#] PokeBalls: ' + str(balls_stock[1]))
- logger.log('[#] GreatBalls: ' + str(balls_stock[2]))
- logger.log('[#] UltraBalls: ' + str(balls_stock[3]))
-
+ logger.log('')
+ logger.log('--- {username} ---'.format(**player), 'cyan')
self.get_player_info()
+ logger.log('Pokemon Bag: {}/{}'.format(self.get_inventory_count('pokemon'), player['max_pokemon_storage']), 'cyan')
+ logger.log('Items: {}/{}'.format(self.get_inventory_count('item'), player['max_item_storage']), 'cyan')
+ logger.log('Stardust: {}'.format(stardust) + ' | Pokecoins: {}'.format(pokecoins), 'cyan')
+ # Pokeball Output
+ logger.log('PokeBalls: ' + str(balls_stock[1]) +
+ ' | GreatBalls: ' + str(balls_stock[2]) +
+ ' | UltraBalls: ' + str(balls_stock[3]), 'cyan')
+ logger.log('Razz Berries: ' + str(self.item_inventory_count(701)), 'cyan')
+
+ logger.log('')
if self.config.initial_transfer:
worker = InitialTransferWorker(self)
worker.work()
- logger.log('[#]')
+ logger.log('')
self.update_inventory()
+ # send empty map_cells and then our position
+ self.update_web_location([],*self.position)
def catch_pokemon(self, pokemon):
worker = PokemonCatchWorker(pokemon, self)
@@ -205,6 +322,11 @@ def drop_item(self, item_id, count):
#{'responses': {'RECYCLE_INVENTORY_ITEM': {'result': 1, 'new_count': 46}}, 'status_code': 1, 'auth_ticket': {'expire_timestamp_ms': 1469306228058L, 'start': '/HycFyfrT4t2yB2Ij+yoi+on778aymMgxY6RQgvrGAfQlNzRuIjpcnDd5dAxmfoTqDQrbz1m2dGqAIhJ+eFapg==', 'end': 'f5NOZ95a843tgzprJo4W7Q=='}, 'request_id': 8145806132888207460L}
return inventory_req
+ def use_lucky_egg(self):
+ self.api.use_item_xp_boost(item_id=301)
+ inventory_req = self.api.call()
+ return inventory_req
+
def update_inventory(self):
self.api.get_inventory()
response = self.api.call()
@@ -228,7 +350,7 @@ def update_inventory(self):
continue
self.inventory.append(item['inventory_item_data'][
'item'])
-
+
def pokeball_inventory(self):
self.api.get_player().get_inventory()
@@ -237,9 +359,8 @@ def pokeball_inventory(self):
'inventory_delta']['inventory_items']
user_web_inventory = 'web/inventory-%s.json' % (self.config.username)
- if os.path.isfile(user_web_inventory):
- with open(user_web_inventory, 'w') as outfile:
- json.dump(inventory_dict, outfile)
+ with open(user_web_inventory, 'w') as outfile:
+ json.dump(inventory_dict, outfile)
# get player balls stock
# ----------------------
@@ -284,6 +405,8 @@ def item_inventory_count(self, id):
def _set_starting_position(self):
+ has_position = False
+
if self.config.test:
# TODO: Add unit tests
return
@@ -295,26 +418,29 @@ def _set_starting_position(self):
self.position = location
self.api.set_position(*self.position)
logger.log('')
- logger.log(u'[x] Address found: {}'.format(self.config.location.decode(
+ logger.log(u'Location Found: {}'.format(self.config.location.decode(
'utf-8')))
- logger.log('[x] Position in-game set as: {}'.format(self.position))
+ logger.log('GeoPosition: {}'.format(self.position))
logger.log('')
+ has_position = True
return
except:
logger.log('[x] The location given using -l could not be parsed. Checking for a cached location.')
pass
- if self.config.location_cache and not self.config.location:
+ if self.config.location_cache and not has_position:
try:
#
# save location flag used to pull the last known location from
# the location.json
+ logger.log('[x] Parsing cached location...')
with open('data/last-location-%s.json' %
(self.config.username)) as f:
location_json = json.load(f)
self.position = (location_json['lat'],
location_json['lng'], 0.0)
+ print(self.position)
self.api.set_position(*self.position)
logger.log('')
@@ -325,13 +451,11 @@ def _set_starting_position(self):
self.position))
logger.log('')
+ has_position = True
return
except:
- if not self.config.location:
- sys.exit(
- "No cached Location. Please specify initial location.")
- else:
- pass
+ sys.exit(
+ "No cached Location. Please specify initial location.")
def _get_pos_by_name(self, location_name):
# Check if the given location is already a coordinate.
@@ -351,51 +475,13 @@ def _get_pos_by_name(self, location_name):
return (loc.latitude, loc.longitude, loc.altitude)
- def _filter_ignored_pokemons(self, cell):
- process_ignore = False
- try:
- with open("./data/catch-ignore.yml", 'r') as y:
- ignores = yaml.load(y)['ignore']
- if len(ignores) > 0:
- process_ignore = True
- except Exception, e:
- pass
-
- if process_ignore:
- #
- # remove any wild pokemon
- try:
- for p in cell['wild_pokemons'][:]:
- pokemon_id = p['pokemon_data']['pokemon_id']
- pokemon_name = filter(
- lambda x: int(x.get('Number')) == pokemon_id,
- self.pokemon_list)[0]['Name']
-
- if pokemon_name in ignores:
- cell['wild_pokemons'].remove(p)
- except KeyError:
- pass
-
- #
- # remove catchable pokemon
- try:
- for p in cell['catchable_pokemons'][:]:
- pokemon_id = p['pokemon_id']
- pokemon_name = filter(
- lambda x: int(x.get('Number')) == pokemon_id,
- self.pokemon_list)[0]['Name']
-
- if pokemon_name in ignores:
- cell['catchable_pokemons'].remove(p)
- except KeyError:
- pass
-
def heartbeat(self):
self.api.get_player()
self.api.get_hatched_eggs()
self.api.get_inventory()
self.api.check_awarded_badges()
self.api.call()
+ self.update_web_location() # updates every tick
def get_inventory_count(self, what):
self.api.get_inventory()
@@ -454,24 +540,14 @@ def get_player_info(self):
int(playerdata.get('experience', 0)))
if 'level' in playerdata:
- logger.log(
- '[#] -- Level: {level}'.format(
- **playerdata))
-
- if 'experience' in playerdata:
- logger.log(
- '[#] -- Experience: {experience}'.format(
- **playerdata))
- logger.log(
- '[#] -- Experience until next level: {}'.format(
- nextlvlxp))
+ if 'experience' in playerdata:
+ logger.log('Level: {level}'.format(**playerdata) +
+ ' (Next Level: {} XP)'.format(nextlvlxp) +
+ ' (Total: {experience} XP)'.format(**playerdata), 'cyan')
+
if 'pokemons_captured' in playerdata:
- logger.log(
- '[#] -- Pokemon Captured: {pokemons_captured}'.format(
- **playerdata))
-
- if 'poke_stop_visits' in playerdata:
- logger.log(
- '[#] -- Pokestops Visited: {poke_stop_visits}'.format(
- **playerdata))
+ if 'poke_stop_visits' in playerdata:
+ logger.log(
+ 'Pokemon Captured: {pokemons_captured}'.format(**playerdata) +
+ ' | Pokestops Visited: {poke_stop_visits}'.format(**playerdata), 'cyan')
diff --git a/pokemongo_bot/cell_workers/evolve_all_worker.py b/pokemongo_bot/cell_workers/evolve_all_worker.py
index 9c46dc7555..c3e40a2629 100644
--- a/pokemongo_bot/cell_workers/evolve_all_worker.py
+++ b/pokemongo_bot/cell_workers/evolve_all_worker.py
@@ -1,8 +1,9 @@
-from utils import distance, format_dist
-from pokemongo_bot.human_behaviour import sleep
-from pokemongo_bot import logger
from sets import Set
+from pokemongo_bot import logger
+from pokemongo_bot.human_behaviour import sleep
+
+
class EvolveAllWorker(object):
def __init__(self, bot):
self.api = bot.api
diff --git a/pokemongo_bot/cell_workers/initial_transfer_worker.py b/pokemongo_bot/cell_workers/initial_transfer_worker.py
index f09b91e07c..1419887a47 100644
--- a/pokemongo_bot/cell_workers/initial_transfer_worker.py
+++ b/pokemongo_bot/cell_workers/initial_transfer_worker.py
@@ -1,5 +1,4 @@
import json
-import os
from pokemongo_bot.human_behaviour import sleep
from pokemongo_bot import logger
@@ -11,13 +10,8 @@ def __init__(self, bot):
self.api = bot.api
def work(self):
- logger.log('[x] Initial Transfer.')
-
- logger.log(
- '[x] Preparing to transfer all duplicate Pokemon, keeping the highest CP of each type.')
-
- logger.log('[x] Will NOT transfer anything above CP {}'.format(
- self.config.initial_transfer))
+ logger.log('Cleaning up Pokemon Bag of anything below {} CP'.format(
+ self.config.initial_transfer), 'cyan')
pokemon_groups = self._initial_transfer_get_groups()
@@ -34,14 +28,14 @@ def work(self):
if self.config.initial_transfer and group_cp[x] > self.config.initial_transfer:
continue
- print('[x] Transferring {} with CP {}'.format(
+ logger.log('Exchanging {} with {} CP'.format(
self.pokemon_list[id - 1]['Name'], group_cp[x]))
self.api.release_pokemon(
pokemon_id=pokemon_groups[id][group_cp[x]])
response_dict = self.api.call()
sleep(2)
- logger.log('[x] Transferring Done.')
+ logger.log('Pokemon Bag has been cleaned up!', 'green')
def _initial_transfer_get_groups(self):
pokemon_groups = {}
@@ -51,9 +45,8 @@ def _initial_transfer_get_groups(self):
'inventory_delta']['inventory_items']
user_web_inventory = 'web/inventory-%s.json' % (self.config.username)
- if os.path.isfile(user_web_inventory):
- with open(user_web_inventory, 'w') as outfile:
- json.dump(inventory_dict, outfile)
+ with open(user_web_inventory, 'w') as outfile:
+ json.dump(inventory_dict, outfile)
for pokemon in inventory_dict:
try:
diff --git a/pokemongo_bot/cell_workers/move_to_fort_worker.py b/pokemongo_bot/cell_workers/move_to_fort_worker.py
index 3af7befcd3..2889d74d41 100644
--- a/pokemongo_bot/cell_workers/move_to_fort_worker.py
+++ b/pokemongo_bot/cell_workers/move_to_fort_worker.py
@@ -1,13 +1,15 @@
from utils import distance, format_dist
from pokemongo_bot.human_behaviour import sleep
from pokemongo_bot import logger
+from pokemongo_bot.step_walker import StepWalker
class MoveToFortWorker(object):
def __init__(self, fort, bot):
+ self.bot = bot
self.fort = fort
self.api = bot.api
self.config = bot.config
- self.stepper = bot.stepper
+ self.navigator = bot.navigator
self.position = bot.position
def work(self):
@@ -18,22 +20,34 @@ def work(self):
dist = distance(self.position[0], self.position[1], lat, lng)
- # print('[#] Found fort {} at distance {}m'.format(fortID, dist))
- logger.log('[#] Found fort {} at distance {}'.format(
+ # print('Found fort {} at distance {}m'.format(fortID, dist))
+ logger.log('Found fort {} at distance {}'.format(
fortID, format_dist(dist, unit)))
if dist > 10:
- logger.log('[#] Need to move closer to Pokestop')
+ logger.log('Need to move closer to Pokestop')
position = (lat, lng, 0.0)
if self.config.walk > 0:
- self.stepper._walk_to(self.config.walk, *position)
+ step_walker = StepWalker(
+ self.bot,
+ self.config.walk,
+ self.api._position_lat,
+ self.api._position_lng,
+ position[0],
+ position[1]
+ )
+
+ while True:
+ if step_walker.step():
+ break
+
else:
self.api.set_position(*position)
self.api.player_update(latitude=lat, longitude=lng)
response_dict = self.api.call()
- logger.log('[#] Arrived at Pokestop')
+ logger.log('Arrived at Pokestop')
sleep(2)
return response_dict
diff --git a/pokemongo_bot/cell_workers/pokemon_catch_worker.py b/pokemongo_bot/cell_workers/pokemon_catch_worker.py
index 05ace03865..ec5fc753d2 100644
--- a/pokemongo_bot/cell_workers/pokemon_catch_worker.py
+++ b/pokemongo_bot/cell_workers/pokemon_catch_worker.py
@@ -2,9 +2,10 @@
import time
from sets import Set
-from utils import distance
-from pokemongo_bot.human_behaviour import sleep
+
from pokemongo_bot import logger
+from pokemongo_bot.human_behaviour import sleep
+
class PokemonCatchWorker(object):
BAG_FULL = 'bag_full'
@@ -33,8 +34,11 @@ def work(self):
if 'ENCOUNTER' in response_dict['responses']:
if 'status' in response_dict['responses']['ENCOUNTER']:
if response_dict['responses']['ENCOUNTER']['status'] is 7:
- logger.log('[x] Pokemon Bag is full!', 'red')
- return PokemonCatchWorker.BAG_FULL
+ if self.config.initial_transfer:
+ logger.log('Pokemon Bag is full!', 'red')
+ return PokemonCatchWorker.BAG_FULL
+ else:
+ raise RuntimeError('Pokemon Bag is full!')
if response_dict['responses']['ENCOUNTER']['status'] is 1:
cp = 0
@@ -59,10 +63,10 @@ def work(self):
'pokemon_id']) - 1
pokemon_name = self.pokemon_list[
int(pokemon_num)]['Name']
- logger.log('[#] A Wild {} appeared! [CP {}] [Potential {}]'.format(
+ logger.log('A Wild {} appeared! [CP {}] [Potential {}]'.format(
pokemon_name, cp, pokemon_potential), 'yellow')
- logger.log('[#] IV [Stamina/Attack/Defense] = [{}/{}/{}]'.format(
+ logger.log('IV [Stamina/Attack/Defense] = [{}/{}/{}]'.format(
pokemon['pokemon_data']['individual_stamina'],
pokemon['pokemon_data']['individual_attack'],
pokemon['pokemon_data']['individual_defense']
@@ -71,6 +75,10 @@ def work(self):
# Simulate app
sleep(3)
+ if not self.should_capture_pokemon(pokemon_name, cp, pokemon_potential, response_dict):
+ #logger.log('[x] Rule prevents capture.')
+ return False
+
balls_stock = self.bot.pokeball_inventory()
while(True):
@@ -81,8 +89,32 @@ def work(self):
pokeball = 2 # then use great balls
elif balls_stock[3] > 0: # or if great balls are out of stock too, and player has ultra balls...
pokeball = 3 # then use ultra balls
+
+ ## Use berry to increase success chance.
+ berry_id = 701 # @ TODO: use better berries if possible
+ berries_count = self.bot.item_inventory_count(berry_id)
+ if(catch_rate[pokeball-1] < 0.5 and berries_count > 0): # and berry is in stock
+ success_percentage = '{0:.2f}'.format(catch_rate[pokeball-1]*100)
+ logger.log('Catch Rate with normal Pokeball is low ({}%). Throwing {}... ({} left!)'.format(success_percentage,self.item_list[str(berry_id)],berries_count-1))
+
+ if balls_stock[pokeball] is 0:
+ break
+
+ self.api.use_item_capture(
+ item_id=berry_id,
+ encounter_id = encounter_id,
+ spawn_point_guid = spawnpoint_id
+ )
+ response_dict = self.api.call()
+ if response_dict and response_dict['status_code'] is 1 and 'item_capture_mult' in response_dict['responses']['USE_ITEM_CAPTURE']:
+
+ for i in range(len(catch_rate)):
+ catch_rate[i] = catch_rate[i] * response_dict['responses']['USE_ITEM_CAPTURE']['item_capture_mult']
+
+ success_percentage = '{0:.2f}'.format(catch_rate[pokeball-1]*100)
+ logger.log('Catch Rate with normal Pokeball has increased to {}%'.format(success_percentage))
else:
- pokeball = 0 # player doesn't have any of pokeballs, great balls or ultra balls
+ logger.log('Fail to use berry. Status Code: {}'.format(response_dict['status_code']),'red')
while(pokeball < 3):
if catch_rate[pokeball-1] < 0.35 and balls_stock[pokeball+1] > 0:
@@ -92,17 +124,17 @@ def work(self):
break
# @TODO, use the best ball in stock to catch VIP (Very Important Pokemon: Configurable)
-
- if pokeball is 0:
+
+ if balls_stock[pokeball] is 0:
logger.log(
- '[x] Out of pokeballs, switching to farming mode...', 'red')
+ 'Out of pokeballs, switching to farming mode...', 'red')
# Begin searching for pokestops.
self.config.mode = 'farm'
return PokemonCatchWorker.NO_POKEBALLS
balls_stock[pokeball] = balls_stock[pokeball] - 1
success_percentage = '{0:.2f}'.format(catch_rate[pokeball-1]*100)
- logger.log('[x] Using {} (chance: {}%)... ({} left!)'.format(
+ logger.log('Using {} (chance: {}%)... ({} left!)'.format(
self.item_list[str(pokeball)],
success_percentage,
balls_stock[pokeball]
@@ -126,23 +158,24 @@ def work(self):
'CATCH_POKEMON']['status']
if status is 2:
logger.log(
- '[-] Attempted to capture {}- failed.. trying again!'.format(pokemon_name), 'red')
+ '[-] Attempted to capture {} - failed.. trying again!'.format(pokemon_name), 'red')
sleep(2)
continue
if status is 3:
logger.log(
- '[x] Oh no! {} vanished! :('.format(pokemon_name), 'red')
+ 'Oh no! {} vanished! :('.format(pokemon_name), 'red')
if status is 1:
- logger.log(
- '[x] Captured {}! [CP {}] [IV {}]'.format(
- pokemon_name,
- cp,
- pokemon_potential
- ), 'green'
- )
-
+
id_list2 = self.count_pokemon_inventory()
-
+
+ logger.log('Captured {}! [CP {}] [{}/{}/{}]'.format(
+ pokemon_name,
+ cp,
+ pokemon['pokemon_data']['individual_stamina'],
+ pokemon['pokemon_data']['individual_attack'],
+ pokemon['pokemon_data']['individual_defense']
+ ), 'blue')
+
if self.config.evolve_captured:
pokemon_to_transfer = list(Set(id_list2) - Set(id_list1))
self.api.evolve_pokemon(pokemon_id=pokemon_to_transfer[0])
@@ -150,10 +183,10 @@ def work(self):
status = response_dict['responses']['EVOLVE_POKEMON']['result']
if status == 1:
logger.log(
- '[#] {} has been evolved!'.format(pokemon_name), 'green')
+ '{} has been evolved!'.format(pokemon_name), 'green')
else:
logger.log(
- '[x] Failed to evolve {}!'.format(pokemon_name))
+ 'Failed to evolve {}!'.format(pokemon_name))
if self.should_release_pokemon(pokemon_name, cp, pokemon_potential, response_dict):
# Transfering Pokemon
@@ -165,10 +198,8 @@ def work(self):
self.transfer_pokemon(
pokemon_to_transfer[0])
logger.log(
- '[#] {} has been exchanged for candy!'.format(pokemon_name), 'green')
- else:
- logger.log(
- '[x] Captured {}! [CP {}]'.format(pokemon_name, cp), 'green')
+ '{} has been exchanged for candy!'.format(pokemon_name), 'green')
+
break
time.sleep(5)
@@ -231,76 +262,96 @@ def counting_pokemon(self, response_dict, id_list):
return id_list
+ def should_capture_pokemon(self, pokemon_name, cp, iv, response_dict):
+ catch_config = self._get_catch_config_for(pokemon_name)
+ cp_iv_logic = catch_config.get('logic')
+ if not cp_iv_logic:
+ cp_iv_logic = self._get_catch_config_for('any').get('logic', 'and')
+
+ catch_results = {
+ 'cp': False,
+ 'iv': False,
+ }
+
+ if catch_config.get('never_catch', False):
+ return False
+
+ if catch_config.get('always_catch', False):
+ return True
+
+ catch_cp = catch_config.get('catch_above_cp', 0)
+ if cp > catch_cp:
+ catch_results['cp'] = True
+
+ catch_iv = catch_config.get('catch_above_iv', 0)
+ if iv > catch_iv:
+ catch_results['iv'] = True
+
+ logic_to_function = {
+ 'or': lambda x, y: x or y,
+ 'and': lambda x, y: x and y
+ }
+
+ #logger.log(
+ # "Catch config for {}: CP {} {} IV {}".format(
+ # pokemon_name,
+ # catch_cp,
+ # cp_iv_logic,
+ # catch_iv
+ # ), 'yellow'
+ #)
+
+ return logic_to_function[cp_iv_logic](*catch_results.values())
+
+ def _get_catch_config_for(self, pokemon):
+ catch_config = self.config.catch.get(pokemon)
+ if not catch_config:
+ catch_config = self.config.catch['any']
+ return catch_config
+
def should_release_pokemon(self, pokemon_name, cp, iv, response_dict):
- if self._check_always_capture_exception_for(pokemon_name):
+ release_config = self._get_release_config_for(pokemon_name)
+ cp_iv_logic = release_config.get('logic')
+ if not cp_iv_logic:
+ cp_iv_logic = self._get_release_config_for('any').get('logic', 'and')
+
+ release_results = {
+ 'cp': False,
+ 'iv': False,
+ }
+
+ if release_config.get('never_release', False):
return False
- else:
- release_config = self._get_release_config_for(pokemon_name)
- cp_iv_logic = release_config.get('cp_iv_logic')
- if not cp_iv_logic:
- cp_iv_logic = self._get_release_config_for('any').get('cp_iv_logic', 'and')
-
- release_results = {
- 'cp': False,
- 'iv': False,
- }
-
- if 'release_under_cp' in release_config:
- min_cp = release_config['release_under_cp']
- if cp < min_cp:
- release_results['cp'] = True
-
- if 'release_under_iv' in release_config:
- min_iv = release_config['release_under_iv']
- if iv < min_iv:
- release_results['iv'] = True
-
- if release_config.get('always_release'):
- return True
-
- logic_to_function = {
- 'or': lambda x, y: x or y,
- 'and': lambda x, y: x and y
- }
-
- #logger.log(
- # "[x] Release config for {}: CP {} {} IV {}".format(
- # pokemon_name,
- # min_cp,
- # cp_iv_logic,
- # min_iv
- # ), 'yellow'
- #)
-
- return logic_to_function[cp_iv_logic](*release_results.values())
+
+ if release_config.get('always_release', False):
+ return True
+
+ release_cp = release_config.get('release_below_cp', 0)
+ if cp < release_cp:
+ release_results['cp'] = True
+
+ release_iv = release_config.get('release_below_iv', 0)
+ if iv < release_iv:
+ release_results['iv'] = True
+
+ logic_to_function = {
+ 'or': lambda x, y: x or y,
+ 'and': lambda x, y: x and y
+ }
+
+ #logger.log(
+ # "Release config for {}: CP {} {} IV {}".format(
+ # pokemon_name,
+ # min_cp,
+ # cp_iv_logic,
+ # min_iv
+ # ), 'yellow'
+ #)
+
+ return logic_to_function[cp_iv_logic](*release_results.values())
def _get_release_config_for(self, pokemon):
- release_config = self.config.release_config.get(pokemon)
+ release_config = self.config.release.get(pokemon)
if not release_config:
- release_config = self.config.release_config['any']
+ release_config = self.config.release['any']
return release_config
-
- def _get_exceptions(self):
- exceptions = self.config.release_config.get('exceptions')
- if not exceptions:
- return None
- return exceptions
-
- def _get_always_capture_list(self):
- exceptions = self._get_exceptions()
- if not exceptions:
- return []
- always_capture_list = exceptions['always_capture']
- if not always_capture_list:
- return []
- return always_capture_list
-
- def _check_always_capture_exception_for(self, pokemon_name):
- always_capture_list = self._get_always_capture_list()
- if not always_capture_list:
- return False
- else:
- for pokemon in always_capture_list:
- if pokemon_name == str(pokemon):
- return True
- return False
diff --git a/pokemongo_bot/cell_workers/seen_fort_worker.py b/pokemongo_bot/cell_workers/seen_fort_worker.py
index a1faafa66d..c5709ad511 100644
--- a/pokemongo_bot/cell_workers/seen_fort_worker.py
+++ b/pokemongo_bot/cell_workers/seen_fort_worker.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
-import json
import time
-from math import radians, sqrt, sin, cos, atan2
-from pgoapi.utilities import f2i, h2f
-from utils import print_green, print_yellow, print_red, format_time
-from pokemongo_bot.human_behaviour import sleep
+
+from pgoapi.utilities import f2i
+
from pokemongo_bot import logger
+from pokemongo_bot.human_behaviour import sleep
+from utils import format_time
class SeenFortWorker(object):
@@ -18,7 +18,6 @@ def __init__(self, fort, bot):
self.config = bot.config
self.item_list = bot.item_list
self.rest_time = 50
- self.stepper = bot.stepper
def work(self):
lat = self.fort['latitude']
@@ -35,8 +34,8 @@ def work(self):
fort_name = fort_details['name'].encode('utf8', 'replace')
else:
fort_name = 'Unknown'
- logger.log('[#] Now at Pokestop: ' + fort_name + ' - Spinning...',
- 'yellow')
+ logger.log('Now at Pokestop: ' + fort_name + ' - Spinning...',
+ 'cyan')
sleep(2)
self.api.fort_search(fort_id=self.fort['id'],
fort_latitude=lat,
@@ -49,11 +48,11 @@ def work(self):
spin_details = response_dict['responses']['FORT_SEARCH']
if spin_details['result'] == 1:
- logger.log("[+] Loot: ", 'green')
+ logger.log("Loot: ", 'green')
experience_awarded = spin_details.get('experience_awarded',
False)
if experience_awarded:
- logger.log("[+] " + str(experience_awarded) + " xp",
+ logger.log(str(experience_awarded) + " xp",
'green')
items_awarded = spin_details.get('items_awarded', False)
@@ -69,13 +68,11 @@ def work(self):
for item_id, item_count in tmp_count_items.iteritems():
item_name = self.item_list[str(item_id)]
- logger.log("[+] " + str(item_count) +
- "x " + item_name +
- " (Total: " + str(self.bot.item_inventory_count(item_id)) + ")", 'green')
+ logger.log('- ' + str(item_count) + "x " + item_name + " (Total: " + str(self.bot.item_inventory_count(item_id)) + ")", 'yellow')
# RECYCLING UNWANTED ITEMS
if str(item_id) in self.config.item_filter:
- logger.log("[+] Recycling " + str(item_count) + "x " + item_name + "...", 'green')
+ logger.log("-- Recycling " + str(item_count) + "x " + item_name + "...", 'green')
#RECYCLE_INVENTORY_ITEM
response_dict_recycle = self.bot.drop_item(item_id=item_id, count=item_count)
@@ -85,9 +82,9 @@ def work(self):
'result' in response_dict_recycle['responses']['RECYCLE_INVENTORY_ITEM']:
result = response_dict_recycle['responses']['RECYCLE_INVENTORY_ITEM']['result']
if result is 1: # Request success
- logger.log("[+] Recycling success", 'green')
+ logger.log("-- Recycled " + item_name + "!", 'green')
else:
- logger.log("[+] Recycling failed!", 'red')
+ logger.log("-- Recycling " + item_name + "has failed!", 'red')
else:
logger.log("[#] Nothing found.", 'yellow')
@@ -95,7 +92,7 @@ def work(self):
'cooldown_complete_timestamp_ms')
if pokestop_cooldown:
seconds_since_epoch = time.time()
- logger.log('[#] PokeStop on cooldown. Time left: ' + str(
+ logger.log('PokeStop on cooldown. Time left: ' + str(
format_time((pokestop_cooldown / 1000) -
seconds_since_epoch)))
@@ -115,11 +112,11 @@ def work(self):
'cooldown_complete_timestamp_ms')
if pokestop_cooldown:
seconds_since_epoch = time.time()
- logger.log('[#] PokeStop on cooldown. Time left: ' + str(
+ logger.log('PokeStop on cooldown. Time left: ' + str(
format_time((pokestop_cooldown / 1000) -
seconds_since_epoch)))
elif spin_details['result'] == 4:
- print_red("[#] Inventory is full, switching to catch mode...")
+ logger.log("Inventory is full, switching to catch mode...", 'red')
self.config.mode = 'poke'
if 'chain_hack_sequence_number' in response_dict['responses'][
@@ -128,7 +125,7 @@ def work(self):
return response_dict['responses']['FORT_SEARCH'][
'chain_hack_sequence_number']
else:
- print_yellow('[#] may search too often, lets have a rest')
+ logger.log('Possibly searching too often - taking a short rest :)', 'yellow')
return 11
sleep(8)
return 0
diff --git a/pokemongo_bot/cell_workers/utils.py b/pokemongo_bot/cell_workers/utils.py
index bd31375ccc..4d866e6dde 100644
--- a/pokemongo_bot/cell_workers/utils.py
+++ b/pokemongo_bot/cell_workers/utils.py
@@ -3,9 +3,29 @@
import struct
from math import cos, asin, sqrt
from colorama import init
+from s2sphere import CellId, LatLng
init()
+def get_cellid(lat, long, radius=10):
+ origin = CellId.from_lat_lng(LatLng.from_degrees(lat, long)).parent(15)
+ walk = [origin.id()]
+
+ # 10 before and 10 after
+ next = origin.next()
+ prev = origin.prev()
+ for i in range(radius):
+ walk.append(prev.id())
+ walk.append(next.id())
+ next = next.next()
+ prev = prev.prev()
+ return sorted(walk)
+
+def encode(cellid):
+ output = []
+ encoder._VarintEncoder()(output.append, cellid)
+ return ''.join(output)
+
def distance(lat1, lon1, lat2, lon2):
p = 0.017453292519943295
a = 0.5 - cos((lat2 - lat1) * p) / 2 + cos(lat1 * p) * \
diff --git a/pokemongo_bot/human_behaviour.py b/pokemongo_bot/human_behaviour.py
index 6d4c434f92..2da9d1e365 100644
--- a/pokemongo_bot/human_behaviour.py
+++ b/pokemongo_bot/human_behaviour.py
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
import time
-from math import ceil
-from random import random, randint
+from random import random, uniform
def sleep(seconds, delta=0.3):
- jitter = ceil(delta * seconds)
- sleep_time = randint(int(seconds - jitter), int(seconds + jitter))
- time.sleep(sleep_time)
+ time.sleep(jitter(seconds,delta))
+
+def jitter(value, delta=0.3):
+ jitter = delta * value
+ return uniform(value-jitter, value+jitter)
def random_lat_long_delta():
diff --git a/pokemongo_bot/logger.py b/pokemongo_bot/logger.py
index 151e578a0f..636f7e371e 100644
--- a/pokemongo_bot/logger.py
+++ b/pokemongo_bot/logger.py
@@ -9,14 +9,16 @@
def log(string, color = 'white'):
colorHex = {
+ 'red': '91m',
'green': '92m',
'yellow': '93m',
- 'red': '91m'
+ 'blue': '94m',
+ 'cyan': '96m'
}
if color not in colorHex:
- print('[' + time.strftime("%Y-%m-%d %H:%M:%S") + '] '+ string)
+ print('[' + time.strftime("%H:%M:%S") + '] '+ string)
else:
- print(u'\033['+ colorHex[color] + '[' + time.strftime("%Y-%m-%d %H:%M:%S") + '] ' + string.decode('utf-8') + '\033[0m')
+ print('[' + time.strftime("%H:%M:%S") + '] ' + u'\033['+ colorHex[color] + string.decode('utf-8') + '\033[0m')
if lcd:
if(string):
lcd.message(string)
diff --git a/pokemongo_bot/polyline_stepper.py b/pokemongo_bot/polyline_stepper.py
index 32d07fc1c9..1ac25dd9a4 100644
--- a/pokemongo_bot/polyline_stepper.py
+++ b/pokemongo_bot/polyline_stepper.py
@@ -1,56 +1,43 @@
# -*- coding: utf-8 -*-
-from polyline_walker import PolylineWalker
+from math import ceil
+
from stepper import Stepper
-from human_behaviour import sleep, random_lat_long_delta
+
+import logger
+from cell_workers.utils import i2f
+from human_behaviour import sleep
+from polyline_walker import PolylineWalker
class PolylineStepper(Stepper):
def _walk_to(self, speed, lat, lng, alt):
- origin = ','.join([str(self.api._position_lat), str(self.api._position_lng)])
+ origin = ','.join([str(i2f(self.api._position_lat)), str(i2f(self.api._position_lng))])
destination = ','.join([str(lat), str(lng)])
- polyline_walker = PolylineWalker(origin, destination, self.speed)
+ polyline_walker = PolylineWalker(origin, destination, speed)
proposed_origin = polyline_walker.points[0]
proposed_destination = polyline_walker.points[-1]
proposed_lat = proposed_origin[0]
proposed_lng = proposed_origin[1]
if proposed_lat != lat and proposed_lng != lng:
+ logger.log('[#] Using _old_walk_to to go to the proposed_origin: {}'
+ .format(proposed_origin))
self._old_walk_to(speed, proposed_lat, proposed_lng, alt)
- while proposed_destination != polyline_walker.get_pos()[0]:
- cLat, cLng = polyline_walker.get_pos()[0]
- self.api.set_position(cLat, cLng, alt)
- self.bot.heartbeat()
- self._work_at_position(i2f(self.api._position_lat), i2f(self.api._position_lng), alt, False)
- sleep(1) # sleep one second plus a random delta
- if proposed_lat != self.api._position_lat and proposed_lng != self.api._position_lng:
- self._old_walk_to(speed, lat, lng, alt)
-
- def _old_walk_to(self, speed, lat, lng, alt):
- dist = distance(
- i2f(self.api._position_lat), i2f(self.api._position_lng), lat, lng)
- steps = (dist + 0.0) / (speed + 0.0) # may be rational number
- intSteps = int(steps)
- residuum = steps - intSteps
- logger.log('[#] Walking from ' + str((i2f(self.api._position_lat), i2f(
- self.api._position_lng))) + " to " + str(str((lat, lng))) +
- " for approx. " + str(format_time(ceil(steps))))
- if steps != 0:
- dLat = (lat - i2f(self.api._position_lat)) / steps
- dLng = (lng - i2f(self.api._position_lng)) / steps
-
- for i in range(intSteps):
- cLat = i2f(self.api._position_lat) + \
- dLat + random_lat_long_delta()
- cLng = i2f(self.api._position_lng) + \
- dLng + random_lat_long_delta()
+ if proposed_origin != proposed_destination:
+ duration = polyline_walker.get_total_distance() / speed
+ logger.log('[#] Using PolylineWalker from {} to {} for approx. {} seconds.'
+ .format(proposed_origin, proposed_destination, ceil(duration)))
+ while proposed_destination != polyline_walker.get_pos()[0]:
+ cLat, cLng = polyline_walker.get_pos()[0]
self.api.set_position(cLat, cLng, alt)
self.bot.heartbeat()
+ self._work_at_position(i2f(self.api._position_lat), i2f(self.api._position_lng), alt, False)
sleep(1) # sleep one second plus a random delta
- self._work_at_position(
- i2f(self.api._position_lat), i2f(self.api._position_lng),
- alt, False)
+ if proposed_lat != self.api._position_lat and proposed_lng != self.api._position_lng:
+ logger.log('[#] Using _old_walk_to to go from the proposed destination : {} to {}'
+ .format(proposed_destination, (lat, lng)))
- self.api.set_position(lat, lng, alt)
- self.bot.heartbeat()
- logger.log("[#] Finished walking")
+ self._old_walk_to(speed, lat, lng, alt)
+ def _old_walk_to(self, speed, lat, lng, alt):
+ return super(PolylineStepper, self)._walk_to(speed, lat, lng, alt)
diff --git a/pokemongo_bot/polyline_walker/polyline_tester.py b/pokemongo_bot/polyline_walker/polyline_tester.py
index fdf547fdd6..a8d0554e16 100644
--- a/pokemongo_bot/polyline_walker/polyline_tester.py
+++ b/pokemongo_bot/polyline_walker/polyline_tester.py
@@ -1,8 +1,11 @@
import time
+from math import ceil
+
import haversine
import polyline
-from math import ceil
+
from polyline_walker import PolylineWalker
+
a = PolylineWalker('Poststrasse+20,Zug,CH', 'Guggiweg+7,Zug,CH', 100)
print('Walking polyline: ', a.polyline)
print('Encoded level: ','B'*len(a.points))
diff --git a/pokemongo_bot/polyline_walker/polyline_walker.py b/pokemongo_bot/polyline_walker/polyline_walker.py
index e6a92c1c5e..2c5be3daaa 100644
--- a/pokemongo_bot/polyline_walker/polyline_walker.py
+++ b/pokemongo_bot/polyline_walker/polyline_walker.py
@@ -1,10 +1,12 @@
-import requests
-import polyline
-import haversine
import time
-from itertools import chain
+from itertools import chain
from math import ceil
+import haversine
+import polyline
+import requests
+
+
class PolylineWalker(object):
def __init__(self, origin, destination, speed):
@@ -85,7 +87,7 @@ def get_pos(self):
def calculate_coord(self, percentage, o, d):
lat = o[0]+ (d[0] -o[0]) * percentage
lon = o[1]+ (d[1] -o[1]) * percentage
- return [(round(lat, 5), round(lon, 5))]
+ return [(lat, lon)]
def get_total_distance(self):
return ceil(sum([haversine.haversine(*x)*1000 for x in self.walk_steps()]))
diff --git a/pokemongo_bot/spiral_navigator.py b/pokemongo_bot/spiral_navigator.py
new file mode 100644
index 0000000000..770d609ee9
--- /dev/null
+++ b/pokemongo_bot/spiral_navigator.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+import logger
+from cell_workers.utils import distance, i2f, format_dist
+from human_behaviour import sleep
+from step_walker import StepWalker
+
+
+class SpiralNavigator(object):
+ def __init__(self, bot):
+ self.bot = bot
+ self.api = bot.api
+ self.config = bot.config
+
+ self.pos = 1
+ self.x = 0
+ self.y = 0
+ self.dx = 0
+ self.dy = -1
+ self.steplimit = self.config.max_steps
+ self.steplimit2 = self.steplimit**2
+ self.origin_lat = self.bot.position[0]
+ self.origin_lon = self.bot.position[1]
+ self._step_walker = None
+
+ def take_step(self):
+ position = (self.origin_lat, self.origin_lon, 0.0)
+
+ logger.log('Scanning area for objects....')
+ # logger.log('[#] Scanning area for objects ({} / {})'.format(
+ # (step + 1), self.steplimit**2))
+ if self.config.debug:
+ logger.log(
+ 'steplimit: {} x: {} y: {} pos: {} dx: {} dy {}'.format(
+ self.steplimit2, self.x, self.y, self.pos, self.dx,
+ self.dy))
+ # Scan location math
+
+ if -self.steplimit2 / 2 < self.x <= self.steplimit2 / 2 and -self.steplimit2 / 2 < self.y <= self.steplimit2 / 2:
+ position = (self.x * 0.0025 + self.origin_lat,
+ self.y * 0.0025 + self.origin_lon, 0)
+ if self.config.walk > 0:
+ if not self._step_walker:
+ self._step_walker = StepWalker(
+ self.bot,
+ self.config.walk,
+ self.api._position_lat,
+ self.api._position_lng,
+ position[0],
+ position[1]
+ )
+
+ dist = distance(
+ i2f(self.api._position_lat),
+ i2f(self.api._position_lng),
+ position[0],
+ position[1]
+ )
+
+ logger.log('Walking from ' + str((i2f(self.api._position_lat), i2f(
+ self.api._position_lng))) + " to " + str((str(position[0:2]))) + " " + format_dist(dist, self.config.distance_unit))
+
+ if self._step_walker.step():
+ self._step_walker = None
+ else:
+ self.api.set_position(*position)
+ if self.x == self.y or self.x < 0 and self.x == -self.y or self.x > 0 and self.x == 1 - self.y:
+ (self.dx, self.dy) = (-self.dy, self.dx)
+
+ if distance(
+ i2f(self.api._position_lat),
+ i2f(self.api._position_lng),
+ position[0],
+ position[1]
+ ) <= 1 or (self.config.walk > 0 and self._step_walker == None):
+ (self.x, self.y) = (self.x + self.dx, self.y + self.dy)
+ sleep(1)
+ return position[0:2]
diff --git a/pokemongo_bot/step_walker.py b/pokemongo_bot/step_walker.py
new file mode 100644
index 0000000000..23e723b77c
--- /dev/null
+++ b/pokemongo_bot/step_walker.py
@@ -0,0 +1,71 @@
+from math import sqrt
+
+from cell_workers.utils import distance, i2f
+from human_behaviour import random_lat_long_delta, sleep
+
+
+class StepWalker(object):
+
+ def __init__(self, bot, speed, initLat, initLng, destLat, destLng):
+ self.bot = bot
+ self.api = bot.api
+
+ dist = distance(
+ i2f(initLat),
+ i2f(initLng),
+ destLat,
+ destLng
+ )
+
+ self.speed = speed
+
+ self.destLat = destLat
+ self.destLng = destLng
+
+ self.steps = (dist + 0.0) / (speed + 0.0)
+
+ if dist < speed or self.steps < 1:
+ self.dLat = 0
+ self.dLng = 0
+ self.magnitude = 0;
+ else:
+ self.dLat = (destLat - i2f(initLat)) / self.steps
+ self.dLng = (destLng - i2f(initLng)) / self.steps
+ self.magnitude = self._pythagorean(self.dLat, self.dLng)
+
+ def step(self):
+ dist = distance(
+ i2f(self.api._position_lat),
+ i2f(self.api._position_lng),
+ self.destLat,
+ self.destLng
+ )
+ # print 'distance'
+ # print dist
+
+ if (self.dLat == 0 and self.dLng == 0) or dist < self.speed:
+ self.api.set_position(self.destLat, self.destLng, 0)
+ return True
+
+ totalDLat = (self.destLat - i2f(self.api._position_lat))
+ totalDLng = (self.destLng - i2f(self.api._position_lng))
+ magnitude = self._pythagorean(totalDLat, totalDLng)
+ unitLat = totalDLat / magnitude
+ unitLng = totalDLng / magnitude
+
+ scaledDLat = unitLat * self.magnitude
+ scaledDLng = unitLng * self.magnitude
+
+ cLat = i2f(self.api._position_lat) + scaledDLat + random_lat_long_delta()
+ cLng = i2f(self.api._position_lng) + scaledDLng + random_lat_long_delta()
+
+ self.api.set_position(cLat, cLng, 0)
+ self.bot.position = (cLat,cLng,0) # set position so we can use it later on
+ self.bot.heartbeat()
+ sleep(1) # sleep one second plus a random delta
+ # self._work_at_position(
+ # i2f(self.api._position_lat), i2f(self.api._position_lng),
+ # alt, False)
+
+ def _pythagorean(self, lat, lng):
+ return sqrt((lat ** 2) + (lng ** 2))
diff --git a/pokemongo_bot/stepper.py b/pokemongo_bot/stepper.py
deleted file mode 100644
index fda5e63fb8..0000000000
--- a/pokemongo_bot/stepper.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import json
-import time
-import pprint
-
-from math import ceil
-from s2sphere import CellId, LatLng
-from google.protobuf.internal import encoder
-
-from human_behaviour import sleep, random_lat_long_delta
-from cell_workers.utils import distance, i2f, format_time
-
-from pgoapi.utilities import f2i, h2f
-import logger
-
-
-class Stepper(object):
- def __init__(self, bot):
- self.bot = bot
- self.api = bot.api
- self.config = bot.config
-
- self.pos = 1
- self.x = 0
- self.y = 0
- self.dx = 0
- self.dy = -1
- self.steplimit = self.config.max_steps
- self.steplimit2 = self.steplimit**2
- self.origin_lat = self.bot.position[0]
- self.origin_lon = self.bot.position[1]
-
- def take_step(self):
- position = (self.origin_lat, self.origin_lon, 0.0)
-
- self.api.set_position(*position)
- for step in range(self.steplimit2):
- # starting at 0 index
- logger.log('[#] Scanning area for objects ({} / {})'.format(
- (step + 1), self.steplimit**2))
- if self.config.debug:
- logger.log(
- 'steplimit: {} x: {} y: {} pos: {} dx: {} dy {}'.format(
- self.steplimit2, self.x, self.y, self.pos, self.dx,
- self.dy))
- # Scan location math
- if -self.steplimit2 / 2 < self.x <= self.steplimit2 / 2 and -self.steplimit2 / 2 < self.y <= self.steplimit2 / 2:
- position = (self.x * 0.0025 + self.origin_lat,
- self.y * 0.0025 + self.origin_lon, 0)
- if self.config.walk > 0:
- self._walk_to(self.config.walk, *position)
- else:
- self.api.set_position(*position)
- print('[#] {}'.format(position))
- if self.x == self.y or self.x < 0 and self.x == -self.y or self.x > 0 and self.x == 1 - self.y:
- (self.dx, self.dy) = (-self.dy, self.dx)
-
- (self.x, self.y) = (self.x + self.dx, self.y + self.dy)
-
- self._work_at_position(position[0], position[1], position[2], True)
- sleep(10)
-
- def _walk_to(self, speed, lat, lng, alt):
- dist = distance(
- i2f(self.api._position_lat), i2f(self.api._position_lng), lat, lng)
- steps = (dist + 0.0) / (speed + 0.0) # may be rational number
- intSteps = int(steps)
- residuum = steps - intSteps
- logger.log('[#] Walking from ' + str((i2f(self.api._position_lat), i2f(
- self.api._position_lng))) + " to " + str(str((lat, lng))) +
- " for approx. " + str(format_time(ceil(steps))))
- if steps != 0:
- dLat = (lat - i2f(self.api._position_lat)) / steps
- dLng = (lng - i2f(self.api._position_lng)) / steps
-
- for i in range(intSteps):
- cLat = i2f(self.api._position_lat) + \
- dLat + random_lat_long_delta()
- cLng = i2f(self.api._position_lng) + \
- dLng + random_lat_long_delta()
- self.api.set_position(cLat, cLng, alt)
- self.bot.heartbeat()
- sleep(1) # sleep one second plus a random delta
- self._work_at_position(
- i2f(self.api._position_lat), i2f(self.api._position_lng),
- alt, False)
-
- self.api.set_position(lat, lng, alt)
- self.bot.heartbeat()
- logger.log("[#] Finished walking")
-
- def _work_at_position(self, lat, lng, alt, pokemon_only=False):
- cellid = self._get_cellid(lat, lng)
- timestamp = [0, ] * len(cellid)
- self.api.get_map_objects(latitude=f2i(lat),
- longitude=f2i(lng),
- since_timestamp_ms=timestamp,
- cell_id=cellid)
-
- response_dict = self.api.call()
- # pprint.pprint(response_dict)
- # Passing Variables through a file
- if response_dict and 'responses' in response_dict:
- if 'GET_MAP_OBJECTS' in response_dict['responses']:
- if 'map_cells' in response_dict['responses'][
- 'GET_MAP_OBJECTS']:
- user_web_location = 'web/location-%s.json' % (self.config.username)
- if os.path.isfile(user_web_location):
- with open(user_web_location, 'w') as outfile:
- json.dump(
- {'lat': lat,
- 'lng': lng,
- 'cells': response_dict[
- 'responses']['GET_MAP_OBJECTS']['map_cells']},
- outfile)
-
- user_data_lastlocation = 'data/last-location-%s.json' % (self.config.username)
- if os.path.isfile(user_data_lastlocation):
- with open(user_data_lastlocation, 'w') as outfile:
- outfile.truncate()
- json.dump({'lat': lat, 'lng': lng}, outfile)
-
- if response_dict and 'responses' in response_dict:
- if 'GET_MAP_OBJECTS' in response_dict['responses']:
- if 'status' in response_dict['responses']['GET_MAP_OBJECTS']:
- if response_dict['responses']['GET_MAP_OBJECTS'][
- 'status'] is 1:
- map_cells = response_dict['responses'][
- 'GET_MAP_OBJECTS']['map_cells']
- position = (lat, lng, alt)
- # Sort all by distance from current pos- eventually this should build graph & A* it
- # print(map_cells)
- #print( s2sphere.from_token(x['s2_cell_id']) )
- map_cells.sort(key=lambda x: distance(lat, lng, x['forts'][0]['latitude'], x[
- 'forts'][0]['longitude']) if 'forts' in x and x['forts'] != [] else 1e6)
- for cell in map_cells:
- self.bot.work_on_cell(cell, position, pokemon_only)
-
- def _get_cellid(self, lat, long, radius=10):
- origin = CellId.from_lat_lng(LatLng.from_degrees(lat, long)).parent(15)
- walk = [origin.id()]
-
- # 10 before and 10 after
- next = origin.next()
- prev = origin.prev()
- for i in range(radius):
- walk.append(prev.id())
- walk.append(next.id())
- next = next.next()
- prev = prev.prev()
- return sorted(walk)
-
- def _encode(self, cellid):
- output = []
- encoder._VarintEncoder()(output.append, cellid)
- return ''.join(output)
diff --git a/travis-pythoncheck.py b/pylint-recursive.py
similarity index 55%
rename from travis-pythoncheck.py
rename to pylint-recursive.py
index 1095b04a12..2ced31db96 100644
--- a/travis-pythoncheck.py
+++ b/pylint-recursive.py
@@ -1,33 +1,37 @@
#! /usr/bin/env python
'''
+Author: gregorynicholas (github), modified by Jacob Henderson (jacohend, github)
Module that runs pylint on all python scripts found in a directory tree..
-'''
+'''
import os
import re
import sys
-total = 0.0
-count = 0
+passed = 0
+failed = 0
+errors = list()
def check(module):
+ global passed, failed
'''
apply pylint to the file specified if it is a *.py file
'''
- global total, count
-
if module[-3:] == ".py":
-
+
print "CHECKING ", module
pout = os.popen('pylint %s'% module, 'r')
for line in pout:
- if re.match("E....:.", line):
- print line
if "Your code has been rated at" in line:
- print line
- score = re.findall("\d.\d\d", line)[0]
- total += float(score)
- count += 1
+ print "PASSED pylint inspection: " + line
+ passed += 1
+ return True
+ if "-error" in line:
+ print "FAILED pylint inspection: " + line
+ failed += 1
+ errors.append("FILE: " + module)
+ errors.append("FAILED pylint inspection: " + line)
+ return False
if __name__ == "__main__":
try:
@@ -36,13 +40,18 @@ def check(module):
except IndexError:
print "no directory specified, defaulting to current working directory"
BASE_DIRECTORY = os.getcwd()
+
print "looking for *.py scripts in subdirectories of ", BASE_DIRECTORY
for root, dirs, files in os.walk(BASE_DIRECTORY):
for name in files:
filepath = os.path.join(root, name)
check(filepath)
-
- print "==" * 50
- print "%d modules found"% count
- print "AVERAGE SCORE = %.02f"% (total / count)
\ No newline at end of file
+ print "Passed: " + str(passed) + " Failed: " + str(failed)
+ print "\n"
+ print "Showing errors:"
+ if (str(failed)):
+ for err in errors:
+ print err
+
+ sys.exit("Pylint failed with errors")
diff --git a/release_config.json.example b/release_config.json.example
deleted file mode 100644
index 06d4687e9a..0000000000
--- a/release_config.json.example
+++ /dev/null
@@ -1,174 +0,0 @@
-{
- "any": {
- "release_under_cp": 400,
- "release_under_iv": 0.9,
- "cp_iv_logic": "and"
- },
-
- "Bulbasaur": { "release_under_cp": 374, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Ivysaur": { "release_under_cp": 571, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Venusaur": { "release_under_cp": 902, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Charmander": { "release_under_cp": 333, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Charmeleon": { "release_under_cp": 544, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Charizard": { "release_under_cp": 909, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Squirtle": { "release_under_cp": 352, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Wartortle": { "release_under_cp": 552, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Blastoise": { "release_under_cp": 888, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Caterpie": { "release_under_cp": 156, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Metapod": { "release_under_cp": 168, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Butterfree": { "release_under_cp": 508, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Weedle": { "release_under_cp": 156, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Kakuna": { "release_under_cp": 170, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Beedrill": { "release_under_cp": 504, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Pidgey": { "release_under_cp": 237, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Pidgeotto": { "release_under_cp": 427, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Pidgeot": { "release_under_cp": 729, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Rattata": { "release_under_cp": 204, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Raticate": { "release_under_cp": 504, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Spearow": { "release_under_cp": 240, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Fearow": { "release_under_cp": 609, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Ekans": { "release_under_cp": 288, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Arbok": { "release_under_cp": 616, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Pikachu": { "release_under_cp": 309, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Raichu": { "release_under_cp": 708, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Sandshrew": { "release_under_cp": 278, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Sandslash": { "release_under_cp": 631, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Nidoran F": { "release_under_cp": 304, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Nidorina": { "release_under_cp": 489, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Nidoqueen": { "release_under_cp": 868, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Nidoran M": { "release_under_cp": 295, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Nidorino": { "release_under_cp": 480, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Nidoking": { "release_under_cp": 864, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Clefairy": { "release_under_cp": 420, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Clefable": { "release_under_cp": 837, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Vulpix": { "release_under_cp": 290, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Ninetales": { "release_under_cp": 763, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Jigglypuff": { "release_under_cp": 321, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Wigglytuff": { "release_under_cp": 760, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Zubat": { "release_under_cp": 225, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Golbat": { "release_under_cp": 672, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Oddish": { "release_under_cp": 400, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Gloom": { "release_under_cp": 590, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Vileplume": { "release_under_cp": 871, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Paras": { "release_under_cp": 319, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Parasect": { "release_under_cp": 609, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Venonat": { "release_under_cp": 360, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Venomoth": { "release_under_cp": 660, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Diglett": { "release_under_cp": 158, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Dugtrio": { "release_under_cp": 408, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Meowth": { "release_under_cp": 264, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Persian": { "release_under_cp": 568, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Psyduck": { "release_under_cp": 386, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Golduck": { "release_under_cp": 832, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Mankey": { "release_under_cp": 307, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Primeape": { "release_under_cp": 650, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Growlithe": { "release_under_cp": 465, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Arcanine": { "release_under_cp": 1041, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Poliwag": { "release_under_cp": 278, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Poliwhirl": { "release_under_cp": 468, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Poliwrath": { "release_under_cp": 876, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Abra": { "release_under_cp": 208, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Kadabra": { "release_under_cp": 396, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Alakazam": { "release_under_cp": 633, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Machop": { "release_under_cp": 381, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Machoke": { "release_under_cp": 614, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Machamp": { "release_under_cp": 907, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Bellsprout": { "release_under_cp": 391, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Weepinbell": { "release_under_cp": 602, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Victreebel": { "release_under_cp": 883, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Tentacool": { "release_under_cp": 316, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Tentacruel": { "release_under_cp": 775, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Geodude": { "release_under_cp": 297, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Graveler": { "release_under_cp": 501, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Golem": { "release_under_cp": 804, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Ponyta": { "release_under_cp": 530, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Rapidash": { "release_under_cp": 768, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Slowpoke": { "release_under_cp": 424, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Slowbro": { "release_under_cp": 907, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Magnemite": { "release_under_cp": 312, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Magneton": { "release_under_cp": 657, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Farfetch'd": { "release_under_cp": 441, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Doduo": { "release_under_cp": 297, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Dodrio": { "release_under_cp": 640, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Seel": { "release_under_cp": 386, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Dewgong": { "release_under_cp": 748, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Grimer": { "release_under_cp": 448, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Muk": { "release_under_cp": 909, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Shellder": { "release_under_cp": 288, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Cloyster": { "release_under_cp": 717, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Gastly": { "release_under_cp": 280, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Haunter": { "release_under_cp": 482, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Gengar": { "release_under_cp": 724, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Onix": { "release_under_cp": 300, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Drowzee": { "release_under_cp": 374, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Hypno": { "release_under_cp": 763, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Krabby": { "release_under_cp": 276, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Kingler": { "release_under_cp": 636, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Voltorb": { "release_under_cp": 292, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Electrode": { "release_under_cp": 576, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Exeggcute": { "release_under_cp": 384, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Exeggutor": { "release_under_cp": 1032, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Cubone": { "release_under_cp": 352, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Marowak": { "release_under_cp": 578, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Hitmonlee": { "release_under_cp": 520, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Hitmonchan": { "release_under_cp": 530, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Lickitung": { "release_under_cp": 568, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Koffing": { "release_under_cp": 403, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Weezing": { "release_under_cp": 784, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Rhyhorn": { "release_under_cp": 412, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Rhydon": { "release_under_cp": 782, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Chansey": { "release_under_cp": 235, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Tangela": { "release_under_cp": 607, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Kangaskhan": { "release_under_cp": 712, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Horsea": { "release_under_cp": 278, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Seadra": { "release_under_cp": 597, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Goldeen": { "release_under_cp": 336, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Seaking": { "release_under_cp": 712, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Staryu": { "release_under_cp": 326, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Starmie": { "release_under_cp": 763, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Mr. Mime": { "release_under_cp": 520, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Scyther": { "release_under_cp": 724, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Jynx": { "release_under_cp": 600, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Electabuzz": { "release_under_cp": 739, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Magmar": { "release_under_cp": 792, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Pinsir": { "release_under_cp": 741, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Tauros": { "release_under_cp": 643, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Magikarp": { "release_under_cp": 91, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Gyarados": { "release_under_cp": 938, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Lapras": { "release_under_cp": 1041, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Ditto": { "release_under_cp": 321, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Eevee": { "release_under_cp": 376, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Vaporeon": { "release_under_cp": 984, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Jolteon": { "release_under_cp": 746, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Flareon": { "release_under_cp": 924, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Porygon": { "release_under_cp": 590, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Omanyte": { "release_under_cp": 391, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Omastar": { "release_under_cp": 780, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Kabuto": { "release_under_cp": 386, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Kabutops": { "release_under_cp": 744, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Aerodactyl": { "release_under_cp": 756, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Snorlax": { "release_under_cp": 1087, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Articuno": { "release_under_cp": 1039, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Zapdos": { "release_under_cp": 1087, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Moltres": { "release_under_cp": 1132, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Dratini": { "release_under_cp": 343, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Dragonair": { "release_under_cp": 609, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Dragonite": { "release_under_cp": 1221, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Mewtwo": { "release_under_cp": 1447, "release_under_iv": 0.8, "cp_iv_logic": "and" },
- "Mew": { "release_under_cp": 1152, "release_under_iv": 0.8, "cp_iv_logic": "and" },
-
- "exceptions": {
- "always_capture": [
- "Arcanine",
- "Lapras",
- "Dragonite",
- "Snorlax",
- "Blastoise",
- "Moltres",
- "Articuno",
- "Zapdos",
- "Mew",
- "Mewtwo"
- ]
- }
-}
diff --git a/web b/web
index dc742c598a..83463c2bcd 160000
--- a/web
+++ b/web
@@ -1 +1 @@
-Subproject commit dc742c598a2636337bd358dae8a558ef02159e8e
+Subproject commit 83463c2bcd5c4360adbc41c61e6572406fec54ce