diff --git a/.gitignore b/.gitignore index 3c504a3774..550648c7b6 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,8 @@ web/ data/last-location*.json data/cells-*.json data/map-caught-*.json +data/recent-forts-*.json +user_web_catchable # Multiple config configs/* diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c363f3d804..33aee29d31 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -69,3 +69,4 @@ * extink * Quantra * pmquan + * net8q diff --git a/README.md b/README.md index 1c0fa41910..bc81fa9858 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,16 @@ # PokemonGo-Bot PokemonGo bot is a project created by the [PokemonGoF](https://github.com/PokemonGoF) team. -The project is currently setup in two main branches. `dev` also known as `beta` and `master` also known as `stable`. Submit your PR's to `dev`. +The project is currently setup in two main branches. `dev` also known as `beta` and `master` also known as `stable`. Make sure to submit your PR's to `dev`. -If you need any help please don't create an issue here on github we have a great community on Slack, [Click here to join the chat!](https://pokemongo-bot.herokuapp.com). You can count on the community in #help channel. +## Support +Configuration issues/help - If you need any help please don't create an issue as we have a great community on Slack. You can count on the community in #help channel. + - [Click here to signup (first time only)](https://pokemongo-bot.herokuapp.com) + - [Join if you're already a member](https://pokemongo-bot.slack.com/messages/general/). + +[Bugs / Issues](https://github.com/PokemonGoF/PokemonGo-Bot/issues?q=is%3Aissue+sort%3Aupdated-desc) - If you discover a bug in the bot, please [search our issue tracker first](https://github.com/PokemonGoF/PokemonGo-Bot/issues?q=is%3Aissue+sort%3Aupdated-desc). If it hasn't been reported, please [create a new issue](https://github.com/PokemonGoF/PokemonGo-Bot/issues/new) and ensure you follow the template so that our team can assist you as quickly as possible + +[Feature Requests](http://feathub.com/PokemonGoF/PokemonGo-Bot) - If you have a great idea to improve the bot don't create an issue, use our [feature hub](http://feathub.com/PokemonGoF/PokemonGo-Bot). While you're there vote on other features to let the devs know what is most important to you ## Table of Contents - [Installation](https://github.com/PokemonGoF/PokemonGo-Bot/blob/dev/docs/installation.md) diff --git a/configs/config.json.example b/configs/config.json.example index 838acc1d4b..7727fd0c8b 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -33,7 +33,7 @@ "config": { "enabled": false, "min_interval": 10, - "stats": ["uptime", "stardust_earned", "xp_earned", "xp_per_hour", "stops_visited"], + "stats": ["username", "uptime", "stardust_earned", "xp_earned", "xp_per_hour", "stops_visited"], "terminal_log": true, "terminal_title": true } diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 8bb944da33..f1338a09d5 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -226,6 +226,7 @@ Key | Info **{iv_defense}** | Individial Defense *(0-15)* of the current specific pokemon **{iv_stamina}** | Individial Stamina *(0-15)* of the current specific pokemon **{iv_ads}** | Joined IV values in `(attack)/(defense)/(stamina)` format (*e.g. 4/12/9*, matches web UI format -- A/D/S) +**{iv_ads_hex}** | Joined IV values of `(attack)(defense)(stamina)` in HEX (*e.g. 4C9* for A/D/S = 4/12/9) **{iv_sum}** | Sum of the Individial Values *(0-45, e.g. 45 when 3 perfect 15 IVs)* | **Basic Values of the pokemon (identical for all of one kind)** **{base_attack}** | Basic Attack *(40-284)* of the current pokemon kind diff --git a/pokecli.py b/pokecli.py index 064c87c9ba..e88b21c55b 100644 --- a/pokecli.py +++ b/pokecli.py @@ -43,6 +43,7 @@ from pokemongo_bot.base_dir import _base_dir from pokemongo_bot.health_record import BotEvent from pokemongo_bot.plugin_loader import PluginLoader +from pokemongo_bot.api_wrapper import PermaBannedException try: from demjson import jsonlint @@ -64,11 +65,11 @@ class SIGINTRecieved(Exception): pass def main(): bot = False - try: - def handle_sigint(*args): - raise SIGINTRecieved - signal.signal(signal.SIGINT, handle_sigint) + def handle_sigint(*args): + raise SIGINTRecieved + signal.signal(signal.SIGINT, handle_sigint) + try: logger.info('PokemonGO Bot v1.0') sys.stdout = codecs.getwriter('utf8')(sys.stdout) sys.stderr = codecs.getwriter('utf8')(sys.stderr) @@ -102,7 +103,7 @@ def handle_sigint(*args): while True: bot.tick() - except (KeyboardInterrupt, SIGINTRecieved): + except KeyboardInterrupt: bot.event_manager.emit( 'bot_exit', sender=bot, @@ -137,8 +138,24 @@ def handle_sigint(*args): ) time.sleep(30) + except PermaBannedException: + bot.event_manager.emit( + 'api_error', + sender=bot, + level='info', + formatted='Probably permabanned, Game Over ! Play again at https://club.pokemon.com/us/pokemon-trainer-club/sign-up/' + ) except GeocoderQuotaExceeded: raise Exception("Google Maps API key over requests limit.") + except SIGINTRecieved: + if bot: + bot.event_manager.emit( + 'bot_interrupted', + sender=bot, + level='info', + formatted='Bot caught SIGINT. Shutting down.' + ) + report_summary(bot) except Exception as e: # always report session summary and then raise exception if bot: diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index ff5257c043..27ce65f8e4 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -150,6 +150,7 @@ def _register_events(self): self.event_manager.register_event('bot_start') self.event_manager.register_event('bot_exit') + self.event_manager.register_event('bot_interrupted') # sleep stuff self.event_manager.register_event( diff --git a/pokemongo_bot/api_wrapper.py b/pokemongo_bot/api_wrapper.py index 7224b0b4e4..2c51f32ff2 100644 --- a/pokemongo_bot/api_wrapper.py +++ b/pokemongo_bot/api_wrapper.py @@ -10,6 +10,9 @@ from human_behaviour import sleep +class PermaBannedException(Exception): + pass + class ApiWrapper(PGoApi): def __init__(self): PGoApi.__init__(self) @@ -77,6 +80,14 @@ def is_response_valid(self, result, request_callers): if not isinstance(result['responses'], dict): return False + try: + # Permaban symptom is empty response to GET_INVENTORY and status_code = 3 + if result['status_code'] == 3 and 'GET_INVENTORY' in request_callers and not result['responses']['GET_INVENTORY']: + raise PermaBannedException + except KeyError: + # Still wrong + return False + # the response can still programatically be valid at this point # but still be wrong. we need to check if the server did sent what we asked it for request_caller in request_callers: diff --git a/pokemongo_bot/cell_workers/follow_path.py b/pokemongo_bot/cell_workers/follow_path.py index 1532695bd8..a99e8623ed 100644 --- a/pokemongo_bot/cell_workers/follow_path.py +++ b/pokemongo_bot/cell_workers/follow_path.py @@ -19,7 +19,7 @@ def initialize(self): if self.path_start_mode == 'closest': self.ptr = self.find_closest_point_idx(self.points) - + else: self.ptr = 0 @@ -83,7 +83,7 @@ def find_closest_point_idx(self, points): botlng = self.bot.api._position_lng lat = float(point['lat']) lng = float(point['lng']) - + dist = distance( botlat, botlng, @@ -98,6 +98,9 @@ def find_closest_point_idx(self, points): return return_idx def work(self): + last_lat = self.bot.api._position_lat + last_lng = self.bot.api._position_lng + point = self.points[self.ptr] lat = float(point['lat']) lng = float(point['lng']) @@ -115,11 +118,11 @@ def work(self): is_at_destination = True else: - self.bot.api.set_position(lat, lng) + self.bot.api.set_position(lat, lng, 0) dist = distance( - self.bot.api._position_lat, - self.bot.api._position_lng, + last_lat, + last_lng, lat, lng ) @@ -132,4 +135,14 @@ def work(self): else: self.ptr += 1 + self.emit_event( + 'position_update', + formatted="Walking from {last_position} to {current_position} ({distance} {distance_unit})", + data={ + 'last_position': (last_lat, last_lng, 0), + 'current_position': (lat, lng, 0), + 'distance': dist, + 'distance_unit': 'm' + } + ) return [lat, lng] diff --git a/pokemongo_bot/cell_workers/follow_spiral.py b/pokemongo_bot/cell_workers/follow_spiral.py index f175369e45..1e6afef9d8 100644 --- a/pokemongo_bot/cell_workers/follow_spiral.py +++ b/pokemongo_bot/cell_workers/follow_spiral.py @@ -66,9 +66,19 @@ def _generate_spiral(starting_lat, starting_lng, step_size, step_limit): return coords def work(self): + last_lat = self.bot.api._position_lat + last_lng = self.bot.api._position_lng + point = self.points[self.ptr] self.cnt += 1 + dist = distance( + last_lat, + last_lng, + point['lat'], + point['lng'] + ) + if self.bot.config.walk > 0: step_walker = StepWalker( self.bot, @@ -77,19 +87,12 @@ def work(self): point['lng'] ) - dist = distance( - self.bot.api._position_lat, - self.bot.api._position_lng, - point['lat'], - point['lng'] - ) - if self.cnt == 1: self.emit_event( 'position_update', formatted="Walking from {last_position} to {current_position} ({distance} {distance_unit})", data={ - 'last_position': self.bot.position, + 'last_position': (last_lat, last_lng, 0), 'current_position': (point['lat'], point['lng'], 0), 'distance': dist, 'distance_unit': 'm' @@ -99,14 +102,20 @@ def work(self): if step_walker.step(): step_walker = None else: - self.bot.api.set_position(point['lat'], point['lng']) - - if distance( - self.bot.api._position_lat, - self.bot.api._position_lng, - point['lat'], - point['lng'] - ) <= 1 or (self.bot.config.walk > 0 and step_walker == None): + self.bot.api.set_position(point['lat'], point['lng'], 0) + + self.emit_event( + 'position_update', + formatted="Teleported from {last_position} to {current_position} ({distance} {distance_unit})", + data={ + 'last_position': (last_lat, last_lng, 0), + 'current_position': (point['lat'], point['lng'], 0), + 'distance': dist, + 'distance_unit': 'm' + } + ) + + if dist <= 1 or (self.bot.config.walk > 0 and step_walker == None): if self.ptr + self.direction >= len(self.points) or self.ptr + self.direction <= -1: self.direction *= -1 if len(self.points) != 1: diff --git a/pokemongo_bot/cell_workers/nickname_pokemon.py b/pokemongo_bot/cell_workers/nickname_pokemon.py index c396723c73..cb5ab8e741 100644 --- a/pokemongo_bot/cell_workers/nickname_pokemon.py +++ b/pokemongo_bot/cell_workers/nickname_pokemon.py @@ -1,9 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + import os import json from pokemongo_bot.base_task import BaseTask from pokemongo_bot.human_behaviour import sleep from pokemongo_bot.inventory import pokemons, Pokemon, Attack +import re + DEFAULT_IGNORE_FAVORITES = False DEFAULT_GOOD_ATTACK_THRESHOLD = 0.7 @@ -82,6 +87,7 @@ class NicknamePokemon(BaseTask): {iv_pct2} IV perfection (in 00-99 format - 2 chars) So 99 is best (it's a 100% perfection) {iv_pct1} IV perfection (in 0-9 format - 1 char) + {iv_ads_hex} Joined IV values in HEX (e.g. 4C9) # Basic Values of the pokemon (identical for all of one kind) {base_attack} Basic Attack (40-284) of the current pokemon kind @@ -291,7 +297,8 @@ def _generate_new_nickname(self, pokemon, template): """ # Filter template - template = template.lower().strip() + # only convert the keys to lowercase, leaving the format specifier alone + template = re.sub(r"{[\w_\d]*", lambda x:x.group(0).lower(), template).strip() # Individial Values of the current specific pokemon (different for each) iv_attack = pokemon.iv_attack @@ -341,6 +348,8 @@ def _generate_new_nickname(self, pokemon, template): iv_stamina=iv_stamina, # Joined IV values like: 4/12/9 iv_ads='/'.join(map(str, iv_list)), + # Joined IV values in HEX like: 4C9 + iv_ads_hex = ''.join(map(lambda x: format(x, 'X'), iv_list)), # Sum of the Individial Values iv_sum=iv_sum, # IV perfection (in 000-100 format - 3 chars) diff --git a/pokemongo_bot/cell_workers/pokemon_catch_worker.py b/pokemongo_bot/cell_workers/pokemon_catch_worker.py index d30718eaea..49a96690fe 100644 --- a/pokemongo_bot/cell_workers/pokemon_catch_worker.py +++ b/pokemongo_bot/cell_workers/pokemon_catch_worker.py @@ -217,6 +217,7 @@ def _use_berry(self, berry_id, berry_count, encounter_id, catch_rate_by_ball, cu # softban? else: new_catch_rate_by_ball = catch_rate_by_ball + self.bot.softban = True self.emit_event( 'softban', level='warning', diff --git a/pokemongo_bot/cell_workers/recycle_items.py b/pokemongo_bot/cell_workers/recycle_items.py index e48dff3cd8..79c9e9efc4 100644 --- a/pokemongo_bot/cell_workers/recycle_items.py +++ b/pokemongo_bot/cell_workers/recycle_items.py @@ -12,8 +12,6 @@ DEFAULT_MIN_EMPTY_SPACE = 6 class RecycleItems(BaseTask): - SUPPORTED_TASK_API_VERSION = 1 - """ Recycle undesired items if there is less than five space in inventory. You can use either item's name or id. For the full list of items see ../../data/items.json @@ -40,6 +38,8 @@ class RecycleItems(BaseTask): } } """ + SUPPORTED_TASK_API_VERSION = 1 + def initialize(self): self.items_filter = self.config.get('item_filter', {}) @@ -73,42 +73,41 @@ def should_run(self): def work(self): """ - Discard items if necessary. - :return: Returns wether or not the task went well + Start the process of recycling items if necessary. + :return: Returns whether or not the task went well :rtype: WorkerResult """ - # TODO: Use new inventory everywhere and then remove the inventory update - # Updating inventory + + # TODO: Use new inventory everywhere and then remove this inventory update inventory.refresh_inventory() + worker_result = WorkerResult.SUCCESS if self.should_run(): - # For each user's item in inventory recycle it if needed for item_in_inventory in inventory.items().all(): - amount_to_recycle = self.get_amount_to_recycle(item_in_inventory) - if self.item_should_be_recycled(item_in_inventory, amount_to_recycle): + if self.item_should_be_recycled(item_in_inventory): + # Make the bot appears more human action_delay(self.bot.config.action_wait_min, self.bot.config.action_wait_max) - if ItemRecycler(self.bot, item_in_inventory, amount_to_recycle).work() == WorkerResult.ERROR: + # If at any recycling process call we got an error, we consider that the result of this task is error too. + if ItemRecycler(self.bot, item_in_inventory, self.get_amount_to_recycle(item_in_inventory)).work() == WorkerResult.ERROR: worker_result = WorkerResult.ERROR return worker_result - def item_should_be_recycled(self, item, amount_to_recycle): + def item_should_be_recycled(self, item): """ Returns a value indicating whether the item should be recycled. - :param amount_to_recycle: - :param item: + :param item: The Item to test :return: True if the title should be recycled; otherwise, False. :rtype: bool """ - return (item.name in self.items_filter or str( - item.id) in self.items_filter) and amount_to_recycle > 0 + return (item.name in self.items_filter or str(item.id) in self.items_filter) and self.get_amount_to_recycle(item) > 0 def get_amount_to_recycle(self, item): """ Determine the amount to recycle accordingly to user config - :param item: Item to determine the amount to recycle + :param item: Item to determine the amount to recycle. :return: The amount to recycle :rtype: int """ diff --git a/pokemongo_bot/cell_workers/spin_fort.py b/pokemongo_bot/cell_workers/spin_fort.py index eee88525c5..cc25936350 100644 --- a/pokemongo_bot/cell_workers/spin_fort.py +++ b/pokemongo_bot/cell_workers/spin_fort.py @@ -149,7 +149,7 @@ def work(self): def get_forts_in_range(self): forts = self.bot.get_forts(order_by_distance=True) - for fort in forts: + for fort in reversed(forts): if 'cooldown_complete_timestamp_ms' in fort: self.bot.fort_timeouts[fort["id"]] = fort['cooldown_complete_timestamp_ms'] forts.remove(fort) diff --git a/pokemongo_bot/inventory.py b/pokemongo_bot/inventory.py index ccac6f43be..c59ba4b046 100644 --- a/pokemongo_bot/inventory.py +++ b/pokemongo_bot/inventory.py @@ -106,18 +106,46 @@ def captured(self, pokemon_id): return False return self._data[pokemon_id]['times_captured'] > 0 + class Item(object): + """ + Representation of an item. + """ def __init__(self, item_id, item_count): + """ + Initialise an instance of an item + :param item_id: ID of the item + :type item_id: int + :param item_count: Quantity of the item + :type item_count: int + :return: An item + :rtype: Item + """ self.id = item_id self.name = Items.name_for(self.id) self.count = item_count def remove(self, amount): + """ + Remove a specified amount of an item from the cached inventory. + Note that it does **NOT** removes it in the server, it only removes it from the local cached inventory. + :param amount: Amount to remove + :type amount: int + :return: Nothing + :rtype: None + """ if self.count < amount: raise Exception('Tried to remove more {} than you have'.format(self.name)) self.count -= amount def add(self, amount): + """ + Add a specified amount of the item to the local cached inventory + :param amount: Amount to add + :type amount: int + :return: Nothing. + :rtype: None + """ if amount < 0: raise Exception('Must add positive amount of {}'.format(self.name)) self.count += amount @@ -132,15 +160,41 @@ class Items(_BaseInventoryComponent): STATIC_DATA_FILE = os.path.join(_base_dir, 'data', 'items.json') def parse(self, item_data): + """ + Make an instance of an Item from raw item data. + :param item_data: Item data to make an item from + :return: Instance of the Item. + :rtype: Item + """ item_id = item_data.get(Items.ID_FIELD, None) item_count = item_data['count'] if 'count' in item_data else 0 return Item(item_id, item_count) + def all(self): + """ + Get EVERY Item from the cached inventory. + :return: List of evey item in the cached inventory + :rtype: list of Item + """ + return list(self._data.values()) + def get(self, item_id): + """ + Get ONE Item from the cached inventory. + :param item_id: Item's ID to search for. + :return: Instance of the item from the cached inventory + :rtype: Item + """ return self._data.setdefault(item_id, Item(item_id, 0)) @classmethod def name_for(cls, item_id): + """ + Search the name for an item from its ID. + :param item_id: Item's ID to search for. + :return: Item's name. + :rtype: str + """ return cls.STATIC_DATA[str(item_id)] @classmethod @@ -178,6 +232,7 @@ def has_space_for_loot(cls): return cls.get_space_left() >= max_number_of_items_looted_at_stop + class Pokemons(_BaseInventoryComponent): TYPE = 'pokemon_data' ID_FIELD = 'id' @@ -1093,30 +1148,66 @@ def _calc_cp(base_attack, base_defense, base_stamina, # # Usage helpers +# TODO : Complete the doc +# Only type return have been filled for now. It helps the IDE to suggest methods of the class. def init_inventory(bot): + """ + Initialises the cached inventory, retrieves data from the server. + :param bot: Instance of the bot. + :type bot: pokemongo_bot.PokemonGoBot + :return: Nothing. + :rtype: None + """ global _inventory _inventory = Inventory(bot) def refresh_inventory(): + """ + Refreshes the cached inventory, retrieves data from the server. + :return: Nothing. + :rtype: None + """ _inventory.refresh() def get_item_inventory_size(): + """ + Access to the Item inventory size. + :return: Item inventory size. + :rtype: int + """ _inventory.retrieve_item_inventory_size() return _inventory.item_inventory_size def pokedex(): + """ + + :return: + :rtype: Pokedex + """ return _inventory.pokedex def candies(refresh=False): + """ + + :param refresh: + :return: + :rtype: Candies + """ if refresh: refresh_inventory() return _inventory.candy def pokemons(refresh=False): + """ + + :param refresh: + :return: + :rtype: Pokemons + """ if refresh: refresh_inventory() return _inventory.pokemons @@ -1124,24 +1215,44 @@ def pokemons(refresh=False): def items(): """ - Access to the cached item inventory - :return: Instance of the cached item inventory + Access to the cached item inventory. + :return: Instance of the cached item inventory. :rtype: Items """ return _inventory.items def types_data(): + """ + + :return: + :rtype: Types + """ return Types def levels_to_cpm(): + """ + + :return: + :rtype: LevelToCPm + """ return LevelToCPm def fast_attacks(): + """ + + :return: + :rtype: FastAttacks + """ return FastAttacks def charged_attacks(): + """ + + :return: + :rtype: ChargedAttack + """ return ChargedAttacks diff --git a/pokemongo_bot/services/item_recycle_worker.py b/pokemongo_bot/services/item_recycle_worker.py index e59270d4ea..aa02b366a2 100644 --- a/pokemongo_bot/services/item_recycle_worker.py +++ b/pokemongo_bot/services/item_recycle_worker.py @@ -5,16 +5,16 @@ RECYCLE_REQUEST_RESPONSE_SUCCESS = 1 class ItemRecycler(BaseTask): - SUPPORTED_TASK_API_VERSION = 1 """ This class contains details of recycling process. """ + SUPPORTED_TASK_API_VERSION = 1 def __init__(self, bot, item_to_recycle, amount_to_recycle): """ Initialise an instance of ItemRecycler :param bot: The instance of the Bot :param item_to_recycle: The item to recycle - :type item_to_recycle: Item + :type item_to_recycle: inventory.Item :param amount_to_recycle: The amount to recycle :type amount_to_recycle: int :return: Nothing. @@ -27,8 +27,8 @@ def __init__(self, bot, item_to_recycle, amount_to_recycle): def work(self): """ - Recycle an item - :return: Returns wether or not the task went well + Start the recycling process + :return: Returns whether or not the task went well :rtype: WorkerResult """ if self.should_run(): @@ -43,7 +43,7 @@ def work(self): def should_run(self): """ - Returns a value indicating whether or mot the recycler should be run. + Returns a value indicating whether or not the recycler should be run. :return: True if the recycler should be run; otherwise, False. :rtype: bool """ @@ -87,7 +87,7 @@ def _emit_recycle_succeed(self): """ self.emit_event( 'item_discarded', - formatted='Discarded {amount}x {item}).', + formatted='Discarded {amount}x {item}.', data={ 'amount': str(self.amount_to_recycle), 'item': self.item_to_recycle.name, diff --git a/pokemongo_bot/step_walker.py b/pokemongo_bot/step_walker.py index 97a4f14b47..0e7c27bb2b 100644 --- a/pokemongo_bot/step_walker.py +++ b/pokemongo_bot/step_walker.py @@ -25,7 +25,10 @@ def __init__(self, bot, speed, dest_lat, dest_lng): self.destLng = dest_lng self.totalDist = max(1, self.dist) - self.steps = (self.dist + 0.0) / (speed + 0.0) + if speed == 0: + self.steps = 1 + else: + self.steps = (self.dist + 0.0) / (speed + 0.0) if self.dist < speed or int(self.steps) <= 1: self.dLat = 0 diff --git a/setup.sh b/setup.sh index 9535bc7d10..26c0eb4745 100755 --- a/setup.sh +++ b/setup.sh @@ -8,17 +8,18 @@ cd $pokebotpath git pull git submodule update --init --recursive git submodule foreach git pull origin master -virtualenv . source bin/activate +pip install -r requirements.txt --upgrade pip install -r requirements.txt } function Pokebotencrypt () { -echo "Start to make encrypt.so" -if [ "$(uname -s)" == "Darwin" ]; then #Mac platform - curl -O http://pgoapi.com/pgoencrypt.tar.gz -elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then #GNU/Linux platform - wget http://pgoapi.com/pgoencrypt.tar.gz +echo "Start to make encrypt.so." +if [ -x "$(command -v curl)" ] +then +curl -O http://pgoapi.com/pgoencrypt.tar.gz +else +wget http://pgoapi.com/pgoencrypt.tar.gz fi tar -xf pgoencrypt.tar.gz cd pgoencrypt/src/ @@ -43,7 +44,7 @@ Input location read -p "Input gmapkey " gmapkey cp -f configs/config.json.example configs/config.json && chmod 755 configs/config.json -if [ "$auth" = "2" ] +if [ "$auth" = "2" ] || [ "$auth" = "ptc" ] then sed -i "s/google/ptc/g" configs/config.json fi @@ -56,27 +57,42 @@ echo "Edit ./configs/config.json to modify any other config." function Pokebotinstall () { cd $pokebotpath -if [ -f /etc/debian_version ] +if [ "$(uname -s)" == "Darwin" ] +then +echo "You are on Mac os" +sudo brew update +sudo brew install --devel protobuf +elif [ -x "$(command -v apt-get)" ] then echo "You are on Debian/Ubuntu" sudo apt-get update -sudo apt-get -y install python python-pip python-dev build-essential git virtualenv -elif [ -f /etc/redhat-release ] +sudo apt-get -y install python python-pip python-dev gcc make git +elif [ -x "$(command -v yum)" ] then echo "You are on CentOS/RedHat" -sudo yum -y install epel-release -sudo yum -y install python-pip -elif [ "$(uname -s)" == "Darwin" ] +sudo yum -y install epel-release gcc make +sudo yum -y install python-pip python-devel +elif [ -x "$(command -v pacman)" ] then -echo "You are on Mac os" -sudo brew update -sudo brew install --devel protobuf +echo "You are on Arch Linux" +sudo pacman -Sy python2 python2-pip gcc make +elif [ -x "$(command -v dnf)" ] +then +echo "You are on Fedora/RHEL" +sudo dnf update +sudo dnf -y install python-pip python-devel gcc make +elif [ -x "$(command -v zypper)" ] +then +echo "You are on Open SUSE" +sudo zypper update +sudo zypper -y install python-pip python-devel gcc make else -echo "Please check if you have python pip protobuf gcc make installed on your device." +echo "Please check if you have python pip gcc make installed on your device." echo "Wait 5 seconds to continue or Use ctrl+c to interrupt this shell." sleep 5 fi sudo pip install virtualenv +Pokebotreset Pokebotupdate Pokebotencrypt echo "Install complete. Starting to generate config.json." @@ -87,6 +103,12 @@ function Pokebotreset () { cd $pokebotpath git fetch --all git reset --hard origin/dev +if [ -x "$(command -v python2)" ] +then +virtualenv -p python2 . +else +virtualenv . +fi Pokebotupdate } @@ -119,7 +141,7 @@ cp -f $pokebotpath/configs/config*.json $backuppath/ cp -f $pokebotpath/configs/*.gpx $backuppath/ cp -f $pokebotpath/configs/path*.json $backuppath/ cp -f $pokebotpath/web/config/userdata.js $backuppath/ -echo "Backup complete" +echo "Backup complete." ;; --config|-c) Pokebotconfig diff --git a/tests/nickname_test.py b/tests/nickname_test.py index 6d35d55c55..e4dc102d08 100644 --- a/tests/nickname_test.py +++ b/tests/nickname_test.py @@ -18,6 +18,7 @@ def test_nickname_generation(self): self.assertNicks('{iv_defense}', ['4', '14']) self.assertNicks('{iv_stamina}', ['8', '0']) self.assertNicks('{iv_ads}', ['9/4/8', '6/14/0']) + self.assertNicks('{iv_ads_hex}', ['948', '6E0']) self.assertNicks('{iv_sum}', ['21', '20']) self.assertNicks('{iv_pct}', ['047', '044']) self.assertNicks('{iv_pct2}', ['46', '44']) @@ -52,9 +53,13 @@ def test_nickname_generation(self): self.assertNicks('{pokemon.fast_attack.dps:.2f}', ['12.00', '10.91']) self.assertNicks('{pokemon.fast_attack.dps:.0f}', ['12', '11']) self.assertNicks('{iv_pct}_{iv_ads}', ['047_9/4/8', '044_6/14/0']) + self.assertNicks('{iv_pct}_{iv_ads_hex}', ['047_948', '044_6E0']) self.assertNicks( '{ivcp_pct2}_{iv_pct2}_{iv_ads}', ['48_46_9/4/8', '38_44_6/14/0']) + self.assertNicks( + '{ivcp_pct2}_{iv_pct2}_{iv_ads_hex}', + ['48_46_948', '38_44_6E0']) self.assertNicks( '{attack_code}{attack_pct1}{defense_pct1}{ivcp_pct1}{name}', ['Lh474Golbat', 'nn853Rattata'])