├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── API.md ├── FAQ.md ├── LICENSE ├── README.md ├── USAGE.md ├── authors.md ├── build.gradle ├── checkstyle.xml ├── config.properties.template ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── json-template.json ├── lib ├── api-1.1.1.jar └── com.google.guava │ └── com │ └── google │ └── common │ └── util │ └── concurrent │ └── AtomicDouble.java ├── settings.gradle └── src ├── main ├── kotlin │ └── ink │ │ └── abb │ │ └── pogo │ │ └── scraper │ │ ├── Bot.kt │ │ ├── Context.kt │ │ ├── Main.kt │ │ ├── PokemonGoBotApplication.kt │ │ ├── Settings.kt │ │ ├── Task.kt │ │ ├── Values.kt │ │ ├── controllers │ │ └── BotController.kt │ │ ├── gui │ │ └── SocketServer.kt │ │ ├── services │ │ └── BotService.kt │ │ ├── tasks │ │ ├── BypassSoftban.kt │ │ ├── CatchOneNearbyPokemon.kt │ │ ├── DropUselessItems.kt │ │ ├── EvolvePokemon.kt │ │ ├── Export.kt │ │ ├── GetMapRandomDirection.kt │ │ ├── HatchEggs.kt │ │ ├── LootOneNearbyPokestop.kt │ │ ├── ProcessPokestops.kt │ │ ├── ReleasePokemon.kt │ │ ├── SetBuddyPokemon.kt │ │ ├── UpdateProfile.kt │ │ ├── Walk.kt │ │ └── WalkToStartPokestop.kt │ │ └── util │ │ ├── ApiAuthProvider.kt │ │ ├── Byte.kt │ │ ├── Log.kt │ │ ├── String.kt │ │ ├── credentials │ │ ├── Credentials.kt │ │ ├── GoogleAutoCredentials.kt │ │ ├── GoogleCredentials.kt │ │ └── PtcCredentials.kt │ │ ├── data │ │ ├── EggData.kt │ │ ├── ItemData.kt │ │ ├── LocationData.kt │ │ ├── PokedexData.kt │ │ ├── PokemonData.kt │ │ └── ProfileData.kt │ │ ├── directions │ │ ├── RouteProviderEnum.kt │ │ └── RouteRequest.kt │ │ ├── io │ │ ├── ExportCSVWriter.kt │ │ ├── ExportJSONWriter.kt │ │ └── SettingsJSONWriter.kt │ │ ├── logback │ │ └── MarkerFilter.kt │ │ ├── map │ │ └── Fort.kt │ │ └── pokemon │ │ ├── CatchablePokemon.kt │ │ ├── MapPokemon.kt │ │ └── PokemonData.kt └── resources │ ├── application.properties │ ├── banner.txt │ └── logback.xml └── test └── kotlin └── ink └── abb └── pogo └── scraper └── TestSettings.kt /.gitattributes: -------------------------------------------------------------------------------- 1 | config.properties.template eol=crlf 2 | json-template.json eol=crlf 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Thank you for wanting to help with this project! Pull requests and bug reports are always welcome, there are just some small things you should review before submitting. 3 | 4 | ## Issues 5 | If you find any issue with the bot, feel free to open an issue report. If it's just a small issue you might want to consider just asking on Slack (see below). 6 | 7 | ## Feature Requests 8 | You may open an issue also to request new features. Make sure you describe what you are missing in the library and add any pointers someone might need to implement it. 9 | 10 | ## Pull Requests 11 | If you consider submitting a pull request, please note the following: 12 | 13 | 1. All pull requests **must** be submitted to the `develop` branch. The `master` branch is exclusively mutable by release. PRs against `master` will not be merged. 14 | 2. The project is licensed under [GNU GPLv3](../LICENSE) thus all code you submit will be subject to this license. 15 | 16 | ## Contact 17 | If you have any questions regarding the development of this bot, you can ask those on the `#pokemongobot` channel on the [Pokemon GO Reverse Engineering Slack](https://pkre.slack.com/). You can [get your invite here](https://shielded-earth-81203.herokuapp.com/). 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites (Remove this section if you want) 2 | Make sure you... 3 | 4 | * Have checked the FAQ if your question has not been answered there. 5 | * Have searched the existing issues if your question has not been asked here. 6 | * Post a FULL stacktrace and not only a part of it. 7 | * Fill in all the fields correctly, it helps us immensly if we get a complete image of your situation. 8 | 9 | **Description:** 10 | [Short description of the issue observed. If this is feature request you can modify the template as required.] 11 | 12 | **Steps to reproduce:** 13 | 14 | 1. [Step 1] 15 | 2. [Step 2] 16 | 17 | **Expected behavior:** 18 | [What should happen?] 19 | 20 | **Actual behavior:** 21 | [What actually happens] 22 | 23 | **Stacktrace (If it's a crash):** 24 | [Please use pastebin if it's too long] 25 | 26 | **Version:** 27 | [The version of the bot you used; when self-compiled: :exclamation: the commit hash :exclamation:] 28 | 29 | **Operating System:** 30 | [Your operating system] 31 | 32 | **Java version:** 33 | [Full output of `java -version`] 34 | 35 | Leave this sentence in your issue as proof that you have read and used this template. 36 | 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites (Remove this section if you want) 2 | Make sure you... 3 | 4 | * Follow the [contribution guidelines](CONTRIBUTING.md) 5 | * Submit this PR against the `develop` branch. 6 | 7 | **Fixed issue:** [Reference the issue number here, or remove if not a fix] 8 | 9 | **Changes made:** 10 | 11 | * List your changes here 12 | * Change 2... 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | *.class 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | ### Gradle template 16 | .gradle 17 | build/ 18 | 19 | # Ignore Gradle GUI config 20 | gradle-app.setting 21 | 22 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 23 | !gradle-wrapper.jar 24 | 25 | # Cache of project 26 | .gradletasknamecache 27 | 28 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 29 | # gradle/wrapper/gradle-wrapper.properties 30 | ### JetBrains template 31 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 32 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 33 | 34 | # User-specific stuff: 35 | .idea/ 36 | *.iml 37 | 38 | # Sensitive or high-churn files: 39 | .idea/dataSources.ids 40 | .idea/dataSources.xml 41 | .idea/dataSources.local.xml 42 | .idea/sqlDataSources.xml 43 | .idea/dynamic.xml 44 | .idea/uiDesigner.xml 45 | 46 | # Gradle: 47 | .idea/gradle.xml 48 | .idea/libraries 49 | 50 | # Mongo Explorer plugin: 51 | .idea/mongoSettings.xml 52 | 53 | ## File-based project format: 54 | *.iws 55 | 56 | ## Plugin-specific files: 57 | 58 | # IntelliJ 59 | /out/ 60 | 61 | # mpeltonen/sbt-idea plugin 62 | .idea_modules/ 63 | 64 | # JIRA plugin 65 | atlassian-ide-plugin.xml 66 | 67 | # Crashlytics plugin (for Android Studio and IntelliJ) 68 | com_crashlytics_export_strings.xml 69 | crashlytics.properties 70 | crashlytics-build.properties 71 | fabric.properties 72 | 73 | # PTC Login credentials 74 | config.properties 75 | 76 | # OS X files 77 | .DS_Store 78 | 79 | # exported file 80 | export_*.* 81 | 82 | bot-settings/ 83 | logs/ 84 | 85 | altitude_cache.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | branches: 7 | only: 8 | - master 9 | - develop 10 | 11 | script: 12 | - ./gradlew build 13 | 14 | before_cache: 15 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 16 | - rm -rf $HOME/.gradle/caches/*/plugin-resolution/ 17 | 18 | cache: 19 | directories: 20 | - $HOME/.gradle/caches/ 21 | - $HOME/.gradle/wrapper/ 22 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # REST API Documentation 2 | 3 | Control your bot externally via the REST API. 4 | 5 | ## Bot configuration 6 | 7 | Secure your API by putting a secure (randomized) password in the `rest_api_password` setting in your config.properties file or the `restApiPassword` setting in your JSON file(s). 8 | 9 | Each bot has its own password, so use the correct one in the next step (or if you are lazy, use the same value everywhere). 10 | 11 | If the configuration key is not set, it will be generated and saved in the config file. 12 | 13 | ## Authentication 14 | 15 | Request an access token with a `POST` to `http://localhost:8080/api/bot/{name}/auth` where `{name}` is the name of your bot (= `default` by default) and the raw body must be the value from the `rest_api_password` setting from the first step. 16 | 17 | Mind the default server port, which is 8080 (to change this, run the bot as `java -jar PokemonGoBot.jar --server-port=XXXX`) and do NOT use the socket port defined in your bot configuration (which is by default 8001). 18 | 19 | You will get a response from the server with a random session token which must be placed in the `X-PGB-ACCESS-TOKEN` header for further use of the API. 20 | 21 | If the authentication fails, you will get a HTTP status code 401 (Unauthorized). Make sure the configured password is correct, and your client sends a correct raw message (ex `curl -d` appends the raw body with `=` which fails the auth). 22 | 23 | ## REST API Endpoints 24 | 25 | * General end-point: 26 | - GET `/api/bots` => List all bots (this does not need the `X-PGB-ACCESS-TOKEN` header) 27 | 28 | * Bot end-point: 29 | - POST `/api/bot/{name}/load` => Load bot 30 | - POST `/api/bot/{name}/unload` => Unload bot 31 | - POST `/api/bot/{name}/reload` => Reload bot 32 | - POST `/api/bot/{name}/stop` => Stop bot 33 | - POST `/api/bot/{name}/start` => Start bot 34 | 35 | * Pokemon end-point: 36 | - GET `/api/bot/{name}/pokemons` => List all pokemons 37 | - POST `/api/bot/{name}/pokemon/{id}/transfer` => Transfer pokemon 38 | - POST `/api/bot/{name}/pokemon/{id}/evolve` => Evolve pokemon (HTTP status code 400 (Bad Request) when not enough candy) 39 | - POST `/api/bot/{name}/pokemon/{id}/powerup` => Power-up pokemon (HTTP status code 400 (Bad Request) when not enough candy or stardust) 40 | - POST `/api/bot/{name}/pokemon/{id}/favorite` => Toggle favorite for this pokemon 41 | - POST `/api/bot/{name}/pokemon/{id}/rename` => Rename pokemon, request body MUST be the new name of the pokemon 42 | 43 | * Item end-point: 44 | - GET `/api/bot/{name}/items` => List all items 45 | - DELETE `/api/bot/{name}/item/{id}/drop/{quantity}` => Drop `quantity` of this item (HTTP status code 400 (Bad Request) when invalid quantity) 46 | - POST `/api/bot/{name}/useIncense` => Use an incense (HTTP status code 400 (Bad Request) when not enough incense) 47 | - POST `/api/bot/{name}/useLuckyEgg` => Use a lucky egg (HTTP status code 400 (Bad Request) when not enough lucky eggs) 48 | 49 | * Misc end-points: 50 | - GET `/api/bot/{name}/location` => Get bot current location 51 | - POST `/api/bot/{name}/location/{latitude}/{longitude}` => Change bot location (HTTP status code 400 (Bad Request) on invalid `latitude` or `longitude`) 52 | - GET `/api/bot/{name}/profile` => Get account profile 53 | - GET `/api/bot/{name}/pokedex` => Get account pokedex 54 | - GET `/api/bot/{name}/eggs` => Get eggs 55 | 56 | ## Examples 57 | 58 | * A very simple [proof of concept with javascript](https://gist.github.com/Sieberkev/0f96f190615cebf15a07ca2a8a2a61ca) can be found here. 59 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ## I got `BUILD FAILED` 4 | 5 | There is a lot of output with the reason WHY the build fails, please read it through carefully and fix any errors mentionned in the output. 6 | 7 | ## I get an `AsyncPokemonGoException: Unknown exception occurred` / `Error running loop ProfileLoop!` / `Error running loop BotLoop!` 8 | 9 | Only that is not enough, check the whole stacktrace for the correct error. 10 | 11 | ## I get an `InvalidProtocolBufferException: Contents of buffer are null` 12 | 13 | This is a known issue with the currently used Pokemon GO API. The bot sends too many requests in a too short time to the servers, which return with a null value. We hope to get that fixed shortly, but is not something you can simply fix with some configuration settings or by putting extra Thread.sleep() in the code. 14 | 15 | ## After login I see `Accepting ToS` and get a `RemoteServerException: Your account may be banned! please try from the official client.` 16 | 17 | If you CAN login to the official client, complete the tutorial (catch your first pokemon, touch your first pokestop) and try again. 18 | 19 | If you CAN'T login in the official client but see "Error fetching game data": RIP items/eggs/pokemons, your account got permabanned. You knew this was possible since you did read and violate the ToS of the official game. 20 | 21 | ## The bot doesn't find/catch Pokemon 22 | 23 | Currently not reproducible so can't be reliably fixed. Look here for some people that managed to get it fixed: https://github.com/jabbink/PokemonGoBot/issues/21 24 | 25 | Some possible issues: 26 | 27 | * Make sure your system time is (semi-)correct (let it autosync with an online server) 28 | * Make sure the mobile app is not on (kill the process if need be) 29 | * Make sure the account you're botting on did do the initial tutorial (mainly catching a starter Pokemon) 30 | * Make sure your item bank is empty 31 | * Make sure you have enough pokeballs and your pokebank is not full 32 | * You caught too many pokemons in a short time and are softbanned (don't catch more than 1000 Pokemon in 23 hours, [source](https://www.reddit.com/r/pokemongodev/comments/4xkqmq/new_ban_types_and_their_causes/)). 33 | 34 | ## The bot doesn't find/loot Pokestops 35 | 36 | Some possible issues: 37 | 38 | * Your startlocation is wrong and/or there are no pokestops nearby. 39 | * You looted too many pokestops in a short time and are softbanned (don't loot more than 2000 Pokestops in 23 hours, [source](https://www.reddit.com/r/pokemongodev/comments/4xkqmq/new_ban_types_and_their_causes/)) 40 | 41 | ## The bot doesn't walk 42 | 43 | Some possible issues: 44 | 45 | * You're camping a Pokestop with a lure (should be visible in log) 46 | * You're resting (should be visible in log) 47 | 48 | ## Immediately after starting I get a LoginFailedException 49 | 50 | - Make sure the provided credentials in the `config.properties` file are correct. 51 | - If you're using PTC, your credentials are correct and your password is longer than 15 characters, only enter the first 15 characters of your account and the login should work. 52 | - If there is a token present, remove it and retry. 53 | 54 | ## After 1.5 hours I get a errors with PTC/Google login `LoginFailedException: Invalid Auth status code recieved, token not refreshed?` 55 | 56 | Known issue with the Java API handling login for PTC/Google. 57 | 58 | ## I get a RemoteServerException or something about "502" 59 | 60 | The Pokemon Go servers are offline/too busy. Check IsPokemonGoDownOrNot.com 61 | 62 | ## The GUI only loads the command line. 63 | 64 | Separate the windows as they're conjoined together. 65 | 66 | ## Where did the GUI go? 67 | 68 | The GUI is now hosted on http://ui.pogobot.club/ 69 | 70 | This URL is also shown in the console when you launch the bot. 71 | 72 | ## Where did the GPX support go? 73 | 74 | If you like the old UI with GPX support, go to http://ui.pogobot.club/0.5.0-alpha4/map.html 75 | 76 | If your bot runs the GUI socket on port `8001`, everything should work automatically, if not, append your IP address and port to the URL like this: http://ui.pogobot.club/0.5.0-alpha4/map.html#127.0.0.1:8001 77 | 78 | ## The bot refuses to login to my Google account 79 | 80 | Make sure you enter the full email address (including `@`) in the `username=` property. If you use a JSON configuration, make sure the credential type is set as `google-auto`. 81 | 82 | ## Error: unable to access jarfile xxxxx 83 | 84 | Make sure you are working in the directory where the JAR file is located. If you JAR file is located in `C:\Users\userprofilename\Desktop\pogobot\` you can change your directory in the console with the command `cd C:\Users\userprofilename\Desktop\pogobot\` (do not simply copy/paste this command but alter it to your location first). 85 | 86 | ## I want to use multiple bots 87 | 88 | Define the bot configurations in multiple JSON files in the `bot-settings` directory. 89 | 90 | Refer to the file named [`json-template.json`](./json-template.json) for an example. 91 | 92 | If you use a PTC account, set credential type to "ptc", if you use a Google account, set credential type to `google-auto`! 93 | 94 | Make sure all JSON files have a different `guiPortSocket` or set that port to `0`! 95 | 96 | ## I get "Address already in use" 97 | 98 | It is possible you did not close the bot correctly (do not close the console window, but do `CTRL+C` first to close the process) and it is still running in the background. Kill all running JAVA processes and restart the bot. 99 | 100 | If that is not the case, you are either trying to run multiple bots by running Java multiple times, multiple bot configurations use the same `guiPortSocket` or the port is simply in use by another application. 101 | 102 | To change the socket ports, change `guiPortSocket` (`gui_port_socket` in `config.properties`) to another port, or disable the GUI by setting this to `0`. 103 | 104 | Also the management API for multiple bots requires a port. By default `8080` is used. To change this, run the bot as `java -jar PokemonGoBot.jar --server.port=XXXX`. If you don't need that interface, run the bot as `java -jar PokemonGoBot.jar --spring.main.web-environment=false`! 105 | 106 | ## I get JsonMappingException "Can not deserialize instance of java.util.ArrayList out of VALUE_FALSE token" 107 | 108 | The followStreets settings changed from boolean to array. 109 | You have to remove the "followStreets" line on your "default.json" file. 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS BOT IS DEFUNCT AND NOT MAINTAINED 2 | 3 | ## DO NOT ATTEMPT TO USE IT, ARCHIVE ONLY 4 | 5 | # Pokemon Go Bot 6 | 7 | [![Build Status](https://travis-ci.org/jabbink/PokemonGoBot.svg?branch=develop)](https://travis-ci.org/jabbink/PokemonGoBot) 8 | 9 | ## Welcome to _the_ Pokemon Go Bot 10 | 11 | ## FAQ 12 | 13 | Before opening an issue or explaining your problem on Discord, please refer to the existing [Issues](https://github.com/jabbink/PokemonGoBot/issues) and the [FAQ](./FAQ.md) first! 14 | 15 | ## Usage 16 | 17 | [Click here to view how to use this bot](./USAGE.md) 18 | 19 | ### Screenshot 20 | 21 | [![GUI Screenshot](http://ui.pogobot.club/img/gui-screenshot-01.png)](https://github.com/jabbink/PokemonGoBot/blob/gh-pages/README.md) 22 | 23 | ## Features 24 | 25 | * Login with Google and Pokemon Trainer Club 26 | * Walk (while following roads (optional)) from Pokestop to Pokestop (highly configurable) 27 | * Collect all Pokemon that are within reach 28 | * Automatically drop useless items and bad Pokemon (100% configurable) 29 | * Hatch eggs 30 | * Export your Pokemon to many different export formats (to keep track in Excel/other external applications) 31 | 32 | # Contributing 33 | If you want to help and add a new feature, you can create a pull request to merge in the `develop` branch and not in the `master`. 34 | As the name says, the `develop` branch is for developing where we'll add new features, with your help; instead we'll update the `master` every now and then, and from that we'll release a new jar. 35 | 36 | # Donations 37 | 38 | If you really like this bot and would like to donate, you can send Bitcoin donations to **1PoGoboTzD9jS43qrQYogMFW6nmNfK1BGi** 39 | 40 | If you rather use PayPal, please click here: 41 | 42 | [![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B2ZM2WREK7B32) 43 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | ## ***Please Read***: 4 | 5 | - We're very happy to have you partake in this experience with us and even possibly contribute. 6 | - However, due to an overwhelming amount of attention from the public this repository has seen an onslaught of attention. 7 | - With that said, please do your due diligence and research your problem without opening unnecessary issue tickets. 8 | - *Searching* here, reddit or Google will more than likely provide you with an answer. 9 | - Common issues may be found at the bottom of this page. 10 | - Those that are actively contributing to this project utilize the ticket system for tracking technical issues and 11 | having to answer the same question can really clog up the pipes for people who are presenting an original problem. 12 | - For legitimate technical issues **PLEASE** abide by the given template and provide as much information as possible. 13 | - For extensive logs, please use PasteBin. 14 | 15 | ## Prebuilt 16 | 17 | 1. Make sure you have Oracle Java 1.8 JRE (JDK works too) installed (`java -version` in a command line) 18 | - If not, go [here](http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html). 19 | 2. Download the latest release from [here](https://github.com/jabbink/PokemonGoBot/releases). 20 | 3. Download [config.properties.template](https://raw.githubusercontent.com/jabbink/PokemonGoBot/master/config.properties.template) and save it in the same directory 21 | 4. Rename `config.properties.template` to `config.properties` (make sure your operating system doesn't rename it to `config.properties.txt`) 22 | 5. Fill in the blanks 23 | 6. Open a terminal (or `cmd.exe` on Windows) 24 | 7. Use `cd` to go into the directory with your config and the downloaded `.jar` 25 | 8. `java -jar PokemonGoBot-VERSION.jar` (replace version with the downloaded one, or type `PokemonGoBot-` and press `TAB`) 26 | 27 | ## From source 28 | 29 | 1. Make sure you have Oracle Java JDK 1.8 installed (`java -version` in a command line) 30 | - If not, go [here](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). 31 | 2. Clone this repo: `git clone https://github.com/jabbink/PokemonGoBot.git && cd PokemonGoBot` or download the zip 32 | 3. Run from terminal/cmd: `gradlew build` 33 | 4. Copy `./config.properties.template` to `./config.properties` 34 | 5. Modify `config.properties` as you please 35 | 6. To run the bot directly from console run `gradlew run` 36 | 7. :exclamation: If you use JetBrains IntelliJ, install the Lombok plugin and enable Settings -> Compiler -> Annotation Processors -> Enable annotation processing :exclamation: 37 | 38 | ## Stopping the bot 39 | 40 | Do not simply close the console window, but press `CTRL+C` first to terminate the bot gracefully. Otherwise there's a chance the process will still run in the background and give an `address in use` error when you want to restart it. -------------------------------------------------------------------------------- /authors.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | (in no particular order) 4 | 5 | @jabbink 6 | 7 | @SwipeX 8 | 9 | @vmarchaud 10 | 11 | @pr0ves 12 | 13 | @apottere 14 | 15 | @CosmoRing 16 | 17 | @dommerq 18 | 19 | @meMo-Minsk 20 | 21 | @shilch 22 | 23 | @Sieberkev 24 | 25 | @KyleBoyer 26 | 27 | @pashorizer 28 | 29 | @FrancYescO 30 | 31 | @Peyphour 32 | 33 | @langerhans 34 | 35 | @citruz 36 | 37 | @AlexBoulyga 38 | 39 | @mineme45 40 | 41 | @Turtizzle 42 | 43 | @svombergen 44 | 45 | @kasagon 46 | 47 | @krystiancharubin 48 | 49 | @urbalazs 50 | 51 | @BerserkerJP 52 | 53 | @i32ropie 54 | 55 | @zenwaichi 56 | 57 | @Agronis 58 | 59 | @rraumberger 60 | 61 | @vdianl 62 | 63 | @sodanakin 64 | 65 | @ToMeRhh 66 | 67 | @Pblolpb 68 | 69 | @kom-au 70 | 71 | @szymanskip 72 | 73 | @Cypherke 74 | 75 | @muschter 76 | 77 | @Forty-Tw0 78 | 79 | @ReignOfComputer 80 | 81 | @Demelza 82 | 83 | @sirsean 84 | 85 | @Machtergreifung 86 | 87 | @bg-git-hub 88 | 89 | @Loewe1000 90 | 91 | @Enochen 92 | 93 | @joligario 94 | 95 | @jozsi 96 | 97 | @EpicAwesomeWolf 98 | 99 | @JoeyyT 100 | 101 | @eruecco87 102 | 103 | @pureexe 104 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'ink.abb' 2 | version '1.0.3' 3 | 4 | buildscript { 5 | ext { 6 | kotlin_version = '1.0.3' 7 | springBootVersion = '1.3.6.RELEASE' 8 | jacksonVersion = '2.8.0' 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 17 | classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion" 18 | } 19 | } 20 | 21 | apply plugin: 'java' 22 | apply plugin: 'kotlin' 23 | apply plugin: 'application' 24 | apply plugin: 'spring-boot' 25 | apply plugin: 'checkstyle' 26 | 27 | mainClassName = 'ink.abb.pogo.scraper.MainKt' 28 | 29 | sourceCompatibility = 1.8 30 | targetCompatibility = 1.8 31 | 32 | repositories { 33 | mavenCentral() 34 | jcenter() 35 | maven { url "https://jitpack.io" } 36 | } 37 | 38 | def sourceResourcesFolder = "$projectDir/src/main/resources" 39 | 40 | sourceSets { 41 | main { 42 | kotlin { 43 | srcDir 'src/main/kotlin' 44 | } 45 | java { 46 | srcDir 'lib/com.google.guava' 47 | } 48 | resources.srcDirs sourceResourcesFolder 49 | } 50 | } 51 | 52 | checkstyle { 53 | toolVersion = "7.1" 54 | checkstyleMain.configFile = new File(rootDir, "checkstyle.xml") 55 | checkstyleMain.source = "src/main/kotlin" 56 | checkstyleMain.ignoreFailures = false 57 | checkstyleMain.showViolations = true 58 | } 59 | 60 | configurations { 61 | compile.exclude module: "spring-boot-starter-tomcat" 62 | } 63 | 64 | dependencies { 65 | //compile files('../PokeGOAPI-Java/library/build/libs/PokeGOAPI-library-all-0.3.0.jar') 66 | 67 | compile files('lib/api-1.1.1.jar') 68 | 69 | // TODO: Wait for jitpack to fix itself... 70 | //compile 'com.github.Grover-c13:PokeGOAPI-Java:Development-SNAPSHOT' 71 | 72 | compile 'com.squareup.okhttp3:okhttp:3.4.1' 73 | compile 'io.reactivex:rxkotlin:0.60.0' 74 | compile 'svarzee.gps:gpsoauth:0.3' 75 | compile 'com.squareup.moshi:moshi:1.2.0' 76 | compile 'net.jpountz.lz4:lz4:1.3.0' 77 | compile 'com.google.protobuf:protobuf-java:3.0.0-beta-3' 78 | 79 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 80 | compile "com.corundumstudio.socketio:netty-socketio:1.7.7" 81 | 82 | compile 'org.springframework.boot:spring-boot-starter' 83 | compile 'org.springframework.boot:spring-boot-starter-web' 84 | compile 'org.springframework.boot:spring-boot-starter-undertow' 85 | 86 | compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion 87 | compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion 88 | compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: jacksonVersion 89 | compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: jacksonVersion 90 | 91 | compile group: 'com.google.maps', name: 'google-maps-services', version: '0.1.15' 92 | 93 | testCompile group: 'junit', name: 'junit', version: '4.11' 94 | testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 95 | } 96 | 97 | jar { 98 | manifest { 99 | attributes( 100 | 'Class-Path': configurations.compile.collect { it.getName() }.join(' '), 101 | 'Main-Class': mainClassName 102 | ) 103 | } 104 | } 105 | 106 | task createProperties(dependsOn: processResources) << { 107 | String versionDir = "$buildDir/resources/main/ink/abb/pogo/scraper" 108 | new File(versionDir).mkdirs() 109 | new File("$versionDir/version.properties").withWriter { w -> 110 | Properties p = new Properties() 111 | p['version'] = project.version.toString() 112 | p.store w, null 113 | } 114 | } 115 | 116 | classes { 117 | dependsOn createProperties 118 | } 119 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 53 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jabbink/PokemonGoBot/ad0db0aca621b4169af06697a310af570bb98490/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 03 10:38:42 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /json-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "latitude" : 0, 3 | "longitude" : 0, 4 | "saveLocationOnShutdown" : true, 5 | "savedLatitude" : 0, 6 | "savedLongitude" : 0, 7 | "credentials" : { 8 | "type" : "ptc", 9 | "username" : "", 10 | "password" : "" 11 | }, 12 | "proxyServer" : "", 13 | "proxyPort" : "-1", 14 | "proxyType" : "SOCKS", 15 | "proxyUsername" : "", 16 | "proxyPassword" : "", 17 | "restApiPassword" : "", 18 | "speed" : 2.8, 19 | "randomSpeedRange" : 0.0, 20 | "followStreets" : [], 21 | "mapzenApiKey" : "", 22 | "googleApiKey" : "", 23 | "groupItemsByType" : false, 24 | "dropItems" : true, 25 | "itemDropDelay" : -1, 26 | "uselessItems" : { 27 | "ITEM_REVIVE" : 20, 28 | "ITEM_MAX_REVIVE" : 10, 29 | "ITEM_POTION" : 0, 30 | "ITEM_SUPER_POTION" : 30, 31 | "ITEM_HYPER_POTION" : 50, 32 | "ITEM_MAX_POTION" : 50, 33 | "ITEM_POKE_BALL" : 40, 34 | "ITEM_GREAT_BALL" : 50, 35 | "ITEM_ULTRA_BALL" : 50, 36 | "ITEM_MASTER_BALL" : 10, 37 | "ITEM_RAZZ_BERRY" : 40, 38 | "ITEM_LUCKY_EGG" : -1, 39 | "ITEM_INCENSE_ORDINARY" : -1, 40 | "ITEM_TROY_DISK" : -1 41 | }, 42 | "profileUpdateTimer" : 60, 43 | "timerWalkToStartPokestop" : -1, 44 | "randomNextPokestopSelection" : 5, 45 | "campLurePokestop" : -1, 46 | "desiredCatchProbability" : 0.4, 47 | "desiredCatchProbabilityUnwanted" : 0.0, 48 | "autotransfer" : true, 49 | "autotransferTimeDelay" : -1, 50 | "randomBallThrows" : false, 51 | "waitBetweenThrows" : false, 52 | "keepPokemonAmount" : 1, 53 | "maxPokemonAmount" : -1, 54 | "displayKeepalive" : true, 55 | "displayPokestopName" : false, 56 | "displayPokestopRewards" : true, 57 | "displayPokemonCatchRewards" : true, 58 | "displayIfPokemonFromLure" : true, 59 | "lootPokestop" : true, 60 | "catchPokemon" : true, 61 | "autoFillIncubator" : true, 62 | "sortByIv" : false, 63 | "desiredCurveRate" : 0.0, 64 | "neverUseBerries" : true, 65 | "allowLeaveStartArea" : false, 66 | "spawnRadius" : -1, 67 | "banSpinCount" : 0, 68 | "transferCpThreshold" : 400, 69 | "transferCpMinThreshold" : -1, 70 | "transferIvThreshold" : 80, 71 | "ignoredPokemon" : [ "EEVEE", "MEWTWO", "CHARMANDER" ], 72 | "obligatoryTransfer" : [ "DODUO", "RATTATA", "CATERPIE", "PIDGEY" ], 73 | "evolveBeforeTransfer" : [ "CATERPIE", "RATTATA", "WEEDLE", "PIDGEY" ], 74 | "neverCatchPokemon" : [], 75 | "evolveStackLimit" : 100, 76 | "useLuckyEgg" : 1, 77 | "evolveTimeDelay" : 300, 78 | "export" : "", 79 | "guiPortSocket" : 8001, 80 | "initialMapSize" : 9, 81 | "waitChance" : 0.0, 82 | "waitTimeMin" : 0, 83 | "waitTimeMax" : 0, 84 | "botTimeoutAfterMinutes" : -1, 85 | "botTimeoutAfterCatchingPokemon" : -1, 86 | "botTimeoutAfterVisitingPokestops" : -1, 87 | "buddyPokemon" : "" 88 | } 89 | 90 | -------------------------------------------------------------------------------- /lib/api-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jabbink/PokemonGoBot/ad0db0aca621b4169af06697a310af570bb98490/lib/api-1.1.1.jar -------------------------------------------------------------------------------- /lib/com.google.guava/com/google/common/util/concurrent/AtomicDouble.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Written by Doug Lea and Martin Buchholz with assistance from 3 | * members of JCP JSR-166 Expert Group and released to the public 4 | * domain, as explained at 5 | * http://creativecommons.org/publicdomain/zero/1.0/ 6 | */ 7 | 8 | /* 9 | * Source: 10 | * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/extra/AtomicDouble.java?revision=1.13 11 | * (Modified to adapt to guava coding conventions and 12 | * to use AtomicLongFieldUpdater instead of sun.misc.Unsafe) 13 | */ 14 | 15 | package com.google.common.util.concurrent; 16 | 17 | import static java.lang.Double.doubleToRawLongBits; 18 | import static java.lang.Double.longBitsToDouble; 19 | 20 | import java.util.concurrent.atomic.AtomicLongFieldUpdater; 21 | 22 | /** 23 | * A {@code double} value that may be updated atomically. See the 24 | * {@link java.util.concurrent.atomic} package specification for 25 | * description of the properties of atomic variables. An {@code 26 | * AtomicDouble} is used in applications such as atomic accumulation, 27 | * and cannot be used as a replacement for a {@link Double}. However, 28 | * this class does extend {@code Number} to allow uniform access by 29 | * tools and utilities that deal with numerically-based classes. 30 | * 31 | *

This class compares primitive {@code double} 32 | * values in methods such as {@link #compareAndSet} by comparing their 33 | * bitwise representation using {@link Double#doubleToRawLongBits}, 34 | * which differs from both the primitive double {@code ==} operator 35 | * and from {@link Double#equals}, as if implemented by: 36 | *

 {@code
 37 |  * static boolean bitEquals(double x, double y) {
 38 |  *   long xBits = Double.doubleToRawLongBits(x);
 39 |  *   long yBits = Double.doubleToRawLongBits(y);
 40 |  *   return xBits == yBits;
 41 |  * }}
42 | * 43 | *

It is possible to write a more scalable updater, at the cost of 44 | * giving up strict atomicity. See for example 45 | * 46 | * DoubleAdder 47 | * and 48 | * 49 | * DoubleMaxUpdater. 50 | * 51 | * @author Doug Lea 52 | * @author Martin Buchholz 53 | * @since 11.0 54 | */ 55 | public class AtomicDouble extends Number implements java.io.Serializable { 56 | private static final long serialVersionUID = 0L; 57 | 58 | private transient volatile long value; 59 | 60 | private static final AtomicLongFieldUpdater updater = 61 | AtomicLongFieldUpdater.newUpdater(AtomicDouble.class, "value"); 62 | 63 | /** 64 | * Creates a new {@code AtomicDouble} with the given initial value. 65 | * 66 | * @param initialValue the initial value 67 | */ 68 | public AtomicDouble(double initialValue) { 69 | value = doubleToRawLongBits(initialValue); 70 | } 71 | 72 | /** 73 | * Creates a new {@code AtomicDouble} with initial value {@code 0.0}. 74 | */ 75 | public AtomicDouble() { 76 | // assert doubleToRawLongBits(0.0) == 0L; 77 | } 78 | 79 | /** 80 | * Gets the current value. 81 | * 82 | * @return the current value 83 | */ 84 | public final double get() { 85 | return longBitsToDouble(value); 86 | } 87 | 88 | /** 89 | * Sets to the given value. 90 | * 91 | * @param newValue the new value 92 | */ 93 | public final void set(double newValue) { 94 | long next = doubleToRawLongBits(newValue); 95 | value = next; 96 | } 97 | 98 | /** 99 | * Eventually sets to the given value. 100 | * 101 | * @param newValue the new value 102 | */ 103 | public final void lazySet(double newValue) { 104 | set(newValue); 105 | // TODO(user): replace with code below when jdk5 support is dropped. 106 | // long next = doubleToRawLongBits(newValue); 107 | // updater.lazySet(this, next); 108 | } 109 | 110 | /** 111 | * Atomically sets to the given value and returns the old value. 112 | * 113 | * @param newValue the new value 114 | * @return the previous value 115 | */ 116 | public final double getAndSet(double newValue) { 117 | long next = doubleToRawLongBits(newValue); 118 | return longBitsToDouble(updater.getAndSet(this, next)); 119 | } 120 | 121 | /** 122 | * Atomically sets the value to the given updated value 123 | * if the current value is bitwise equal 124 | * to the expected value. 125 | * 126 | * @param expect the expected value 127 | * @param update the new value 128 | * @return {@code true} if successful. False return indicates that 129 | * the actual value was not bitwise equal to the expected value. 130 | */ 131 | public final boolean compareAndSet(double expect, double update) { 132 | return updater.compareAndSet(this, 133 | doubleToRawLongBits(expect), 134 | doubleToRawLongBits(update)); 135 | } 136 | 137 | /** 138 | * Atomically sets the value to the given updated value 139 | * if the current value is bitwise equal 140 | * to the expected value. 141 | * 142 | *

May 144 | * fail spuriously 145 | * and does not provide ordering guarantees, so is only rarely an 146 | * appropriate alternative to {@code compareAndSet}. 147 | * 148 | * @param expect the expected value 149 | * @param update the new value 150 | * @return {@code true} if successful 151 | */ 152 | public final boolean weakCompareAndSet(double expect, double update) { 153 | return updater.weakCompareAndSet(this, 154 | doubleToRawLongBits(expect), 155 | doubleToRawLongBits(update)); 156 | } 157 | 158 | /** 159 | * Atomically adds the given value to the current value. 160 | * 161 | * @param delta the value to add 162 | * @return the previous value 163 | */ 164 | public final double getAndAdd(double delta) { 165 | while (true) { 166 | long current = value; 167 | double currentVal = longBitsToDouble(current); 168 | double nextVal = currentVal + delta; 169 | long next = doubleToRawLongBits(nextVal); 170 | if (updater.compareAndSet(this, current, next)) { 171 | return currentVal; 172 | } 173 | } 174 | } 175 | 176 | /** 177 | * Atomically adds the given value to the current value. 178 | * 179 | * @param delta the value to add 180 | * @return the updated value 181 | */ 182 | public final double addAndGet(double delta) { 183 | while (true) { 184 | long current = value; 185 | double currentVal = longBitsToDouble(current); 186 | double nextVal = currentVal + delta; 187 | long next = doubleToRawLongBits(nextVal); 188 | if (updater.compareAndSet(this, current, next)) { 189 | return nextVal; 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * Returns the String representation of the current value. 196 | * @return the String representation of the current value 197 | */ 198 | public String toString() { 199 | return Double.toString(get()); 200 | } 201 | 202 | /** 203 | * Returns the value of this {@code AtomicDouble} as an {@code int} 204 | * after a narrowing primitive conversion. 205 | */ 206 | public int intValue() { 207 | return (int) get(); 208 | } 209 | 210 | /** 211 | * Returns the value of this {@code AtomicDouble} as a {@code long} 212 | * after a narrowing primitive conversion. 213 | */ 214 | public long longValue() { 215 | return (long) get(); 216 | } 217 | 218 | /** 219 | * Returns the value of this {@code AtomicDouble} as a {@code float} 220 | * after a narrowing primitive conversion. 221 | */ 222 | public float floatValue() { 223 | return (float) get(); 224 | } 225 | 226 | /** 227 | * Returns the value of this {@code AtomicDouble} as a {@code double}. 228 | */ 229 | public double doubleValue() { 230 | return get(); 231 | } 232 | 233 | /** 234 | * Saves the state to a stream (that is, serializes it). 235 | * 236 | * @serialData The current value is emitted (a {@code double}). 237 | */ 238 | private void writeObject(java.io.ObjectOutputStream s) 239 | throws java.io.IOException { 240 | s.defaultWriteObject(); 241 | 242 | s.writeDouble(get()); 243 | } 244 | 245 | /** 246 | * Reconstitutes the instance from a stream (that is, deserializes it). 247 | */ 248 | private void readObject(java.io.ObjectInputStream s) 249 | throws java.io.IOException, ClassNotFoundException { 250 | s.defaultReadObject(); 251 | 252 | set(s.readDouble()); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'PokemonGoBot' 2 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/Context.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper 10 | 11 | import com.google.common.util.concurrent.AtomicDouble 12 | import ink.abb.pogo.api.PoGoApi 13 | import com.google.maps.GeoApiContext 14 | import ink.abb.pogo.scraper.gui.SocketServer 15 | import java.time.LocalDateTime 16 | import java.util.concurrent.atomic.AtomicBoolean 17 | import java.util.concurrent.atomic.AtomicInteger 18 | import java.util.concurrent.atomic.AtomicLong 19 | 20 | data class Context( 21 | val api: PoGoApi, 22 | val lat: AtomicDouble, 23 | val lng: AtomicDouble, 24 | 25 | val startXp: AtomicLong, 26 | val startTime: LocalDateTime, 27 | val pokemonStats: Pair, 28 | val luredPokemonStats: AtomicInteger, 29 | val pokestops: AtomicInteger, 30 | val itemStats: Pair, 31 | var walkingSpeed: AtomicDouble, 32 | 33 | val blacklistedEncounters: MutableSet, 34 | val server: SocketServer, 35 | 36 | val pokemonInventoryFullStatus: AtomicBoolean = AtomicBoolean(false), 37 | 38 | var restApiPassword: String, 39 | var s2Cache: MutableMap, 40 | var restApiToken: String = "", 41 | 42 | val walking: AtomicBoolean = AtomicBoolean(false), 43 | 44 | val pauseWalking: AtomicBoolean = AtomicBoolean(false), 45 | 46 | val geoApiContext: GeoApiContext? 47 | ) 48 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/Main.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper 10 | 11 | import com.google.common.util.concurrent.AtomicDouble 12 | import ink.abb.pogo.api.PoGoApiImpl 13 | import ink.abb.pogo.api.auth.CredentialProvider 14 | import ink.abb.pogo.api.auth.GoogleAutoCredentialProvider 15 | import ink.abb.pogo.api.auth.PtcCredentialProvider 16 | import ink.abb.pogo.api.util.SystemTimeImpl 17 | import ink.abb.pogo.scraper.services.BotService 18 | import ink.abb.pogo.scraper.util.Log 19 | import ink.abb.pogo.scraper.util.credentials.GoogleAutoCredentials 20 | import ink.abb.pogo.scraper.util.credentials.GoogleCredentials 21 | import ink.abb.pogo.scraper.util.credentials.PtcCredentials 22 | import ink.abb.pogo.scraper.util.directions.getAltitude 23 | import okhttp3.Credentials 24 | import okhttp3.OkHttpClient 25 | import org.springframework.boot.SpringApplication 26 | import java.io.File 27 | import java.io.FileInputStream 28 | import java.io.FileNotFoundException 29 | import java.net.InetSocketAddress 30 | import java.net.Proxy 31 | import java.nio.file.Paths 32 | import java.util.* 33 | import java.util.logging.LogManager 34 | import javax.swing.text.rtf.RTFEditorKit 35 | 36 | val time = SystemTimeImpl() 37 | 38 | fun getAuth(settings: Settings, http: OkHttpClient): CredentialProvider { 39 | val credentials = settings.credentials 40 | val auth = if (credentials is GoogleCredentials) { 41 | Log.red("Google User Credential Provider is deprecated; Use google-auto") 42 | System.exit(1) 43 | null 44 | } else if (credentials is GoogleAutoCredentials) { 45 | GoogleAutoCredentialProvider(http, credentials.username, credentials.password, time) 46 | } else if (credentials is PtcCredentials) { 47 | PtcCredentialProvider(http, credentials.username, credentials.password, time) 48 | } else { 49 | throw IllegalStateException("Unknown credentials: ${credentials.javaClass}") 50 | } 51 | 52 | return auth!! 53 | } 54 | 55 | fun main(args: Array) { 56 | LogManager.getLogManager().reset() 57 | SpringApplication.run(PokemonGoBotApplication::class.java, *args) 58 | } 59 | 60 | fun loadProperties(filename: String): Properties { 61 | val properties = Properties() 62 | Log.green("Trying to read ${Paths.get(filename).toAbsolutePath()}") 63 | var failed = false 64 | try { 65 | FileInputStream(filename).use { 66 | try { 67 | properties.load(it) 68 | } catch (e: Exception) { 69 | failed = true 70 | } 71 | } 72 | } catch (e: FileNotFoundException) { 73 | throw e 74 | } 75 | 76 | if (failed) { 77 | FileInputStream(filename).use { 78 | val rtfParser = RTFEditorKit() 79 | val document = rtfParser.createDefaultDocument() 80 | rtfParser.read(it.reader(), document, 0) 81 | val text = document.getText(0, document.length) 82 | properties.load(text.byteInputStream()) 83 | Log.red("Config file encoded as Rich Text Format (RTF)!") 84 | } 85 | } 86 | return properties 87 | } 88 | 89 | fun startDefaultBot(http: OkHttpClient, service: BotService) { 90 | var properties: Properties? = null 91 | 92 | val attemptFilenames = arrayOf("config.properties", "config.properties.txt", "config.properties.rtf") 93 | 94 | val dir = File(System.getProperty("java.class.path")).absoluteFile.parentFile 95 | 96 | var filename = "" 97 | 98 | fileLoop@ for (path in arrayOf(Paths.get("").toAbsolutePath(), dir)) { 99 | for (attemptFilename in attemptFilenames) { 100 | try { 101 | filename = attemptFilename 102 | properties = loadProperties("${path.toString()}/$filename") 103 | break@fileLoop 104 | } catch (e: FileNotFoundException) { 105 | Log.red("$filename file not found") 106 | } 107 | } 108 | } 109 | 110 | if (properties == null) { 111 | Log.red("No config files found. Exiting.") 112 | System.exit(1) 113 | return 114 | } else { 115 | val settings = SettingsParser(properties).createSettingsFromProperties() 116 | service.addBot(startBot(settings, http)) 117 | } 118 | } 119 | 120 | 121 | fun startBot(settings: Settings, http: OkHttpClient): Bot { 122 | 123 | var proxyHttp: OkHttpClient? = null 124 | 125 | if (!settings.proxyServer.equals("") && settings.proxyPort > 0) { 126 | Log.normal("Setting up proxy server for bot ${settings.name}: ${settings.proxyServer}:${settings.proxyPort}") 127 | 128 | val proxyType: Proxy.Type 129 | if (settings.proxyType.equals("HTTP")) 130 | proxyType = Proxy.Type.HTTP 131 | else if (settings.proxyType.equals("SOCKS")) 132 | proxyType = Proxy.Type.SOCKS 133 | else 134 | proxyType = Proxy.Type.DIRECT 135 | 136 | proxyHttp = http.newBuilder() 137 | .proxy(Proxy(proxyType, InetSocketAddress(settings.proxyServer, settings.proxyPort))) 138 | .proxyAuthenticator { route, response -> 139 | response.request().newBuilder() 140 | .header("Proxy-Authorization", Credentials.basic(settings.proxyUsername, settings.proxyPassword)) 141 | .build() 142 | } 143 | .build() 144 | } 145 | 146 | 147 | Log.normal("Logging in to game server...") 148 | 149 | val auth = 150 | if (proxyHttp == null) { 151 | getAuth(settings, http) 152 | } else { 153 | getAuth(settings, proxyHttp) 154 | } 155 | 156 | val api = 157 | if (proxyHttp == null) 158 | PoGoApiImpl(http, auth, time) 159 | else 160 | PoGoApiImpl(proxyHttp, auth, time) 161 | 162 | 163 | val lat = AtomicDouble(settings.latitude) 164 | val lng = AtomicDouble(settings.longitude) 165 | 166 | if (settings.saveLocationOnShutdown && settings.savedLatitude != 0.0 && settings.savedLongitude != 0.0) { 167 | lat.set(settings.savedLatitude) 168 | lng.set(settings.savedLongitude) 169 | Log.normal("Loaded last saved location (${settings.savedLatitude}, ${settings.savedLongitude})") 170 | } 171 | 172 | api.setLocation(lat.get(), lng.get(), 0.0) 173 | 174 | api.start() 175 | 176 | Log.normal("Logged in successfully") 177 | 178 | print("Getting profile data from pogo server") 179 | while (!api.initialized) { 180 | print(".") 181 | Thread.sleep(1000) 182 | } 183 | println(".") 184 | Thread.sleep(1000) 185 | 186 | val bot = Bot(api, settings) 187 | 188 | bot.start() 189 | 190 | return bot 191 | } 192 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/PokemonGoBotApplication.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper 10 | 11 | import ink.abb.pogo.scraper.services.BotService 12 | import ink.abb.pogo.scraper.util.ApiAuthProvider 13 | import okhttp3.OkHttpClient 14 | import org.springframework.beans.factory.annotation.Autowired 15 | import org.springframework.boot.CommandLineRunner 16 | import org.springframework.boot.autoconfigure.SpringBootApplication 17 | import org.springframework.context.annotation.Bean 18 | import org.springframework.stereotype.Component 19 | import org.springframework.web.servlet.config.annotation.CorsRegistry 20 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry 21 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer 22 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter 23 | import java.util.concurrent.TimeUnit 24 | import kotlin.concurrent.thread 25 | 26 | 27 | @SpringBootApplication 28 | open class PokemonGoBotApplication { 29 | 30 | @Autowired 31 | lateinit var authProvider: ApiAuthProvider 32 | 33 | @Bean 34 | open fun httpClient(): OkHttpClient { 35 | val builder = OkHttpClient.Builder() 36 | .connectTimeout(60, TimeUnit.SECONDS) 37 | .readTimeout(60, TimeUnit.SECONDS) 38 | .writeTimeout(60, TimeUnit.SECONDS) 39 | return builder.build() 40 | } 41 | 42 | @Bean 43 | open fun corsConfigurer(): WebMvcConfigurer { 44 | return object : WebMvcConfigurerAdapter() { 45 | override fun addCorsMappings(registry: CorsRegistry) { 46 | registry.addMapping("/**") 47 | .allowedOrigins("*") 48 | .allowedMethods("GET", "POST", "PUT", "DELETE") 49 | } 50 | } 51 | } 52 | 53 | @Bean 54 | open fun interceptorConfigurer(): WebMvcConfigurer { 55 | return object : WebMvcConfigurerAdapter() { 56 | override fun addInterceptors(registry: InterceptorRegistry) { 57 | registry.addInterceptor(authProvider) 58 | .addPathPatterns("/api/bot/**") 59 | .excludePathPatterns("/api/bot/*/auth") 60 | } 61 | } 62 | } 63 | 64 | 65 | @Component 66 | open class BotRunner : CommandLineRunner { 67 | @Autowired 68 | lateinit var http: OkHttpClient 69 | 70 | @Autowired 71 | lateinit var botRunService: BotService 72 | 73 | 74 | override fun run(vararg args: String?) { 75 | val JSONConfigBotNames = botRunService.getJSONConfigBotNames() 76 | 77 | if (JSONConfigBotNames.size < 1) { 78 | thread(name = "default") { 79 | startDefaultBot(http, botRunService) 80 | } 81 | } else { 82 | JSONConfigBotNames.forEach { 83 | thread(name = it) { 84 | botRunService.submitBot(it) 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/Task.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper 10 | 11 | interface Task { 12 | fun run(bot: Bot, ctx: Context, settings: Settings) 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/Values.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper 10 | 11 | val requiredXp = arrayOf(0, 1000, 3000, 6000, 10000, 15000, 21000, 28000, 36000, 45000, 55000, 65000, 75000, 12 | 85000, 100000, 120000, 140000, 160000, 185000, 210000, 260000, 335000, 435000, 560000, 710000, 900000, 1100000, 13 | 1350000, 1650000, 2000000, 2500000, 3000000, 3750000, 4750000, 6000000, 7500000, 9500000, 12000000, 15000000, 20000000) 14 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/gui/SocketServer.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.gui 10 | 11 | import POGOProtos.Data.PokemonDataOuterClass 12 | import com.corundumstudio.socketio.Configuration 13 | import com.corundumstudio.socketio.SocketConfig 14 | import com.corundumstudio.socketio.SocketIOServer 15 | import com.google.common.geometry.S2LatLng 16 | import ink.abb.pogo.api.cache.Pokestop 17 | import ink.abb.pogo.scraper.Context 18 | import ink.abb.pogo.scraper.requiredXp 19 | import ink.abb.pogo.scraper.util.Log 20 | import ink.abb.pogo.scraper.util.data.PokemonData 21 | import ink.abb.pogo.scraper.util.pokemon.eggKmWalked 22 | import ink.abb.pogo.scraper.util.pokemon.getIvPercentage 23 | import ink.abb.pogo.scraper.util.pokemon.getStatsFormatted 24 | import io.netty.util.concurrent.Future 25 | import java.util.concurrent.CountDownLatch 26 | import java.util.concurrent.atomic.AtomicInteger 27 | 28 | class SocketServer { 29 | private var ctx: Context? = null 30 | private var server: SocketIOServer? = null 31 | 32 | val coordinatesToGoTo = mutableListOf() 33 | 34 | fun start(ctx: Context, port: Int) { 35 | val config = Configuration() 36 | config.port = port 37 | config.socketConfig = SocketConfig().apply { 38 | isReuseAddress = true 39 | } 40 | 41 | this.ctx = ctx 42 | 43 | server = SocketIOServer(config) 44 | server?.addEventListener("init", EventInit::class.java) { client, data, ackRequest -> 45 | run { 46 | sendProfile() 47 | sendPokebank() 48 | sendEggs() 49 | setLocation(ctx.api.latitude, ctx.api.longitude) 50 | } 51 | } 52 | server?.addEventListener("goto", EventGoto::class.java) { client, data, ackRequest -> 53 | run { 54 | if (data.lat != null && data.lng != null) { 55 | coordinatesToGoTo.add(S2LatLng.fromDegrees(data.lat!!, data.lng!!)) 56 | } 57 | } 58 | } 59 | 60 | var startAttempt: Future? = null 61 | do { 62 | Log.normal("Attempting to bind Socket Server to port $port") 63 | try { 64 | startAttempt = server?.startAsync()?.syncUninterruptibly() 65 | } catch (e: Exception) { 66 | Log.red("Failed to bind Socket Server to port $port; retrying in 5 seconds") 67 | Thread.sleep(5000) 68 | } 69 | } while (startAttempt == null) 70 | Log.green("Bound Socket Server to port $port") 71 | } 72 | 73 | fun stop() { 74 | server?.stop() 75 | } 76 | 77 | fun sendGotoDone() { 78 | server?.broadcastOperations?.sendEvent("gotoDone") 79 | } 80 | 81 | fun sendProfile() { 82 | if (ctx != null) { 83 | val profile = EventProfile() 84 | profile.username = ctx!!.api.playerData.username 85 | profile.team = ctx!!.api.playerData.team.name 86 | profile.stardust = ctx!!.api.inventory.currencies.getOrPut("STARDUST", { AtomicInteger(0) }).get() 87 | profile.level = ctx!!.api.inventory.playerStats.level 88 | val curLevelXP = ctx!!.api.inventory.playerStats.experience - requiredXp[ctx!!.api.inventory.playerStats.level - 1] 89 | profile.levelXp = curLevelXP 90 | val nextXP = if (ctx!!.api.inventory.playerStats.level == requiredXp.size) { 91 | curLevelXP 92 | } else { 93 | (requiredXp[ctx!!.api.inventory.playerStats.level] - requiredXp[ctx!!.api.inventory.playerStats.level - 1]).toLong() 94 | } 95 | val ratio = ((curLevelXP.toDouble() / nextXP.toDouble()) * 100).toInt() 96 | profile.levelRatio = ratio 97 | profile.pokebank = ctx!!.api.inventory.pokemon.size 98 | profile.pokebankMax = ctx!!.api.playerData.maxPokemonStorage 99 | profile.items = ctx!!.api.inventory.size 100 | profile.itemsMax = ctx!!.api.playerData.maxItemStorage 101 | server?.broadcastOperations?.sendEvent("profile", profile) 102 | } 103 | } 104 | 105 | fun sendPokebank() { 106 | if (ctx != null) { 107 | val pokebank = EventPokebank() 108 | 109 | for (pokemon in ctx!!.api.inventory.pokemon) { 110 | pokebank.pokemon.add(PokemonData().buildFromPokemon(pokemon.value)) 111 | } 112 | server?.broadcastOperations?.sendEvent("pokebank", pokebank) 113 | } 114 | } 115 | 116 | fun sendPokestop(pokestop: Pokestop) { 117 | val pokestopObj = EventPokestop() 118 | pokestopObj.id = pokestop.id 119 | pokestopObj.name = pokestop.name 120 | pokestopObj.lat = pokestop.fortData.latitude 121 | pokestopObj.lng = pokestop.fortData.longitude 122 | server?.broadcastOperations?.sendEvent("pokestop", pokestopObj) 123 | } 124 | 125 | fun setLocation(lat: Double, lng: Double) { 126 | val newLocation = EventNewLocation() 127 | newLocation.lat = lat 128 | newLocation.lng = lng 129 | server?.broadcastOperations?.sendEvent("newLocation", newLocation) 130 | } 131 | 132 | fun newPokemon(lat: Double, lng: Double, pokemon: PokemonDataOuterClass.PokemonData) { 133 | val newPokemon = EventNewPokemon() 134 | newPokemon.lat = lat 135 | newPokemon.lng = lng 136 | newPokemon.id = pokemon.id 137 | newPokemon.pokemonId = pokemon.pokemonId.number 138 | newPokemon.name = pokemon.pokemonId.name 139 | newPokemon.cp = pokemon.cp 140 | newPokemon.iv = pokemon.getIvPercentage() 141 | newPokemon.stats = pokemon.getStatsFormatted() 142 | newPokemon.individualStamina = pokemon.individualStamina 143 | newPokemon.individualAttack = pokemon.individualAttack 144 | newPokemon.individualDefense = pokemon.individualDefense 145 | newPokemon.creationTimeMs = pokemon.creationTimeMs 146 | newPokemon.move1 = pokemon.move1.name 147 | newPokemon.move2 = pokemon.move2.name 148 | newPokemon.deployedFortId = pokemon.deployedFortId 149 | newPokemon.stamina = pokemon.stamina 150 | newPokemon.maxStamina = pokemon.stamina 151 | server?.broadcastOperations?.sendEvent("newPokemon", newPokemon) 152 | } 153 | 154 | fun releasePokemon(id: Long) { 155 | val release = EventReleasePokemon() 156 | release.id = id 157 | server?.broadcastOperations?.sendEvent("releasePokemon", release) 158 | } 159 | 160 | fun sendLog(type: String, text: String) { 161 | val log = EventLog() 162 | log.type = type 163 | log.text = text 164 | server?.broadcastOperations?.sendEvent("log", log) 165 | } 166 | 167 | fun sendEggs() { 168 | if (ctx != null) { 169 | val eggs = EventEggs() 170 | for (egg in ctx!!.api.inventory.eggs) { 171 | val eggObj = EventEggs.Egg() 172 | eggObj.distanceWalked = egg.value.pokemonData.eggKmWalked(ctx!!.api) 173 | eggObj.distanceTarget = egg.value.pokemonData.eggKmWalkedTarget 174 | eggs.eggs.add(eggObj) 175 | } 176 | server?.broadcastOperations?.sendEvent("eggs", eggs) 177 | } 178 | } 179 | 180 | class EventInit { 181 | 182 | } 183 | 184 | class EventGoto { 185 | var lat: Double? = null 186 | var lng: Double? = null 187 | } 188 | 189 | class EventProfile { 190 | var username: String? = null 191 | var team: String? = null 192 | var stardust: Int? = null 193 | var level: Int? = null 194 | var levelXp: Long? = null 195 | var levelRatio: Int? = null 196 | var pokebank: Int? = null 197 | var pokebankMax: Int? = null 198 | var items: Int? = null 199 | var itemsMax: Int? = null 200 | } 201 | 202 | class EventPokebank { 203 | var pokemon = mutableListOf() 204 | } 205 | 206 | class EventPokestop { 207 | var id: String? = null 208 | var name: String? = null 209 | var lat: Double? = null 210 | var lng: Double? = null 211 | } 212 | 213 | class EventNewLocation { 214 | var lat: Double? = null 215 | var lng: Double? = null 216 | } 217 | 218 | class EventNewPokemon { 219 | var lat: Double? = null 220 | var lng: Double? = null 221 | var id: Long? = null 222 | var pokemonId: Int? = null 223 | var name: String? = null 224 | var cp: Int? = null 225 | var iv: Int? = null 226 | var stats: String? = null 227 | var individualStamina: Int? = null 228 | var individualAttack: Int? = null 229 | var individualDefense: Int? = null 230 | var creationTimeMs: Long? = null 231 | var move1: String? = null 232 | var move2: String? = null 233 | var deployedFortId: String? = null 234 | var stamina: Int? = null 235 | var maxStamina: Int? = null 236 | } 237 | 238 | class EventReleasePokemon { 239 | var id: Long? = null 240 | } 241 | 242 | class EventLog { 243 | var type: String? = null 244 | var text: String? = null 245 | } 246 | 247 | class EventEggs { 248 | var eggs = mutableListOf() 249 | 250 | class Egg { 251 | var distanceWalked: Double? = null 252 | var distanceTarget: Double? = null 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/services/BotService.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.services 10 | 11 | import ink.abb.pogo.scraper.Bot 12 | import ink.abb.pogo.scraper.Context 13 | import ink.abb.pogo.scraper.Settings 14 | import ink.abb.pogo.scraper.startBot 15 | import ink.abb.pogo.scraper.util.Log 16 | import ink.abb.pogo.scraper.util.credentials.GoogleAutoCredentials 17 | import ink.abb.pogo.scraper.util.io.SettingsJSONWriter 18 | import okhttp3.OkHttpClient 19 | import org.springframework.beans.factory.annotation.Autowired 20 | import org.springframework.stereotype.Service 21 | import java.io.File 22 | import java.util.concurrent.CountDownLatch 23 | import javax.annotation.PreDestroy 24 | import kotlin.concurrent.thread 25 | 26 | @Service 27 | class BotService { 28 | 29 | @Autowired 30 | lateinit var http: OkHttpClient 31 | 32 | private val bots: MutableList = mutableListOf() 33 | val settingsJSONWriter = SettingsJSONWriter() 34 | 35 | fun submitBot(name: String): Settings { 36 | val settings = settingsJSONWriter.load(name) 37 | addBot(startBot(settings, http)) 38 | 39 | settingsJSONWriter.save(settings) // Is this needed after starting? 40 | 41 | return settings 42 | } 43 | 44 | @Synchronized 45 | fun addBot(bot: Bot) { 46 | bots.add(bot) 47 | } 48 | 49 | @Synchronized 50 | fun removeBot(bot: Bot) { 51 | bots.remove(bot) 52 | } 53 | 54 | fun getJSONConfigBotNames(): List { 55 | return settingsJSONWriter.getJSONConfigBotNames() 56 | } 57 | 58 | fun getBotContext(name: String): Context { 59 | val bot = bots.find { it.settings.name == name } 60 | 61 | bot ?: throw IllegalArgumentException("Bot $name doesn't exists !") 62 | 63 | return bot.ctx 64 | } 65 | 66 | @Synchronized 67 | fun getAllBotSettings(): List { 68 | return bots.map { it.settings.copy(credentials = GoogleAutoCredentials(), restApiPassword = "") } 69 | } 70 | 71 | @Synchronized 72 | fun doWithBot(name: String, action: (bot: Bot) -> Unit): Boolean { 73 | val bot = bots.find { it.settings.name == name } ?: return false 74 | 75 | action(bot) 76 | return true 77 | } 78 | 79 | @PreDestroy 80 | @Synchronized 81 | fun stopAllBots() { 82 | val latch = CountDownLatch(bots.size) 83 | bots.forEach { 84 | thread { 85 | it.stop() 86 | latch.countDown() 87 | } 88 | } 89 | 90 | latch.await() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/BypassSoftban.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import ink.abb.pogo.api.cache.Pokestop 12 | import ink.abb.pogo.scraper.Bot 13 | import ink.abb.pogo.scraper.Context 14 | import ink.abb.pogo.scraper.Settings 15 | import ink.abb.pogo.scraper.Task 16 | import ink.abb.pogo.scraper.util.Log 17 | import ink.abb.pogo.scraper.util.map.loot 18 | 19 | class BypassSoftban(val pokestop: Pokestop) : Task { 20 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 21 | repeat(settings.banSpinCount) { i -> 22 | pokestop.loot() 23 | 24 | if ((i + 1) % 10 == 0) 25 | Log.yellow("${i + 1}/${settings.banSpinCount}") 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/CatchOneNearbyPokemon.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Networking.Responses.CatchPokemonResponseOuterClass.CatchPokemonResponse 12 | import POGOProtos.Networking.Responses.DiskEncounterResponseOuterClass 13 | import POGOProtos.Networking.Responses.EncounterResponseOuterClass 14 | import POGOProtos.Networking.Responses.EncounterResponseOuterClass.EncounterResponse.Status 15 | import ink.abb.pogo.api.cache.MapPokemon 16 | import ink.abb.pogo.scraper.Bot 17 | import ink.abb.pogo.scraper.Context 18 | import ink.abb.pogo.scraper.Settings 19 | import ink.abb.pogo.scraper.Task 20 | import ink.abb.pogo.scraper.util.Log 21 | import ink.abb.pogo.scraper.util.directions.getAltitude 22 | import ink.abb.pogo.scraper.util.pokemon.* 23 | import java.util.concurrent.atomic.AtomicInteger 24 | 25 | class CatchOneNearbyPokemon : Task { 26 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 27 | // STOP WALKING 28 | ctx.pauseWalking.set(true) 29 | val pokemon = ctx.api.map.getPokemon(ctx.api.latitude, ctx.api.longitude, settings.initialMapSize).filter { !ctx.blacklistedEncounters.contains(it.encounterId) && it.inRange } 30 | 31 | val hasPokeballs = ctx.api.inventory.hasPokeballs 32 | 33 | /*Pokeball.values().forEach { 34 | Log.yellow("${it.ballType}: ${ctx.api.cachedInventories.itemBag.getItem(it.ballType).count}") 35 | }*/ 36 | 37 | if (!hasPokeballs) { 38 | ctx.pauseWalking.set(false) 39 | return 40 | } 41 | 42 | if (pokemon.isNotEmpty()) { 43 | val catchablePokemon = pokemon.first() 44 | if (settings.obligatoryTransfer.contains(catchablePokemon.pokemonId) && settings.desiredCatchProbabilityUnwanted == -1.0 || settings.neverCatchPokemon.contains(catchablePokemon.pokemonId)) { 45 | ctx.blacklistedEncounters.add(catchablePokemon.encounterId) 46 | Log.normal("Found pokemon ${catchablePokemon.pokemonId}; blacklisting because it's unwanted") 47 | ctx.pauseWalking.set(false) 48 | return 49 | } 50 | Log.green("Found pokemon ${catchablePokemon.pokemonId}") 51 | 52 | ctx.api.setLocation(ctx.lat.get(), ctx.lng.get(), getAltitude(ctx.lat.get(), ctx.lng.get(), ctx)) 53 | 54 | val encounter = catchablePokemon.encounter() 55 | val encounterResult = encounter.toBlocking().first().response 56 | val wasFromLure = catchablePokemon.encounterKind == MapPokemon.EncounterKind.DISK 57 | if ((encounterResult is DiskEncounterResponseOuterClass.DiskEncounterResponse && encounterResult.result == DiskEncounterResponseOuterClass.DiskEncounterResponse.Result.SUCCESS) || 58 | (encounterResult is EncounterResponseOuterClass.EncounterResponse && encounterResult.status == Status.ENCOUNTER_SUCCESS)) { 59 | val pokemonData = if (encounterResult is DiskEncounterResponseOuterClass.DiskEncounterResponse) { 60 | encounterResult.pokemonData 61 | } else if (encounterResult is EncounterResponseOuterClass.EncounterResponse) { 62 | encounterResult.wildPokemon.pokemonData 63 | } else { 64 | // TODO ugly 65 | null 66 | }!! 67 | Log.green("Encountered pokemon ${catchablePokemon.pokemonId} " + 68 | "with CP ${pokemonData.cp} and IV ${pokemonData.getIvPercentage()}%") 69 | // TODO wrong parameters 70 | val (shouldRelease, reason) = pokemonData.shouldTransfer(settings, hashMapOf(), AtomicInteger(0)) 71 | val desiredCatchProbability = if (shouldRelease) { 72 | Log.yellow("Using desired_catch_probability_unwanted because $reason") 73 | settings.desiredCatchProbabilityUnwanted 74 | } else { 75 | settings.desiredCatchProbability 76 | } 77 | if (desiredCatchProbability == -1.0) { 78 | ctx.blacklistedEncounters.add(catchablePokemon.encounterId) 79 | Log.normal("CP/IV of encountered pokemon ${catchablePokemon.pokemonId} turns out to be too low; blacklisting encounter") 80 | ctx.pauseWalking.set(false) 81 | return 82 | } 83 | 84 | val isBallCurved = (Math.random() < settings.desiredCurveRate) 85 | val captureProbability = if (encounterResult is DiskEncounterResponseOuterClass.DiskEncounterResponse) { 86 | encounterResult.captureProbability 87 | } else if (encounterResult is EncounterResponseOuterClass.EncounterResponse) { 88 | encounterResult.captureProbability 89 | } else { 90 | // TODO ugly 91 | null 92 | }!! 93 | // TODO: Give settings object to the catch function instead of the seperate values 94 | val catch = catchablePokemon.catch( 95 | captureProbability, 96 | ctx.api.inventory, 97 | desiredCatchProbability, 98 | isBallCurved, 99 | !settings.neverUseBerries, 100 | settings.randomBallThrows, 101 | settings.waitBetweenThrows, 102 | -1) 103 | val catchResult = catch.toBlocking().first() 104 | if (catchResult == null) { 105 | // prevent trying it in the next iteration 106 | ctx.blacklistedEncounters.add(catchablePokemon.encounterId) 107 | Log.red("No Pokeballs in your inventory; blacklisting Pokemon") 108 | ctx.pauseWalking.set(false) 109 | return 110 | } 111 | val result = catchResult.response 112 | 113 | // TODO: temp fix for server timing issues regarding GetMapObjects 114 | ctx.blacklistedEncounters.add(catchablePokemon.encounterId) 115 | if (result.status == CatchPokemonResponse.CatchStatus.CATCH_SUCCESS) { 116 | ctx.pokemonStats.first.andIncrement 117 | if (wasFromLure) { 118 | ctx.luredPokemonStats.andIncrement 119 | } 120 | var message = "Caught a " 121 | if (settings.displayIfPokemonFromLure) { 122 | if (wasFromLure) 123 | message += "lured " 124 | else 125 | message += "wild " 126 | } 127 | message += "${catchablePokemon.pokemonId} with CP ${pokemonData.cp} and IV" + 128 | " (${pokemonData.individualAttack}-${pokemonData.individualDefense}-${pokemonData.individualStamina}) ${pokemonData.getIvPercentage()}%" 129 | if (settings.displayPokemonCatchRewards) { 130 | message += ": [${result.captureAward.xpList.sum()}x XP, ${result.captureAward.candyList.sum()}x " + 131 | "Candy, ${result.captureAward.stardustList.sum()}x Stardust]" 132 | } 133 | Log.cyan(message) 134 | 135 | ctx.server.newPokemon(catchablePokemon.latitude, catchablePokemon.longitude, pokemonData) 136 | ctx.server.sendProfile() 137 | } else { 138 | Log.red("Capture of ${catchablePokemon.pokemonId} failed with status : ${result.status}") 139 | if (result.status == CatchPokemonResponse.CatchStatus.CATCH_ERROR) { 140 | Log.red("Blacklisting pokemon to prevent infinite loop") 141 | } 142 | } 143 | } else { 144 | if (encounterResult is DiskEncounterResponseOuterClass.DiskEncounterResponse) { 145 | Log.red("Encounter failed with result: ${encounterResult.result}") 146 | if (encounterResult.result == DiskEncounterResponseOuterClass.DiskEncounterResponse.Result.ENCOUNTER_ALREADY_FINISHED) { 147 | ctx.blacklistedEncounters.add(catchablePokemon.encounterId) 148 | } 149 | } else if (encounterResult is EncounterResponseOuterClass.EncounterResponse) { 150 | Log.red("Encounter failed with result: ${encounterResult.status}") 151 | if (encounterResult.status == Status.ENCOUNTER_CLOSED) { 152 | ctx.blacklistedEncounters.add(catchablePokemon.encounterId) 153 | } 154 | } 155 | if ((encounterResult is DiskEncounterResponseOuterClass.DiskEncounterResponse && encounterResult.result == DiskEncounterResponseOuterClass.DiskEncounterResponse.Result.POKEMON_INVENTORY_FULL) || 156 | (encounterResult is EncounterResponseOuterClass.EncounterResponse && encounterResult.status == Status.POKEMON_INVENTORY_FULL)) { 157 | Log.red("Inventory is full, temporarily disabling catching of pokemon") 158 | 159 | ctx.pokemonInventoryFullStatus.set(true) 160 | } 161 | } 162 | ctx.pauseWalking.set(false) 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/DropUselessItems.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Inventory.Item.ItemIdOuterClass.ItemId 12 | import POGOProtos.Networking.Responses.RecycleInventoryItemResponseOuterClass 13 | import ink.abb.pogo.api.request.RecycleInventoryItem 14 | import ink.abb.pogo.scraper.Bot 15 | import ink.abb.pogo.scraper.Context 16 | import ink.abb.pogo.scraper.Settings 17 | import ink.abb.pogo.scraper.Task 18 | import ink.abb.pogo.scraper.util.Log 19 | import java.util.concurrent.atomic.AtomicInteger 20 | 21 | class DropUselessItems : Task { 22 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 23 | // ignores the items that have -1 24 | val itemsToDrop = settings.uselessItems.filter { it.value != -1 } 25 | if (settings.groupItemsByType) dropGroupedItems(ctx, itemsToDrop, settings) else dropItems(ctx, itemsToDrop, settings) 26 | } 27 | 28 | /** 29 | * Drops the excess items by group 30 | */ 31 | fun dropGroupedItems(ctx: Context, items: Map, settings: Settings) { 32 | // map with what items to keep in what amounts 33 | val itemsToDrop = mutableMapOf() 34 | // adds not groupable items on map 35 | itemsToDrop.putAll(items.filter { singlesFilter.contains(it.key) }) 36 | // groups items 37 | val groupedItems = groupItems(items) 38 | // adds new items to the map 39 | val itemBag = ctx.api.inventory.items 40 | groupedItems.forEach groupedItems@ { 41 | var groupCount = 0 42 | it.key.forEach { groupCount += itemBag.getOrPut(it, { AtomicInteger(0) }).get() } 43 | var neededToDrop = groupCount - it.value 44 | if (neededToDrop > 0) 45 | it.key.forEach { 46 | val item = itemBag.getOrPut(it, { AtomicInteger(0) }) 47 | if (neededToDrop <= item.get()) { 48 | itemsToDrop.put(it, item.get() - neededToDrop) 49 | return@groupedItems 50 | } else { 51 | neededToDrop -= item.get() 52 | itemsToDrop.put(it, 0) 53 | } 54 | } 55 | } 56 | // drops excess items 57 | dropItems(ctx, itemsToDrop, settings) 58 | } 59 | 60 | /** 61 | * Groups the items using the groupFilters 62 | * Each group contains the list of itemIds of the group and sum of all its number 63 | */ 64 | fun groupItems(items: Map): Map, Int> { 65 | val groupedItems = mutableMapOf, Int>() 66 | groupFilters.forEach { 67 | val filter = it 68 | val filteredItems = items.filter { filter.contains(it.key) } 69 | groupedItems.put(filteredItems.keys.toTypedArray(), filteredItems.values.sum()) 70 | } 71 | return groupedItems 72 | } 73 | 74 | // Items that can be grouped 75 | val groupFilters = arrayOf( 76 | arrayOf(ItemId.ITEM_REVIVE, ItemId.ITEM_MAX_REVIVE), 77 | arrayOf(ItemId.ITEM_POTION, ItemId.ITEM_SUPER_POTION, ItemId.ITEM_HYPER_POTION, ItemId.ITEM_MAX_POTION), 78 | arrayOf(ItemId.ITEM_POKE_BALL, ItemId.ITEM_GREAT_BALL, ItemId.ITEM_ULTRA_BALL, ItemId.ITEM_MASTER_BALL) 79 | ) 80 | 81 | // Items that cant be grouped 82 | val singlesFilter = arrayOf(ItemId.ITEM_RAZZ_BERRY, ItemId.ITEM_LUCKY_EGG, ItemId.ITEM_INCENSE_ORDINARY, ItemId.ITEM_TROY_DISK) 83 | 84 | /** 85 | * Drops the excess items by item 86 | */ 87 | fun dropItems(ctx: Context, items: Map, settings: Settings) { 88 | val itemBag = ctx.api.inventory.items 89 | items.forEach { 90 | val item = itemBag.getOrPut(it.key, { AtomicInteger(0) }) 91 | val count = item.get() - it.value 92 | if (count > 0) { 93 | val dropItem = it.key 94 | val drop = RecycleInventoryItem().withCount(count).withItemId(dropItem) 95 | 96 | val result = ctx.api.queueRequest(drop).toBlocking().first().response 97 | if (result.result == RecycleInventoryItemResponseOuterClass.RecycleInventoryItemResponse.Result.SUCCESS) { 98 | ctx.itemStats.second.getAndAdd(count) 99 | Log.yellow("Dropped ${count}x ${dropItem.name}") 100 | ctx.server.sendProfile() 101 | } else { 102 | Log.red("Failed to drop ${count}x ${dropItem.name}: $result") 103 | } 104 | } 105 | if (settings.itemDropDelay != (-1).toLong()) { 106 | val itemDropDelay = settings.itemDropDelay / 2 + (Math.random() * settings.itemDropDelay).toLong() 107 | Thread.sleep(itemDropDelay) 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/EvolvePokemon.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Inventory.Item.ItemIdOuterClass 12 | import POGOProtos.Inventory.Item.ItemTypeOuterClass 13 | import POGOProtos.Networking.Responses.EvolvePokemonResponseOuterClass 14 | import ink.abb.pogo.api.request.EvolvePokemon 15 | import ink.abb.pogo.api.request.UseItemXpBoost 16 | import ink.abb.pogo.api.util.PokemonMetaRegistry 17 | import ink.abb.pogo.scraper.Bot 18 | import ink.abb.pogo.scraper.Context 19 | import ink.abb.pogo.scraper.Settings 20 | import ink.abb.pogo.scraper.Task 21 | import ink.abb.pogo.scraper.util.Log 22 | import ink.abb.pogo.scraper.util.pokemon.getIvPercentage 23 | import java.util.concurrent.atomic.AtomicInteger 24 | 25 | class EvolvePokemon : Task { 26 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 27 | //count the current stack of possible evolves 28 | var countEvolveStack = 0 29 | val groupedPokemonForCount = ctx.api.inventory.pokemon.map { it.value }.groupBy { it.pokemonData.pokemonId } 30 | groupedPokemonForCount.forEach { 31 | if (settings.evolveBeforeTransfer.contains(it.key)) { 32 | // Get pokemonFamily meta information 33 | val pokemonMeta = PokemonMetaRegistry.getMeta(it.key) 34 | var maxPossibleEvolves: Int = 0 35 | 36 | if (pokemonMeta.candyToEvolve > 0) { 37 | maxPossibleEvolves = bot.api.inventory.candies.getOrPut(pokemonMeta.family, { AtomicInteger(0) }).get() / pokemonMeta.candyToEvolve 38 | } else { 39 | Log.red("${it.key} is in evolve list but is unevolvable") 40 | } 41 | 42 | // Add the minimum value, depending on which is the bottleneck, amount of candy, or pokemon of this type in pokebank: 43 | countEvolveStack += Math.min(maxPossibleEvolves, it.value.count()) 44 | } 45 | } 46 | Log.yellow("Stack of pokemon ready to evolve: $countEvolveStack/${settings.evolveStackLimit}") 47 | 48 | // use lucky egg if above evolve stack limit and evolve the whole stack 49 | if (countEvolveStack >= settings.evolveStackLimit) { 50 | val startingXP = ctx.api.inventory.playerStats.experience 51 | ctx.pauseWalking.set(true) 52 | if (settings.useLuckyEgg == 1) { 53 | Log.yellow("Starting stack evolve of $countEvolveStack pokemon using lucky egg") 54 | val activeEgg = bot.api.inventory.appliedItems.find { it.itemType == ItemTypeOuterClass.ItemType.ITEM_TYPE_XP_BOOST } 55 | if (activeEgg != null) { 56 | Log.green("Already have an active egg") 57 | } else { 58 | val luckyEgg = UseItemXpBoost().withItemId(ItemIdOuterClass.ItemId.ITEM_LUCKY_EGG) 59 | val result = ctx.api.queueRequest(luckyEgg).toBlocking().first().response 60 | Log.yellow("Result of using lucky egg: ${result.result.toString()}") 61 | } 62 | } else { 63 | Log.yellow("Starting stack evolve of $countEvolveStack pokemon without lucky egg") 64 | } 65 | var countEvolved = 0 66 | ctx.api.inventory.pokemon.forEach { 67 | if (settings.evolveBeforeTransfer.contains(it.value.pokemonData.pokemonId)) { 68 | val pokemonMeta = PokemonMetaRegistry.getMeta(it.value.pokemonData.pokemonId) 69 | if (bot.api.inventory.candies.getOrPut(pokemonMeta.family, { AtomicInteger(0) }).get() >= pokemonMeta.candyToEvolve) { 70 | val pokemonData = it.value.pokemonData 71 | Log.yellow("Evolving ${pokemonData.pokemonId.name} CP ${pokemonData.cp} IV ${pokemonData.getIvPercentage()}%") 72 | val evolve = EvolvePokemon().withPokemonId(it.key) 73 | val evolveResult = ctx.api.queueRequest(evolve).toBlocking().first().response 74 | if (evolveResult.result == EvolvePokemonResponseOuterClass.EvolvePokemonResponse.Result.SUCCESS) { 75 | countEvolved++ 76 | val evolvedPokemon = evolveResult.evolvedPokemonData 77 | Log.yellow("Successfully evolved in ${evolvedPokemon.pokemonId.name} CP ${evolvedPokemon.cp} IV ${evolvedPokemon.getIvPercentage()}%") 78 | ctx.server.releasePokemon(pokemonData.id) 79 | } else { 80 | Log.red("Evolve of ${pokemonData.pokemonId.name} CP ${pokemonData.cp} IV ${pokemonData.getIvPercentage()}% failed: ${evolveResult.result.toString()}") 81 | } 82 | 83 | } else { 84 | Log.red("Not enough candy (${bot.api.inventory.candies.getOrPut(pokemonMeta.family, { AtomicInteger(0) }).get()}/${pokemonMeta.candyToEvolve}) to evolve ${it.value.pokemonData.pokemonId.name} CP ${it.value.pokemonData.cp} IV ${it.value.pokemonData.getIvPercentage()}%") 85 | } 86 | } 87 | } 88 | val endXP = ctx.api.inventory.playerStats.experience 89 | ctx.pauseWalking.set(false) 90 | Log.yellow("Finished evolving $countEvolved pokemon; ${endXP - startingXP} xp gained") 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/GetMapRandomDirection.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import ink.abb.pogo.scraper.Bot 12 | import ink.abb.pogo.scraper.Context 13 | import ink.abb.pogo.scraper.Settings 14 | import ink.abb.pogo.scraper.Task 15 | import ink.abb.pogo.scraper.util.Log 16 | import ink.abb.pogo.scraper.util.directions.getAltitude 17 | 18 | class GetMapRandomDirection : Task { 19 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 20 | // query a small area to keep alive 21 | val lat = ctx.lat.get() + randomLatLng() 22 | val lng = ctx.lng.get() + randomLatLng() 23 | 24 | if (settings.displayKeepalive) Log.normal("Getting map of ($lat, $lng)") 25 | ctx.api.setLocation(lat, lng, getAltitude(lat, lng, ctx)) 26 | } 27 | 28 | fun randomLatLng(): Double { 29 | return Math.random() * 0.0001 - 0.00005 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/HatchEggs.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Networking.Responses.UseItemEggIncubatorResponseOuterClass 12 | import ink.abb.pogo.api.request.GetHatchedEggs 13 | import ink.abb.pogo.api.request.UseItemEggIncubator 14 | import ink.abb.pogo.scraper.Bot 15 | import ink.abb.pogo.scraper.Context 16 | import ink.abb.pogo.scraper.Settings 17 | import ink.abb.pogo.scraper.Task 18 | import ink.abb.pogo.scraper.util.Log 19 | import ink.abb.pogo.scraper.util.pokemon.getIvPercentage 20 | import ink.abb.pogo.scraper.util.pokemon.incubated 21 | 22 | class HatchEggs : Task { 23 | 24 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 25 | // not necessary, update profile is executed before this already in the tasks 26 | //bot.api.queueRequest(GetInventory().withLastTimestampMs(0)) 27 | bot.api.queueRequest(GetHatchedEggs()).subscribe { 28 | val response = it.response 29 | response.pokemonIdList.forEachIndexed { index, it -> 30 | val newPokemon = ctx.api.inventory.pokemon[it] 31 | val candy = response.candyAwardedList[index] 32 | val experience = response.experienceAwardedList[index] 33 | val stardust = response.stardustAwardedList[index] 34 | val stats = "+${candy} candy; +${experience} XP; +${stardust} stardust" 35 | if (newPokemon == null) { 36 | Log.cyan("Hatched pokemon; $stats") 37 | } else { 38 | Log.cyan("Hatched ${newPokemon.pokemonData.pokemonId.name} with ${newPokemon.pokemonData.cp} CP " + 39 | "and ${newPokemon.pokemonData.getIvPercentage()}% IV; $stats") 40 | } 41 | } 42 | } 43 | 44 | val incubators = ctx.api.inventory.eggIncubators 45 | val eggs = ctx.api.inventory.eggs 46 | 47 | val freeIncubators = incubators.map { it.value } 48 | .filter { it.targetKmWalked < bot.api.inventory.playerStats.kmWalked } 49 | .sortedByDescending { it.usesRemaining } 50 | val filteredEggs = eggs.map { it.value } 51 | .filter { !it.pokemonData.incubated } 52 | .sortedByDescending { it.pokemonData.eggKmWalkedTarget } 53 | if (freeIncubators.isNotEmpty() && filteredEggs.isNotEmpty() && settings.autoFillIncubator) { 54 | var eggResult = filteredEggs.first() 55 | if (freeIncubators.first().usesRemaining == 0) { 56 | eggResult = filteredEggs.last() 57 | } 58 | val use = UseItemEggIncubator().withPokemonId(eggResult.pokemonData.id).withItemId(freeIncubators.first().id) 59 | bot.api.queueRequest(use).subscribe { 60 | val response = it.response 61 | 62 | if (response.result == UseItemEggIncubatorResponseOuterClass.UseItemEggIncubatorResponse.Result.SUCCESS) { 63 | Log.cyan("Put egg of ${eggResult.pokemonData.eggKmWalkedTarget}km in unused incubator") 64 | } else { 65 | Log.red("Failed to put egg in incubator; error: ${response.result}") 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/LootOneNearbyPokestop.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Networking.Responses.FortSearchResponseOuterClass 12 | import POGOProtos.Networking.Responses.FortSearchResponseOuterClass.FortSearchResponse.Result 13 | import ink.abb.pogo.api.cache.Pokestop 14 | import ink.abb.pogo.scraper.Bot 15 | import ink.abb.pogo.scraper.Context 16 | import ink.abb.pogo.scraper.Settings 17 | import ink.abb.pogo.scraper.Task 18 | import ink.abb.pogo.scraper.util.Log 19 | import ink.abb.pogo.scraper.util.directions.getAltitude 20 | import ink.abb.pogo.scraper.util.map.canLoot 21 | import ink.abb.pogo.scraper.util.map.distance 22 | import ink.abb.pogo.scraper.util.map.loot 23 | import java.text.DecimalFormat 24 | import java.util.* 25 | 26 | class LootOneNearbyPokestop(val sortedPokestops: List, val lootTimeouts: HashMap) : Task { 27 | 28 | private var cooldownPeriod = 5 29 | 30 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 31 | // STOP WALKING! until loot is done 32 | ctx.pauseWalking.set(true) 33 | ctx.api.setLocation(ctx.lat.get(), ctx.lng.get(), getAltitude(ctx.lat.get(), ctx.lng.get(), ctx)) 34 | val nearbyPokestops = sortedPokestops.filter { 35 | it.canLoot(lootTimeouts = lootTimeouts) 36 | } 37 | 38 | if (nearbyPokestops.isNotEmpty()) { 39 | val closest = nearbyPokestops.first() 40 | var pokestopID = closest.id 41 | if (settings.displayPokestopName) { 42 | pokestopID = "\"${closest.name}\"" 43 | } 44 | Log.normal("Looting nearby pokestop $pokestopID") 45 | 46 | val result = closest.loot().toBlocking().first().response 47 | 48 | if (result.itemsAwardedCount != 0) { 49 | ctx.itemStats.first.getAndAdd(result.itemsAwardedCount) 50 | } 51 | 52 | if (result.experienceAwarded > 0) { 53 | ctx.server.sendProfile() 54 | } 55 | 56 | when (result.result) { 57 | Result.SUCCESS -> { 58 | ctx.server.sendPokestop(closest) 59 | ctx.server.sendProfile() 60 | var message = "Looted pokestop $pokestopID; +${result.experienceAwarded} XP" 61 | if (settings.displayPokestopRewards) 62 | message += ": ${result.itemsAwardedList.groupBy { it.itemId.name }.map { "${it.value.size}x${it.key}" }}" 63 | Log.green(message) 64 | lootTimeouts.put(closest.id, closest.cooldownCompleteTimestampMs) 65 | checkForBan(result, closest, bot, settings) 66 | } 67 | Result.INVENTORY_FULL -> { 68 | ctx.server.sendPokestop(closest) 69 | ctx.server.sendProfile() 70 | var message = "Looted pokestop $pokestopID; +${result.experienceAwarded} XP, but inventory is full" 71 | if (settings.displayPokestopRewards) 72 | message += ": ${result.itemsAwardedList.groupBy { it.itemId.name }.map { "${it.value.size}x${it.key}" }}" 73 | 74 | Log.red(message) 75 | lootTimeouts.put(closest.id, closest.cooldownCompleteTimestampMs) 76 | } 77 | Result.OUT_OF_RANGE -> { 78 | Log.red("Pokestop out of range; our calculated distance: ${DecimalFormat("#0.00").format(closest.distance)}m") 79 | if (closest.distance < ctx.api.fortSettings.interactionRangeMeters) { 80 | Log.red("Server is lying to us (${Math.round(closest.distance)}m < ${Math.round(ctx.api.fortSettings.interactionRangeMeters)}m!); blacklisting for $cooldownPeriod minutes") 81 | lootTimeouts.put(closest.id, ctx.api.currentTimeMillis() + cooldownPeriod * 60 * 1000) 82 | } 83 | } 84 | Result.IN_COOLDOWN_PERIOD -> { 85 | lootTimeouts.put(closest.id, ctx.api.currentTimeMillis() + cooldownPeriod * 60 * 1000) 86 | Log.red("Pokestop still in cooldown mode; blacklisting for $cooldownPeriod minutes") 87 | } 88 | Result.NO_RESULT_SET -> { 89 | lootTimeouts.put(closest.id, ctx.api.currentTimeMillis() + cooldownPeriod * 60 * 1000) 90 | Log.red("Server refuses to loot this Pokestop (usually temporary issue); blacklisting for $cooldownPeriod minutes") 91 | } 92 | else -> Log.yellow(result.result.toString()) 93 | } 94 | } 95 | // unlock walk block 96 | ctx.pauseWalking.set(false) 97 | } 98 | 99 | private fun checkForBan(result: FortSearchResponseOuterClass.FortSearchResponse, pokestop: Pokestop, bot: Bot, settings: Settings) { 100 | if (settings.banSpinCount > 0 && result.experienceAwarded == 0 && result.itemsAwardedCount == 0) { 101 | Log.red("Looks like a ban. Trying to bypass softban by repeatedly spinning the pokestop.") 102 | bot.task(BypassSoftban(pokestop)) 103 | Log.yellow("Finished softban bypass attempt. Continuing.") 104 | // Add pokestop to cooldown list to prevent immediate retry in the next loop 105 | lootTimeouts.put(pokestop.id, bot.ctx.api.currentTimeMillis() + cooldownPeriod * 60 * 1000) 106 | } 107 | } 108 | } 109 | 110 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/ProcessPokestops.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import ink.abb.pogo.api.cache.Pokestop 12 | import ink.abb.pogo.scraper.Bot 13 | import ink.abb.pogo.scraper.Context 14 | import ink.abb.pogo.scraper.Settings 15 | import ink.abb.pogo.scraper.Task 16 | import ink.abb.pogo.scraper.util.Log 17 | import ink.abb.pogo.scraper.util.map.distance 18 | import ink.abb.pogo.scraper.util.pokemon.distance 19 | import ink.abb.pogo.scraper.util.map.inRangeForLuredPokemon 20 | import java.util.* 21 | import java.util.concurrent.TimeUnit 22 | 23 | /** 24 | * Task that handles catching pokemon, activating stops, and walking to a new target. 25 | */ 26 | class ProcessPokestops(var pokestops: List) : Task { 27 | 28 | val refetchTime = TimeUnit.SECONDS.toMillis(30) 29 | var lastFetch: Long = 0 30 | 31 | private val lootTimeouts = HashMap() 32 | var startPokestop: Pokestop? = null 33 | 34 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 35 | var writeCampStatus = false 36 | if (lastFetch + refetchTime < bot.api.currentTimeMillis()) { 37 | writeCampStatus = true 38 | lastFetch = bot.api.currentTimeMillis() 39 | if (settings.allowLeaveStartArea) { 40 | try { 41 | val newStops = ctx.api.map.getPokestops(ctx.api.latitude, ctx.api.longitude, 9) 42 | if (newStops.size > 0) { 43 | pokestops = newStops 44 | } 45 | } catch (e: Exception) { 46 | // ignored failed request 47 | } 48 | } 49 | } 50 | val sortedPokestops = pokestops.sortedWith(Comparator { a, b -> 51 | a.distance.compareTo(b.distance) 52 | }) 53 | if (startPokestop == null) 54 | startPokestop = sortedPokestops.first() 55 | 56 | if (settings.lootPokestop) { 57 | val loot = LootOneNearbyPokestop(sortedPokestops, lootTimeouts) 58 | try { 59 | bot.task(loot) 60 | } catch (e: Exception) { 61 | ctx.pauseWalking.set(false) 62 | } 63 | } 64 | 65 | if (settings.campLurePokestop > 0 && !ctx.pokemonInventoryFullStatus.get() && settings.catchPokemon) { 66 | val luresInRange = sortedPokestops.filter { 67 | it.inRangeForLuredPokemon() && it.fortData.hasLureInfo() 68 | } 69 | if (luresInRange.size >= settings.campLurePokestop) { 70 | if (writeCampStatus) { 71 | Log.green("${luresInRange.size} lure(s) in range, pausing") 72 | } 73 | return 74 | } 75 | } 76 | val walk = Walk(sortedPokestops, lootTimeouts) 77 | 78 | bot.task(walk) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/ReleasePokemon.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Networking.Responses.ReleasePokemonResponseOuterClass.ReleasePokemonResponse.Result 12 | import ink.abb.pogo.api.util.PokemonMetaRegistry 13 | import ink.abb.pogo.scraper.Bot 14 | import ink.abb.pogo.scraper.Context 15 | import ink.abb.pogo.scraper.Settings 16 | import ink.abb.pogo.scraper.Task 17 | import ink.abb.pogo.scraper.util.Log 18 | import ink.abb.pogo.scraper.util.pokemon.getIv 19 | import ink.abb.pogo.scraper.util.pokemon.getIvPercentage 20 | import ink.abb.pogo.scraper.util.pokemon.shouldTransfer 21 | import java.util.concurrent.atomic.AtomicInteger 22 | 23 | class ReleasePokemon : Task { 24 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 25 | val pokemonMap = ctx.api.inventory.pokemon 26 | // prevent concurrent modification exception 27 | val groupedPokemon = pokemonMap.map { it.value }.groupBy { it.pokemonData.pokemonId } 28 | val sortByIV = settings.sortByIv 29 | val pokemonCounts = hashMapOf() 30 | 31 | groupedPokemon.forEach { 32 | val sorted = if (sortByIV) { 33 | it.value.sortedByDescending { it.pokemonData.getIv() } 34 | } else { 35 | it.value.sortedByDescending { it.pokemonData.cp } 36 | } 37 | for ((index, pokemon) in sorted.withIndex()) { 38 | // don't drop favorited, deployed, or nicknamed pokemon 39 | val isFavourite = pokemon.pokemonData.nickname.isNotBlank() || 40 | pokemon.pokemonData.favorite != 0 || 41 | !pokemon.pokemonData.deployedFortId.isEmpty() || 42 | (ctx.api.playerData.hasBuddyPokemon() && ctx.api.playerData.buddyPokemon.id == pokemon.pokemonData.id) 43 | if (!isFavourite) { 44 | val ivPercentage = pokemon.pokemonData.getIvPercentage() 45 | // never transfer highest rated Pokemon (except for obligatory transfer) 46 | if (settings.obligatoryTransfer.contains(pokemon.pokemonData.pokemonId) || index >= settings.keepPokemonAmount) { 47 | val (shouldRelease, reason) = pokemon.pokemonData.shouldTransfer(settings, pokemonCounts, 48 | bot.api.inventory.candies.getOrPut(PokemonMetaRegistry.getMeta(pokemon.pokemonData.pokemonId).family, { AtomicInteger(0) })) 49 | 50 | if (shouldRelease) { 51 | Log.yellow("Going to transfer ${pokemon.pokemonData.pokemonId.name} with " + 52 | "CP ${pokemon.pokemonData.cp} and IV $ivPercentage%; reason: $reason") 53 | val result = bot.api.queueRequest(ink.abb.pogo.api.request.ReleasePokemon().withPokemonId(pokemon.pokemonData.id)).toBlocking().first().response 54 | 55 | if (result.result == Result.SUCCESS) { 56 | Log.green("Successfully transfered ${pokemon.pokemonData.pokemonId.name} with " + 57 | "CP ${pokemon.pokemonData.cp} and IV $ivPercentage%") 58 | if (ctx.pokemonInventoryFullStatus.get()) { 59 | // Just released a pokemon so the inventory is not full anymore 60 | ctx.pokemonInventoryFullStatus.set(false) 61 | if (settings.catchPokemon) 62 | Log.green("Inventory freed, enabling catching of pokemon") 63 | } 64 | ctx.pokemonStats.second.andIncrement 65 | ctx.server.releasePokemon(pokemon.pokemonData.id) 66 | ctx.server.sendProfile() 67 | } else { 68 | Log.red("Failed to transfer ${pokemon.pokemonData.pokemonId.name}: ${result.result}") 69 | } 70 | 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/SetBuddyPokemon.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Networking.Responses.SetBuddyPokemonResponseOuterClass 12 | import ink.abb.pogo.api.request.SetBuddyPokemon 13 | import ink.abb.pogo.scraper.Bot 14 | import ink.abb.pogo.scraper.Context 15 | import ink.abb.pogo.scraper.Settings 16 | import ink.abb.pogo.scraper.Task 17 | import ink.abb.pogo.scraper.util.Log 18 | 19 | 20 | class SetBuddyPokemon : Task { 21 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 22 | var replaceBuddy = false 23 | val currentBuddy = if (ctx.api.playerData.hasBuddyPokemon()) { 24 | ctx.api.inventory.pokemon[ctx.api.playerData.buddyPokemon.id] 25 | } else { 26 | null 27 | } 28 | if (currentBuddy != null) { 29 | if (settings.buddyPokemon.toUpperCase().trim() != currentBuddy.pokemonData.pokemonId.name) { 30 | replaceBuddy = true 31 | } 32 | } else { 33 | replaceBuddy = true 34 | } 35 | if (replaceBuddy) { 36 | val desiredBuddies = ctx.api.inventory.pokemon.filter { 37 | it.value.pokemonData.pokemonId.name == settings.buddyPokemon.toUpperCase().trim() 38 | }.toList() 39 | if (desiredBuddies.size > 0) { 40 | val setBuddyRequest = SetBuddyPokemon().withPokemonId(desiredBuddies[0].first) 41 | val response = ctx.api.queueRequest(setBuddyRequest).toBlocking().first().response 42 | if (response.result == SetBuddyPokemonResponseOuterClass.SetBuddyPokemonResponse.Result.SUCCESS) { 43 | Log.green("Updated Buddy Pokemon to ${ctx.api.inventory.pokemon[response.updatedBuddy.id]?.pokemonData?.pokemonId?.name}") 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/UpdateProfile.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import POGOProtos.Networking.Responses.LevelUpRewardsResponseOuterClass 12 | import ink.abb.pogo.api.request.CheckAwardedBadges 13 | import ink.abb.pogo.api.request.GetInventory 14 | import ink.abb.pogo.api.request.LevelUpRewards 15 | import ink.abb.pogo.scraper.* 16 | import ink.abb.pogo.scraper.util.Log 17 | import java.text.DecimalFormat 18 | import java.text.NumberFormat 19 | import java.time.LocalDateTime 20 | import java.time.temporal.ChronoUnit 21 | import java.util.* 22 | import java.util.concurrent.atomic.AtomicInteger 23 | 24 | class UpdateProfile : Task { 25 | var lastLevelCheck: Int = 0 26 | 27 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 28 | bot.api.queueRequest(GetInventory().withLastTimestampMs(0)).subscribe { 29 | val curLevelXP = bot.api.inventory.playerStats.experience - requiredXp[bot.api.inventory.playerStats.level - 1] 30 | val nextXP = if (bot.api.inventory.playerStats.level == requiredXp.size) { 31 | curLevelXP 32 | } else { 33 | (requiredXp[bot.api.inventory.playerStats.level] - requiredXp[bot.api.inventory.playerStats.level - 1]).toLong() 34 | } 35 | val ratio = DecimalFormat("#0.00").format(curLevelXP.toDouble() / nextXP.toDouble() * 100.0) 36 | val timeDiff = ChronoUnit.MINUTES.between(ctx.startTime, LocalDateTime.now()) 37 | val xpPerHour: Long = if (timeDiff != 0L) { 38 | (bot.api.inventory.playerStats.experience - ctx.startXp.get()) / timeDiff * 60 39 | } else { 40 | 0 41 | } 42 | val nextLevel: String = if (xpPerHour != 0L) { 43 | "${DecimalFormat("#0").format((nextXP.toDouble() - curLevelXP.toDouble()) / xpPerHour.toDouble())}h${Math.round(((nextXP.toDouble() - curLevelXP.toDouble()) / xpPerHour.toDouble()) % 1 * 60)}m" 44 | } else { 45 | "Unknown" 46 | } 47 | 48 | Log.magenta("Profile update: ${bot.api.inventory.playerStats.experience} XP on LVL ${bot.api.inventory.playerStats.level}; $curLevelXP/$nextXP ($ratio%) to LVL ${bot.api.inventory.playerStats.level + 1}") 49 | Log.magenta("XP gain: ${NumberFormat.getInstance().format(bot.api.inventory.playerStats.experience - ctx.startXp.get())} XP in ${ChronoUnit.MINUTES.between(ctx.startTime, LocalDateTime.now())} mins; " + 50 | "XP rate: ${NumberFormat.getInstance().format(xpPerHour)}/hr; Next level in: $nextLevel") 51 | Log.magenta("Pokemon caught/transferred: ${ctx.pokemonStats.first.get()}/${ctx.pokemonStats.second.get()}; " + 52 | "Pokemon caught from lures: ${ctx.luredPokemonStats.get()}; " + 53 | "Items caught/dropped: ${ctx.itemStats.first.get()}/${ctx.itemStats.second.get()};") 54 | Log.magenta("Pokebank ${bot.api.inventory.pokemon.size + bot.api.inventory.eggs.size}/${bot.api.playerData.maxPokemonStorage}; " + 55 | "Stardust ${bot.api.inventory.currencies.getOrPut("STARDUST", { AtomicInteger(0) }).get()}; " + 56 | "Inventory ${bot.api.inventory.size}/${bot.api.playerData.maxItemStorage}" 57 | ) 58 | if (bot.api.inventory.pokemon.size + bot.api.inventory.eggs.size < bot.api.playerData.maxPokemonStorage && ctx.pokemonInventoryFullStatus.get()) 59 | ctx.pokemonInventoryFullStatus.set(false) 60 | else if (bot.api.inventory.pokemon.size + bot.api.inventory.eggs.size >= bot.api.playerData.maxPokemonStorage && !ctx.pokemonInventoryFullStatus.get()) 61 | ctx.pokemonInventoryFullStatus.set(true) 62 | 63 | if (settings.catchPokemon && ctx.pokemonInventoryFullStatus.get()) 64 | Log.red("Pokemon inventory is full, not catching!") 65 | 66 | ctx.server.sendProfile() 67 | } 68 | 69 | for (i in (lastLevelCheck + 1)..bot.api.inventory.playerStats.level) { 70 | //Log.magenta("Accepting rewards for level $i...") 71 | bot.api.queueRequest(LevelUpRewards().withLevel(i)).subscribe { 72 | val result = it.response 73 | if (result.result == LevelUpRewardsResponseOuterClass.LevelUpRewardsResponse.Result.AWARDED_ALREADY) { 74 | if (i > lastLevelCheck) { 75 | //Log.magenta("Already accepted awards for level ${i}, updating $lastLevelCheck = $i") 76 | lastLevelCheck = i 77 | } 78 | return@subscribe 79 | } 80 | 81 | var message = "Accepting rewards for level $i" 82 | 83 | val sb_rewards = StringJoiner(", ") 84 | for (reward in result.itemsAwardedList) { 85 | sb_rewards.add("${reward.itemCount}x ${reward.itemId.name}") 86 | } 87 | message += "; Rewards: [$sb_rewards]" 88 | 89 | if (result.itemsUnlockedCount > 0) { 90 | val sb_unlocks = StringJoiner(", ") 91 | for (item in result.itemsUnlockedList) { 92 | sb_unlocks.add("${item.name}") 93 | } 94 | message += "; Unlocks: [$sb_unlocks]" 95 | } 96 | 97 | Log.magenta(message) 98 | 99 | if (i > lastLevelCheck) { 100 | lastLevelCheck = i 101 | } 102 | } 103 | } 104 | 105 | bot.api.queueRequest(CheckAwardedBadges()).subscribe { 106 | val result = it.response 107 | result.awardedBadgesList.forEach { 108 | // TODO: Does not work?! 109 | /*bot.api.queueRequest(EquipBadge().withBadgeType(it)).subscribe { 110 | println(it.response.toString()) 111 | }*/ 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/tasks/WalkToStartPokestop.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.tasks 10 | 11 | import com.google.common.geometry.S2LatLng 12 | import ink.abb.pogo.api.cache.Pokestop 13 | import ink.abb.pogo.scraper.Bot 14 | import ink.abb.pogo.scraper.Context 15 | import ink.abb.pogo.scraper.Settings 16 | import ink.abb.pogo.scraper.Task 17 | import ink.abb.pogo.scraper.util.Log 18 | import ink.abb.pogo.scraper.util.directions.getRouteCoordinates 19 | import ink.abb.pogo.scraper.util.pokemon.inRange 20 | import java.util.concurrent.atomic.AtomicBoolean 21 | 22 | class WalkToStartPokestop(val startPokeStop: Pokestop) : Task { 23 | override fun run(bot: Bot, ctx: Context, settings: Settings) { 24 | if (settings.followStreets.isNotEmpty()) walkRoute(bot, ctx, settings) 25 | else walk(bot, ctx, settings) 26 | 27 | } 28 | 29 | fun walk(bot: Bot, ctx: Context, settings: Settings) { 30 | ctx.walking.set(true) 31 | val end = S2LatLng.fromDegrees(startPokeStop.fortData.latitude, startPokeStop.fortData.longitude) 32 | val start = S2LatLng.fromDegrees(ctx.lat.get(), ctx.lng.get()) 33 | val diff = end.sub(start) 34 | val distance = start.getEarthDistance(end) 35 | val timeout = 200L 36 | // prevent division by 0 37 | if (settings.speed.equals(0)) { 38 | notifyWalkDone(ctx, bot) 39 | return 40 | } 41 | val timeRequired = distance / settings.speed 42 | val stepsRequired = timeRequired / (timeout.toDouble() / 1000.toDouble()) 43 | // prevent division by 0 44 | if (stepsRequired.equals(0)) { 45 | notifyWalkDone(ctx, bot) 46 | return 47 | } 48 | val deltaLat = diff.latDegrees() / stepsRequired 49 | val deltaLng = diff.lngDegrees() / stepsRequired 50 | 51 | Log.cyan("Walking to starting Pokestop ${startPokeStop.name} in ${stepsRequired.toInt()} steps.") 52 | var remainingSteps = stepsRequired 53 | val pauseWalk: AtomicBoolean = AtomicBoolean(false) 54 | var pauseCounter = 2 55 | bot.runLoop(timeout, "WalkingLoop") { cancel -> 56 | if (pauseWalk.get()) { 57 | Thread.sleep(timeout * 2) 58 | pauseCounter-- 59 | if (!(ctx.api.inventory.hasPokeballs && bot.api.map.getPokemon(bot.api.latitude, bot.api.longitude, 3).filter { 60 | !ctx.blacklistedEncounters.contains(it.encounterId) && it.inRange 61 | }.size > 0 && settings.catchPokemon)) { 62 | // api break free 63 | pauseWalk.set(false) 64 | pauseCounter = 0 65 | } 66 | // fixed tries break free 67 | if (pauseCounter > 0) { 68 | return@runLoop 69 | } else { 70 | pauseWalk.set(false) 71 | } 72 | } 73 | // don't run away when there are still Pokemon around 74 | if (remainingSteps.toInt().mod(20) == 0 && pauseCounter > 0) { 75 | if (ctx.api.inventory.hasPokeballs && bot.api.map.getPokemon(bot.api.latitude, bot.api.longitude, 3).filter { 76 | !ctx.blacklistedEncounters.contains(it.encounterId) && it.inRange 77 | }.size > 0 && settings.catchPokemon) { 78 | // Stop walking 79 | Log.normal("Pausing to catch pokemon...") 80 | pauseCounter = 2 81 | pauseWalk.set(true) 82 | return@runLoop 83 | } 84 | } 85 | 86 | pauseCounter = 2 87 | val lat = ctx.lat.addAndGet(deltaLat) 88 | val lng = ctx.lng.addAndGet(deltaLng) 89 | 90 | ctx.server.setLocation(lat, lng) 91 | 92 | remainingSteps-- 93 | if (remainingSteps.toInt().mod(20) == 0) Log.cyan("Starting Pokestop reached in ${remainingSteps.toInt()} steps.") 94 | if (remainingSteps <= 0) { 95 | Log.normal("Destination reached.") 96 | notifyWalkDone(ctx, bot) 97 | cancel() 98 | } 99 | } 100 | } 101 | 102 | fun walkRoute(bot: Bot, ctx: Context, settings: Settings) { 103 | ctx.walking.set(true) 104 | if (settings.speed.equals(0)) { 105 | notifyWalkDone(ctx, bot) 106 | return 107 | } 108 | val timeout = 200L 109 | val coordinatesList = getRouteCoordinates(ctx.lat.get(), ctx.lng.get(), startPokeStop.fortData.latitude, startPokeStop.fortData.longitude, settings, ctx.geoApiContext!!) 110 | if (coordinatesList.size <= 0) { 111 | walk(bot, ctx, settings) 112 | } else { 113 | val pauseWalk: AtomicBoolean = AtomicBoolean(false) 114 | var pauseCounter = 2 115 | bot.runLoop(timeout, "WalkingLoop") { cancel -> 116 | if (pauseWalk.get()) { 117 | Thread.sleep(timeout * 2) 118 | pauseCounter-- 119 | if (!(ctx.api.inventory.hasPokeballs && bot.api.map.getPokemon(bot.api.latitude, bot.api.longitude, 3).filter { 120 | !ctx.blacklistedEncounters.contains(it.encounterId) && it.inRange 121 | }.size > 0 && settings.catchPokemon)) { 122 | // api break free 123 | pauseWalk.set(false) 124 | pauseCounter = 0 125 | } 126 | // fixed tries break free 127 | if (pauseCounter > 0) { 128 | return@runLoop 129 | } else { 130 | pauseWalk.set(false) 131 | } 132 | } 133 | // don't run away when there are still Pokemon around 134 | if (pauseCounter > 0 && ctx.api.inventory.hasPokeballs && bot.api.map.getPokemon(bot.api.latitude, bot.api.longitude, 3).filter { 135 | !ctx.blacklistedEncounters.contains(it.encounterId) && it.inRange 136 | }.size > 0 && settings.catchPokemon) { 137 | // Stop walking 138 | Log.normal("Pausing to catch pokemon...") 139 | pauseCounter = 2 140 | pauseWalk.set(true) 141 | return@runLoop 142 | } 143 | pauseCounter = 2 144 | val start = S2LatLng.fromDegrees(ctx.lat.get(), ctx.lng.get()) 145 | val step = coordinatesList.first() 146 | coordinatesList.removeAt(0) 147 | val diff = step.sub(start) 148 | val distance = start.getEarthDistance(step) 149 | val timeRequired = distance / settings.speed 150 | val stepsRequired = timeRequired / (timeout.toDouble() / 1000.toDouble()) 151 | if (stepsRequired.equals(0)) { 152 | notifyWalkDone(ctx, bot) 153 | cancel() 154 | } 155 | val deltaLat = diff.latDegrees() / stepsRequired 156 | val deltaLng = diff.lngDegrees() / stepsRequired 157 | var remainingSteps = stepsRequired 158 | while (remainingSteps > 0) { 159 | ctx.lat.addAndGet(deltaLat) 160 | ctx.lng.addAndGet(deltaLng) 161 | ctx.server.setLocation(ctx.lat.get(), ctx.lng.get()) 162 | remainingSteps-- 163 | Thread.sleep(timeout) 164 | } 165 | 166 | if (coordinatesList.size <= 0) { 167 | notifyWalkDone(ctx, bot) 168 | cancel() 169 | } 170 | } 171 | } 172 | } 173 | 174 | fun notifyWalkDone(ctx: Context, bot: Bot) { 175 | Log.normal("Destination reached.") 176 | ctx.walking.set(false) 177 | bot.prepareWalkBack.set(false) 178 | bot.walkBackLock.set(false) 179 | ctx.server.sendGotoDone() 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/ApiAuthProvider.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util 10 | 11 | import ink.abb.pogo.scraper.services.BotService 12 | import org.springframework.beans.factory.annotation.Autowired 13 | import org.springframework.stereotype.Component 14 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter 15 | import java.math.BigInteger 16 | import java.security.SecureRandom 17 | import java.util.regex.Pattern 18 | import javax.servlet.http.HttpServletRequest 19 | import javax.servlet.http.HttpServletResponse 20 | 21 | @Component 22 | open class ApiAuthProvider : HandlerInterceptorAdapter() { 23 | 24 | @Autowired 25 | lateinit var service: BotService 26 | 27 | val random: SecureRandom = SecureRandom() 28 | 29 | @Throws(Exception::class) 30 | override fun preHandle(request: HttpServletRequest, 31 | response: HttpServletResponse, handler: Any): Boolean { 32 | 33 | if (request.method.equals("OPTIONS")) 34 | return true // Allow preflight calls 35 | 36 | val pattern = Pattern.compile("\\/api/bot/([A-Za-z0-9\\-_]*)") 37 | val matcher = pattern.matcher(request.requestURI) 38 | if (matcher.find()) { 39 | val token = service.getBotContext(matcher.group(1)).restApiToken 40 | 41 | // If the token is invalid or isn't in the request, nothing will be done 42 | return request.getHeader("X-PGB-ACCESS-TOKEN")!!.equals(token) 43 | } 44 | 45 | return false 46 | } 47 | 48 | fun generateRandomString(): String { 49 | return BigInteger(130, random).toString(32) 50 | } 51 | 52 | fun generateAuthToken(botName: String) { 53 | val token: String = this.generateRandomString() 54 | service.getBotContext(botName).restApiToken = token 55 | 56 | Log.cyan("REST API token for bot $botName : $token has been generated") 57 | } 58 | 59 | fun generateRestPassword(botName: String) { 60 | val password: String = this.generateRandomString() 61 | service.getBotContext(botName).restApiPassword = password 62 | service.doWithBot(botName) { 63 | it.settings.restApiPassword = password 64 | } 65 | 66 | Log.red("Generated restApiPassword: $password") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/Byte.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util 10 | 11 | /** 12 | * Set of chars for a half-byte. 13 | */ 14 | private val CHARS = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') 15 | 16 | /** 17 | * Returns the string of two characters representing the HEX value of the byte. 18 | */ 19 | internal fun Byte.toHexString(): String { 20 | val i = this.toInt() 21 | val char2 = CHARS[i and 0x0f] 22 | val char1 = CHARS[i shr 4 and 0x0f] 23 | return "$char1$char2" 24 | } 25 | 26 | /** 27 | * Returns the HEX representation of ByteArray data. 28 | */ 29 | internal fun ByteArray.toHexString(): String { 30 | val builder = StringBuilder() 31 | for (b in this) { 32 | builder.append(b.toHexString()) 33 | } 34 | return builder.toString() 35 | } 36 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/Log.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util 10 | 11 | import ink.abb.pogo.scraper.Context 12 | import org.slf4j.LoggerFactory 13 | import org.slf4j.Marker 14 | import org.slf4j.MarkerFactory 15 | 16 | class Log { 17 | companion object { 18 | private var ctx: Context? = null 19 | 20 | private val LOGGER = LoggerFactory.getLogger(Log::class.java) 21 | 22 | enum class Color(val marker: Marker) { 23 | BLACK(MarkerFactory.getMarker("black")), 24 | RED(MarkerFactory.getMarker("red")), 25 | GREEN(MarkerFactory.getMarker("green")), 26 | YELLOW(MarkerFactory.getMarker("yellow")), 27 | BLUE(MarkerFactory.getMarker("blue")), 28 | MAGENTA(MarkerFactory.getMarker("magenta")), 29 | CYAN(MarkerFactory.getMarker("cyan")), 30 | WHITE(MarkerFactory.getMarker("white")); 31 | } 32 | 33 | fun info(text: String, color: Color = Color.WHITE, vararg args: Any) { 34 | LOGGER.info(color.marker, text, args) 35 | } 36 | 37 | fun debug(text: String, color: Color = Color.WHITE, vararg args: Any) { 38 | LOGGER.debug(color.marker, text, args) 39 | } 40 | 41 | fun error(text: String, color: Color = Color.WHITE, vararg args: Any) { 42 | LOGGER.error(color.marker, text, args) 43 | } 44 | 45 | fun warn(text: String, color: Color = Color.WHITE, vararg args: Any) { 46 | LOGGER.warn(color.marker, text, args) 47 | } 48 | 49 | fun trace(text: String, color: Color = Color.WHITE, vararg args: Any) { 50 | LOGGER.trace(color.marker, text, args) 51 | } 52 | 53 | fun normal(text: String = "") { 54 | info(text, Color.WHITE) 55 | ctx?.server?.sendLog("normal", text) 56 | } 57 | 58 | fun black(text: String = "") { 59 | info(text, Color.BLACK) 60 | ctx?.server?.sendLog("black", text) 61 | } 62 | 63 | fun red(text: String = "") { 64 | warn(text, Color.RED) 65 | ctx?.server?.sendLog("red", text) 66 | } 67 | 68 | fun green(text: String = "") { 69 | info(text, Color.GREEN) 70 | ctx?.server?.sendLog("green", text) 71 | } 72 | 73 | fun yellow(text: String = "") { 74 | info(text, Color.YELLOW) 75 | ctx?.server?.sendLog("yellow", text) 76 | } 77 | 78 | fun blue(text: String = "") { 79 | info(text, Color.BLUE) 80 | ctx?.server?.sendLog("blue", text) 81 | } 82 | 83 | fun magenta(text: String = "") { 84 | info(text, Color.MAGENTA) 85 | ctx?.server?.sendLog("magenta", text) 86 | } 87 | 88 | fun cyan(text: String = "") { 89 | info(text, Color.CYAN) 90 | ctx?.server?.sendLog("cyan", text) 91 | } 92 | 93 | fun white(text: String = "") { 94 | info(text, Color.WHITE) 95 | ctx?.server?.sendLog("white", text) 96 | } 97 | 98 | fun setContext(ctx: Context) { 99 | this.ctx = ctx 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/String.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util 10 | 11 | /** 12 | * Takes a camel cased identifier name and returns an underscore separated 13 | * name 14 | * 15 | * Example: 16 | * String.camelToUnderscores("thisIsA1Test") == "this_is_a_1_test" 17 | */ 18 | fun String.camelToUnderscores() = "[A-Z\\d]".toRegex().replace(this, { 19 | "_" + it.groups[0]!!.value.toLowerCase() 20 | }) 21 | 22 | /* 23 | * Takes an underscore separated identifier name and returns a camel cased one 24 | * 25 | * Example: 26 | * String.underscoreToCamel("this_is_a_1_test") == "thisIsA1Test" 27 | */ 28 | 29 | fun String.underscoreToCamel() = "_([a-z\\d])".toRegex().replace(this, { 30 | it.groups[1]!!.value.toUpperCase() 31 | }) 32 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/credentials/Credentials.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.credentials 10 | 11 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties 12 | import com.fasterxml.jackson.annotation.JsonSubTypes 13 | import com.fasterxml.jackson.annotation.JsonTypeInfo 14 | 15 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") 16 | @JsonSubTypes( 17 | JsonSubTypes.Type(value = GoogleCredentials::class, name = "google"), 18 | JsonSubTypes.Type(value = GoogleAutoCredentials::class, name = "google-auto"), 19 | JsonSubTypes.Type(value = PtcCredentials::class, name = "ptc") 20 | ) 21 | @JsonIgnoreProperties(ignoreUnknown = true) 22 | interface Credentials 23 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/credentials/GoogleAutoCredentials.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.credentials 10 | 11 | data class GoogleAutoCredentials(var username: String = "", var password: String = "") : Credentials 12 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/credentials/GoogleCredentials.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.credentials 10 | 11 | data class GoogleCredentials(var token: String = "") : Credentials 12 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/credentials/PtcCredentials.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.credentials 10 | 11 | data class PtcCredentials(val username: String = "", val password: String = "") : Credentials 12 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/data/EggData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.data 10 | 11 | import ink.abb.pogo.api.cache.BagPokemon 12 | import ink.abb.pogo.scraper.util.pokemon.eggKmWalked 13 | import ink.abb.pogo.scraper.util.pokemon.incubated 14 | 15 | data class EggData( 16 | var isIncubate: Boolean? = null, 17 | var kmWalked: Double? = null, 18 | var kmTarget: Double? = null 19 | ) { 20 | fun buildFromEggPokemon(egg: BagPokemon): EggData { 21 | this.isIncubate = egg.pokemonData.incubated 22 | this.kmWalked = egg.pokemonData.eggKmWalked(egg.poGoApi) 23 | this.kmTarget = egg.pokemonData.eggKmWalkedTarget 24 | 25 | return this 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/data/ItemData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.data 10 | 11 | import POGOProtos.Inventory.Item.ItemIdOuterClass 12 | 13 | data class ItemData( 14 | var itemId: Int? = null, 15 | var itemName: String? = null, 16 | var count: Int? = null 17 | ) { 18 | fun buildFromItem(item: ItemIdOuterClass.ItemId, count: Int): ItemData { 19 | this.itemId = item.number 20 | this.itemName = item.name 21 | this.count = count 22 | return this 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/data/LocationData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.data 10 | 11 | data class LocationData( 12 | val latitude: Double? = null, 13 | val longitude: Double? = null 14 | ) 15 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/data/PokedexData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.data 10 | 11 | import POGOProtos.Data.PokedexEntryOuterClass 12 | 13 | data class PokedexEntry( 14 | 15 | var timesEncountered: Int? = null, 16 | var timeCaptured: Int? = null, 17 | var pokemonName: String? = null, 18 | var pokemonNumber: Int? = null 19 | ) { 20 | fun buildFromEntry(entry: PokedexEntryOuterClass.PokedexEntry): PokedexEntry { 21 | 22 | this.timesEncountered = entry.timesEncountered 23 | this.timeCaptured = entry.timesCaptured 24 | this.pokemonName = entry.pokemonId.name 25 | this.pokemonNumber = entry.pokemonId.number 26 | 27 | return this 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/data/PokemonData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.data 10 | 11 | import POGOProtos.Enums.PokemonMoveOuterClass 12 | import com.google.common.geometry.S2CellId 13 | import com.google.common.geometry.S2LatLng 14 | import ink.abb.pogo.api.cache.BagPokemon 15 | import ink.abb.pogo.api.util.PokemonMoveMetaRegistry 16 | import ink.abb.pogo.scraper.util.pokemon.* 17 | import java.text.SimpleDateFormat 18 | import java.util.* 19 | import java.util.concurrent.atomic.AtomicInteger 20 | 21 | data class PokemonData( 22 | var id: String? = null, 23 | var pokemonId: Int? = null, 24 | var name: String? = null, 25 | var nickname: String? = null, 26 | var pclass: String? = null, 27 | var type1: String? = null, 28 | var type2: String? = null, 29 | var cp: Int? = null, 30 | var iv: Int? = null, 31 | var stats: String? = null, 32 | var favorite: Boolean? = null, 33 | var cpMultiplier: Float? = null, 34 | var heightM: Float? = null, 35 | var weightKg: Float? = null, 36 | 37 | var individualStamina: Int? = null, 38 | var individualAttack: Int? = null, 39 | var individualDefense: Int? = null, 40 | var candy: Int? = null, 41 | var candiesToEvolve: Int? = null, 42 | var level: Float? = null, 43 | 44 | var move1: String? = null, 45 | var move1Type: String? = null, 46 | var move1Power: Int? = null, 47 | var move1Accuracy: Int? = null, 48 | var move1CritChance: Double? = null, 49 | var move1Time: Int? = null, 50 | var move1Energy: Int? = null, 51 | 52 | var move2: String? = null, 53 | var move2Type: String? = null, 54 | var move2Power: Int? = null, 55 | var move2Accuracy: Int? = null, 56 | var move2CritChance: Double? = null, 57 | var move2Time: Int? = null, 58 | var move2Energy: Int? = null, 59 | 60 | var deployedFortId: String? = null, 61 | var stamina: Int? = null, 62 | var maxStamina: Int? = null, 63 | var maxCp: Int? = null, 64 | var absMaxCp: Int? = null, 65 | var maxCpFullEvolveAndPowerup: Int? = null, 66 | 67 | var candyCostsForPowerup: Int? = null, 68 | var stardustCostsForPowerup: Int? = null, 69 | var creationTime: String? = null, 70 | var creationTimeMs: Long? = null, 71 | var creationLatDegrees: Double? = null, 72 | var creationLngDegrees: Double? = null, 73 | var baseCaptureRate: Double? = null, 74 | var baseFleeRate: Double? = null, 75 | var battlesAttacked: Int? = null, 76 | var battlesDefended: Int? = null, 77 | var isInjured: Boolean? = null, 78 | var isFainted: Boolean? = null, 79 | var cpAfterPowerup: Int? = null 80 | 81 | ) { 82 | fun buildFromPokemon(bagPokemon: BagPokemon): PokemonData { 83 | val pokemon = bagPokemon.pokemonData 84 | val latLng = S2LatLng(S2CellId(pokemon.capturedCellId).toPoint()) 85 | val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") 86 | 87 | val pmeta = pokemon.meta 88 | val pmmeta1 = PokemonMoveMetaRegistry.getMeta(PokemonMoveOuterClass.PokemonMove.forNumber(pokemon.move1.number)) 89 | val pmmeta2 = PokemonMoveMetaRegistry.getMeta(PokemonMoveOuterClass.PokemonMove.forNumber(pokemon.move2.number)) 90 | 91 | this.id = pokemon.id.toString() 92 | this.pokemonId = pokemon.pokemonId.number 93 | this.name = pokemon.pokemonId.name 94 | this.nickname = pokemon.nickname 95 | this.pclass = pmeta.pokemonClass.name 96 | this.type1 = pmeta.type1.name 97 | this.type2 = pmeta.type2.name 98 | this.cp = pokemon.cp 99 | this.iv = pokemon.getIvPercentage() 100 | this.stats = pokemon.getStatsFormatted() 101 | this.favorite = pokemon.favorite > 0 102 | this.cpMultiplier = pokemon.cpMultiplier 103 | this.heightM = pokemon.heightM 104 | this.weightKg = pokemon.weightKg 105 | 106 | this.individualStamina = pokemon.individualStamina 107 | this.individualAttack = pokemon.individualAttack 108 | this.individualDefense = pokemon.individualDefense 109 | this.candy = bagPokemon.poGoApi.inventory.candies.getOrPut(pmeta.family, { AtomicInteger(0) }).get() 110 | this.candiesToEvolve = pmeta.candyToEvolve 111 | this.level = pokemon.level 112 | 113 | this.move1 = pokemon.move1.name 114 | this.move1Type = pmmeta1.type.name 115 | this.move1Power = pmmeta1.power 116 | this.move1Accuracy = pmmeta1.accuracy 117 | this.move1CritChance = pmmeta1.critChance 118 | this.move1Time = pmmeta1.time 119 | this.move1Energy = pmmeta1.energy 120 | 121 | this.move2 = pokemon.move2.name 122 | this.move2Type = pmmeta2.type.name 123 | this.move2Power = pmmeta2.power 124 | this.move2Accuracy = pmmeta2.accuracy 125 | this.move2CritChance = pmmeta2.critChance 126 | this.move2Time = pmmeta2.time 127 | this.move2Energy = pmmeta2.energy 128 | 129 | this.deployedFortId = pokemon.deployedFortId 130 | this.stamina = pokemon.stamina 131 | this.maxStamina = pokemon.staminaMax 132 | this.maxCp = pokemon.maxCp 133 | this.absMaxCp = pokemon.absoluteMaxCp 134 | this.maxCpFullEvolveAndPowerup = pokemon.cpFullEvolveAndPowerup 135 | 136 | this.candyCostsForPowerup = pokemon.candyCostsForPowerup 137 | this.stardustCostsForPowerup = pokemon.stardustCostsForPowerup 138 | this.creationTime = dateFormatter.format(Date(pokemon.creationTimeMs)) 139 | this.creationTimeMs = pokemon.creationTimeMs 140 | this.creationLatDegrees = latLng.latDegrees() 141 | this.creationLngDegrees = latLng.lngDegrees() 142 | this.baseCaptureRate = pmeta.baseCaptureRate 143 | this.baseFleeRate = pmeta.baseFleeRate 144 | this.battlesAttacked = pokemon.battlesAttacked 145 | this.battlesDefended = pokemon.battlesDefended 146 | this.isInjured = pokemon.injured 147 | this.isFainted = pokemon.fainted 148 | this.cpAfterPowerup = pokemon.cpAfterPowerup 149 | 150 | return this 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/data/ProfileData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.data 10 | 11 | import ink.abb.pogo.api.PoGoApi 12 | import java.util.concurrent.atomic.AtomicInteger 13 | 14 | data class ProfileData( 15 | 16 | var name: String? = null, 17 | var level: Int? = null, 18 | var exp: Long? = null, 19 | var expToNextLevel: Long? = null, 20 | var stardust: Int? = null, 21 | var team: String? = null, 22 | var pokebankLimit: Int? = null, 23 | var pokebankUsage: Int? = null, 24 | var backpackLimit: Int? = null, 25 | var backpackUsage: Int? = null, 26 | var coin: Int? = null 27 | 28 | ) { 29 | fun buildFromApi(api: PoGoApi): ProfileData { 30 | this.name = api.playerData.username 31 | this.level = api.inventory.playerStats.level 32 | this.exp = api.inventory.playerStats.experience 33 | this.expToNextLevel = api.inventory.playerStats.nextLevelXp 34 | this.stardust = api.inventory.currencies.getOrPut("STARDUST", { AtomicInteger(0) }).get() 35 | this.team = api.playerData.team.name 36 | this.pokebankLimit = api.playerData.maxPokemonStorage 37 | this.pokebankUsage = api.inventory.pokemon.size 38 | this.backpackLimit = api.playerData.maxItemStorage 39 | this.backpackUsage = api.inventory.size 40 | this.coin = api.inventory.currencies.getOrPut("POKECOIN", { AtomicInteger(0) }).get() 41 | 42 | return this 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/directions/RouteProviderEnum.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.directions 10 | 11 | import com.fasterxml.jackson.databind.ObjectMapper 12 | import com.google.common.geometry.S1Angle 13 | import com.google.common.geometry.S2LatLng 14 | import com.google.maps.DirectionsApi 15 | import com.google.maps.GeoApiContext 16 | import com.google.maps.model.TravelMode 17 | import ink.abb.pogo.scraper.Settings 18 | import java.util.* 19 | import java.util.regex.Pattern 20 | 21 | enum class RouteProviderEnum { 22 | 23 | MAPZEN { 24 | override fun getRoute(startLat: Double, startLong: Double, endLat: Double, endLong: Double, geoApiContext: GeoApiContext): ArrayList { 25 | throw UnsupportedOperationException("not implemented") 26 | } 27 | 28 | override fun createURLString(startLat: Double, startLong: Double, endLat: Double, endLong: Double, apiKey: String): String { 29 | var url = "http://valhalla.mapzen.com/route?json={\"locations\":[{\"lat\":$startLat,\"lon\":$startLong},{\"lat\":$endLat,\"lon\":$endLong}],\"costing\":\"pedestrian\",\"directions_options\":{\"narrative\":\"false\"}}" 30 | if (apiKey.isNotBlank()) { 31 | url += "&api_key=$apiKey" 32 | } 33 | return url 34 | } 35 | 36 | override fun parseRouteResponse(routeParsed: String): ArrayList { 37 | 38 | val jsonRoot = ObjectMapper().readTree(routeParsed) 39 | val status = jsonRoot.path("trip").path("status").asInt() 40 | 41 | // status 0 == no problem 42 | if (status == 0) { 43 | val shape = jsonRoot.path("trip").findValue("shape").textValue() 44 | 45 | // Decode the route shape, look at https://mapzen.com/documentation/turn-by-turn/decoding/ 46 | val precision: Double = 1E6 47 | val latlngList = ArrayList() 48 | var index: Int = 0 49 | var lat: Int = 0 50 | var lng: Int = 0 51 | while (index < shape.length) { 52 | var b: Int 53 | var shift = 0 54 | var result = 0 55 | do { 56 | b = shape[index++].toInt() - 63 57 | result = result or (b and 0x1f shl shift) 58 | shift += 5 59 | } while (b >= 0x20) 60 | val endLat = if (result and 1 != 0) (result shr 1).inv() else result shr 1 61 | lat += endLat 62 | 63 | shift = 0 64 | result = 0 65 | do { 66 | b = shape[index++].toInt() - 63 67 | result = result or (b and 0x1f shl shift) 68 | shift += 5 69 | } while (b >= 0x20) 70 | val endLong = if (result and 1 != 0) (result shr 1).inv() else result shr 1 71 | lng += endLong 72 | 73 | latlngList.add(S2LatLng.fromDegrees(lat / precision, lng / precision)) 74 | } 75 | return latlngList // everything is ok 76 | } 77 | return ArrayList() // can't parse 78 | } 79 | 80 | override fun getApiKey(settings: Settings): String { 81 | return settings.mapzenApiKey 82 | } 83 | }, 84 | 85 | MOBROUTING { 86 | override fun getRoute(startLat: Double, startLong: Double, endLat: Double, endLong: Double, geoApiContext: GeoApiContext): ArrayList { 87 | throw UnsupportedOperationException("not implemented") 88 | } 89 | 90 | override fun createURLString(startLat: Double, startLong: Double, endLat: Double, endLong: Double, apiKey: String): String { 91 | return "http://mobrouting.com/api/dev/gosmore.php?flat=$startLat&flon=$startLong&tlat=$endLat&tlon=$endLong&v=foot&fast=1&layer=mapnik" 92 | } 93 | 94 | override fun parseRouteResponse(routeParsed: String): ArrayList { 95 | if (!routeParsed.contains("0")) { 96 | val matcher = Pattern.compile("(|-)\\d+.\\d+,(|-)\\d+.\\d+").matcher(routeParsed.split("")[1]) 97 | val coordinatesList = ArrayList() 98 | while (matcher.find()) { 99 | coordinatesList.add(matcher.group()) 100 | } 101 | val latlngList = ArrayList() 102 | coordinatesList.forEach { 103 | latlngList.add(S2LatLng(S1Angle.degrees(it.toString().split(",")[1].toDouble()), S1Angle.degrees(it.toString().split(",")[0].toDouble()))) 104 | } 105 | return latlngList // everything is ok 106 | } 107 | return ArrayList() // can't parse 108 | } 109 | 110 | override fun getApiKey(settings: Settings): String { 111 | return "" 112 | } 113 | }, 114 | 115 | PROJECTOSM { 116 | override fun getRoute(startLat: Double, startLong: Double, endLat: Double, endLong: Double, geoApiContext: GeoApiContext): ArrayList { 117 | throw UnsupportedOperationException("not implemented") 118 | } 119 | 120 | override fun createURLString(startLat: Double, startLong: Double, endLat: Double, endLong: Double, apiKey: String): String { 121 | return "http://router.project-osrm.org/viaroute?loc=$startLat,$startLong&loc=$endLat,$endLong&compression=false" 122 | } 123 | 124 | override fun parseRouteResponse(routeParsed: String): ArrayList { 125 | if (routeParsed.contains("\"status\":200")) { 126 | val matcher = Pattern.compile("(|-)\\d+.\\d+,(|-)\\d+.\\d+").matcher(routeParsed.split("route_geometry")[1]) 127 | val coordinatesList = ArrayList() 128 | while (matcher.find()) { 129 | coordinatesList.add(matcher.group()) 130 | } 131 | val latlngList = ArrayList() 132 | coordinatesList.forEach { 133 | latlngList.add(S2LatLng(S1Angle.degrees(it.toString().split(",")[0].toDouble()), S1Angle.degrees(it.toString().split(",")[1].toDouble()))) 134 | } 135 | return latlngList // everything is ok 136 | } 137 | return ArrayList() // can't parse 138 | } 139 | 140 | override fun getApiKey(settings: Settings): String { 141 | return "" 142 | } 143 | }, 144 | 145 | YOURNAVIGATION { 146 | override fun getRoute(startLat: Double, startLong: Double, endLat: Double, endLong: Double, geoApiContext: GeoApiContext): ArrayList { 147 | throw UnsupportedOperationException("not implemented") 148 | } 149 | 150 | override fun createURLString(startLat: Double, startLong: Double, endLat: Double, endLong: Double, apiKey: String): String { 151 | // change v=foot to v=bicycle, foot doesn't work atm, remove fast=1 (default value) 152 | return "http://yournavigation.org/api/dev/route.php?flat=$startLat&flon=$startLong&tlat=$endLat&tlon=$endLong&v=bicycle" 153 | } 154 | 155 | override fun parseRouteResponse(routeParsed: String): ArrayList { 156 | if (!routeParsed.contains("0") && !routeParsed.contains("Please try again later")) { 157 | val matcher = Pattern.compile("(|-)\\d+.\\d+,(|-)\\d+.\\d+").matcher(routeParsed.split("")[1]) 158 | val coordinatesList = ArrayList() 159 | while (matcher.find()) { 160 | coordinatesList.add(matcher.group()) 161 | } 162 | val latlngList = ArrayList() 163 | coordinatesList.forEach { 164 | latlngList.add(S2LatLng(S1Angle.degrees(it.toString().split(",")[1].toDouble()), S1Angle.degrees(it.toString().split(",")[0].toDouble()))) 165 | } 166 | return latlngList // everything is ok 167 | } 168 | return ArrayList() // can't parse 169 | } 170 | 171 | override fun getApiKey(settings: Settings): String { 172 | return "" 173 | } 174 | }, 175 | 176 | GOOGLE { 177 | override fun getRoute(startLat: Double, startLong: Double, endLat: Double, endLong: Double, geoApiContext: GeoApiContext): ArrayList { 178 | try { 179 | val directionsRequest = DirectionsApi.getDirections(geoApiContext, "$startLat,$startLong", "$endLat,$endLong") 180 | directionsRequest.mode(TravelMode.WALKING) 181 | val directions = directionsRequest.await() 182 | val latlngList = ArrayList() 183 | directions.routes.forEach { 184 | it.legs.forEach { 185 | it.steps.forEach { 186 | it.polyline.decodePath().forEach { 187 | latlngList.add(S2LatLng(S1Angle.degrees(it.lat), S1Angle.degrees(it.lng))) 188 | } 189 | } 190 | } 191 | } 192 | return latlngList 193 | } catch (e: Exception) { 194 | return ArrayList() 195 | } 196 | } 197 | 198 | override fun createURLString(startLat: Double, startLong: Double, endLat: Double, endLong: Double, apiKey: String): String { 199 | throw UnsupportedOperationException("not implemented") 200 | } 201 | 202 | override fun getApiKey(settings: Settings): String { 203 | return settings.googleApiKey 204 | } 205 | 206 | override fun parseRouteResponse(routeParsed: String): ArrayList { 207 | throw UnsupportedOperationException("not implemented") 208 | } 209 | 210 | }; 211 | 212 | abstract fun createURLString(startLat: Double, startLong: Double, endLat: Double, endLong: Double, apiKey: String): String 213 | 214 | abstract fun parseRouteResponse(routeParsed: String): ArrayList 215 | 216 | abstract fun getApiKey(settings: Settings): String 217 | 218 | abstract fun getRoute(startLat: Double, startLong: Double, endLat: Double, endLong: Double, geoApiContext: GeoApiContext): ArrayList 219 | 220 | fun usingApiKey(settings: Settings): Boolean { 221 | return getApiKey(settings).isNotBlank() 222 | } 223 | 224 | /** 225 | * We ban a service provider for 1 minute 226 | * If it's still doesn't work, we double the time for every fail (1,2,4,8,16...) 227 | */ 228 | fun banMe() { 229 | if (banTime == 0) { 230 | banTime = 1 231 | } else { 232 | banTime *= 2 233 | } 234 | banDate = Calendar.getInstance() 235 | banDate.add(Calendar.MINUTE, banTime) 236 | } 237 | 238 | fun isBanned(): Boolean { 239 | return Calendar.getInstance().before(banDate) 240 | } 241 | 242 | var lastTry: Calendar = Calendar.getInstance() // when we try to call this route provider last time? 243 | var banDate: Calendar = Calendar.getInstance() // when we can unban this route provider? 244 | var banTime: Int = 0 // how many time this route provider is banned (in minute) 245 | 246 | } 247 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/directions/RouteRequest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.directions 10 | 11 | import com.fasterxml.jackson.databind.ObjectMapper 12 | import com.google.common.geometry.S2CellId 13 | import com.google.common.geometry.S2LatLng 14 | import com.google.maps.GeoApiContext 15 | import com.squareup.okhttp.HttpUrl 16 | import com.squareup.okhttp.OkHttpClient 17 | import com.squareup.okhttp.Request 18 | import ink.abb.pogo.scraper.Context 19 | import ink.abb.pogo.scraper.Settings 20 | import ink.abb.pogo.scraper.util.Log 21 | import java.util.* 22 | 23 | val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36" 24 | 25 | fun getRouteCoordinates(startLat: Double, startLong: Double, endLat: Double, endLong: Double, settings: Settings, geoApiContext: GeoApiContext): ArrayList { 26 | for (routeProvider in settings.followStreets) { 27 | if (routeProvider.isBanned()) { 28 | continue 29 | } 30 | var error: String? 31 | try { 32 | if (routeProvider == RouteProviderEnum.GOOGLE) { 33 | return routeProvider.getRoute(startLat, startLong, endLat, endLong, geoApiContext) 34 | } else { 35 | val url = routeProvider.createURLString(startLat, startLong, endLat, endLong, routeProvider.getApiKey(settings)) 36 | val request = Request.Builder().url(url).header("User-Agent", userAgent).build() 37 | val response = OkHttpClient().newCall(request).execute() 38 | val responseBody = response.body().string() 39 | if (responseBody.length > 0) { 40 | val coordinates = routeProvider.parseRouteResponse(responseBody) 41 | if (coordinates.isNotEmpty()) { 42 | routeProvider.banTime = 0 // everything is ok, reset the bantime 43 | Log.normal("[Route] Got route coordinates from $routeProvider (API KEY: ${routeProvider.usingApiKey(settings)})") 44 | return coordinates 45 | } 46 | } 47 | } 48 | error = "response is not valid or empty" 49 | } catch (e: Exception) { 50 | error = e.message 51 | } finally { 52 | routeProvider.lastTry = Calendar.getInstance() 53 | } 54 | routeProvider.banMe() 55 | Log.red("[Route] Error from $routeProvider: $error (banned for ${routeProvider.banTime} min) (API KEY: ${routeProvider.usingApiKey(settings)})") 56 | } 57 | Log.red("[Route] No more route providers, go directly to pokestops/waypoints") 58 | return ArrayList() 59 | } 60 | 61 | fun getRouteCoordinates(start: S2LatLng, end: S2LatLng, settings: Settings, geoApiContext: GeoApiContext): ArrayList { 62 | return getRouteCoordinates(start.latDegrees(), start.lngDegrees(), end.latDegrees(), end.lngDegrees(), settings, geoApiContext) 63 | } 64 | 65 | fun isValidRouteProvider(routeName: String): Boolean { 66 | try { 67 | RouteProviderEnum.valueOf(routeName) 68 | return true 69 | } catch (e: IllegalArgumentException) { 70 | return false 71 | } 72 | } 73 | 74 | fun getAltitude(latitude: Double, longitude: Double, ctx: Context): Double { 75 | val rand = (Math.random() * 3) + 1 76 | val cellId = S2CellId.fromLatLng(S2LatLng.fromDegrees(latitude, longitude)).parent(15).id().toString() 77 | var elevation = 10.0 78 | var foundEle = false 79 | 80 | if (ctx.s2Cache.containsKey(cellId) && ctx.s2Cache[cellId] != null) { 81 | return ctx.s2Cache[cellId]!! + rand 82 | } 83 | 84 | try { 85 | val url = HttpUrl.parse("https://maps.googleapis.com/maps/api/elevation/json?locations=$latitude,$longitude&sensor=true").newBuilder().build() 86 | val request = Request.Builder().url(url).build() 87 | val result: Map<*, *> 88 | result = ObjectMapper().readValue(OkHttpClient().newCall(request).execute().body().string(), Map::class.java) 89 | val results = result["results"] as List<*> 90 | val firstResult = results[0] as Map<*, *> 91 | elevation = firstResult["elevation"].toString().toDouble() 92 | foundEle = true 93 | ctx.s2Cache[cellId] = elevation 94 | } catch(ex: Exception) { 95 | val url = HttpUrl.parse("https://elevation.mapzen.com/height?json={\"shape\":[{\"lat\":$latitude,\"lon\":$longitude}]}").newBuilder().build() 96 | val request = Request.Builder().url(url).build() 97 | 98 | try { 99 | val result: Map<*, *> 100 | result = ObjectMapper().readValue(OkHttpClient().newCall(request).execute().body().string(), Map::class.java) 101 | elevation = result["height"].toString().replace("[^\\d\\-]".toRegex(), "").toDouble() 102 | foundEle = true 103 | ctx.s2Cache[cellId] = elevation 104 | } catch (exi: Exception) { 105 | Log.red("Can't get elevation, using ${elevation + rand}...") 106 | } 107 | } 108 | 109 | if (foundEle) { 110 | val inp = java.io.RandomAccessFile("altitude_cache.json", "rw") 111 | try { 112 | val lock = inp.channel.lock() 113 | try { 114 | var altitudeReloadStr = "" 115 | val by = ByteArray(inp.length().toInt()) 116 | inp.readFully(by) 117 | for (byt in by) { 118 | altitudeReloadStr += byt.toChar().toString() 119 | } 120 | inp.setLength(0) 121 | val altitudeReload: MutableMap = 122 | try { 123 | @Suppress("UNCHECKED_CAST") 124 | (ObjectMapper().readValue(altitudeReloadStr, MutableMap::class.java) as MutableMap) 125 | } catch (ex: Exception) { 126 | mutableMapOf() 127 | } 128 | for ((s2CellId, ele) in altitudeReload) { 129 | ctx.s2Cache[s2CellId] = ele 130 | } 131 | Log.normal("Saving altitude cache file...") 132 | ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(inp, ctx.s2Cache) 133 | } finally { 134 | lock.release() 135 | } 136 | } finally { 137 | inp.close() 138 | } 139 | } 140 | 141 | return elevation + rand 142 | } 143 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/io/ExportCSVWriter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.io 10 | 11 | import java.io.FileOutputStream 12 | import java.io.OutputStreamWriter 13 | import java.io.PrintWriter 14 | import java.util.* 15 | 16 | class ExportCSVWriter(val filename: String = "export.csv", val delimiter: String = ",") { 17 | fun write(profile: Map, eggs: ArrayList>, items: ArrayList>, pokemons: ArrayList>) { 18 | // UTF-8 with BOM to fix borked UTF-8 chars in MS Excel (for nickname output) 19 | // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 20 | FileOutputStream(filename).use { 21 | it.write(239) 22 | it.write(187) 23 | it.write(191) 24 | val pw = PrintWriter(OutputStreamWriter(it, "UTF-8")) 25 | 26 | // Print Profile 27 | pw.println("Overview Profile") 28 | for ((key, value) in profile) { 29 | pw.println(createCSVLine(arrayOf(key, value).toSet(), delimiter)) 30 | } 31 | pw.println("") 32 | 33 | // Print Eggs 34 | pw.println("Overview Eggs") 35 | pw.println(createCSVLine(eggs.first().keys, delimiter)) 36 | 37 | for (egg in eggs) { 38 | pw.println(createCSVLine(egg.values, delimiter)) 39 | } 40 | pw.println("") 41 | 42 | // Print Items 43 | pw.println("Overview Items") 44 | pw.println(createCSVLine(items.first().keys, delimiter)) 45 | 46 | for (item in items) { 47 | pw.println(createCSVLine(item.values, delimiter)) 48 | } 49 | pw.println("") 50 | 51 | // Print pokemons 52 | pw.println("Overview Pokebank") 53 | pw.println(createCSVLine(pokemons.first().keys, delimiter)) 54 | 55 | for (pokemon in pokemons) { 56 | pw.println(createCSVLine(pokemon.values, delimiter)) 57 | } 58 | 59 | pw.close() 60 | } 61 | } 62 | 63 | private fun createCSVLine(line: Collection, delimiter: String): String { 64 | val sb = StringJoiner(delimiter) 65 | for (value in line) { 66 | var result = value 67 | 68 | // https://tools.ietf.org/html/rfc4180 69 | if (result.contains("\"")) { 70 | result = result.replace("\"", "\"\"") 71 | } 72 | 73 | sb.add(result) 74 | } 75 | 76 | return sb.toString() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/io/ExportJSONWriter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.io 10 | 11 | import com.fasterxml.jackson.databind.ObjectMapper 12 | import java.io.File 13 | import java.util.* 14 | 15 | class ExportJSONWriter(val filename: String = "export.json") { 16 | fun write(profile: Map, eggs: ArrayList>, items: ArrayList>, pokemons: ArrayList>) { 17 | val mapper = ObjectMapper() 18 | val export = JSON_export(profile, eggs, items, pokemons) 19 | 20 | mapper.writerWithDefaultPrettyPrinter().writeValue(File(filename), export) 21 | } 22 | } 23 | 24 | data class JSON_export( 25 | var profile: Map, 26 | var eggs: ArrayList>, 27 | var items: ArrayList>, 28 | var pokemons: ArrayList> 29 | ) 30 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/io/SettingsJSONWriter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.io 10 | 11 | import com.fasterxml.jackson.databind.ObjectMapper 12 | import com.fasterxml.jackson.module.kotlin.registerKotlinModule 13 | import ink.abb.pogo.scraper.Settings 14 | import ink.abb.pogo.scraper.util.Log 15 | import java.io.File 16 | 17 | class SettingsJSONWriter { 18 | val mapper: ObjectMapper = ObjectMapper().registerKotlinModule() 19 | val root = File("./bot-settings").absoluteFile!! 20 | 21 | init { 22 | root.mkdirs() 23 | } 24 | 25 | fun getJSONConfigBotNames(): List { 26 | return root.list().filter { it.endsWith(".json") }.map { it.replace(Regex("\\.json$"), "") } 27 | } 28 | 29 | fun save(settings: Settings) { 30 | Log.normal("Saving settings for ${settings.name}") 31 | File(root, "${settings.name}.json").bufferedWriter().use { 32 | it.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(settings)) 33 | } 34 | } 35 | 36 | fun load(name: String): Settings { 37 | val save = File(root, "$name.json") 38 | if (!save.isFile) { 39 | throw IllegalArgumentException("No save file found for name: $name") 40 | } 41 | 42 | return mapper.readValue(save, Settings::class.java).withName(name) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/logback/MarkerFilter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.logback 10 | 11 | import ch.qos.logback.classic.spi.ILoggingEvent 12 | import ch.qos.logback.core.filter.AbstractMatcherFilter 13 | import ch.qos.logback.core.spi.FilterReply 14 | import org.slf4j.Marker 15 | import org.slf4j.MarkerFactory 16 | 17 | class MarkerFilter : AbstractMatcherFilter() { 18 | private var markerToMatch: Marker? = null 19 | 20 | override fun start() { 21 | if (markerToMatch != null) 22 | super.start() 23 | else 24 | addError("no marker configured") 25 | } 26 | 27 | override fun decide(event: ILoggingEvent): FilterReply { 28 | val marker = event.marker 29 | if (!isStarted) 30 | return FilterReply.NEUTRAL 31 | if (marker == null) 32 | return onMismatch 33 | if (markerToMatch!!.contains(marker)) 34 | return onMatch 35 | return onMismatch 36 | } 37 | 38 | fun setMarker(markerStr: String?) { 39 | if (null != markerStr) 40 | markerToMatch = MarkerFactory.getMarker(markerStr) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/map/Fort.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.map 10 | 11 | import com.google.common.geometry.S2LatLng 12 | import ink.abb.pogo.api.cache.Fort 13 | import ink.abb.pogo.api.cache.Pokestop 14 | import ink.abb.pogo.api.request.FortSearch 15 | import rx.Observable 16 | 17 | fun Pokestop.canLoot(ignoreDistance: Boolean = false, lootTimeouts: Map): Boolean { 18 | val canLoot = lootTimeouts.getOrElse(id, { cooldownCompleteTimestampMs }) < poGoApi.currentTimeMillis() 19 | return (ignoreDistance || inRange(poGoApi.fortSettings.interactionRangeMeters)) && canLoot 20 | } 21 | 22 | fun Pokestop.inRange(maxDistance: Double): Boolean { 23 | return distance < maxDistance 24 | } 25 | 26 | fun Pokestop.inRangeForLuredPokemon(): Boolean { 27 | return distance < poGoApi.mapSettings.encounterRangeMeters 28 | } 29 | 30 | fun Pokestop.loot(): Observable { 31 | val loot: FortSearch = FortSearch().withFortId(fortData.id).withFortLatitude(fortData.latitude).withFortLongitude(fortData.longitude) 32 | return poGoApi.queueRequest(loot) 33 | } 34 | 35 | val Fort.distance: Double 36 | get() { 37 | val playerLocation = S2LatLng.fromDegrees(poGoApi.latitude, poGoApi.longitude) 38 | val fortLocation = S2LatLng.fromDegrees(fortData.latitude, fortData.longitude) 39 | return playerLocation.getEarthDistance(fortLocation) 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/pokemon/CatchablePokemon.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.pokemon 10 | 11 | import POGOProtos.Data.Capture.CaptureProbabilityOuterClass.CaptureProbability 12 | import POGOProtos.Inventory.Item.ItemIdOuterClass.ItemId 13 | import POGOProtos.Networking.Responses.CatchPokemonResponseOuterClass.CatchPokemonResponse.CatchStatus 14 | import ink.abb.pogo.api.cache.Inventory 15 | import ink.abb.pogo.api.cache.MapPokemon 16 | import ink.abb.pogo.api.request.CatchPokemon 17 | import ink.abb.pogo.api.request.UseItemCapture 18 | import ink.abb.pogo.scraper.util.Log 19 | import java.util.concurrent.atomic.AtomicInteger 20 | 21 | /** 22 | * Extension function to make the code more readable in the CatchOneNearbyPokemon task 23 | */ 24 | fun MapPokemon.catch(normalizedHitPosition: Double = 1.0, 25 | normalizedReticleSize: Double = 1.95 + Math.random() * 0.05, 26 | spinModifier: Double = 0.85 + Math.random() * 0.15, 27 | ballType: ItemId = ItemId.ITEM_POKE_BALL): rx.Observable { 28 | return poGoApi.queueRequest(CatchPokemon() 29 | .withEncounterId(encounterId) 30 | .withHitPokemon(true) 31 | .withNormalizedHitPosition(normalizedHitPosition) 32 | .withNormalizedReticleSize(normalizedReticleSize) 33 | .withPokeball(ballType) 34 | .withSpinModifier(spinModifier) 35 | .withSpawnPointId(spawnPointId)) 36 | } 37 | 38 | fun MapPokemon.catch(captureProbability: CaptureProbability, inventory: Inventory, desiredCatchProbability: Double, alwaysCurve: Boolean = false, allowBerries: Boolean = false, randomBallThrows: Boolean = false, waitBetweenThrows: Boolean = false, amount: Int): rx.Observable { 39 | var catch: rx.Observable 40 | var numThrows = 0 41 | do { 42 | catch = catch(captureProbability, inventory, desiredCatchProbability, alwaysCurve, allowBerries, randomBallThrows) 43 | val first = catch.toBlocking().first() 44 | if (first != null) { 45 | val result = first.response 46 | if (result.status != CatchStatus.CATCH_ESCAPE && result.status != CatchStatus.CATCH_MISSED) { 47 | break 48 | } 49 | 50 | if (waitBetweenThrows) { 51 | val waitTime = (Math.random() * 2900 + 100) 52 | Log.blue("Pokemon got out of the ball. Waiting for ca. ${Math.round(waitTime / 1000)} second(s) until next throw") 53 | Thread.sleep(waitTime.toLong()) 54 | } 55 | numThrows++ 56 | } 57 | } while (amount < 0 || numThrows < amount) 58 | 59 | return catch 60 | } 61 | 62 | fun MapPokemon.catch(captureProbability: CaptureProbability, inventory: Inventory, desiredCatchProbability: Double, alwaysCurve: Boolean = false, allowBerries: Boolean = false, randomBallThrows: Boolean = false): rx.Observable { 63 | val ballTypes = captureProbability.pokeballTypeList 64 | val probabilities = captureProbability.captureProbabilityList 65 | //Log.yellow(probabilities.toString()) 66 | var ball: ItemId? = null 67 | var needCurve = alwaysCurve 68 | var needRazzBerry = false 69 | var highestAvailable: ItemId? = null 70 | var catchProbability = 0f 71 | 72 | for ((index, ballType) in ballTypes.withIndex()) { 73 | val probability = probabilities.get(index) 74 | val ballAmount = inventory.items.getOrPut(ballType, { AtomicInteger(0) }).get() 75 | if (ballAmount == 0) { 76 | //Log.yellow("Don't have any ${ballType}") 77 | continue 78 | } else { 79 | //Log.yellow("Have ${ballAmount} of ${ballType}") 80 | highestAvailable = ballType 81 | catchProbability = probability 82 | } 83 | if (probability >= desiredCatchProbability) { 84 | catchProbability = probability 85 | ball = ballType 86 | break 87 | } else if (probability >= desiredCatchProbability - 0.1) { 88 | ball = ballType 89 | needCurve = true 90 | catchProbability = probability + 0.1f 91 | break 92 | } else if (probability >= desiredCatchProbability - 0.2) { 93 | ball = ballType 94 | needCurve = true 95 | needRazzBerry = true 96 | catchProbability = probability + 0.2f 97 | break 98 | } 99 | } 100 | 101 | if (highestAvailable == null) { 102 | /*Log.red("No pokeballs?!") 103 | Log.red("Has pokeballs: ${itemBag.hasPokeballs()}")*/ 104 | return rx.Observable.just(null) 105 | } 106 | 107 | if (ball == null) { 108 | ball = highestAvailable 109 | needCurve = true 110 | needRazzBerry = true 111 | catchProbability += 0.2f 112 | } 113 | 114 | var logMessage = "Using ${ball.name}" 115 | 116 | val razzBerryCount = inventory.items.getOrPut(ItemId.ITEM_RAZZ_BERRY, { AtomicInteger(0) }).get() 117 | if (allowBerries && razzBerryCount > 0 && needRazzBerry) { 118 | logMessage += "; Using Razz Berry" 119 | poGoApi.queueRequest(UseItemCapture().withEncounterId(encounterId).withItemId(ItemId.ITEM_RAZZ_BERRY).withSpawnPointId(spawnPointId)).toBlocking() 120 | } 121 | if (needCurve) { 122 | logMessage += "; Using curve" 123 | } 124 | logMessage += "; achieved catch probability: ${Math.round(catchProbability * 100.0)}%, desired: ${Math.round(desiredCatchProbability * 100.0)}%" 125 | Log.yellow(logMessage) 126 | //excellent throw value 127 | var recticleSize = 1.7 + Math.random() * 0.3 128 | 129 | if (randomBallThrows) { 130 | //excellent throw if capture probability is still less then desired 131 | if (catchProbability <= desiredCatchProbability) { 132 | // the recticle size is already set for an excelent throw 133 | } 134 | //if catch probability is too high... 135 | else { 136 | // we substract the difference from the recticle size, the lower this size, the worse the ball 137 | recticleSize = 1 + Math.random() - (catchProbability - desiredCatchProbability) * 0.5 138 | 139 | if (recticleSize > 2) { 140 | recticleSize = 2.0 141 | } else if (recticleSize < 0) { 142 | recticleSize = 0.01 143 | } 144 | 145 | if (recticleSize < 1) { 146 | Log.blue("Your trainer threw a normal ball, no xp/catching bonus, good for pretending to be not a bot however") 147 | } else if (recticleSize >= 1 && recticleSize < 1.3) { 148 | Log.blue("Your trainer got a 'Nice throw' - nice") 149 | } else if (recticleSize >= 1.3 && recticleSize < 1.7) { 150 | Log.blue("Your trainer got a 'Great throw!'") 151 | } else if (recticleSize > 1.7) { 152 | Log.blue("Your trainer got an 'Excellent throw!' - that's suspicious, might he be a bot?") 153 | } 154 | } 155 | } 156 | 157 | return catch( 158 | normalizedHitPosition = 1.0, 159 | normalizedReticleSize = recticleSize, 160 | spinModifier = if (needCurve) 0.85 + Math.random() * 0.15 else Math.random() * 0.10, 161 | ballType = ball 162 | ) 163 | } 164 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/pokemon/MapPokemon.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.pokemon 10 | 11 | import com.google.common.geometry.S2LatLng 12 | import ink.abb.pogo.api.cache.MapPokemon 13 | import ink.abb.pogo.api.network.ServerRequest 14 | import ink.abb.pogo.api.request.DiskEncounter 15 | import ink.abb.pogo.api.request.Encounter 16 | import rx.Observable 17 | 18 | fun MapPokemon.encounter(): Observable { 19 | val encounter: ServerRequest? = 20 | if (encounterKind == MapPokemon.EncounterKind.NORMAL) { 21 | Encounter().withEncounterId(encounterId).withSpawnPointId(spawnPointId) 22 | } else if (encounterKind == MapPokemon.EncounterKind.DISK) { 23 | DiskEncounter().withEncounterId(encounterId).withFortId(spawnPointId) 24 | } else { 25 | null 26 | } 27 | return poGoApi.queueRequest(encounter!!) 28 | } 29 | 30 | val MapPokemon.inRange: Boolean 31 | get() { 32 | return distance < poGoApi.mapSettings.encounterRangeMeters 33 | } 34 | 35 | 36 | val MapPokemon.distance: Double 37 | get() { 38 | val playerLocation = S2LatLng.fromDegrees(poGoApi.latitude, poGoApi.longitude) 39 | val fortLocation = S2LatLng.fromDegrees(latitude, longitude) 40 | return playerLocation.getEarthDistance(fortLocation) 41 | } 42 | -------------------------------------------------------------------------------- /src/main/kotlin/ink/abb/pogo/scraper/util/pokemon/PokemonData.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper.util.pokemon 10 | 11 | import POGOProtos.Data.PokemonDataOuterClass 12 | import POGOProtos.Data.PokemonDataOuterClass.PokemonData 13 | import POGOProtos.Enums.PokemonIdOuterClass 14 | import ink.abb.pogo.api.PoGoApi 15 | import ink.abb.pogo.api.util.PokemonCpUtils 16 | import ink.abb.pogo.api.util.PokemonMeta 17 | import ink.abb.pogo.api.util.PokemonMetaRegistry 18 | import ink.abb.pogo.scraper.Settings 19 | import java.util.concurrent.atomic.AtomicInteger 20 | 21 | fun PokemonDataOuterClass.PokemonDataOrBuilder.getIv(): Int { 22 | val iv = individualAttack + individualDefense + individualStamina 23 | return iv 24 | } 25 | 26 | fun PokemonDataOuterClass.PokemonDataOrBuilder.getIvPercentage(): Int { 27 | val iv = getIv() 28 | val ivPercentage = (iv * 100) / 45 29 | return ivPercentage 30 | } 31 | 32 | //fun PokemonData.getCpPercentageToPlayer(playerlevel: Int): Int { 33 | // // TODO replace this when api has implemented this see Pokemon.kt 34 | // return -1 35 | //} 36 | 37 | fun PokemonDataOuterClass.PokemonDataOrBuilder.getStatsFormatted(): String { 38 | val details = "Stamina: $individualStamina | Attack: $individualAttack | Defense: $individualDefense" 39 | return details + " | IV: ${getIv()} (${(getIvPercentage())}%)" 40 | } 41 | 42 | val PokemonDataOuterClass.PokemonDataOrBuilder.maxCpForPlayer: Int 43 | // TODO!!! 44 | get() = 0 45 | 46 | fun PokemonDataOuterClass.PokemonDataOrBuilder.getCpPercentageToPlayer(): Int { 47 | return (cp.toDouble() / maxCpForPlayer.toDouble() * 100).toInt() 48 | } 49 | 50 | fun PokemonDataOuterClass.PokemonDataOrBuilder.shouldTransfer(settings: Settings, pokemonCounts: MutableMap, candies: AtomicInteger): Pair { 51 | val obligatoryTransfer = settings.obligatoryTransfer 52 | val ignoredPokemon = settings.ignoredPokemon 53 | val ivPercentage = getIvPercentage() 54 | val minIVPercentage = settings.transferIvThreshold 55 | val minCP = settings.transferCpThreshold 56 | val minCpPercentage = settings.transferCpMinThreshold 57 | 58 | var shouldRelease = obligatoryTransfer.contains(this.pokemonId) 59 | var reason: String = "Obligatory transfer" 60 | if (!ignoredPokemon.contains(this.pokemonId)) { 61 | // shouldn't release? check for IV/CP 62 | if (!shouldRelease) { 63 | var ivTooLow = false 64 | var cpTooLow = false 65 | var maxCpInRange = false 66 | 67 | // never transfer > min IV percentage (unless set to -1) 68 | if (ivPercentage < minIVPercentage || minIVPercentage == -1) { 69 | ivTooLow = true 70 | } 71 | // never transfer > min CP (unless set to -1) 72 | if (this.cp < minCP || minCP == -1) { 73 | cpTooLow = true 74 | } 75 | reason = "CP < $minCP and IV < $minIVPercentage%" 76 | 77 | if (minCpPercentage != -1 && minCpPercentage >= getCpPercentageToPlayer()) { 78 | maxCpInRange = true 79 | reason += " and CP max $maxCpForPlayer: achieved ${getCpPercentageToPlayer()}% <= $minCpPercentage%" 80 | } 81 | shouldRelease = ivTooLow && cpTooLow && (maxCpInRange || minCpPercentage == -1) 82 | } 83 | 84 | // still shouldn't release? Check if we have too many 85 | val max = settings.maxPokemonAmount 86 | val name = this.pokemonId.name 87 | val count = pokemonCounts.getOrElse(name, { 0 }) + 1 88 | pokemonCounts.put(name, count) 89 | 90 | if (!shouldRelease && max != -1 && count > max) { 91 | shouldRelease = true 92 | reason = "Too many" 93 | } 94 | // Save pokemon for evolve stacking 95 | val candyToEvolve = PokemonMetaRegistry.getMeta(this.pokemonId).candyToEvolve 96 | if (shouldRelease && settings.evolveBeforeTransfer.contains(this.pokemonId) && settings.evolveStackLimit > 0) { 97 | val maxToMaintain = candies.get() / candyToEvolve; 98 | if (candyToEvolve > 0 && count > maxToMaintain) { 99 | shouldRelease = true 100 | reason = "Not enough candy ${candies.get()}/$candyToEvolve: max $maxToMaintain" 101 | } else { 102 | shouldRelease = false 103 | } 104 | } 105 | } 106 | return Pair(shouldRelease, reason) 107 | } 108 | 109 | fun PokemonDataOuterClass.PokemonDataOrBuilder.eggKmWalked(poGoApi: PoGoApi): Double { 110 | if (!incubated) { 111 | return 0.0 112 | } 113 | val incubators = poGoApi.inventory.eggIncubators.map { it.value }.filter { 114 | it.id == eggIncubatorId 115 | } 116 | 117 | if (incubators.isNotEmpty()) { 118 | val incubator = incubators.first() 119 | return eggKmWalkedTarget - (incubator.targetKmWalked - poGoApi.inventory.playerStats.kmWalked) 120 | } else { 121 | return 0.0 122 | } 123 | } 124 | 125 | val PokemonDataOuterClass.PokemonDataOrBuilder.incubated: Boolean 126 | get() { 127 | return eggIncubatorId.isNotBlank() 128 | } 129 | 130 | val PokemonDataOuterClass.PokemonDataOrBuilder.injured: Boolean 131 | get() { 132 | return !fainted && stamina < staminaMax 133 | } 134 | 135 | val PokemonDataOuterClass.PokemonDataOrBuilder.fainted: Boolean 136 | get() { 137 | return stamina == 0 138 | } 139 | 140 | val PokemonDataOuterClass.PokemonDataOrBuilder.meta: PokemonMeta 141 | get() = PokemonMetaRegistry.getMeta(this.getPokemonId()) 142 | 143 | val PokemonDataOuterClass.PokemonDataOrBuilder.maxCp: Int 144 | get() { 145 | val pokemonMeta = meta 146 | val attack = getIndividualAttack() + pokemonMeta.baseAttack 147 | val defense = getIndividualDefense() + pokemonMeta.baseDefense 148 | val stamina = getIndividualStamina() + pokemonMeta.baseStamina 149 | return PokemonCpUtils.getMaxCp(attack, defense, stamina) 150 | } 151 | 152 | fun PokemonDataOuterClass.PokemonDataOrBuilder.getMaxCpForLevel(level: Int): Int { 153 | val pokemonMeta = meta 154 | val attack = getIndividualAttack() + pokemonMeta.baseAttack 155 | val defense = getIndividualDefense() + pokemonMeta.baseDefense 156 | val stamina = getIndividualStamina() + pokemonMeta.baseStamina 157 | val playerLevel = level 158 | return PokemonCpUtils.getMaxCpForPlayer(attack, defense, stamina, playerLevel) 159 | } 160 | 161 | val PokemonDataOuterClass.PokemonDataOrBuilder.absoluteMaxCp: Int 162 | get() = PokemonCpUtils.getAbsoluteMaxCp(pokemonId) 163 | 164 | val PokemonDataOuterClass.PokemonDataOrBuilder.cpFullEvolveAndPowerup: Int 165 | get() = getMaxCpFullEvolveAndPowerup(40) 166 | 167 | fun PokemonDataOuterClass.PokemonDataOrBuilder.getMaxCpFullEvolveAndPowerupForLevel(level: Int): Int { 168 | return getMaxCpFullEvolveAndPowerup(level) 169 | } 170 | 171 | private fun PokemonDataOuterClass.PokemonDataOrBuilder.getMaxCpFullEvolveAndPowerup(playerLevel: Int): Int { 172 | val highestUpgradedFamily: PokemonIdOuterClass.PokemonId 173 | if (arrayListOf(PokemonIdOuterClass.PokemonId.VAPOREON, PokemonIdOuterClass.PokemonId.JOLTEON, PokemonIdOuterClass.PokemonId.FLAREON).contains(pokemonId)) { 174 | highestUpgradedFamily = pokemonId 175 | } else if (pokemonId === PokemonIdOuterClass.PokemonId.EEVEE) { 176 | highestUpgradedFamily = PokemonIdOuterClass.PokemonId.FLAREON 177 | } else { 178 | highestUpgradedFamily = PokemonMetaRegistry.getHighestForFamily(meta.family) 179 | } 180 | val pokemonMeta = PokemonMetaRegistry.getMeta(highestUpgradedFamily) 181 | val attack = individualAttack + pokemonMeta.baseAttack 182 | val defense = individualDefense + pokemonMeta.baseDefense 183 | val stamina = individualStamina + pokemonMeta.baseStamina 184 | return PokemonCpUtils.getMaxCpForPlayer(attack, defense, stamina, playerLevel) 185 | } 186 | 187 | val PokemonDataOuterClass.PokemonDataOrBuilder.combinedCpMultiplier: Float 188 | get() = cpMultiplier + additionalCpMultiplier 189 | 190 | val PokemonDataOuterClass.PokemonDataOrBuilder.cpAfterEvolve: Int 191 | get() { 192 | if (arrayListOf(PokemonIdOuterClass.PokemonId.VAPOREON, PokemonIdOuterClass.PokemonId.JOLTEON, PokemonIdOuterClass.PokemonId.FLAREON).contains(pokemonId)) { 193 | return cp 194 | } 195 | val highestUpgradedFamily = PokemonMetaRegistry.getHighestForFamily(meta.family) 196 | if (pokemonId === highestUpgradedFamily) { 197 | return cp 198 | } 199 | var pokemonMeta = PokemonMetaRegistry.getMeta(highestUpgradedFamily) 200 | val secondHighest = pokemonMeta.parentId 201 | if (getPokemonId() === secondHighest) { 202 | val attack = individualAttack + pokemonMeta.baseAttack 203 | val defense = individualDefense + pokemonMeta.baseDefense 204 | val stamina = individualStamina + pokemonMeta.baseStamina 205 | return PokemonCpUtils.getCp(attack, defense, stamina, combinedCpMultiplier) 206 | } 207 | pokemonMeta = PokemonMetaRegistry.getMeta(secondHighest) 208 | val attack = individualAttack + pokemonMeta.baseAttack 209 | val defense = individualDefense + pokemonMeta.baseDefense 210 | val stamina = individualStamina + pokemonMeta.baseStamina 211 | return PokemonCpUtils.getCp(attack, defense, stamina, combinedCpMultiplier) 212 | } 213 | 214 | val PokemonDataOuterClass.PokemonDataOrBuilder.cpAfterFullEvolve: Int 215 | get() { 216 | if (arrayListOf(PokemonIdOuterClass.PokemonId.VAPOREON, PokemonIdOuterClass.PokemonId.JOLTEON, PokemonIdOuterClass.PokemonId.FLAREON).contains(pokemonId)) { 217 | return cp 218 | } 219 | val highestUpgradedFamily = PokemonMetaRegistry.getHighestForFamily(meta.family) 220 | if (pokemonId === highestUpgradedFamily) { 221 | return cp 222 | } 223 | 224 | val pokemonMeta = PokemonMetaRegistry.getMeta(highestUpgradedFamily) 225 | val attack = individualAttack + pokemonMeta.baseAttack 226 | val defense = individualDefense + pokemonMeta.baseDefense 227 | val stamina = individualStamina + pokemonMeta.baseStamina 228 | return PokemonCpUtils.getCp(attack, defense, stamina, combinedCpMultiplier) 229 | } 230 | 231 | val PokemonDataOuterClass.PokemonDataOrBuilder.cpAfterPowerup: Int 232 | get() = PokemonCpUtils.getCpAfterPowerup(cp, combinedCpMultiplier) 233 | 234 | val PokemonDataOuterClass.PokemonDataOrBuilder.candyCostsForPowerup: Int 235 | get() = PokemonCpUtils.getCandyCostsForPowerup(combinedCpMultiplier, numUpgrades) 236 | 237 | val PokemonDataOuterClass.PokemonDataOrBuilder.stardustCostsForPowerup: Int 238 | get() = PokemonCpUtils.getStartdustCostsForPowerup(combinedCpMultiplier, numUpgrades) 239 | 240 | val PokemonDataOuterClass.PokemonDataOrBuilder.level: Float 241 | get() = PokemonCpUtils.getLevelFromCpMultiplier(combinedCpMultiplier) 242 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=PokemonGoBot 2 | server.port=8080 3 | logging.level.*=ERROR 4 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ,'\ 2 | _.----. ____ ,' _\ ___ ___ ____ ;-. , 3 | _,-' `. | | /`. \,-' | \ / | | \ |`. \ '. .'/ 4 | \ __ \ '-. | / `. ___ | \/ | '-. \ | | \ \ .---. .-' / 5 | \. \ \ | __ | |/ ,','_ `. | | __ | \| | '. ' `\_.' 6 | \ \/ /,' _`.| ,' / / / / | ,' _`.| | | |(),() | , 7 | \ ,-'/ / \ ,' | \/ / ,`.| / / \ | | ( __ / .' \ 8 | \ \ | \_/ | `-. \ `' /| | || \_/ | |\ | .''.___.'--,/\_,| 9 | \ \ \ / `-.`.___,-' | |\ /| \ / | | | { / \ } | 10 | \ \ `.__,'| |`-._ `| |__| \/ | `.__,'| | | | '.\ /_.' / 11 | \_.-' |__| `-._ | '-.| '-.| | | |'-.-', `; _.' 12 | `' '-._| | | | |` 13 | `""`""`"""` -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | black 7 | DENY 8 | ACCEPT 9 | 10 | 11 | %black(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 12 | 13 | 14 | 15 | 16 | 17 | red 18 | DENY 19 | ACCEPT 20 | 21 | 22 | %red(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 23 | 24 | 25 | 26 | 27 | 28 | green 29 | DENY 30 | ACCEPT 31 | 32 | 33 | %green(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 34 | 35 | 36 | 37 | 38 | 39 | yellow 40 | DENY 41 | ACCEPT 42 | 43 | 44 | %yellow(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 45 | 46 | 47 | 48 | 49 | 50 | blue 51 | DENY 52 | ACCEPT 53 | 54 | 55 | %blue(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 56 | 57 | 58 | 59 | 60 | 61 | magenta 62 | DENY 63 | ACCEPT 64 | 65 | 66 | %magenta(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 67 | 68 | 69 | 70 | 71 | 72 | cyan 73 | DENY 74 | ACCEPT 75 | 76 | 77 | %cyan(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 78 | 79 | 80 | 81 | 82 | 83 | white 84 | DENY 85 | ACCEPT 86 | 87 | 88 | %white(%d{dd MMM HH:mm:ss} [%thread] - %msg%n) 89 | 90 | 91 | 92 | 93 | logs/bot.log 94 | 95 | logs/bot.log.%d{yyyy-MM-dd}.%i 96 | 98 | 50MB 99 | 100 | 101 | 102 | %d{dd MMM HH:mm:ss} [%thread] - %msg%n 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /src/test/kotlin/ink/abb/pogo/scraper/TestSettings.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) 3 | * This program comes with ABSOLUTELY NO WARRANTY; 4 | * This is free software, and you are welcome to redistribute it under certain conditions. 5 | * 6 | * For more information, refer to the LICENSE file in this repositories root directory 7 | */ 8 | 9 | package ink.abb.pogo.scraper 10 | 11 | import com.fasterxml.jackson.databind.ObjectMapper 12 | import com.fasterxml.jackson.module.kotlin.registerKotlinModule 13 | import ink.abb.pogo.scraper.util.camelToUnderscores 14 | import ink.abb.pogo.scraper.util.underscoreToCamel 15 | import ink.abb.pogo.scraper.util.credentials.* 16 | import org.junit.Assert 17 | import org.junit.Test 18 | import java.io.File 19 | import java.io.FileInputStream 20 | import java.util.* 21 | import java.util.regex.Pattern 22 | import kotlin.reflect.memberProperties 23 | 24 | class TestSettings { 25 | 26 | @Test 27 | fun testDefaultSettings() { 28 | val properties = Properties() 29 | FileInputStream("config.properties.template").use { 30 | properties.load(it) 31 | } 32 | 33 | properties.setProperty("latitude", "0.0") 34 | properties.setProperty("longitude", "0.0") 35 | val settingsFromTemplate = SettingsParser(properties).createSettingsFromProperties() 36 | val settingsFromJsonTemplate = ObjectMapper().registerKotlinModule().readValue(File("json-template.json"), Settings::class.java).withName("default") 37 | val settingsFromCode = Settings(name = "default", latitude = 0.0, longitude = 0.0, credentials = GoogleCredentials()) 38 | val settingsFromCodeForJson = Settings(name = "default", latitude = 0.0, longitude = 0.0, credentials = PtcCredentials()) 39 | 40 | Assert.assertEquals(settingsFromCode.toString().split(",").joinToString("\n"), settingsFromTemplate.toString().split(",").joinToString("\n")) 41 | Assert.assertEquals(settingsFromCodeForJson.toString().split(",").joinToString("\n"), settingsFromJsonTemplate.toString().split(",").joinToString("\n")) 42 | } 43 | 44 | @Test 45 | fun testTemplateKeys() { 46 | // version should not be contained in the template 47 | val propertyBlacklist = setOf( 48 | // automatically injected; user shouldn't set it 49 | "version", 50 | // TODO: need to actually test these values 51 | "item_revive", 52 | "item_max_revive", 53 | "item_potion", 54 | "item_super_potion", 55 | "item_hyper_potion", 56 | "item_max_potion", 57 | "item_poke_ball", 58 | "item_great_ball", 59 | "item_ultra_ball", 60 | "item_master_ball", 61 | "item_razz_berry", 62 | "item_lucky_egg", 63 | "item_incense", 64 | "item_lure_module", 65 | "uselessItems", 66 | // Bot immediately converts this and stores it in password 67 | "base64_password", 68 | // Bot throws this away 69 | "token", 70 | "password", 71 | "username", 72 | "credentials", 73 | // Bot combines this in its own property 74 | "startingLocation", 75 | // only in JSON config 76 | "name", 77 | "type", 78 | "ITEM_REVIVE", 79 | "ITEM_MAX_REVIVE", 80 | "ITEM_SUPER_POTION", 81 | "ITEM_HYPER_POTION", 82 | "ITEM_MAX_POTION", 83 | "ITEM_POKE_BALL", 84 | "ITEM_GREAT_BALL", 85 | "ITEM_ULTRA_BALL", 86 | "ITEM_MASTER_BALL", 87 | "ITEM_RAZZ_BERRY", 88 | "ITEM_LUCKY_EGG", 89 | "ITEM_INCENSE_ORDINARY", 90 | "ITEM_TROY_DISK", 91 | "ITEM_POTION" 92 | ) 93 | 94 | val properties = Properties() 95 | FileInputStream("config.properties.template").use { 96 | properties.load(it) 97 | } 98 | val memberNames = Settings::class.memberProperties.filter { !propertyBlacklist.contains(it.name) }.map { it.name } 99 | val propertyNames = properties.keys.map { it.toString() }.filter { !propertyBlacklist.contains(it) } 100 | propertyNames.forEach { 101 | val templateName = it 102 | val settingsName = templateName.underscoreToCamel() 103 | Assert.assertTrue("$templateName set in template, $settingsName not found in Settings", memberNames.contains(settingsName)) 104 | } 105 | memberNames.forEach { 106 | val settingsName = it 107 | val templateName = settingsName.camelToUnderscores() 108 | Assert.assertNotNull("$settingsName set in Settings, $templateName not found in template", properties.get(templateName)) 109 | } 110 | 111 | val jsonkeys = ArrayList() 112 | val regex = "\"([\\w]*)\"\\s:\\s" 113 | val pattern = Pattern.compile(regex) 114 | File("json-template.json").forEachLine { 115 | val matcher = pattern.matcher(it) 116 | while (matcher.find()) { 117 | jsonkeys.add(matcher.group(1)) 118 | } 119 | } 120 | val jsonNames = jsonkeys.filter { !propertyBlacklist.contains(it) } 121 | jsonNames.forEach { 122 | Assert.assertTrue("$it set in json template, $it not found in Settings", memberNames.contains(it)) 123 | } 124 | memberNames.forEach { 125 | Assert.assertTrue("$it set in Settings, $it not found in json template", jsonkeys.contains(it)) 126 | } 127 | } 128 | 129 | } 130 | --------------------------------------------------------------------------------