Skip to content

Commit e539267

Browse files
bstpierrMFizz
authored andcommitted
Add metrics logging and output on close (PokemonGoF#1059)
* Add metrics logging and output on close Output looks like the following: [17:10:07] Exiting PokemonGo Bot [17:10:07] [17:10:07] Ran for 0:00:20 [17:10:07] Total XP Earned: 210 Averaging: 36534.47/h [17:10:07] Travelled 0.01km [17:10:07] Visited 0 stops [17:10:07] Encountered 1 pokemon, 1 caught, 0 released, 0 evolved, 0 never seen before [17:10:07] Threw 1 pokeball [17:10:07] Earned 100 Stardust [17:10:07] [17:10:07] Highest CP Pokemon: Nidoran M [CP: 75] [IV: 1/10/5] Potential: 0.36 [17:10:07] Most Perfect Pokemon: Nidoran M [CP: 75] [IV: 1/10/5] Potential: 0.36 * Added Metrics class to collect end of run stats Tried to come up with a reasonable division of labour for how to gather the information. Open to feedback! * Revert logging changes Didn’t mean to affect this message any more.
1 parent 43c665b commit e539267

File tree

4 files changed

+143
-2
lines changed

4 files changed

+143
-2
lines changed

pokecli.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import ssl
3434
import sys
3535
import time
36+
from datetime import timedelta
3637
from getpass import getpass
3738
from pgoapi.exceptions import NotLoggedInException
3839

@@ -240,6 +241,7 @@ def main():
240241
try:
241242
bot = PokemonGoBot(config)
242243
bot.start()
244+
bot.metrics.capture_stats()
243245

244246
logger.log('Starting PokemonGo Bot....', 'green')
245247

@@ -249,8 +251,28 @@ def main():
249251
except KeyboardInterrupt:
250252
logger.log('Exiting PokemonGo Bot', 'red')
251253
finished = True
252-
# TODO Add number of pokemon catched, pokestops visited, highest CP
253-
# pokemon catched, etc.
254+
if bot.metrics.start_time is None:
255+
return # Bot didn't actually start, no metrics to show.
256+
257+
metrics = bot.metrics
258+
metrics.capture_stats()
259+
logger.log('')
260+
logger.log('Ran for {}'.format(metrics.runtime()), 'red')
261+
logger.log('Total XP Earned: {} Average: {:.2f}/h'.format(metrics.xp_earned, metrics.xp_per_hour()), 'red')
262+
logger.log('Travelled {:.2f}km'.format(metrics.distance_travelled()), 'red')
263+
logger.log('Visited {} stops'.format(metrics.visits['latest'] - metrics.visits['start']), 'red')
264+
logger.log('Encountered {} pokemon, {} caught, {} released, {} evolved, {} never seen before'
265+
.format(metrics.num_encounters(), metrics.num_captures(), metrics.releases,
266+
metrics.num_evolutions(), metrics.num_new_mons()), 'red')
267+
logger.log('Threw {} pokeball{}'.format(metrics.num_throws(), '' if metrics.num_throws() == 1 else 's'),
268+
'red')
269+
logger.log('Earned {} Stardust'.format(metrics.earned_dust()), 'red')
270+
logger.log('')
271+
if metrics.highest_cp is not None:
272+
logger.log('Highest CP Pokemon: {}'.format(metrics.highest_cp['desc']), 'red')
273+
if metrics.most_perfect is not None:
274+
logger.log('Most Perfect Pokemon: {}'.format(metrics.most_perfect['desc']), 'red')
275+
254276

255277
except NotLoggedInException:
256278
logger.log('[x] Error while connecting to the server, please wait %s minutes' % config.reconnecting_timeout, 'red')

pokemongo_bot/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from cell_workers.utils import distance, get_cellid, encode, i2f
1919
from human_behaviour import sleep
2020
from item_list import Item
21+
from metrics import Metrics
2122
from spiral_navigator import SpiralNavigator
2223

2324

@@ -26,6 +27,7 @@ def __init__(self, config):
2627
self.config = config
2728
self.pokemon_list = json.load(open(os.path.join('data', 'pokemon.json')))
2829
self.item_list = json.load(open(os.path.join('data', 'items.json')))
30+
self.metrics = Metrics(self)
2931

3032
def start(self):
3133
self._setup_logging()

pokemongo_bot/cell_workers/pokemon_catch_worker.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ def work(self):
188188

189189
id_list2 = self.count_pokemon_inventory()
190190

191+
self.bot.metrics.captured_pokemon(pokemon_name, cp, iv_display, pokemon_potential)
192+
191193
logger.log('Captured {}! [CP {}] [{}]'.format(
192194
pokemon_name,
193195
cp,
@@ -196,6 +198,7 @@ def work(self):
196198

197199
if self.config.evolve_captured:
198200
pokemon_to_transfer = list(Set(id_list2) - Set(id_list1))
201+
# No need to capture this even for metrics, player stats includes it.
199202
self.api.evolve_pokemon(pokemon_id=pokemon_to_transfer[0])
200203
response_dict = self.api.call()
201204
status = response_dict['responses']['EVOLVE_POKEMON']['result']

pokemongo_bot/metrics.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import time
2+
3+
from datetime import timedelta
4+
5+
6+
class Metrics(object):
7+
8+
def __init__(self, bot):
9+
self.bot = bot
10+
self.start_time = None
11+
self.dust = {'start': None, 'latest': None}
12+
self.xp = {'start': None, 'latest': None}
13+
self.distance = {'start': None, 'latest': None}
14+
self.encounters = {'start': None, 'latest': None}
15+
self.throws = {'start': None, 'latest': None}
16+
self.captures = {'start': None, 'latest': None}
17+
self.visits = {'start': None, 'latest': None}
18+
self.unique_mons = {'start': None, 'latest': None}
19+
self.evolutions = {'start': None, 'latest': None}
20+
21+
self.releases = 0
22+
self.highest_cp = {'cp': 0, 'desc': ''}
23+
self.most_perfect = {'potential': 0, 'desc': ''}
24+
25+
def runtime(self):
26+
return str(timedelta(seconds=round(time.time() - self.start_time)))
27+
28+
def xp_earned(self):
29+
return self.xp['latest'] - self.xp['start']
30+
31+
def xp_per_hour(self):
32+
return self.xp_earned/(time.time() - self.start_time/60/60)
33+
34+
def distance_travelled(self):
35+
return self.distance['latest'] - self.distance['start']
36+
37+
def num_encounters(self):
38+
return self.encounters['latest'] - self.encounters['start']
39+
40+
def num_throws(self):
41+
return self.throws['latest'] - self.throws['start']
42+
43+
def num_captures(self):
44+
return self.captures['latest'] - self.captures['start']
45+
46+
def num_visits(self):
47+
return self.visits['latest'] - self.visits['start']
48+
49+
def num_new_mons(self):
50+
return self.unique_mons['latest'] - self.unique_mons['start']
51+
52+
def num_evolutions(self):
53+
return self.evolutions['latest'] - self.evolutions['start']
54+
55+
def earned_dust(self):
56+
return self.dust['latest'] - self.dust['start']
57+
58+
def captured_pokemon(self, name, cp, iv_display, potential):
59+
if cp > self.highest_cp['cp']:
60+
self.highest_cp = \
61+
{'cp': cp, 'desc': '{} [CP: {}] [IV: {}] Potential: {} '
62+
.format(name, cp, iv_display, potential)}
63+
64+
if potential > self.most_perfect['potential']:
65+
self.most_perfect = \
66+
{'potential': cp, 'desc': '{} [CP: {}] [IV: {}] Potential: {} '
67+
.format(name, cp, iv_display, potential)}
68+
return
69+
70+
def released_pokemon(self, count=1):
71+
self.releases += count
72+
73+
def capture_stats(self):
74+
if self.start_time is None: self.start_time = time.time()
75+
self.bot.api.get_inventory()
76+
self.bot.api.get_player()
77+
response_dict = self.bot.api.call()
78+
try:
79+
self.dust['latest'] = response_dict['responses']['GET_PLAYER']['player_data']['currencies'][1]['amount']
80+
if self.dust['start'] is None: self.dust['start'] = self.dust['latest']
81+
for item in response_dict['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']:
82+
if 'inventory_item_data' in item:
83+
if 'player_stats' in item['inventory_item_data']:
84+
playerdata = item['inventory_item_data']['player_stats']
85+
86+
self.xp['latest'] = playerdata.get('experience', 0)
87+
if self.xp['start'] is None: self.xp['start'] = self.xp['latest']
88+
89+
self.visits['latest'] = playerdata.get('poke_stop_visits', 0)
90+
if self.visits['start'] is None: self.visits['start'] = self.visits['latest']
91+
92+
self.captures['latest'] = playerdata.get('pokemons_captured', 0)
93+
if self.captures['start'] is None: self.captures['start'] = self.captures['latest']
94+
95+
self.distance['latest'] = playerdata.get('km_walked', 0)
96+
if self.distance['start'] is None: self.distance['start'] = self.distance['latest']
97+
98+
self.encounters['latest'] = playerdata.get('pokemons_encountered', 0)
99+
if self.encounters['start'] is None: self.encounters['start'] = self.encounters['latest']
100+
101+
self.throws['latest'] = playerdata.get('pokeballs_thrown', 0)
102+
if self.throws['start'] is None: self.throws['start'] = self.throws['latest']
103+
104+
self.unique_mons['latest'] = playerdata.get('unique_pokedex_entries', 0)
105+
if self.unique_mons['start'] is None: self.unique_mons['start'] = self.unique_mons['latest']
106+
107+
self.visits['latest'] = playerdata.get('poke_stop_visits', 0)
108+
if self.visits['start'] is None: self.visits['start'] = self.visits['latest']
109+
110+
self.evolutions['latest'] = playerdata.get('evolutions', 0)
111+
if self.evolutions['start'] is None: self.evolutions['start'] = self.evolutions['latest']
112+
except KeyError:
113+
# Nothing we can do if there's no player info.
114+
return

0 commit comments

Comments
 (0)