diff --git a/config.properties.template b/config.properties.template index 8b8467af1..8bb094f30 100644 --- a/config.properties.template +++ b/config.properties.template @@ -179,6 +179,12 @@ use_lucky_egg=1 # Port where the socketserver should listen, 0 = do not listen gui_port_socket=8001 +# Allow sniping (Recommended value: false) +want_sniping false + +# Port where the sniping information is sent to +sniping_port=16969 + # Export player/pokemondata on Profile Update. Options are: # *empty* Don't export (default) # CSV Correct USA/UK CSV format ("," as delimiter and "." as decimal separator) diff --git a/json-template.json b/json-template.json index 41c42fc98..0037dfdd0 100644 --- a/json-template.json +++ b/json-template.json @@ -67,6 +67,7 @@ "useLuckyEgg" : 1, "export" : "", "guiPortSocket" : 8001, + "snipingPort": 16969, "initialMapSize" : 9, "waitChance" : 0.0, "waitTimeMin" : 0, diff --git a/src/main/kotlin/ink/abb/pogo/scraper/Bot.kt b/src/main/kotlin/ink/abb/pogo/scraper/Bot.kt index cf6337911..0d8b71bd8 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/Bot.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/Bot.kt @@ -86,7 +86,7 @@ class Bot(val api: PokemonGo, val settings: Settings) { "Have ${it.pokemonId.name} (${it.nickname}) with ${it.cp} CP and IV $IV% \r\n ${it.getStatsFormatted()}" }.forEach { Log.normal(it) } - val keepalive = GetMapRandomDirection() + val keepalive = GetMapRandomDirection(isForSniping=false) val drop = DropUselessItems() val profile = UpdateProfile() val catch = CatchOneNearbyPokemon() @@ -94,6 +94,7 @@ class Bot(val api: PokemonGo, val settings: Settings) { val evolve = EvolvePokemon() val hatchEggs = HatchEggs() val export = Export() + val sniper = SnipeListener() if (settings.export.length > 0) task(export) @@ -156,7 +157,6 @@ class Bot(val api: PokemonGo, val settings: Settings) { task(drop) if (settings.autotransfer) task(release) - } runLoop(500, "PokestopLoop") { @@ -166,6 +166,8 @@ class Bot(val api: PokemonGo, val settings: Settings) { task(WalkToStartPokestop(process.startPokestop as Pokestop)) } + + Log.setContext(ctx) if (settings.guiPortSocket > 0) { @@ -178,6 +180,10 @@ class Bot(val api: PokemonGo, val settings: Settings) { Log.green("Open the map on http://ui.pogobot.club/") } + if (settings.wantSniping && settings.snipingPort > 0) { + task(sniper) + Log.normal("Listening for snipe info on port ${settings.snipingPort}") + } if (settings.timerWalkToStartPokestop > 0) runLoop(TimeUnit.SECONDS.toMillis(settings.timerWalkToStartPokestop), "BotWalkBackLoop") { diff --git a/src/main/kotlin/ink/abb/pogo/scraper/Context.kt b/src/main/kotlin/ink/abb/pogo/scraper/Context.kt index 930829a14..5df5d9be0 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/Context.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/Context.kt @@ -36,6 +36,7 @@ data class Context( val walking: AtomicBoolean = AtomicBoolean(false), - val pauseWalking: AtomicBoolean = AtomicBoolean(false) + val pauseWalking: AtomicBoolean = AtomicBoolean(false), + val pauseForSniping: AtomicBoolean = AtomicBoolean(false) ) diff --git a/src/main/kotlin/ink/abb/pogo/scraper/Settings.kt b/src/main/kotlin/ink/abb/pogo/scraper/Settings.kt index 6da7a6ce3..44675173a 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/Settings.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/Settings.kt @@ -123,6 +123,10 @@ class SettingsParser(val properties: Properties) { guiPortSocket = getPropertyIfSet("Port where the socketserver should listen", "gui_port_socket", defaults.guiPortSocket, String::toInt), + wantSniping = getPropertyIfSet("Enable or disable the sniping feature", "want_sniping", defaults.wantSniping, String::toBoolean), + + snipingPort = getPropertyIfSet("Port where the sniping information will be sent to", "sniping_port", defaults.snipingPort, String::toInt), + initialMapSize = getPropertyIfSet("Initial map size (S2 tiles) to fetch", "initial_map_size", defaults.initialMapSize, String::toInt), waitChance = getPropertyIfSet("Chance to wait on a pokestop", "wait_chance", defaults.waitChance, String::toDouble), @@ -256,6 +260,9 @@ data class Settings( val guiPortSocket: Int = 8001, + val wantSniping: Boolean = false, + val snipingPort: Int = 16969, + var initialMapSize: Int = 9, val version: String = Settings.version, diff --git a/src/main/kotlin/ink/abb/pogo/scraper/Values.kt b/src/main/kotlin/ink/abb/pogo/scraper/Values.kt index 14998fad8..a584ebfb9 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/Values.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/Values.kt @@ -10,4 +10,21 @@ package ink.abb.pogo.scraper val requiredXp = arrayOf(0, 1000, 3000, 6000, 10000, 15000, 21000, 28000, 36000, 45000, 55000, 65000, 75000, 85000, 100000, 120000, 140000, 160000, 185000, 210000, 260000, 335000, 435000, 560000, 710000, 900000, 1100000, - 1350000, 1650000, 2000000, 2500000, 3000000, 3750000, 4750000, 6000000, 7500000, 9500000, 12000000, 15000000, 20000000) \ No newline at end of file + 1350000, 1650000, 2000000, 2500000, 3000000, 3750000, 4750000, 6000000, 7500000, 9500000, 12000000, 15000000, 20000000) + +val pokedexId2Names = arrayOf("missingno", "bulbasaur", "ivysaur", "venosaur", "charmander", "charmeleon", "charizard", "squirtle", "wartortle", "blastoise", + "caterpie", "metapod", "butterfree", "weedle", "kakuna", "beedrill", "pidgey", "pidgeotto", "pidgeot", "rattata", + "raticate", "spearow", "fearow", "ekans", "arbok", "pikachu", "raichu", "sandshrew", "sandslash", "nidoran", + "nidorina", "nidoqueen", "nidoran", "nidorino", "nidoking", "clefairy", "clefable", "vulpix", "ninetales", "ninetails", + "jigglypuff", "wigglytuff", "zubat", "golbat", "oddish", "gloom", "vileplume", "paras", "parasect", "venonat", + "venomoth", "diglett", "dugtrio", "meowth", "persian", "psyduck", "golduck", "mankey", "primeape", "growlithe", + "arcanine", "poliwag", "poliwhirl", "poliwrath", "abra", "kadabra", "alakazam", "machop", "machoke", "machamp", + "bellsprout", "weepinbell", "victreebell", "tentacool", "tentacruel", "geodude", "graveler", "golem", "ponyta", + "rapidash", "slowpoke", "slowbro", "magnemite", "magneton", "farfetch", "doduo", "dodrio", "seel", "dewgong", + "grimer", "muk", "shellder", "cloyster", "gastly", "haunter", "gengar", "onix", "drowzee", "hypno", "krabby", + "kingler", "voltorb", "electrode", "exeggcute", "exeggutor", "cubone", "marowak", "hitmonlee", "hitmonchan", + "lickitung", "koffing", "weezing", "rhyhorn", "rhydon", "chansey", "tangela", "kangaskhan", "horsea", "seadra", + "goldeen", "seaking", "staryu", "starmie", "mime", "scyther", "jynx", "electrabuzz", "magmar", "pinsir", "tauros", + "magikarp", "gyarados", "lapras", "ditto", "eevee", "vaporeon", "jolteon", "flareon", "porygon", "omanyte", "omastar", + "kabuto", "kabutops", "aerodactyl", "snorlax", "articuno", "zapdos", "moltres", "dratini", "dragonair", "dragonite", + "mewtwo", "mew", "venusaur") diff --git a/src/main/kotlin/ink/abb/pogo/scraper/tasks/CatchOneNearbyPokemon.kt b/src/main/kotlin/ink/abb/pogo/scraper/tasks/CatchOneNearbyPokemon.kt index aab95a63b..e8751b6a5 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/tasks/CatchOneNearbyPokemon.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/tasks/CatchOneNearbyPokemon.kt @@ -28,6 +28,9 @@ import ink.abb.pogo.scraper.util.pokemon.shouldTransfer class CatchOneNearbyPokemon : Task { override fun run(bot: Bot, ctx: Context, settings: Settings) { // STOP WALKING + if (ctx.pauseForSniping.get()) { + return + } ctx.pauseWalking.set(true) val pokemon = ctx.api.map.getCatchablePokemon(ctx.blacklistedEncounters) diff --git a/src/main/kotlin/ink/abb/pogo/scraper/tasks/GetMapRandomDirection.kt b/src/main/kotlin/ink/abb/pogo/scraper/tasks/GetMapRandomDirection.kt index 90390faf1..9cc593cd6 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/tasks/GetMapRandomDirection.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/tasks/GetMapRandomDirection.kt @@ -14,9 +14,14 @@ import ink.abb.pogo.scraper.Settings import ink.abb.pogo.scraper.Task import ink.abb.pogo.scraper.util.Log -class GetMapRandomDirection : Task { +class GetMapRandomDirection(val isForSniping: Boolean) : Task { override fun run(bot: Bot, ctx: Context, settings: Settings) { // query a small area to keep alive + + if (ctx.pauseForSniping.get() && !isForSniping) { + return + } + val lat = ctx.lat.get() + randomLatLng() val lng = ctx.lng.get() + randomLatLng() diff --git a/src/main/kotlin/ink/abb/pogo/scraper/tasks/ProcessPokestops.kt b/src/main/kotlin/ink/abb/pogo/scraper/tasks/ProcessPokestops.kt index c8349bf54..97208eaa9 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/tasks/ProcessPokestops.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/tasks/ProcessPokestops.kt @@ -29,6 +29,10 @@ class ProcessPokestops(var pokestops: MutableCollection) : Task { var startPokestop: Pokestop? = null override fun run(bot: Bot, ctx: Context, settings: Settings) { + if (ctx.pauseForSniping.get()) { + return + } + var writeCampStatus = false if (lastFetch + refetchTime < bot.api.currentTimeMillis()) { writeCampStatus = true diff --git a/src/main/kotlin/ink/abb/pogo/scraper/tasks/SnipeListener.kt b/src/main/kotlin/ink/abb/pogo/scraper/tasks/SnipeListener.kt new file mode 100644 index 000000000..35c8a5cd3 --- /dev/null +++ b/src/main/kotlin/ink/abb/pogo/scraper/tasks/SnipeListener.kt @@ -0,0 +1,60 @@ +/** + * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) + * This program comes with ABSOLUTELY NO WARRANTY; + * This is free software, and you are welcome to redistribute it under certain conditions. + * + * For more information, refer to the LICENSE file in this repositories root directory + */ + +package ink.abb.pogo.scraper.tasks + +import ink.abb.pogo.scraper.Bot +import ink.abb.pogo.scraper.Context +import ink.abb.pogo.scraper.Settings +import ink.abb.pogo.scraper.Task +import ink.abb.pogo.scraper.pokedexId2Names +import kotlin.collections.Map +import java.io.* +import java.net.* +import java.util.ArrayList +import com.fasterxml.jackson.databind.* +import ink.abb.pogo.scraper.util.Log + +class SnipeListener : Task { + override fun run(bot: Bot, ctx: Context, settings: Settings) { + val clientSocket = Socket("localhost", settings.snipingPort) + + while (true) { + val dataReceived: BufferedReader + val multipleSnipes = ArrayList>() + + try { + dataReceived = BufferedReader(InputStreamReader(clientSocket.inputStream)) + val results = dataReceived.readLine().split("\n") + Log.red("$results") + for (result in results) + { + val result_map = ObjectMapper().readValue(result, Map::class.java) + multipleSnipes.add(result_map) + } + + } + catch (e: Exception) { + continue + } + + // We use a for loop because it processes multiple pokemon snipes at once. + for (snipe in multipleSnipes) + { + val latitude = snipe["Latitude"].toString().toDouble() + val longitude = snipe["Longitude"].toString().toDouble() + val pokemonId = snipe["Id"].toString().toInt() + val pokemonName: String = (pokedexId2Names[pokemonId + 1]).toString() + + Log.green(text="ayo data: $snipe with name $pokemonName") + bot.task(SnipePokemon(latitude, longitude, pokemonName)) + } + + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/ink/abb/pogo/scraper/tasks/SnipePokemon.kt b/src/main/kotlin/ink/abb/pogo/scraper/tasks/SnipePokemon.kt new file mode 100644 index 000000000..5df2f431a --- /dev/null +++ b/src/main/kotlin/ink/abb/pogo/scraper/tasks/SnipePokemon.kt @@ -0,0 +1,153 @@ +/** + * Pokemon Go Bot Copyright (C) 2016 PokemonGoBot-authors (see authors.md for more information) + * This program comes with ABSOLUTELY NO WARRANTY; + * This is free software, and you are welcome to redistribute it under certain conditions. + * + * For more information, refer to the LICENSE file in this repositories root directory + */ + +package ink.abb.pogo.scraper.tasks + +import POGOProtos.Networking.Responses.CatchPokemonResponseOuterClass.CatchPokemonResponse +import POGOProtos.Networking.Responses.EncounterResponseOuterClass.EncounterResponse.Status +import com.pokegoapi.api.inventory.Pokeball +import com.pokegoapi.api.map.pokemon.encounter.DiskEncounterResult +import ink.abb.pogo.scraper.Bot +import ink.abb.pogo.scraper.Context +import ink.abb.pogo.scraper.Settings +import ink.abb.pogo.scraper.Task +import ink.abb.pogo.scraper.util.Log +import ink.abb.pogo.scraper.util.cachedInventories +import ink.abb.pogo.scraper.util.inventory.hasPokeballs +import ink.abb.pogo.scraper.util.map.getCatchablePokemon +import ink.abb.pogo.scraper.util.pokemon.catch +import ink.abb.pogo.scraper.util.pokemon.getIvPercentage +import ink.abb.pogo.scraper.util.pokemon.getStatsFormatted +import ink.abb.pogo.scraper.util.pokemon.shouldTransfer + +class SnipePokemon (val latitude: Double, val longitude: Double, val pokemonName: String): Task { + override fun run(bot: Bot, ctx: Context, settings: Settings) { + ctx.pauseWalking.set(true) + ctx.pauseForSniping.set(true) + val hasPokeballs = ctx.api.cachedInventories.itemBag.hasPokeballs() + if (!hasPokeballs) { + ctx.pauseWalking.set(false) + return + } + + var oldLatitude = ctx.lat.get() + var oldLongitude = ctx.lng.get() + + ctx.lat.set(latitude); ctx.lng.set(longitude) + ctx.api.setLocation(latitude, longitude, 0.0) + bot.task(GetMapRandomDirection(isForSniping=true)) + + val pokemon = ctx.api.map.getCatchablePokemon(ctx.blacklistedEncounters) + + Log.cyan("Sniper Found $pokemon at long/lat ${ctx.lng.get()}/${ctx.lat.get()}") + val catchablePokemon = pokemon.find { it.pokemonId.toString().toLowerCase().equals(pokemonName.toLowerCase()) } + + Log.cyan(text="$catchablePokemon") + if (null != catchablePokemon) { + if (settings.obligatoryTransfer.contains(catchablePokemon.pokemonId) && settings.desiredCatchProbabilityUnwanted == -1.0) { + ctx.blacklistedEncounters.add(catchablePokemon.encounterId) + Log.normal("Found pokemon ${catchablePokemon.pokemonId}; blacklisting it because it's unwanted") + ctx.pauseWalking.set(false) + return + } + Log.green("Found pokemon ${catchablePokemon.pokemonId}") + + val encounterResult = catchablePokemon.encounterPokemon() + val wasFromLure = encounterResult is DiskEncounterResult + if (encounterResult.wasSuccessful()) { + val pokemonData = encounterResult.pokemonData + Log.green("Encountered pokemon ${catchablePokemon.pokemonId} " + + "with CP ${pokemonData.cp} and IV ${pokemonData.getIvPercentage()}%") + + ctx.lat.set(oldLatitude); ctx.lng.set(oldLongitude) + ctx.api.setLocation(oldLatitude, oldLongitude, 0.0) + + bot.task(GetMapRandomDirection(isForSniping=true)) + + val (shouldRelease, reason) = pokemonData.shouldTransfer(settings) + val desiredCatchProbability = if (shouldRelease) { + Log.yellow("Using desired_catch_probability_unwanted because $reason") + settings.desiredCatchProbabilityUnwanted + } else { + settings.desiredCatchProbability + } + if (desiredCatchProbability == -1.0) { + ctx.blacklistedEncounters.add(catchablePokemon.encounterId) + Log.normal("CP/IV of encountered pokemon ${catchablePokemon.pokemonId} is too low; blacklisting encounter") + ctx.pauseWalking.set(false) + return + } + + val isCurveBall = (Math.random() < settings.desiredCurveRate) + val result = catchablePokemon.catch( + encounterResult.captureProbability, + ctx.api.cachedInventories.itemBag, + desiredCatchProbability, + isCurveBall, + !settings.neverUseBerries, + settings.randomBallThrows, + settings.waitBetweenThrows, + -1) + + if (result == null) { + ctx.blacklistedEncounters.add(catchablePokemon.encounterId) + Log.red("No Pokeballs in your inventory; blacklisting Pokemon") + ctx.pauseWalking.set(false) + return + } + + ctx.blacklistedEncounters.add(catchablePokemon.encounterId) + if (result.status == CatchPokemonResponse.CatchStatus.CATCH_SUCCESS) { + ctx.pokemonStats.first.andIncrement + if (wasFromLure) { + ctx.luredPokemonStats.andIncrement + } + val iv = (pokemonData.individualAttack + pokemonData.individualDefense + pokemonData.individualStamina) * 100 / 45 + var message = "Caught a ${catchablePokemon.pokemonId} " + + "with CP ${pokemonData.cp} and IV $iv%" + message += "\r\n ${pokemonData.getStatsFormatted()}" + if (settings.displayIfPokemonFromLure) { + if (encounterResult is DiskEncounterResult) + message += " (lured pokemon) " + else + message += " (wild pokemon) " + } + if (settings.displayPokemonCatchRewards) + message += ": [${result.xpList.sum()}x XP, ${result.candyList.sum()}x " + + "Candy, ${result.stardustList.sum()}x Stardust]" + Log.cyan(message) + + ctx.server.newPokemon(catchablePokemon.latitude, catchablePokemon.longitude, pokemonData) + ctx.server.sendProfile() + } else { + Log.red("Capture of ${catchablePokemon.pokemonId} failed with status : ${result.status}") + if (result.status == CatchPokemonResponse.CatchStatus.CATCH_ERROR) { + Log.red("Blacklisting pokemon to prevent infinite loop") + } + } + } else { + // We need to set this back to the old value. + ctx.lat.set(oldLatitude); ctx.lng.set(oldLongitude) + ctx.api.setLocation(oldLatitude, oldLongitude, 0.0) + + Log.red("Encounter failed with result: ${encounterResult.status}") + if (encounterResult.status == Status.POKEMON_INVENTORY_FULL) { + Log.red("Disabling catching of Pokemon") + + ctx.pokemonInventoryFullStatus.second.set(true) + + settings.catchPokemon = false + } + } + } + ctx.pauseWalking.set(false) + bot.task(GetMapRandomDirection(isForSniping=false)) + ctx.pauseForSniping.set(false) + + } +} diff --git a/src/main/kotlin/ink/abb/pogo/scraper/tasks/WalkToStartPokestop.kt b/src/main/kotlin/ink/abb/pogo/scraper/tasks/WalkToStartPokestop.kt index 2c7ca9f04..e97dd0d40 100644 --- a/src/main/kotlin/ink/abb/pogo/scraper/tasks/WalkToStartPokestop.kt +++ b/src/main/kotlin/ink/abb/pogo/scraper/tasks/WalkToStartPokestop.kt @@ -23,6 +23,9 @@ import java.util.concurrent.atomic.AtomicBoolean class WalkToStartPokestop(val startPokeStop: Pokestop) : Task { override fun run(bot: Bot, ctx: Context, settings: Settings) { + if (ctx.pauseForSniping.get()) { + return + } if (settings.followStreets) walkRoute(bot, ctx, settings) else walk(bot, ctx, settings)