Advertisement
Guest User

Untitled

a guest
Aug 7th, 2016
500
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.68 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. pgoapi - Pokemon Go API
  5. Copyright (c) 2016 tjado <https://github.com/tejado>
  6.  
  7. Permission is hereby granted, free of charge, to any person obtaining a copy
  8. of this software and associated documentation files (the "Software"), to deal
  9. in the Software without restriction, including without limitation the rights
  10. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. copies of the Software, and to permit persons to whom the Software is
  12. furnished to do so, subject to the following conditions:
  13.  
  14. The above copyright notice and this permission notice shall be included in all
  15. copies or substantial portions of the Software.
  16.  
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  21. DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  22. OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
  23. OR OTHER DEALINGS IN THE SOFTWARE.
  24.  
  25. Author: tjado <https://github.com/tejado>
  26. """
  27.  
  28. import argparse
  29. import codecs
  30. import json
  31. import logging
  32. import os
  33. import ssl
  34. import sys
  35. import time
  36. from datetime import timedelta
  37. from getpass import getpass
  38. from pgoapi.exceptions import NotLoggedInException, ServerSideRequestThrottlingException, ServerBusyOrOfflineException
  39. from geopy.exc import GeocoderQuotaExceeded
  40.  
  41. from pokemongo_bot import PokemonGoBot, TreeConfigBuilder
  42. from pokemongo_bot.health_record import BotEvent
  43.  
  44. reload(sys)
  45. sys.setdefaultencoding('utf8')
  46.  
  47. if sys.version_info >= (2, 7, 9):
  48. ssl._create_default_https_context = ssl._create_unverified_context
  49.  
  50. logging.basicConfig(
  51. level=logging.INFO,
  52. format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s')
  53. logger = logging.getLogger('cli')
  54. logger.setLevel(logging.INFO)
  55.  
  56. def main():
  57. try:
  58. logger.info('PokemonGO Bot v1.0')
  59. sys.stdout = codecs.getwriter('utf8')(sys.stdout)
  60. sys.stderr = codecs.getwriter('utf8')(sys.stderr)
  61.  
  62. config = init_config()
  63. if not config:
  64. return
  65.  
  66. logger.info('Configuration initialized')
  67. health_record = BotEvent(config)
  68. health_record.login_success()
  69.  
  70. finished = False
  71.  
  72. while not finished:
  73. try:
  74. bot = PokemonGoBot(config)
  75. bot.start()
  76. tree = TreeConfigBuilder(bot, config.raw_tasks).build()
  77. bot.workers = tree
  78. bot.metrics.capture_stats()
  79.  
  80. bot.event_manager.emit(
  81. 'bot_start',
  82. sender=bot,
  83. level='info',
  84. formatted='Starting bot...'
  85. )
  86.  
  87. while True:
  88. bot.tick()
  89.  
  90. except KeyboardInterrupt:
  91. bot.event_manager.emit(
  92. 'bot_exit',
  93. sender=bot,
  94. level='info',
  95. formatted='Exiting bot.'
  96. )
  97. finished = True
  98. report_summary(bot)
  99.  
  100. except NotLoggedInException:
  101. wait_time = config.reconnecting_timeout * 60
  102. bot.event_manager.emit(
  103. 'api_error',
  104. sender=bot,
  105. level='info',
  106. formmated='Log logged in, reconnecting in {:s}'.format(wait_time)
  107. )
  108. time.sleep(wait_time)
  109. except ServerBusyOrOfflineException:
  110. bot.event_manager.emit(
  111. 'api_error',
  112. sender=bot,
  113. level='info',
  114. formatted='Server busy or offline'
  115. )
  116. except ServerSideRequestThrottlingException:
  117. bot.event_manager.emit(
  118. 'api_error',
  119. sender=bot,
  120. level='info',
  121. formatted='Server is throttling, reconnecting in 30 seconds'
  122. )
  123. time.sleep(30)
  124.  
  125. except GeocoderQuotaExceeded:
  126. raise Exception("Google Maps API key over requests limit.")
  127. except Exception as e:
  128. # always report session summary and then raise exception
  129. if bot:
  130. report_summary(bot)
  131.  
  132. raise e
  133.  
  134. def report_summary(bot):
  135. if bot.metrics.start_time is None:
  136. return # Bot didn't actually start, no metrics to show.
  137.  
  138. metrics = bot.metrics
  139. metrics.capture_stats()
  140. logger.info('')
  141. logger.info('Ran for {}'.format(metrics.runtime()))
  142. logger.info('Total XP Earned: {} Average: {:.2f}/h'.format(metrics.xp_earned(), metrics.xp_per_hour()))
  143. logger.info('Travelled {:.2f}km'.format(metrics.distance_travelled()))
  144. logger.info('Visited {} stops'.format(metrics.visits['latest'] - metrics.visits['start']))
  145. logger.info('Encountered {} pokemon, {} caught, {} released, {} evolved, {} never seen before'
  146. .format(metrics.num_encounters(), metrics.num_captures(), metrics.releases,
  147. metrics.num_evolutions(), metrics.num_new_mons()))
  148. logger.info('Threw {} pokeball{}'.format(metrics.num_throws(), '' if metrics.num_throws() == 1 else 's'))
  149. logger.info('Earned {} Stardust'.format(metrics.earned_dust()))
  150. logger.info('')
  151. if metrics.highest_cp is not None:
  152. logger.info('Highest CP Pokemon: {}'.format(metrics.highest_cp['desc']))
  153. if metrics.most_perfect is not None:
  154. logger.info('Most Perfect Pokemon: {}'.format(metrics.most_perfect['desc']))
  155.  
  156. def init_config():
  157. parser = argparse.ArgumentParser()
  158. config_file = "configs/config.json"
  159. web_dir = "web"
  160.  
  161. # If config file exists, load variables from json
  162. load = {}
  163.  
  164. # Select a config file code
  165. parser.add_argument("-cf", "--config", help="Config File to use")
  166. config_arg = parser.parse_known_args() and parser.parse_known_args()[0].config or None
  167. if config_arg and os.path.isfile(config_arg):
  168. with open(config_arg) as data:
  169. load.update(json.load(data))
  170. elif os.path.isfile(config_file):
  171. logger.info('No config argument specified, checking for /configs/config.json')
  172. with open(config_file) as data:
  173. load.update(json.load(data))
  174. else:
  175. logger.info('Error: No /configs/config.json or specified config')
  176.  
  177. # Read passed in Arguments
  178. required = lambda x: not x in load
  179. add_config(
  180. parser,
  181. load,
  182. short_flag="-a",
  183. long_flag="--auth_service",
  184. help="Auth Service ('ptc' or 'google')",
  185. required=required("auth_service"),
  186. default=None
  187. )
  188. add_config(
  189. parser,
  190. load,
  191. short_flag="-u",
  192. long_flag="--username",
  193. help="Username",
  194. default=None
  195. )
  196. add_config(
  197. parser,
  198. load,
  199. short_flag="-ws",
  200. long_flag="--websocket.server_url",
  201. help="Connect to websocket server at given url",
  202. default=False
  203. )
  204. add_config(
  205. parser,
  206. load,
  207. short_flag="-wss",
  208. long_flag="--websocket.start_embedded_server",
  209. help="Start embedded websocket server",
  210. default=False
  211. )
  212. add_config(
  213. parser,
  214. load,
  215. short_flag="-wsr",
  216. long_flag="--websocket.remote_control",
  217. help="Enable remote control through websocket (requires websocekt server url)",
  218. default=False
  219. )
  220. add_config(
  221. parser,
  222. load,
  223. short_flag="-p",
  224. long_flag="--password",
  225. help="Password",
  226. default=None
  227. )
  228. add_config(
  229. parser,
  230. load,
  231. short_flag="-l",
  232. long_flag="--location",
  233. help="Location",
  234. type=parse_unicode_str,
  235. default=''
  236. )
  237. add_config(
  238. parser,
  239. load,
  240. short_flag="-lc",
  241. long_flag="--location_cache",
  242. help="Bot will start at last known location",
  243. type=bool,
  244. default=False
  245. )
  246. add_config(
  247. parser,
  248. load,
  249. long_flag="--forts.spin",
  250. help="Enable Spinning Pokestops",
  251. type=bool,
  252. default=True,
  253. )
  254. add_config(
  255. parser,
  256. load,
  257. short_flag="-w",
  258. long_flag="--walk",
  259. help=
  260. "Walk instead of teleport with given speed (meters per second, e.g. 2.5)",
  261. type=float,
  262. default=2.5
  263. )
  264. add_config(
  265. parser,
  266. load,
  267. short_flag="-k",
  268. long_flag="--gmapkey",
  269. help="Set Google Maps API KEY",
  270. type=str,
  271. default=None
  272. )
  273. add_config(
  274. parser,
  275. load,
  276. short_flag="-e",
  277. long_flag="--show_events",
  278. help="Show events",
  279. type=bool,
  280. default=False
  281. )
  282. add_config(
  283. parser,
  284. load,
  285. short_flag="-d",
  286. long_flag="--debug",
  287. help="Debug Mode",
  288. type=bool,
  289. default=False
  290. )
  291. add_config(
  292. parser,
  293. load,
  294. short_flag="-t",
  295. long_flag="--test",
  296. help="Only parse the specified location",
  297. type=bool,
  298. default=False
  299. )
  300. add_config(
  301. parser,
  302. load,
  303. short_flag="-du",
  304. long_flag="--distance_unit",
  305. help="Set the unit to display distance in (e.g, km for kilometers, mi for miles, ft for feet)",
  306. type=str,
  307. default='km'
  308. )
  309. add_config(
  310. parser,
  311. load,
  312. short_flag="-ec",
  313. long_flag="--evolve_captured",
  314. help="(Ad-hoc mode) Pass \"all\" or a list of pokemon to evolve (e.g., \"Pidgey,Weedle,Caterpie\"). Bot will attempt to evolve all the pokemon captured!",
  315. type=str,
  316. default=[]
  317. )
  318. add_config(
  319. parser,
  320. load,
  321. short_flag="-rt",
  322. long_flag="--reconnecting_timeout",
  323. help="Timeout between reconnecting if error occured (in minutes, e.g. 15)",
  324. type=float,
  325. default=15.0
  326. )
  327. add_config(
  328. parser,
  329. load,
  330. short_flag="-hr",
  331. long_flag="--health_record",
  332. help="Send anonymous bot event to GA for bot health record. Set \"health_record\":false if you need disable it.",
  333. type=bool,
  334. default=True
  335. )
  336. add_config(
  337. parser,
  338. load,
  339. short_flag="-ac",
  340. long_flag="--forts.avoid_circles",
  341. help="Avoids circles (pokestops) of the max size set in max_circle_size flag",
  342. type=bool,
  343. default=False,
  344. )
  345. add_config(
  346. parser,
  347. load,
  348. short_flag="-mcs",
  349. long_flag="--forts.max_circle_size",
  350. help="If avoid_circles flag is set, this flag specifies the maximum size of circles (pokestops) avoided",
  351. type=int,
  352. default=10,
  353. )
  354. add_config(
  355. parser,
  356. load,
  357. long_flag="--catch_randomize_reticle_factor",
  358. help="Randomize factor for pokeball throwing accuracy (DEFAULT 1.0 means no randomize: always 'Excellent' throw. 0.0 randomizes between normal and 'Excellent' throw)",
  359. type=float,
  360. default=1.0
  361. )
  362. add_config(
  363. parser,
  364. load,
  365. long_flag="--catch_randomize_spin_factor",
  366. help="Randomize factor for pokeball curve throwing (DEFAULT 1.0 means no randomize: always perfect 'Super Spin' curve ball. 0.0 randomizes between normal and 'Super Spin' curve ball)",
  367. type=float,
  368. default=1.0
  369. )
  370. add_config(
  371. parser,
  372. load,
  373. long_flag="--map_object_cache_time",
  374. help="Amount of seconds to keep the map object in cache (bypass Niantic throttling)",
  375. type=float,
  376. default=5.0
  377. )
  378.  
  379. # Start to parse other attrs
  380. config = parser.parse_args()
  381. if not config.username and 'username' not in load:
  382. config.username = raw_input("Username: ")
  383. if not config.password and 'password' not in load:
  384. config.password = getpass("Password: ")
  385.  
  386. config.catch = load.get('catch', {})
  387. config.release = load.get('release', {})
  388. config.action_wait_max = load.get('action_wait_max', 4)
  389. config.action_wait_min = load.get('action_wait_min', 1)
  390. config.raw_tasks = load.get('tasks', [])
  391.  
  392. config.vips = load.get('vips', {})
  393.  
  394. if config.map_object_cache_time < 0.0:
  395. parser.error("--map_object_cache_time is out of range! (should be >= 0.0)")
  396. return None
  397.  
  398. if len(config.raw_tasks) == 0:
  399. logging.error("No tasks are configured. Did you mean to configure some behaviors? Read https://github.com/PokemonGoF/PokemonGo-Bot/wiki/Configuration-files#configuring-tasks for more information")
  400. return None
  401.  
  402. if config.auth_service not in ['ptc', 'google']:
  403. logging.error("Invalid Auth service specified! ('ptc' or 'google')")
  404. return None
  405.  
  406. def task_configuration_error(flag_name):
  407. parser.error("""
  408. \"{}\" was removed from the configuration options.
  409. You can now change the behavior of the bot by modifying the \"tasks\" key.
  410. Read https://github.com/PokemonGoF/PokemonGo-Bot/wiki/Configuration-files#configuring-tasks for more information.
  411. """.format(flag_name))
  412.  
  413. old_flags = ['mode', 'catch_pokemon', 'spin_forts', 'forts_spin', 'hatch_eggs', 'release_pokemon', 'softban_fix',
  414. 'longer_eggs_first', 'evolve_speed', 'use_lucky_egg', 'item_filter', 'evolve_all', 'evolve_cp_min', 'max_steps']
  415. for flag in old_flags:
  416. if flag in load:
  417. task_configuration_error(flag)
  418. return None
  419.  
  420. nested_old_flags = [('forts', 'spin'), ('forts', 'move_to_spin'), ('navigator', 'path_mode'), ('navigator', 'path_file'), ('navigator', 'type')]
  421. for outer, inner in nested_old_flags:
  422. if load.get(outer, {}).get(inner, None):
  423. task_configuration_error('{}.{}'.format(outer, inner))
  424. return None
  425.  
  426. if (config.evolve_captured
  427. and (not isinstance(config.evolve_captured, str)
  428. or str(config.evolve_captured).lower() in ["true", "false"])):
  429. parser.error('"evolve_captured" should be list of pokemons: use "all" or "none" to match all ' +
  430. 'or none of the pokemons, or use a comma separated list such as "Pidgey,Weedle,Caterpie"')
  431. return None
  432.  
  433. if not (config.location or config.location_cache):
  434. parser.error("Needs either --use-location-cache or --location.")
  435. return None
  436.  
  437. if config.catch_randomize_reticle_factor < 0 or 1 < config.catch_randomize_reticle_factor:
  438. parser.error("--catch_randomize_reticle_factor is out of range! (should be 0 <= catch_randomize_reticle_factor <= 1)")
  439. return None
  440.  
  441. if config.catch_randomize_spin_factor < 0 or 1 < config.catch_randomize_spin_factor:
  442. parser.error("--catch_randomize_spin_factor is out of range! (should be 0 <= catch_randomize_spin_factor <= 1)")
  443. return None
  444.  
  445. # create web dir if not exists
  446. try:
  447. os.makedirs(web_dir)
  448. except OSError:
  449. if not os.path.isdir(web_dir):
  450. raise
  451.  
  452. if config.evolve_captured and isinstance(config.evolve_captured, str):
  453. config.evolve_captured = [str(pokemon_name).strip() for pokemon_name in config.evolve_captured.split(',')]
  454.  
  455. fix_nested_config(config)
  456. return config
  457.  
  458. def add_config(parser, json_config, short_flag=None, long_flag=None, **kwargs):
  459. if not long_flag:
  460. raise Exception('add_config calls requires long_flag parameter!')
  461.  
  462. full_attribute_path = long_flag.split('--')[1]
  463. attribute_name = full_attribute_path.split('.')[-1]
  464.  
  465. if '.' in full_attribute_path: # embedded config!
  466. embedded_in = full_attribute_path.split('.')[0: -1]
  467. for level in embedded_in:
  468. json_config = json_config.get(level, {})
  469.  
  470. if 'default' in kwargs:
  471. kwargs['default'] = json_config.get(attribute_name, kwargs['default'])
  472. if short_flag:
  473. args = (short_flag, long_flag)
  474. else:
  475. args = (long_flag,)
  476. parser.add_argument(*args, **kwargs)
  477.  
  478.  
  479. def fix_nested_config(config):
  480. config_dict = config.__dict__
  481.  
  482. for key, value in config_dict.iteritems():
  483. if '.' in key:
  484. new_key = key.replace('.', '_')
  485. config_dict[new_key] = value
  486. del config_dict[key]
  487.  
  488. def parse_unicode_str(string):
  489. try:
  490. return string.decode('utf8')
  491. except UnicodeEncodeError:
  492. return string
  493.  
  494.  
  495. if __name__ == '__main__':
  496. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement