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