Esempio n. 1
0
    def _setup_event_system(self):
        handlers = []

        if self.config.logging and 'color' in self.config.logging and self.config.logging['color']:
            handlers.append(ColoredLoggingHandler(self))
        else:
            handlers.append(LoggingHandler(self))

        if self.config.enable_social:
            handlers.append(SocialHandler(self))

        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self,
                self.config.websocket_server_url
            )
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        # @var EventManager
        self.event_manager = EventManager(self.config.walker_limit_output, *handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)
Esempio n. 2
0
def facade_main():

    print("Client: I want to have a big party for my wife's birthday.\n")


    em = EventManager()
    em.arrange()
Esempio n. 3
0
    def _setup_event_system(self):
        handlers = []
        if self.config.logging_color:
            handlers.append(ColoredLoggingHandler())
        else:
            handlers.append(LoggingHandler())

        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(
                    self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self, self.config.websocket_server_url)
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        self.event_manager = EventManager(*handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)
Esempio n. 4
0
    def __init__(self, verbose=False, visual_debug=False):
        self.verbose = verbose
        self.visual_debug = visual_debug

        ipdb.launch_ipdb_on_exception()

        # Control Stuff
        self.researched_warpgate = False  # Remove me later

        # Managers and controllers
        self.worker_controller = WorkerController(bot=self,
                                                  verbose=self.verbose)
        self.army_controller = ArmyController(bot=self, verbose=self.verbose)
        self.scouting_controller = ScoutingController(bot=self,
                                                      verbose=self.verbose)
        self.upgrades_controller = UpgradesController(bot=self,
                                                      verbose=self.verbose)
        self.robotics_facility_controller = RoboticsFacilitiyController(
            bot=self,
            verbose=self.verbose,
        )
        self.gateway_controller = GatewayController(
            bot=self, verbose=self.verbose, auto_morph_to_warpgate=True)
        self.building_controller = BuildingController(bot=self,
                                                      verbose=self.verbose)
        self.event_manager = EventManager()
        self.build_order_controller = BuildOrderController(
            verbose=self.verbose, bot=self)
        self.coordinator = Coordinator(bot=self,
                                       verbose=self.verbose,
                                       build_order='three_gate_blink_all_in')

        self.order_queue = []
    def __init__(self, novel_helper, robot_army):
        self.nh = novel_helper
        self.novel_helper = novel_helper
        self.book_warehouse = self.nh.book_warehouse
        self.robot_army = robot_army
        self.user_map = dict()
        self.init_user_map()
        self.book_map = dict()
        self.init_book_map()

        self.offline_uid_set = set(list(self.user_map.keys()))
        self.recommend_uid_set = set()
        self.choosing_uid_set = set()
        self.click_drive_uid_set = set()
        self.read_drive_uid_set = set()
        self.reading_uid_set = set()
        self.has_online_set = set()

        self._period = 0

        self.rec_sys = RecSysRandom(self)

        self.event_manager = EventManager(self)
        self.event_manager.clean_all_csv()

        self.metrics_client = MetricsClient(self)
def on_handshake(event):
    sock = event.get_initiator()
    message = sock.recv(16)
    connecting_session = uuid.UUID(bytes=message[:16])
    storage = SimpleMemCache()

    # Invalid Handshake
    if len(message) != 16:
        sock.send(Message('SERVER', 'Invalid handshake!').serialize())
        sock.shutdown(SHUT_RDWR)
        sock.close()
        return

    connecting_user = None

    if connecting_session == uuid.UUID(int=0):
        session_id = uuid.uuid4()
        connecting_user = ConnectionUser(sock, session_id, random_name())
        storage.add_user(connecting_user)
        sock.send(Message('SERVER', '{}.{}'.format(session_id.int, connecting_user.name)).serialize())

    elif storage.user_exists(connecting_session):
        connecting_user = storage.get_user_by_id(connecting_session)
        sock.send(Message('SERVER', '{}.{}'.format(connecting_session.int, connecting_user.name)).serialize())

    EventManager().emit(RoomJoinEvent(event.get_room(), connecting_user))
Esempio n. 7
0
    def _setup_event_system(self):
        handlers = []

        if self.config.logging and 'color' in self.config.logging and self.config.logging['color']:
            handlers.append(ColoredLoggingHandler(self))
        else:
            handlers.append(LoggingHandler(self))

        if self.config.enable_social:
            handlers.append(SocialHandler(self))

        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self,
                self.config.websocket_server_url
            )
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        # @var EventManager
        self.event_manager = EventManager(self.config.walker_limit_output, *handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)
Esempio n. 8
0
    def __init__(self, root, is_type_editor):
        """
        Args:
            root: path of project root folder
            is_type_editor: bool. see class docstring                
        """
        self.path = util.normpath(os.path.abspath(root))
        self.is_type_editor = is_type_editor
        self._auto_create_clazz_folder = True

        # Must be the first
        self.event_manager = EventManager()
        self.type_manager = TypeManager()
        self.fs_manager = FileSystemManager(
            self, os.path.join(self.path, const.PROJECT_FOLDER_DATA))
        # should after fs_manager
        self.object_manager = ObjectManager(self)
        # should after object_manager
        self.ref_manager = RefManager()

        # self._langauges = ('en', )
        self._default_language = 'en'
        self._translations = {}
        self._verifier = None
        self._loading_errors = AttrVerifyLogger()

        self.tags = set()

        self._next_ids = {}  # {clazz_name: next_id}
        self._loaded = False
        self._editor_project = None
Esempio n. 9
0
    def _setup_event_system(self):
        handlers = []
        if self.config.logging_color:
            handlers.append(ColoredLoggingHandler())
        else:
            handlers.append(LoggingHandler())

        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self,
                self.config.websocket_server_url
            )
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        self.event_manager = EventManager(*handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)
Esempio n. 10
0
def test():
    event_name = ["Fund", "Extreme"]
    listener = [
        Listener(event_name[0], start_fund),
        Listener(event_name[1], start_extreme)
    ]

    event_manager = EventManager()
    event_manager.AddEventListener(listener[0])

    event_driver = Event_Source(event_manager)

    event_driver.start()
    event_driver.start()
    event_driver.start()

    event_manager.Start()
Esempio n. 11
0
def main_setup(output_name):
    '''
    This should be called at the beginning of main if you're running unit tests
    or simulations.
    We need to set |globals_.event_manager| for most class definitions and helper
    functions to make sense.
    '''
    globals_.event_manager = EventManager(output_name)
    globals_.stats_manager = StatsManager(output_name)
Esempio n. 12
0
    def __init__(self, background=None):
        
        if background!=None:
            self.set_background(background)

        self.event_manager = EventManager()
        self.subscribe(pygame.QUIT, "quit", self)

        self.items = pygame.sprite.Group()
Esempio n. 13
0
def main():
    parse_args()
    ev = EventManager()
    game = Game(ev, WIDTH, HEIGHT)
    keybd = KeyboardController(ev)
    movement_controller = MovementController(ev, game.sprites, game.board)
    apply_fn(
        lambda x: ev.register_listener(x),
        [
            keybd,
            game,
            movement_controller,
        ],
    )
    if IS_HOST:
        begin_on_client_connect(ev, PORT)
    else:
        connect_to_server_host(ev, HOST, PORT)

    reactor.run()
Esempio n. 14
0
 def __init__(self,):
     """
     Définit une Window et un screen qui y est dessiné
     """
     self.surface = pygame.display.set_mode((1024, 574), pg.DOUBLEBUF)
     pygame.display.set_caption("Dunwall's Gate")
     self.eventmanager = EventManager(self)
     self._screen = None
     self.game = None
     self.set_screen(HomeScreen())
     self.fpsClock = pygame.time.Clock()
     self.do_run = True
    def serve_one(self, sock):
        EventManager().emit(HandshakingEvent(self, sock))

        user = None

        for u in self.connecting_users:
            if u.get_socket() == sock:
                user = u

        while True:
            message = sock.recv(1024)
            if len(message) == 0:
                EventManager().emit(RoomExitEvent(self, user))
                break
            else:
                m = Message(raw=message)
                if m.is_command():
                    EventManager().emit(
                        UserCommandEvent(self, user, Command(raw=message)))
                else:
                    EventManager().emit(UserMessageEvent(self, user, m))
    def __init__(self, node, southwest, northeast, extras):

        logging.info("Beginning TwitterStreamer init")

        StreamListener.__init__(self)

        # Lock and target location
        self.lock = threading.Lock()
        self.buckets = {}
        self.deltas = {}
        self.uploaders = {}
        self.location = [southwest[LONG],southwest[LAT],northeast[LONG],northeast[LAT]]
        self.event_manager = EventManager()


        # Upload handler
        self.node = node

        logging.info("TwitterStreamer init successful")
Esempio n. 17
0
class Window():
    def __init__(self,):
        """
        Définit une Window et un screen qui y est dessiné
        """
        self.surface = pygame.display.set_mode((1024, 574), pg.DOUBLEBUF)
        pygame.display.set_caption("Dunwall's Gate")
        self.eventmanager = EventManager(self)
        self._screen = None
        self.game = None
        self.set_screen(HomeScreen())
        self.fpsClock = pygame.time.Clock()
        self.do_run = True

    def set_screen(self, screen):
        """
        Définit l'écran affiché dans la Window
        """
        if self._screen is not None:
            self._screen.surface.fill(0)
            self._screen.shutdown()
            self.eventmanager.purge_callbacks(self._screen)
        self._screen = screen
        self._screen.start(self, self.eventmanager)

    def set_do_run(self, value=True):
        self.do_run = value

    def start_game(self, game):
        self.game = game
        game.start(self)

    def run(self):
        """
        Lance le jeu
        """
        self.eventmanager.on_quit(lambda x: self.set_do_run(False), "global")
        while self.do_run:
            self._screen.update()
            self._screen.draw()
            if self.game:
                self.game.game_event.update()
            self.eventmanager.run(pygame.event.get())
            pygame.display.flip()
            self.fpsClock.tick(FPS)
        pygame.quit()
Esempio n. 18
0
class PokemonGoBot(Datastore):
    @property
    def position(self):
        return self.api.actual_lat, self.api.actual_lng, self.api.actual_alt

    @property
    def noised_position(self):
        return self.api.noised_lat, self.api.noised_lng, self.api.noised_alt

    #@position.setter # these should be called through api now that gps replication is there...
    #def position(self, position_tuple):
    #    self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple

    @property
    def player_data(self):
        """
        Returns the player data as received from the API.
        :return: The player data.
        :rtype: dict
        """
        return self._player

    def __init__(self, config):

        # Database connection MUST be setup before migrations will work
        self.database = _init_database('/data/{}.db'.format(config.username))

        self.config = config
        super(PokemonGoBot, self).__init__()

        self.fort_timeouts = dict()
        self.pokemon_list = json.load(
            open(os.path.join(_base_dir, 'data', 'pokemon.json'))
        )
        self.item_list = json.load(open(os.path.join(_base_dir, 'data', 'items.json')))
        self.metrics = Metrics(self)
        self.latest_inventory = None
        self.cell = None
        self.recent_forts = [None] * config.forts_max_circle_size
        self.tick_count = 0
        self.softban = False
        self.start_position = None
        self.last_map_object = None
        self.last_time_map_object = 0
        self.logger = logging.getLogger(type(self).__name__)
        self.alt = self.config.gps_default_altitude

        # Make our own copy of the workers for this instance
        self.workers = []

        # Theading setup for file writing
        self.web_update_queue = Queue.Queue(maxsize=1)
        self.web_update_thread = threading.Thread(target=self.update_web_location_worker)
        self.web_update_thread.start()

        # Heartbeat limiting
        self.heartbeat_threshold = self.config.heartbeat_threshold
        self.heartbeat_counter = 0
        self.last_heartbeat = time.time()


    def start(self):
        self._setup_event_system()
        self._setup_logging()
        self._setup_api()
        self._load_recent_forts()
        init_inventory(self)
        self.display_player_info()
        self._print_character_info()
        if self.config.pokemon_bag_show_at_start and self.config.pokemon_bag_pokemon_info:
            self._print_list_pokemon()

        random.seed()

    def _setup_event_system(self):
        handlers = []
        if self.config.logging_color:
            handlers.append(ColoredLoggingHandler())
        else:
            handlers.append(LoggingHandler())
        if self.config.enable_social:
            handlers.append(SocialHandler(self))
        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self,
                self.config.websocket_server_url
            )
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        self.event_manager = EventManager(*handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)

            # Registering event:
            # self.event_manager.register_event("location", parameters=['lat', 'lng'])
            #
            # Emitting event should be enough to add logging and send websocket
            # message: :
            # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}),

    def _register_events(self):
        self.event_manager.register_event(
            'location_found',
            parameters=('position', 'location')
        )
        self.event_manager.register_event('api_error')
        self.event_manager.register_event('config_error')

        self.event_manager.register_event('login_started')
        self.event_manager.register_event('login_failed')
        self.event_manager.register_event('login_successful')

        self.event_manager.register_event('set_start_location')
        self.event_manager.register_event('load_cached_location')
        self.event_manager.register_event('location_cache_ignored')
        self.event_manager.register_event(
            'position_update',
            parameters=(
                'current_position',
                'last_position',
                'distance', # optional
                'distance_unit' # optional
            )
        )
        self.event_manager.register_event(
            'path_lap_update',
            parameters=(
                'number_lap',
                'number_lap_max'
            )
        )
        self.event_manager.register_event(
            'path_lap_end',
            parameters=(
                'duration',
                'resume'
            )
        )  
        
        
        self.event_manager.register_event('location_cache_error')

        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(
            'next_sleep',
            parameters=('time',)
        )
        self.event_manager.register_event(
            'bot_sleep',
            parameters=(
                'time_hms',
                'wake'
            )
        )

        # random pause
        self.event_manager.register_event(
            'next_random_pause',
            parameters=(
                'time',
                'duration'
            )
        )
        self.event_manager.register_event(
            'bot_random_pause',
            parameters=(
                'time_hms',
                'resume'
            )
        )

        # random alive pause
        self.event_manager.register_event(
            'next_random_alive_pause',
            parameters=(
                'time',
                'duration'
            )
        )
        self.event_manager.register_event(
            'bot_random_alive_pause',
            parameters=(
                'time_hms',
                'resume'
            )
        )

        # fort stuff
        self.event_manager.register_event(
            'spun_fort',
            parameters=(
                'fort_id',
                'latitude',
                'longitude'
            )
        )
        self.event_manager.register_event(
            'lured_pokemon_found',
            parameters=(
                'fort_id',
                'fort_name',
                'encounter_id',
                'latitude',
                'longitude'
            )
        )
        self.event_manager.register_event(
            'moving_to_fort',
            parameters=(
                'fort_name',
                'distance'
            )
        )
        self.event_manager.register_event(
            'moving_to_lured_fort',
            parameters=(
                'fort_name',
                'distance',
                'lure_distance'
            )
        )
        self.event_manager.register_event(
            'spun_pokestop',
            parameters=(
                'pokestop', 'exp', 'items'
            )
        )
        self.event_manager.register_event(
            'pokestop_empty',
            parameters=('pokestop',)
        )
        self.event_manager.register_event(
            'pokestop_out_of_range',
            parameters=('pokestop',)
        )
        self.event_manager.register_event(
            'pokestop_on_cooldown',
            parameters=('pokestop', 'minutes_left')
        )
        self.event_manager.register_event(
            'unknown_spin_result',
            parameters=('status_code',)
        )
        self.event_manager.register_event('pokestop_searching_too_often')
        self.event_manager.register_event('arrived_at_fort')

        # pokemon stuff
        self.event_manager.register_event(
            'catchable_pokemon',
            parameters=(
                'pokemon_id',
                'spawn_point_id',
                'encounter_id',
                'latitude',
                'longitude',
                'expiration_timestamp_ms',
                'pokemon_name'
            )
        )
        self.event_manager.register_event(
            'pokemon_appeared',
            parameters=(
                'pokemon',
                'ncp',
                'cp',
                'iv',
                'iv_display',
                'encounter_id',
                'latitude',
                'longitude',
                'pokemon_id'
            )
        )
        self.event_manager.register_event('no_pokeballs')
        self.event_manager.register_event('enough_ultraballs')
        self.event_manager.register_event(
            'pokemon_catch_rate',
            parameters=(
                'catch_rate',
                'ball_name',
                'berry_name',
                'berry_count'
            )
        )
        self.event_manager.register_event(
            'threw_berry',
            parameters=(
                'berry_name',
                'ball_name',
                'new_catch_rate'
            )
        )
        self.event_manager.register_event(
            'threw_pokeball',
            parameters=(
                'throw_type',
                'spin_label',
                'ball_name',
                'success_percentage',
                'count_left'
            )
        )
        self.event_manager.register_event(
            'pokemon_capture_failed',
            parameters=('pokemon',)
        )
        self.event_manager.register_event(
            'pokemon_vanished',
            parameters=(
                'pokemon',
                'encounter_id',
                'latitude',
                'longitude',
                'pokemon_id'
            )
        )
        self.event_manager.register_event('pokemon_not_in_range')
        self.event_manager.register_event('pokemon_inventory_full')
        self.event_manager.register_event(
            'pokemon_caught',
            parameters=(
                'pokemon',
                'ncp', 'cp', 'iv', 'iv_display', 'exp',
                'encounter_id',
                'latitude',
                'longitude',
                'pokemon_id'
            )
        )
        self.event_manager.register_event(
            'pokemon_evolved',
            parameters=('pokemon', 'iv', 'cp', 'xp', 'candy')
        )
        self.event_manager.register_event('skip_evolve')
        self.event_manager.register_event('threw_berry_failed', parameters=('status_code',))
        self.event_manager.register_event('vip_pokemon')
        self.event_manager.register_event('gained_candy', parameters=('quantity', 'type'))
        self.event_manager.register_event('catch_limit')

        # level up stuff
        self.event_manager.register_event(
            'level_up',
            parameters=(
                'previous_level',
                'current_level'
            )
        )
        self.event_manager.register_event(
            'level_up_reward',
            parameters=('items',)
        )

        # lucky egg
        self.event_manager.register_event(
            'used_lucky_egg',
            parameters=('amount_left',)
        )
        self.event_manager.register_event('lucky_egg_error')

        # softban
        self.event_manager.register_event('softban')
        self.event_manager.register_event('softban_fix')
        self.event_manager.register_event('softban_fix_done')

        # egg incubating
        self.event_manager.register_event(
            'incubate_try',
            parameters=(
                'incubator_id',
                'egg_id'
            )
        )
        self.event_manager.register_event(
            'incubate',
            parameters=('distance_in_km',)
        )
        self.event_manager.register_event(
            'next_egg_incubates',
            parameters=('eggs_left', 'eggs_inc', 'eggs')
        )
        self.event_manager.register_event('incubator_already_used')
        self.event_manager.register_event('egg_already_incubating')
        self.event_manager.register_event(
            'egg_hatched',
            parameters=(
                'pokemon',
                'cp', 'iv', 'exp', 'stardust', 'candy'
            )
        )

        # discard item
        self.event_manager.register_event(
            'item_discarded',
            parameters=(
                'amount', 'item', 'maximum'
            )
        )
        self.event_manager.register_event(
            'item_discard_skipped',
            parameters=('space',)
        )
        self.event_manager.register_event(
            'item_discard_fail',
            parameters=('item',)
        )

        # inventory
        self.event_manager.register_event('inventory_full')

        # release
        self.event_manager.register_event(
            'keep_best_release',
            parameters=(
                'amount', 'pokemon', 'criteria'
            )
        )
        self.event_manager.register_event(
            'future_pokemon_release',
            parameters=(
                'pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic'
            )
        )
        self.event_manager.register_event(
            'pokemon_release',
            parameters=('pokemon', 'iv', 'cp', 'candy')
        )

        # polyline walker
        self.event_manager.register_event(
            'polyline_request',
            parameters=('url',)
        )

        # cluster
        self.event_manager.register_event(
            'found_cluster',
            parameters=(
                'num_points', 'forts', 'radius', 'distance'
            )
        )
        self.event_manager.register_event(
            'arrived_at_cluster',
            parameters=(
                'num_points', 'forts', 'radius'
            )
        )

        # rename
        self.event_manager.register_event(
            'rename_pokemon',
            parameters=('old_name', 'current_name',)
        )
        self.event_manager.register_event(
            'pokemon_nickname_invalid',
            parameters=('nickname',)
        )
        self.event_manager.register_event(
            'unset_pokemon_nickname',
            parameters=('old_name',)
        )

        # Move To map pokemon
        self.event_manager.register_event(
            'move_to_map_pokemon_fail',
            parameters=('message',)
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_updated_map',
            parameters=('lat', 'lon')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_teleport_to',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_encounter',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_move_towards',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_teleport_back',
            parameters=('last_lat', 'last_lon')
        )
        self.event_manager.register_event(
            'moving_to_pokemon_throught_fort',
            parameters=('fort_name', 'distance','poke_name','poke_dist')
        )

        # cached recent_forts
        self.event_manager.register_event('loaded_cached_forts')
        self.event_manager.register_event('cached_fort')
        self.event_manager.register_event(
            'no_cached_forts',
            parameters=('path', )
        )
        self.event_manager.register_event(
            'error_caching_forts',
            parameters=('path', )
        )
        # database shit
        self.event_manager.register_event('catch_log')
        self.event_manager.register_event('evolve_log')
        self.event_manager.register_event('login_log')
        self.event_manager.register_event('transfer_log')
        self.event_manager.register_event('pokestop_log')
        self.event_manager.register_event('softban_log')

    def tick(self):
        self.health_record.heartbeat()
        self.cell = self.get_meta_cell()

        now = time.time() * 1000

        for fort in self.cell["forts"]:
            timeout = fort.get("cooldown_complete_timestamp_ms", 0)

            if timeout >= now:
                self.fort_timeouts[fort["id"]] = timeout

        self.tick_count += 1

        # Check if session token has expired
        self.check_session(self.position)

        for worker in self.workers:
            if worker.work() == WorkerResult.RUNNING:
                return

    def get_meta_cell(self):
        location = self.position[0:2]
        cells = self.find_close_cells(*location)

        # Combine all cells into a single dict of the items we care about.
        forts = []
        wild_pokemons = []
        catchable_pokemons = []
        for cell in cells:
            if "forts" in cell and len(cell["forts"]):
                forts += cell["forts"]
            if "wild_pokemons" in cell and len(cell["wild_pokemons"]):
                wild_pokemons += cell["wild_pokemons"]
            if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]):
                catchable_pokemons += cell["catchable_pokemons"]

        # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved
        if len(forts) > 1 or not self.cell:
            return {
                "forts": forts,
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }
        # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells.
        else:
            return {
                "forts": self.cell["forts"],
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }

    def update_web_location(self, cells=[], lat=None, lng=None, alt=None):
        # we can call the function with no arguments and still get the position
        # and map_cells
        if lat is None:
            lat = self.api._position_lat
        if lng is None:
            lng = self.api._position_lng
        if alt is None:
            alt = self.api._position_alt

        # dont cache when teleport_to
        if self.api.teleporting:
            return

        if cells == []:
            location = self.position[0:2]
            cells = self.find_close_cells(*location)

        user_data_cells = os.path.join(_base_dir, 'data', 'cells-%s.json' % self.config.username)
        try:
            with open(user_data_cells, 'w') as outfile:
                json.dump(cells, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

        user_web_location = os.path.join(
            _base_dir, 'web', 'location-%s.json' % self.config.username
        )
        # alt is unused atm but makes using *location easier
        try:
            with open(user_web_location, 'w') as outfile:
                json.dump({
                    'lat': lat,
                    'lng': lng,
                    'alt': alt,
                    'cells': cells
                }, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

        user_data_lastlocation = os.path.join(
            _base_dir, 'data', 'last-location-%s.json' % self.config.username
        )
        try:
            with open(user_data_lastlocation, 'w') as outfile:
                json.dump({'lat': lat, 'lng': lng, 'alt': alt, 'start_position': self.start_position}, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

    def find_close_cells(self, lat, lng):
        cellid = get_cell_ids(lat, lng)
        timestamp = [0, ] * len(cellid)
        response_dict = self.get_map_objects(lat, lng, timestamp, cellid)
        map_objects = response_dict.get(
            'responses', {}
        ).get('GET_MAP_OBJECTS', {})
        status = map_objects.get('status', None)

        map_cells = []
        if status and status == 1:
            map_cells = map_objects['map_cells']
            position = (lat, lng, 0)
            map_cells.sort(
                key=lambda x: distance(
                    lat,
                    lng,
                    x['forts'][0]['latitude'],
                    x['forts'][0]['longitude']) if x.get('forts', []) else 1e6
            )
        return map_cells

    def _setup_logging(self):
        # log settings
        # log format

        if self.config.debug:
            log_level = logging.DEBUG
            logging.getLogger("requests").setLevel(logging.DEBUG)
            logging.getLogger("websocket").setLevel(logging.DEBUG)
            logging.getLogger("socketio").setLevel(logging.DEBUG)
            logging.getLogger("engineio").setLevel(logging.DEBUG)
            logging.getLogger("socketIO-client").setLevel(logging.DEBUG)
            logging.getLogger("pgoapi").setLevel(logging.DEBUG)
            logging.getLogger("rpc_api").setLevel(logging.DEBUG)
        else:
            log_level = logging.ERROR
            logging.getLogger("requests").setLevel(logging.ERROR)
            logging.getLogger("websocket").setLevel(logging.ERROR)
            logging.getLogger("socketio").setLevel(logging.ERROR)
            logging.getLogger("engineio").setLevel(logging.ERROR)
            logging.getLogger("socketIO-client").setLevel(logging.ERROR)
            logging.getLogger("pgoapi").setLevel(logging.ERROR)
            logging.getLogger("rpc_api").setLevel(logging.ERROR)

        logging.basicConfig(
            level=log_level,
            format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s'
        )
    def check_session(self, position):

        # Check session expiry
        if self.api._auth_provider and self.api._auth_provider._ticket_expire:

            # prevent crash if return not numeric value
            if not self.is_numeric(self.api._auth_provider._ticket_expire):
                self.logger.info("Ticket expired value is not numeric", 'yellow')
                return

            remaining_time = \
                self.api._auth_provider._ticket_expire / 1000 - time.time()

            if remaining_time < 60:
                self.event_manager.emit(
                    'api_error',
                    sender=self,
                    level='info',
                    formatted='Session stale, re-logging in.'
                )
                self.api = ApiWrapper(config=self.config)
                self.api.set_position(*position)
                self.login()
                self.api.activate_signature(self.get_encryption_lib())

    @staticmethod
    def is_numeric(s):
        try:
            float(s)
            return True
        except ValueError:
            return False

    def login(self):
        self.event_manager.emit(
            'login_started',
            sender=self,
            level='info',
            formatted="Login procedure started."
        )
        lat, lng = self.position[0:2]
        self.api.set_position(lat, lng, self.alt) # or should the alt kept to zero?

        while not self.api.login(
            self.config.auth_service,
            str(self.config.username),
            str(self.config.password)):

            self.event_manager.emit(
                'login_failed',
                sender=self,
                level='info',
                formatted="Login error, server busy. Waiting 10 seconds to try again."
            )
            time.sleep(10)

        with self.database as conn:
            c = conn.cursor()
            c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='login'")

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute('''INSERT INTO login (timestamp, message) VALUES (?, ?)''', (time.time(), 'LOGIN_SUCCESS'))
                break
            else:
                self.event_manager.emit(
                    'login_failed',
                    sender=self,
                    level='info',
                    formatted="Login table not founded, skipping log"
                )
                break

        self.event_manager.emit(
            'login_successful',
            sender=self,
            level='info',
            formatted="Login successful."
        )

    def get_encryption_lib(self):
        if _platform == "Windows" or _platform == "win32":
            # Check if we are on 32 or 64 bit
            if sys.maxsize > 2**32:
                file_name = 'encrypt_64.dll'
            else:
                file_name = 'encrypt.dll'
        else:
            file_name = 'encrypt.so'

        if self.config.encrypt_location == '':
            path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
        else:
            path = self.config.encrypt_location

        full_path = path + '/'+ file_name
        if not os.path.isfile(full_path):
            self.logger.error(file_name + ' is not found! Please place it in the bots root directory or set encrypt_location in config.')
            self.logger.info('Platform: '+ _platform + ' ' + file_name + ' directory: '+ path)
            sys.exit(1)
        else:
            self.logger.info('Found '+ file_name +'! Platform: ' + _platform + ' ' + file_name + ' directory: ' + path)

        return full_path

    def _setup_api(self):
        # instantiate pgoapi
        self.api = ApiWrapper(config=self.config)

        # provide player position on the earth
        self._set_starting_position()

        self.login()
        # chain subrequests (methods) into one RPC call

        self.api.activate_signature(self.get_encryption_lib())
        self.logger.info('')
        # send empty map_cells and then our position
        self.update_web_location()

    def _print_character_info(self):
        # get player profile call
        # ----------------------
        response_dict = self.api.get_player()
        # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2)))
        currency_1 = "0"
        currency_2 = "0"

        if response_dict:
            self._player = response_dict['responses']['GET_PLAYER']['player_data']
            player = self._player
        else:
            self.logger.info(
                "The API didn't return player info, servers are unstable - "
                "retrying.", 'red'
            )
            sleep(5)
            self._print_character_info()

        # @@@ TODO: Convert this to d/m/Y H:M:S
        creation_date = datetime.datetime.fromtimestamp(
            player['creation_timestamp_ms'] / 1e3)
        creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S")

        pokecoins = '0'
        stardust = '0'
        items_inventory = inventory.items()

        if 'amount' in player['currencies'][0]:
            pokecoins = player['currencies'][0]['amount']
        if 'amount' in player['currencies'][1]:
            stardust = player['currencies'][1]['amount']
        self.logger.info('')
        self.logger.info('--- {username} ---'.format(**player))

        self.logger.info(
            'Pokemon Bag: {}/{}'.format(
                inventory.Pokemons.get_space_used(),
                inventory.get_pokemon_inventory_size()
            )
        )
        self.logger.info(
            'Items: {}/{}'.format(
                inventory.Items.get_space_used(),
                inventory.get_item_inventory_size()
            )
        )
        self.logger.info(
            'Stardust: {}'.format(stardust) +
            ' | Pokecoins: {}'.format(pokecoins)
        )
        # Items Output
        self.logger.info(
            'PokeBalls: ' + str(items_inventory.get(1).count) +
            ' | GreatBalls: ' + str(items_inventory.get(2).count) +
            ' | UltraBalls: ' + str(items_inventory.get(3).count) +
            ' | MasterBalls: ' + str(items_inventory.get(4).count))

        self.logger.info(
            'RazzBerries: ' + str(items_inventory.get(701).count) +
            ' | BlukBerries: ' + str(items_inventory.get(702).count) +
            ' | NanabBerries: ' + str(items_inventory.get(703).count))

        self.logger.info(
            'LuckyEgg: ' + str(items_inventory.get(301).count) +
            ' | Incubator: ' + str(items_inventory.get(902).count) +
            ' | TroyDisk: ' + str(items_inventory.get(501).count))

        self.logger.info(
            'Potion: ' + str(items_inventory.get(101).count) +
            ' | SuperPotion: ' + str(items_inventory.get(102).count) +
            ' | HyperPotion: ' + str(items_inventory.get(103).count) +
            ' | MaxPotion: ' + str(items_inventory.get(104).count))

        self.logger.info(
            'Incense: ' + str(items_inventory.get(401).count) +
            ' | IncenseSpicy: ' + str(items_inventory.get(402).count) +
            ' | IncenseCool: ' + str(items_inventory.get(403).count))

        self.logger.info(
            'Revive: ' + str(items_inventory.get(201).count) +
            ' | MaxRevive: ' + str(items_inventory.get(202).count))

        self.logger.info('')

    def _print_list_pokemon(self):
        # get pokemon list
        bag = inventory.pokemons().all()
        id_list =list(set(map(lambda x: x.pokemon_id, bag)))
        id_list.sort()
        pokemon_list = [filter(lambda x: x.pokemon_id == y, bag) for y in id_list]

        show_count = self.config.pokemon_bag_show_count
        show_candies = self.config.pokemon_bag_show_candies
        poke_info_displayed = self.config.pokemon_bag_pokemon_info

        def get_poke_info(info, pokemon):
            poke_info = {
                'cp': 'CP {}'.format(pokemon.cp),
                'iv_ads': 'A/D/S {}/{}/{}'.format(pokemon.iv_attack, pokemon.iv_defense, pokemon.iv_stamina),
                'iv_pct': 'IV {}'.format(pokemon.iv),
                'ivcp': 'IVCP {}'.format(round(pokemon.ivcp,2)),
                'ncp': 'NCP {}'.format(round(pokemon.cp_percent,2)),
                'level': "Level {}".format(pokemon.level),
                'hp': 'HP {}/{}'.format(pokemon.hp, pokemon.hp_max),
                'moveset': 'Moves: {}'.format(pokemon.moveset),
                'dps': 'DPS {}'.format(round(pokemon.moveset.dps, 2))
            }
            if info not in poke_info:
                raise ConfigException("info '{}' isn't available for displaying".format(info))
            return poke_info[info]

        self.logger.info('Pokemon:')

        for pokes in pokemon_list:
            line_p = '#{} {}'.format(pokes[0].pokemon_id, pokes[0].name)
            if show_count:
                line_p += '[{}]'.format(len(pokes))
            if show_candies:
                line_p += '[{} candies]'.format(pokes[0].candy_quantity)
            line_p += ': '
            
            poke_info = ['({})'.format(', '.join([get_poke_info(x, p) for x in poke_info_displayed])) for p in pokes]
            self.logger.info(line_p + ' | '.join(poke_info))

        self.logger.info('')

    def use_lucky_egg(self):
        return self.api.use_item_xp_boost(item_id=301)

    def _set_starting_position(self):

        self.event_manager.emit(
            'set_start_location',
            sender=self,
            level='info',
            formatted='Setting start location.'
        )

        has_position = False

        if self.config.test:
            # TODO: Add unit tests
            return

        if self.config.location:
            location_str = self.config.location
            location = self.get_pos_by_name(location_str.replace(" ", ""))
            msg = "Location found: {location} {position}"
            self.event_manager.emit(
                'location_found',
                sender=self,
                level='info',
                formatted=msg,
                data={
                    'location': location_str,
                    'position': location
                }
            )

            self.api.set_position(*location)

            self.event_manager.emit(
                'position_update',
                sender=self,
                level='info',
                formatted="Now at {current_position}",
                data={
                    'current_position': self.position,
                    'last_position': '',
                    'distance': '',
                    'distance_unit': ''
                }
            )

            self.start_position = self.position

            has_position = True

        if self.config.location_cache:
            try:
                # save location flag used to pull the last known location from
                # the location.json
                self.event_manager.emit(
                    'load_cached_location',
                    sender=self,
                    level='debug',
                    formatted='Loading cached location...'
                )
                with open(os.path.join(_base_dir, 'data', 'last-location-%s.json' %
                    self.config.username)) as f:
                    location_json = json.load(f)
                location = (
                    location_json['lat'],
                    location_json['lng'],
                    location_json['alt'],
                )

                # If location has been set in config, only use cache if starting position has not differed
                if has_position and 'start_position' in location_json:
                    last_start_position = tuple(location_json.get('start_position', []))

                    # Start position has to have been set on a previous run to do this check
                    if last_start_position and last_start_position != self.start_position:
                        msg = 'Going to a new place, ignoring cached location.'
                        self.event_manager.emit(
                            'location_cache_ignored',
                            sender=self,
                            level='debug',
                            formatted=msg
                        )
                        return

                self.api.set_position(*location)
                self.event_manager.emit(
                    'position_update',
                    sender=self,
                    level='debug',
                    formatted='Loaded location {current_position} from cache',
                    data={
                        'current_position': location,
                        'last_position': '',
                        'distance': '',
                        'distance_unit': ''
                    }
                )

                has_position = True
            except Exception:
                if has_position is False:
                    sys.exit(
                        "No cached Location. Please specify initial location."
                    )
                self.event_manager.emit(
                    'location_cache_error',
                    sender=self,
                    level='debug',
                    formatted='Parsing cached location failed.'
                )

    def get_pos_by_name(self, location_name):
        # Check if the given location is already a coordinate.
        if ',' in location_name:
            possible_coordinates = re.findall(
                "[-]?\d{1,3}[.]\d{3,7}", location_name
            )
            if len(possible_coordinates) >= 2:
                # 2 matches, this must be a coordinate. We'll bypass the Google
                # geocode so we keep the exact location.
                self.logger.info(
                    '[x] Coordinates found in passed in location, '
                    'not geocoding.'
                )
                return float(possible_coordinates[0]), float(possible_coordinates[1]), (float(possible_coordinates[2]) if len(possible_coordinates) == 3 else self.alt)

        geolocator = GoogleV3(api_key=self.config.gmapkey)
        loc = geolocator.geocode(location_name, timeout=10)

        return float(loc.latitude), float(loc.longitude), float(loc.altitude)

    def heartbeat(self):
        # Remove forts that we can now spin again.
        now = time.time()
        self.fort_timeouts = {id: timeout for id, timeout
                              in self.fort_timeouts.iteritems()
                              if timeout >= now * 1000}

        if now - self.last_heartbeat >= self.heartbeat_threshold:
            self.last_heartbeat = now
            request = self.api.create_request()
            request.get_player()
            request.check_awarded_badges()
            request.call()
        try:
            self.web_update_queue.put_nowait(True)  # do this outside of thread every tick
        except Queue.Full:
            pass

    def update_web_location_worker(self):
        while True:
            self.web_update_queue.get()
            self.update_web_location()

    def display_player_info(self):
            inventory_items = self.api.get_inventory()
            inventory_items = inventory_items['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']
            player_stats = next((x["inventory_item_data"]["player_stats"]
                     for x in inventory_items
                     if x.get("inventory_item_data", {}).get("player_stats", {})),
                    None)

            if player_stats:

                nextlvlxp = (int(player_stats.get('next_level_xp', 0)) - int(player_stats.get('experience', 0)))

                if 'level' in player_stats and 'experience' in player_stats:
                    self.logger.info(
                        'Level: {level}'.format(
                            **player_stats) +
                        ' (Next Level: {} XP)'.format(
                            nextlvlxp) +
                        ' (Total: {experience} XP)'
                        ''.format(**player_stats))

                if 'pokemons_captured' in player_stats and 'poke_stop_visits' in player_stats:
                    self.logger.info(
                        'Pokemon Captured: '
                        '{pokemons_captured}'.format(
                            **player_stats) +
                        ' | Pokestops Visited: '
                        '{poke_stop_visits}'.format(
                            **player_stats))

    def get_forts(self, order_by_distance=False):
        forts = [fort
                 for fort in self.cell['forts']
                 if 'latitude' in fort and 'type' in fort]

        if order_by_distance:
            forts.sort(key=lambda x: distance(
                self.position[0],
                self.position[1],
                x['latitude'],
                x['longitude']
            ))

        return forts

    def get_map_objects(self, lat, lng, timestamp, cellid):
        if time.time() - self.last_time_map_object < self.config.map_object_cache_time:
            return self.last_map_object

        self.last_map_object = self.api.get_map_objects(
            latitude=f2i(lat),
            longitude=f2i(lng),
            since_timestamp_ms=timestamp,
            cell_id=cellid
        )
        self.last_time_map_object = time.time()

        return self.last_map_object

    def _load_recent_forts(self):
        if not self.config.forts_cache_recent_forts:
            return


        cached_forts_path = os.path.join(_base_dir, 'data', 'recent-forts-%s.json' % self.config.username)
        try:
            # load the cached recent forts
            with open(cached_forts_path) as f:
                cached_recent_forts = json.load(f)

            num_cached_recent_forts = len(cached_recent_forts)
            num_recent_forts = len(self.recent_forts)

            # Handles changes in max_circle_size
            if not num_recent_forts:
                self.recent_forts = []
            elif num_recent_forts > num_cached_recent_forts:
                self.recent_forts[-num_cached_recent_forts:] = cached_recent_forts
            elif num_recent_forts < num_cached_recent_forts:
                self.recent_forts = cached_recent_forts[-num_recent_forts:]
            else:
                self.recent_forts = cached_recent_forts

            self.event_manager.emit(
                'loaded_cached_forts',
                sender=self,
                level='debug',
                formatted='Loaded cached forts...'
            )
        except IOError:
            self.event_manager.emit(
                'no_cached_forts',
                sender=self,
                level='debug',
                formatted='Starting new cached forts for {path}',
                data={'path': cached_forts_path}
            )
class TwitterStreamer(StreamListener):

    def __init__(self, node, southwest, northeast, extras):

        logging.info("Beginning TwitterStreamer init")

        StreamListener.__init__(self)

        # Lock and target location
        self.lock = threading.Lock()
        self.buckets = {}
        self.deltas = {}
        self.uploaders = {}
        self.location = [southwest[LONG],southwest[LAT],northeast[LONG],northeast[LAT]]
        self.event_manager = EventManager()


        # Upload handler
        self.node = node

        logging.info("TwitterStreamer init successful")

    #
    #   Threading Functiosn
    #

    # Start node
    def start(self):

        logging.info("Starting TwitterStreamer")

        # This handles Twitter authetification and the connection to Twitter Streaming API
        auth = OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
        auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

        # 2nd parameter self serves as the StreamListener object
        self.stream = Stream(auth, self)

        logging.debug("Created Stream object")

        # Start streaming with right parameters
        #self.stream.filter(locations=self.location, async=True)
        t = threading.Thread(target=self.stream.filter, kwargs={'locations':self.location, 'async':False})
        t.daemon = True
        t.start()

        logging.info("TwitterStreamer started successfully")

        return t


    def stop(self):

        logging.info("Stopping TwitterStreamer")

        # Disconnect the stream
        self.stream.disconnect()

        # Cancel our uploading threads
        for t in self.uploaders:
            self.uploaders[t].cancel()

        logging.info("TwitterStreamer stopped")


    #
    #   Data Management
    #

    def find_location(self, json):
        # Primary check
        coordinates = json['place']['bounding_box']['coordinates'][0]
        return coordinates

    def record_event(self,long,lat):

        key = str(long) + "," + str(lat)

        # If this is the first time recording for bucket, launch updater
        if key not in self.buckets:
            self.buckets[key] = self.buckets.get(key,0) + 1
            self.deltas[key] = datetime.datetime.now()
            self.post_bucket(key)
        else:
            self.buckets[key] += 1


    def post_bucket(self, key):

        #now = datetime.datetime.now()
        #period = (now - self.deltas[key]).total_seconds()
        #self.deltas[key] = now
        period = 10

        velocity = float(self.buckets[key]) / float(period)

        accel, torque = self.event_manager.record(velocity)

        x, y = [int(i) for i in key.split(',')]

        x = int(x)
        y = int(y)

        data = {
                'x': x,
                'y': y,
                'width': 1,
                'velocity':velocity,
                'acceleration':accel,
                'torque':torque
            }

        self.node.post_results(data)
        self.buckets[key] = 0

        # Rerun again later
        t = threading.Timer(10, self.post_bucket, args=(key,))
        t.daemon = True
        t.start()

        self.uploaders[key] = t

    #
    #   Event-driven functions
    #

    # Runs for every tweet
    def on_status(self, status):

        logging.debug("Status received!")

        json = status._json
        coords = self.find_location(json)

        # Random location within assigned region TODO improve this?
        left    = max(self.location[0], coords[0][0])
        bottom  = max(self.location[1], coords[0][1])
        right   = min(self.location[2], coords[2][0])
        top     = min(self.location[3], coords[2][1])

        long = int(random.uniform(left, right))
        lat  = int(random.uniform(bottom, top))

        #print lat

        self.record_event(long,lat)

        return True

    def on_warning(self, warning):
        logging.warning(str(warning))
        return True

    # Catches errors
    def on_error(self, status):
        logging.critical("ERROR " + str(status))
        return False

    # Handles incoming control signals
    def on_command(self, command):

        # Reformat incoming commands
        name = command['name']
        text = command['text']

        try:
            name = name.encode('UTF-8')
            text = text.encode('UTF-8')
        except:
            return

        # Don't act on messages we sent.
        if name == self.name:
            return

        # Attempt to parse for commands
        tokens = text.split()

        if tokens[0][1:] == self.name or tokens[0][1:] == self.nick:

            if tokens[1] == "close" or tokens[1] == "stop":
                self.close()

            else:
                #self.respond("Sorry, I don't recognize that command.")
                self.log("Unrecognized command: " + text)

    #
    #   Utility functions
    #

    def respond(self, message):
        #self.messages.push({'name':self.nick, 'text':message})
        pass
Esempio n. 20
0
class PokemonGoBot(object):
    @property
    def position(self):
        return self.api._position_lat, self.api._position_lng, 0

    @position.setter
    def position(self, position_tuple):
        self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple

    @property
    def player_data(self):
        """
        Returns the player data as received from the API.
        :return: The player data.
        :rtype: dict
        """
        return self._player

    def __init__(self, config):
        self.config = config
        self.fort_timeouts = dict()
        self.pokemon_list = json.load(
            open(os.path.join('data', 'pokemon.json')))
        self.item_list = json.load(open(os.path.join('data', 'items.json')))
        self.metrics = Metrics(self)
        self.latest_inventory = None
        self.cell = None
        self.recent_forts = [None] * config.forts_max_circle_size
        self.tick_count = 0
        self.softban = False
        self.start_position = None
        self.last_map_object = None
        self.last_time_map_object = 0
        self.logger = logging.getLogger(type(self).__name__)

        # Make our own copy of the workers for this instance
        self.workers = []

        # Theading setup for file writing
        self.web_update_queue = Queue.Queue(maxsize=1)
        self.web_update_thread = threading.Thread(
            target=self.update_web_location_worker)
        self.web_update_thread.start()

    def start(self):
        self._setup_event_system()
        self._setup_logging()
        self._setup_api()

        random.seed()

    def _setup_event_system(self):
        handlers = [LoggingHandler()]
        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(
                    self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self, self.config.websocket_server_url)
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        self.event_manager = EventManager(*handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)

        # Registering event:
        # self.event_manager.register_event("location", parameters=['lat', 'lng'])
        #
        # Emitting event should be enough to add logging and send websocket
        # message: :
        # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}),

    def _register_events(self):
        self.event_manager.register_event('location_found',
                                          parameters=('position', 'location'))
        self.event_manager.register_event('api_error')
        self.event_manager.register_event('config_error')

        self.event_manager.register_event('login_started')
        self.event_manager.register_event('login_failed')
        self.event_manager.register_event('login_successful')

        self.event_manager.register_event('set_start_location')
        self.event_manager.register_event('load_cached_location')
        self.event_manager.register_event('location_cache_ignored')
        self.event_manager.register_event(
            'position_update',
            parameters=(
                'current_position',
                'last_position',
                'distance',  # optional
                'distance_unit'  # optional
            ))
        self.event_manager.register_event('location_cache_error')

        self.event_manager.register_event('bot_start')
        self.event_manager.register_event('bot_exit')

        # sleep stuff
        self.event_manager.register_event('next_sleep', parameters=('time', ))
        self.event_manager.register_event('bot_sleep',
                                          parameters=('time_in_seconds', ))

        # fort stuff
        self.event_manager.register_event('spun_fort',
                                          parameters=('fort_id', 'latitude',
                                                      'longitude'))
        self.event_manager.register_event('lured_pokemon_found',
                                          parameters=('fort_id', 'fort_name',
                                                      'encounter_id',
                                                      'latitude', 'longitude'))
        self.event_manager.register_event('moving_to_fort',
                                          parameters=('fort_name', 'distance'))
        self.event_manager.register_event('moving_to_lured_fort',
                                          parameters=('fort_name', 'distance',
                                                      'lure_distance'))
        self.event_manager.register_event('spun_pokestop',
                                          parameters=('pokestop', 'exp',
                                                      'items'))
        self.event_manager.register_event('pokestop_empty',
                                          parameters=('pokestop', ))
        self.event_manager.register_event('pokestop_out_of_range',
                                          parameters=('pokestop', ))
        self.event_manager.register_event('pokestop_on_cooldown',
                                          parameters=('pokestop',
                                                      'minutes_left'))
        self.event_manager.register_event('unknown_spin_result',
                                          parameters=('status_code', ))
        self.event_manager.register_event('pokestop_searching_too_often')
        self.event_manager.register_event('arrived_at_fort')

        # pokemon stuff
        self.event_manager.register_event(
            'catchable_pokemon',
            parameters=('pokemon_id', 'spawn_point_id', 'encounter_id',
                        'latitude', 'longitude', 'expiration_timestamp_ms'))
        self.event_manager.register_event('pokemon_appeared',
                                          parameters=(
                                              'pokemon',
                                              'cp',
                                              'iv',
                                              'iv_display',
                                          ))
        self.event_manager.register_event('pokemon_catch_rate',
                                          parameters=('catch_rate',
                                                      'berry_name',
                                                      'berry_count'))
        self.event_manager.register_event('threw_berry',
                                          parameters=('berry_name',
                                                      'new_catch_rate'))
        self.event_manager.register_event('threw_pokeball',
                                          parameters=('pokeball',
                                                      'success_percentage',
                                                      'count_left'))
        self.event_manager.register_event('pokemon_escaped',
                                          parameters=('pokemon', ))
        self.event_manager.register_event('pokemon_vanished',
                                          parameters=('pokemon', ))
        self.event_manager.register_event('pokemon_caught',
                                          parameters=('pokemon', 'cp', 'iv',
                                                      'iv_display', 'exp'))
        self.event_manager.register_event('pokemon_evolved',
                                          parameters=('pokemon', 'iv', 'cp'))
        self.event_manager.register_event('pokemon_evolve_fail',
                                          parameters=('pokemon', ))
        self.event_manager.register_event('skip_evolve')
        self.event_manager.register_event('threw_berry_failed',
                                          parameters=('status_code', ))
        self.event_manager.register_event('vip_pokemon')

        # level up stuff
        self.event_manager.register_event('level_up',
                                          parameters=('previous_level',
                                                      'current_level'))
        self.event_manager.register_event('level_up_reward',
                                          parameters=('items', ))

        # lucky egg
        self.event_manager.register_event('used_lucky_egg',
                                          parameters=('amount_left', ))
        self.event_manager.register_event('lucky_egg_error')

        # softban
        self.event_manager.register_event('softban')
        self.event_manager.register_event('softban_fix')
        self.event_manager.register_event('softban_fix_done')

        # egg incubating
        self.event_manager.register_event('incubate_try',
                                          parameters=('incubator_id',
                                                      'egg_id'))
        self.event_manager.register_event('incubate',
                                          parameters=('distance_in_km', ))
        self.event_manager.register_event('next_egg_incubates',
                                          parameters=('distance_in_km', ))
        self.event_manager.register_event('incubator_already_used')
        self.event_manager.register_event('egg_already_incubating')
        self.event_manager.register_event('egg_hatched',
                                          parameters=('pokemon', 'cp', 'iv',
                                                      'exp', 'stardust',
                                                      'candy'))

        # discard item
        self.event_manager.register_event('item_discarded',
                                          parameters=('amount', 'item',
                                                      'maximum'))
        self.event_manager.register_event('item_discard_fail',
                                          parameters=('item', ))

        # inventory
        self.event_manager.register_event('inventory_full')

        # release
        self.event_manager.register_event('keep_best_release',
                                          parameters=('amount', 'pokemon',
                                                      'criteria'))
        self.event_manager.register_event('future_pokemon_release',
                                          parameters=('pokemon', 'cp', 'iv',
                                                      'below_iv', 'below_cp',
                                                      'cp_iv_logic'))
        self.event_manager.register_event('pokemon_release',
                                          parameters=('pokemon', 'cp', 'iv'))

        # polyline walker
        self.event_manager.register_event('polyline_request',
                                          parameters=('url', ))

        # cluster
        self.event_manager.register_event('found_cluster',
                                          parameters=('num_points', 'forts',
                                                      'radius', 'distance'))
        self.event_manager.register_event('arrived_at_cluster',
                                          parameters=('forts', 'radius'))

        # rename
        self.event_manager.register_event('rename_pokemon',
                                          parameters=('old_name',
                                                      'current_name'))
        self.event_manager.register_event('pokemon_nickname_invalid',
                                          parameters=('nickname', ))
        self.event_manager.register_event('unset_pokemon_nickname')

        # Move To map pokemon
        self.event_manager.register_event('move_to_map_pokemon_fail',
                                          parameters=('message', ))
        self.event_manager.register_event('move_to_map_pokemon_updated_map',
                                          parameters=('lat', 'lon'))
        self.event_manager.register_event('move_to_map_pokemon_teleport_to',
                                          parameters=('poke_name', 'poke_dist',
                                                      'poke_lat', 'poke_lon',
                                                      'disappears_in'))
        self.event_manager.register_event('move_to_map_pokemon_encounter',
                                          parameters=('poke_name', 'poke_dist',
                                                      'poke_lat', 'poke_lon',
                                                      'disappears_in'))
        self.event_manager.register_event('move_to_map_pokemon_move_towards',
                                          parameters=('poke_name', 'poke_dist',
                                                      'poke_lat', 'poke_lon',
                                                      'disappears_in'))
        self.event_manager.register_event('move_to_map_pokemon_teleport_back',
                                          parameters=('last_lat', 'last_lon'))

    def tick(self):
        self.health_record.heartbeat()
        self.cell = self.get_meta_cell()
        self.tick_count += 1

        # Check if session token has expired
        self.check_session(self.position[0:2])

        for worker in self.workers:
            if worker.work() == WorkerResult.RUNNING:
                return

    def get_meta_cell(self):
        location = self.position[0:2]
        cells = self.find_close_cells(*location)

        # Combine all cells into a single dict of the items we care about.
        forts = []
        wild_pokemons = []
        catchable_pokemons = []
        for cell in cells:
            if "forts" in cell and len(cell["forts"]):
                forts += cell["forts"]
            if "wild_pokemons" in cell and len(cell["wild_pokemons"]):
                wild_pokemons += cell["wild_pokemons"]
            if "catchable_pokemons" in cell and len(
                    cell["catchable_pokemons"]):
                catchable_pokemons += cell["catchable_pokemons"]

        # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved
        if len(forts) > 1 or not self.cell:
            return {
                "forts": forts,
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }
        # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells.
        else:
            return {
                "forts": self.cell["forts"],
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }

    def update_web_location(self, cells=[], lat=None, lng=None, alt=None):
        # we can call the function with no arguments and still get the position
        # and map_cells
        if lat is None:
            lat = self.api._position_lat
        if lng is None:
            lng = self.api._position_lng
        if alt is None:
            alt = 0

        if cells == []:
            location = self.position[0:2]
            cells = self.find_close_cells(*location)

            # insert detail info about gym to fort
            for cell in cells:
                if 'forts' in cell:
                    for fort in cell['forts']:
                        if fort.get('type') != 1:
                            response_gym_details = self.api.get_gym_details(
                                gym_id=fort.get('id'),
                                player_latitude=lng,
                                player_longitude=lat,
                                gym_latitude=fort.get('latitude'),
                                gym_longitude=fort.get('longitude'))
                            fort['gym_details'] = response_gym_details.get(
                                'responses', {}).get('GET_GYM_DETAILS', None)

        user_data_cells = "data/cells-%s.json" % self.config.username
        with open(user_data_cells, 'w') as outfile:
            json.dump(cells, outfile)

        user_web_location = os.path.join(
            'web', 'location-%s.json' % self.config.username)
        # alt is unused atm but makes using *location easier
        try:
            with open(user_web_location, 'w') as outfile:
                json.dump({
                    'lat': lat,
                    'lng': lng,
                    'alt': alt,
                    'cells': cells
                }, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

        user_data_lastlocation = os.path.join(
            'data', 'last-location-%s.json' % self.config.username)
        try:
            with open(user_data_lastlocation, 'w') as outfile:
                json.dump(
                    {
                        'lat': lat,
                        'lng': lng,
                        'start_position': self.start_position
                    }, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

    def find_close_cells(self, lat, lng):
        cellid = get_cell_ids(lat, lng)
        timestamp = [
            0,
        ] * len(cellid)
        response_dict = self.get_map_objects(lat, lng, timestamp, cellid)
        map_objects = response_dict.get('responses',
                                        {}).get('GET_MAP_OBJECTS', {})
        status = map_objects.get('status', None)

        map_cells = []
        if status and status == 1:
            map_cells = map_objects['map_cells']
            position = (lat, lng, 0)
            map_cells.sort(
                key=lambda x: distance(lat, lng, x['forts'][0]['latitude'], x[
                    'forts'][0]['longitude']) if x.get('forts', []) else 1e6)
        return map_cells

    def _setup_logging(self):
        # log settings
        # log format

        if self.config.debug:
            log_level = logging.DEBUG
            logging.getLogger("requests").setLevel(logging.DEBUG)
            logging.getLogger("websocket").setLevel(logging.DEBUG)
            logging.getLogger("socketio").setLevel(logging.DEBUG)
            logging.getLogger("engineio").setLevel(logging.DEBUG)
            logging.getLogger("socketIO-client").setLevel(logging.DEBUG)
            logging.getLogger("pgoapi").setLevel(logging.DEBUG)
            logging.getLogger("rpc_api").setLevel(logging.DEBUG)
        else:
            log_level = logging.ERROR
            logging.getLogger("requests").setLevel(logging.ERROR)
            logging.getLogger("websocket").setLevel(logging.ERROR)
            logging.getLogger("socketio").setLevel(logging.ERROR)
            logging.getLogger("engineio").setLevel(logging.ERROR)
            logging.getLogger("socketIO-client").setLevel(logging.ERROR)
            logging.getLogger("pgoapi").setLevel(logging.ERROR)
            logging.getLogger("rpc_api").setLevel(logging.ERROR)

        logging.basicConfig(
            level=log_level,
            format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s')

    def check_session(self, position):
        # Check session expiry
        if self.api._auth_provider and self.api._auth_provider._ticket_expire:

            # prevent crash if return not numeric value
            if not self.is_numeric(self.api._auth_provider._ticket_expire):
                self.logger.info("Ticket expired value is not numeric",
                                 'yellow')
                return

            remaining_time = \
                self.api._auth_provider._ticket_expire / 1000 - time.time()

            if remaining_time < 60:
                self.event_manager.emit(
                    'api_error',
                    sender=self,
                    level='info',
                    formatted='Session stale, re-logging in.')
                position = self.position
                self.api = ApiWrapper()
                self.position = position
                self.login()
                self.api.activate_signature(self.get_encryption_lib())

    @staticmethod
    def is_numeric(s):
        try:
            float(s)
            return True
        except ValueError:
            return False

    def login(self):
        self.event_manager.emit('login_started',
                                sender=self,
                                level='info',
                                formatted="Login procedure started.")
        lat, lng = self.position[0:2]
        self.api.set_position(lat, lng, 0)

        while not self.api.login(self.config.auth_service,
                                 str(self.config.username),
                                 str(self.config.password)):

            self.event_manager.emit(
                'login_failed',
                sender=self,
                level='info',
                formatted=
                "Login error, server busy. Waiting 10 seconds to try again.")
            time.sleep(10)

        self.event_manager.emit('login_successful',
                                sender=self,
                                level='info',
                                formatted="Login successful.")

    def get_encryption_lib(self):
        if _platform == "linux" or _platform == "linux2" or _platform == "darwin":
            file_name = 'encrypt.so'
        elif _platform == "Windows" or _platform == "win32":
            # Check if we are on 32 or 64 bit
            if sys.maxsize > 2**32:
                file_name = 'encrypt_64.dll'
            else:
                file_name = 'encrypt.dll'

        if self.config.encrypt_location == '':
            path = os.path.abspath(
                os.path.join(os.path.dirname(__file__), os.pardir))
        else:
            path = self.config.encrypt_location

        full_path = path + '/' + file_name
        if not os.path.isfile(full_path):
            self.logger.error(
                file_name +
                ' is not found! Please place it in the bots root directory or set libencrypt_location in config.'
            )
            self.logger.info('Platform: ' + _platform +
                             ' Encrypt.so directory: ' + path)
            sys.exit(1)
        else:
            self.logger.info('Found ' + file_name + '! Platform: ' +
                             _platform + ' Encrypt.so directory: ' + path)

        return full_path

    def _setup_api(self):
        # instantiate pgoapi
        self.api = ApiWrapper()

        # provide player position on the earth
        self._set_starting_position()

        self.login()
        # chain subrequests (methods) into one RPC call

        self._print_character_info()
        self.api.activate_signature(self.get_encryption_lib())
        self.logger.info('')
        self.update_inventory()
        # send empty map_cells and then our position
        self.update_web_location()

    def _print_character_info(self):
        # get player profile call
        # ----------------------
        response_dict = self.api.get_player()
        # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2)))
        currency_1 = "0"
        currency_2 = "0"

        if response_dict:
            self._player = response_dict['responses']['GET_PLAYER'][
                'player_data']
            player = self._player
        else:
            self.logger.info(
                "The API didn't return player info, servers are unstable - "
                "retrying.", 'red')
            sleep(5)
            self._print_character_info()

        # @@@ TODO: Convert this to d/m/Y H:M:S
        creation_date = datetime.datetime.fromtimestamp(
            player['creation_timestamp_ms'] / 1e3)
        creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S")

        pokecoins = '0'
        stardust = '0'
        items_stock = self.current_inventory()

        if 'amount' in player['currencies'][0]:
            pokecoins = player['currencies'][0]['amount']
        if 'amount' in player['currencies'][1]:
            stardust = player['currencies'][1]['amount']
        self.logger.info('')
        self.logger.info('--- {username} ---'.format(**player))
        self.get_player_info()
        self.logger.info('Pokemon Bag: {}/{}'.format(
            self.get_inventory_count('pokemon'),
            player['max_pokemon_storage']))
        self.logger.info('Items: {}/{}'.format(
            self.get_inventory_count('item'), player['max_item_storage']))
        self.logger.info('Stardust: {}'.format(stardust) +
                         ' | Pokecoins: {}'.format(pokecoins))
        # Items Output
        self.logger.info('PokeBalls: ' + str(items_stock[1]) +
                         ' | GreatBalls: ' + str(items_stock[2]) +
                         ' | UltraBalls: ' + str(items_stock[3]))

        self.logger.info('RazzBerries: ' + str(items_stock[701]) +
                         ' | BlukBerries: ' + str(items_stock[702]) +
                         ' | NanabBerries: ' + str(items_stock[703]))

        self.logger.info('LuckyEgg: ' + str(items_stock[301]) +
                         ' | Incubator: ' + str(items_stock[902]) +
                         ' | TroyDisk: ' + str(items_stock[501]))

        self.logger.info('Potion: ' + str(items_stock[101]) +
                         ' | SuperPotion: ' + str(items_stock[102]) +
                         ' | HyperPotion: ' + str(items_stock[103]) +
                         ' | MaxPotion: ' + str(items_stock[104]))

        self.logger.info('Incense: ' + str(items_stock[401]) +
                         ' | IncenseSpicy: ' + str(items_stock[402]) +
                         ' | IncenseCool: ' + str(items_stock[403]))

        self.logger.info('Revive: ' + str(items_stock[201]) +
                         ' | MaxRevive: ' + str(items_stock[202]))

        self.logger.info('')

    def use_lucky_egg(self):
        return self.api.use_item_xp_boost(item_id=301)

    def get_inventory(self):
        if self.latest_inventory is None:
            self.latest_inventory = self.api.get_inventory()
        return self.latest_inventory

    def update_inventory(self):
        response = self.get_inventory()
        self.inventory = list()
        inventory_items = response.get('responses', {}).get(
            'GET_INVENTORY', {}).get('inventory_delta',
                                     {}).get('inventory_items', {})
        if inventory_items:
            for item in inventory_items:
                item_info = item.get('inventory_item_data', {}).get('item', {})
                if {"item_id", "count"}.issubset(set(item_info.keys())):
                    self.inventory.append(item['inventory_item_data']['item'])

    def current_inventory(self):
        inventory_req = self.get_inventory()
        inventory_dict = inventory_req['responses']['GET_INVENTORY'][
            'inventory_delta']['inventory_items']

        user_web_inventory = 'web/inventory-%s.json' % self.config.username

        with open(user_web_inventory, 'w') as outfile:
            json.dump(inventory_dict, outfile)

        # get player items stock
        # ----------------------
        items_stock = {x.value: 0 for x in list(Item)}

        for item in inventory_dict:
            item_dict = item.get('inventory_item_data', {}).get('item', {})
            item_count = item_dict.get('count')
            item_id = item_dict.get('item_id')

            if item_count and item_id:
                if item_id in items_stock:
                    items_stock[item_id] = item_count
        return items_stock

    def item_inventory_count(self, id):
        inventory_req = self.get_inventory()
        inventory_dict = inventory_req['responses']['GET_INVENTORY'][
            'inventory_delta']['inventory_items']

        if id == 'all':
            return self._all_items_inventory_count(inventory_dict)
        else:
            return self._item_inventory_count_per_id(id, inventory_dict)

    def _item_inventory_count_per_id(self, id, inventory_dict):
        item_count = 0

        for item in inventory_dict:
            item_dict = item.get('inventory_item_data', {}).get('item', {})
            item_id = item_dict.get('item_id', False)
            item_count = item_dict.get('count', False)
            if item_id == int(id) and item_count:
                return item_count
        return 0

    def _all_items_inventory_count(self, inventory_dict):
        item_count_dict = {}

        for item in inventory_dict:
            item_dict = item.get('inventory_item_data', {}).get('item', {})
            item_id = item_dict.get('item_id', False)
            item_count = item_dict.get('count', False)
            if item_id and item_count:
                item_count_dict[item_id] = item_count

        return item_count_dict

    def _set_starting_position(self):

        self.event_manager.emit('set_start_location',
                                sender=self,
                                level='info',
                                formatted='Setting start location.')

        has_position = False

        if self.config.test:
            # TODO: Add unit tests
            return

        if self.config.location:
            location_str = self.config.location
            location = self.get_pos_by_name(location_str.replace(" ", ""))
            msg = "Location found: {location} {position}"
            self.event_manager.emit('location_found',
                                    sender=self,
                                    level='info',
                                    formatted=msg,
                                    data={
                                        'location': location_str,
                                        'position': location
                                    })

            self.api.set_position(*location)

            self.event_manager.emit('position_update',
                                    sender=self,
                                    level='info',
                                    formatted="Now at {current_position}",
                                    data={
                                        'current_position': self.position,
                                        'last_position': '',
                                        'distance': '',
                                        'distance_unit': ''
                                    })

            self.start_position = self.position

            has_position = True

        if self.config.location_cache:
            try:
                # save location flag used to pull the last known location from
                # the location.json
                self.event_manager.emit('load_cached_location',
                                        sender=self,
                                        level='debug',
                                        formatted='Loading cached location...')
                with open('data/last-location-%s.json' %
                          self.config.username) as f:
                    location_json = json.load(f)
                location = (location_json['lat'], location_json['lng'], 0.0)

                # If location has been set in config, only use cache if starting position has not differed
                if has_position and 'start_position' in location_json:
                    last_start_position = tuple(
                        location_json.get('start_position', []))

                    # Start position has to have been set on a previous run to do this check
                    if last_start_position and last_start_position != self.start_position:
                        msg = 'Going to a new place, ignoring cached location.'
                        self.event_manager.emit('location_cache_ignored',
                                                sender=self,
                                                level='debug',
                                                formatted=msg)
                        return

                self.api.set_position(*location)
                self.event_manager.emit(
                    'position_update',
                    sender=self,
                    level='debug',
                    formatted='Loaded location {current_position} from cache',
                    data={
                        'current_position': location,
                        'last_position': '',
                        'distance': '',
                        'distance_unit': ''
                    })

                has_position = True
            except Exception:
                if has_position is False:
                    sys.exit(
                        "No cached Location. Please specify initial location.")
                self.event_manager.emit(
                    'location_cache_error',
                    sender=self,
                    level='debug',
                    formatted='Parsing cached location failed.')

    def get_pos_by_name(self, location_name):
        # Check if the given location is already a coordinate.
        if ',' in location_name:
            possible_coordinates = re.findall("[-]?\d{1,3}[.]\d{3,7}",
                                              location_name)
            if len(possible_coordinates) == 2:
                # 2 matches, this must be a coordinate. We'll bypass the Google
                # geocode so we keep the exact location.
                self.logger.info(
                    '[x] Coordinates found in passed in location, '
                    'not geocoding.')
                return float(possible_coordinates[0]), float(
                    possible_coordinates[1]), float("0.0")

        geolocator = GoogleV3(api_key=self.config.gmapkey)
        loc = geolocator.geocode(location_name, timeout=10)

        return float(loc.latitude), float(loc.longitude), float(loc.altitude)

    def heartbeat(self):
        # Remove forts that we can now spin again.
        self.fort_timeouts = {
            id: timeout
            for id, timeout in self.fort_timeouts.iteritems()
            if timeout >= time.time() * 1000
        }
        request = self.api.create_request()
        request.get_player()
        request.check_awarded_badges()
        request.call()
        try:
            self.web_update_queue.put_nowait(
                True)  # do this outside of thread every tick
        except Queue.Full:
            pass

    def update_web_location_worker(self):
        while True:
            self.web_update_queue.get()
            self.update_web_location()

    def get_inventory_count(self, what):
        response_dict = self.get_inventory()
        inventory_items = response_dict.get('responses', {}).get(
            'GET_INVENTORY', {}).get('inventory_delta',
                                     {}).get('inventory_items', {})
        if inventory_items:
            pokecount = 0
            itemcount = 1
            for item in inventory_items:
                if 'inventory_item_data' in item:
                    if 'pokemon_data' in item['inventory_item_data']:
                        pokecount += 1
                    itemcount += item['inventory_item_data'].get(
                        'item', {}).get('count', 0)
        if 'pokemon' in what:
            return pokecount
        if 'item' in what:
            return itemcount
        return '0'

    def get_player_info(self):
        response_dict = self.get_inventory()
        inventory_items = response_dict.get('responses', {}).get(
            'GET_INVENTORY', {}).get('inventory_delta',
                                     {}).get('inventory_items', {})
        if inventory_items:
            pokecount = 0
            itemcount = 1
            for item in inventory_items:
                # print('item {}'.format(item))
                playerdata = item.get('inventory_item_data',
                                      {}).get('player_stats')
                if playerdata:
                    nextlvlxp = (int(playerdata.get('next_level_xp', 0)) -
                                 int(playerdata.get('experience', 0)))

                    if 'level' in playerdata and 'experience' in playerdata:
                        self.logger.info(
                            'Level: {level}'.format(**playerdata) +
                            ' (Next Level: {} XP)'.format(nextlvlxp) +
                            ' (Total: {experience} XP)'
                            ''.format(**playerdata))

                    if 'pokemons_captured' in playerdata and 'poke_stop_visits' in playerdata:
                        self.logger.info(
                            'Pokemon Captured: '
                            '{pokemons_captured}'.format(**playerdata) +
                            ' | Pokestops Visited: '
                            '{poke_stop_visits}'.format(**playerdata))

    def has_space_for_loot(self):
        number_of_things_gained_by_stop = 5
        enough_space = (
            self.get_inventory_count('item') <
            self._player['max_item_storage'] - number_of_things_gained_by_stop)

        return enough_space

    def get_forts(self, order_by_distance=False):
        forts = [
            fort for fort in self.cell['forts']
            if 'latitude' in fort and 'type' in fort
        ]

        if order_by_distance:
            forts.sort(key=lambda x: distance(self.position[0], self.position[
                1], x['latitude'], x['longitude']))

        return forts

    def get_map_objects(self, lat, lng, timestamp, cellid):
        if time.time(
        ) - self.last_time_map_object < self.config.map_object_cache_time:
            return self.last_map_object

        self.last_map_object = self.api.get_map_objects(
            latitude=f2i(lat),
            longitude=f2i(lng),
            since_timestamp_ms=timestamp,
            cell_id=cellid)
        self.last_time_map_object = time.time()

        return self.last_map_object
Esempio n. 21
0
class TestEventManager(unittest.TestCase):
    def setUp(self):
        self.event_manager = EventManager()
        self.client = MockClient(user_id='60')
        self.client2 = MockClient(user_id='50')

    def test_add_client(self):
        self.assertEqual(len(self.event_manager.clients), 0)

        self.event_manager.add_client(self.client)
        self.assertEqual(len(self.event_manager.clients), 1)
        self.assertEqual(self.event_manager.clients[self.client.user_id].user_id, self.client.user_id)

    def test_remove_client(self):
        self.event_manager.clients[self.client.user_id] = self.client
        self.assertEqual(len(self.event_manager.clients), 1)
        self.assertEqual(self.event_manager.clients[self.client.user_id].user_id, self.client.user_id)

        self.event_manager.remove_client(self.client.user_id)
        self.assertEqual(len(self.event_manager.clients), 0)

    def test_wrong_event(self):
        bogus_event = '666\F/60D50'
        with self.assertRaises(AssertionError):
            self.event_manager.receive_event(bogus_event)

    def test_follow_event_no_user(self):
        follow_event = '666|F|60|50'
        self.assertEqual(len(self.event_manager.clients), 0)
        self.assertEqual(len(self.event_manager.followers), 0)
        self.event_manager.receive_event(follow_event)
        self.assertEqual(len(self.event_manager.clients), 0)
        self.assertEqual(len(self.event_manager.followers), 1)
        self.assertIn('50', self.event_manager.followers)
        self.assertIn('60', self.event_manager.followers['50'])

    def test_follow_event_with_user(self):
        follow_event = '666|F|60|50'
        self.event_manager.add_client(self.client)
        self.event_manager.add_client(self.client2)
        self.assertEqual(len(self.event_manager.clients), 2)
        self.assertEqual(len(self.event_manager.followers), 0)
        self.event_manager.receive_event(follow_event)
        self.assertEqual(len(self.event_manager.clients), 2)
        self.assertEqual(len(self.event_manager.followers), 1)
        self.assertIn('50', self.event_manager.followers)
        self.assertIn('60', self.event_manager.followers['50'])

    def test_un_follow(self):
        self.event_manager.followers.setdefault('60', set()).add('50')
        response = self.event_manager.u_event('50', '60')
        self.assertEqual(len(self.event_manager.followers['60']), 0)
        self.assertEqual(len(response), 0)  # no one is informed

    def test_broadcast_event(self):
        self.event_manager.add_client(self.client)
        self.event_manager.add_client(self.client2)
        response = self.event_manager.b_event()
        self.assertEqual(len(response), 2)
        self.assertIn(self.client.user_id, response)
        self.assertIn(self.client2.user_id, response)

    def test_status_event(self):
        self.event_manager.followers.setdefault(self.client.user_id, set()).add(self.client2.user_id)
        response = self.event_manager.s_event(self.client.user_id)
        self.assertEqual(len(response), 1)
        self.assertIn(self.client2.user_id, response)

    def test_send_message_event(self):
        self.event_manager.followers.setdefault(self.client.user_id, set()).add(self.client2.user_id)
        response = self.event_manager.p_event(self.client.user_id, self.client2.user_id)
        self.assertEqual(len(response), 1)
        self.assertIn(self.client2.user_id, response)
Esempio n. 22
0
 def setUp(self):
     self.event_manager = EventManager()
     self.client = MockClient(user_id='60')
     self.client2 = MockClient(user_id='50')
class ActionMachine(object):
    # DIO
    def __init__(self, novel_helper, robot_army):
        self.nh = novel_helper
        self.novel_helper = novel_helper
        self.book_warehouse = self.nh.book_warehouse
        self.robot_army = robot_army
        self.user_map = dict()
        self.init_user_map()
        self.book_map = dict()
        self.init_book_map()

        self.offline_uid_set = set(list(self.user_map.keys()))
        self.recommend_uid_set = set()
        self.choosing_uid_set = set()
        self.click_drive_uid_set = set()
        self.read_drive_uid_set = set()
        self.reading_uid_set = set()
        self.has_online_set = set()

        self._period = 0

        self.recommend_sys = RecommendSysBuiltin(self)

        self.event_manager = EventManager(self)
        self.event_manager.clean_all_csv()

    def get_period(self):
        return self._period

    def init_user_map(self):
        for regiment in self.robot_army.regiment:
            for robot in regiment.robot:
                uid = robot.uid
                robot_user = RobotUser(robot)
                self._init_use_app_info(robot_user)
                self.user_map[uid] = robot_user

    def init_book_map(self):
        for book_def in self.book_warehouse.book:
            bid = book_def.bid
            self.book_map[bid] = Book(book_def)

    # 因为wv_word_idx_map里的key是word2vec产生的,所以有的词是没有的,要过滤掉
    def _fill_no_title(self, word_list):
        new_word_list = []
        for word in word_list:
            if word in self.nh.wv_word_idx_map:
                new_word_list.append(word)
        if not len(new_word_list):
            return ['no', 'title']
        return new_word_list

    def _fill_empty_word_list(self, word_list):
        word_list = [
            word for word in word_list
            if word in self.novel_helper.wv_word_idx_map
        ]
        if not word_list:
            word_list = ['empty', 'word', 'list']
        return word_list

    def judge_robot_click_book(self, robot_user, book):
        score = self._get_robot_click_book_score(robot_user, book)
        if score > Args.ub_dot_threshold_alpha:
            return True
        return False

    # 统计查询,会先检查sqlite-in-memory的建表状态
    def query(self, sql_text, reprepare=True):
        r = self.event_manager.query(sql_text, reprepare)
        return r

    # 更新用户的addict_value
    def update_addict_value(self, robot_user, update_type, score):
        if random.random() < Args.clean_addict_value_rate:
            robot_user.addict_value = -random.random()
            return
        if update_type == AddictUpdateType.CLICK:
            robot_user.addict_value += score
        elif update_type == AddictUpdateType.CHAPTER:
            robot_user.addict_value += score
        elif update_type == AddictUpdateType.DETEST:
            robot_user.addict_value -= random.random(
            ) * Args.addict_value_detest_rate
        elif update_type == AddictUpdateType.OFFLINE:
            robot_user.addict_value += robot_user.addict_value_restore_rate

    # 驱动分值:点击
    # click_drive_score, chapter_read_drive_score, open_app_drive_score, detest_drive_score
    def _get_robot_click_book_score(self, robot_user, book):
        book_def = book.book_def
        title_word = list(book_def.title.word)
        synopsis_word = list(book_def.synopsis.word)
        book_word_list = self._fill_no_title(title_word + synopsis_word)
        user_word_list = [ei.word for ei in robot_user.robot_def.ei]
        book_vector = self._word_list_vector_pooling(book_word_list)
        user_vector = self._word_list_vector_pooling(user_word_list)
        score_sum = np.dot(book_vector, user_vector)
        score = score_sum / (len(book_word_list) * len(user_word_list))
        return score

    # 对word_list取词向量做pooling是常见操作
    def _word_list_vector_pooling(self, word_list):
        wv_word_idx_map = self.nh.wv_word_idx_map
        wv_vector = self.nh.wv_model.vectors
        word_idx_list = [wv_word_idx_map[word.lower()] for word in word_list]
        word_vector_list = [wv_vector[idx] for idx in word_idx_list]
        sum_vector = np.sum(word_vector_list, axis=0)
        return sum_vector

    # book_word多会用到,robot_word会分成多个sub_robot_word_list与book_word_list做
    #  点积,这代表每次阅读机器人用户的关注点是不同的,同时也能引入随机量
    def _dot_robot_book(self, robot_word_list, book_word_list, sample_loop):
        robot_word_list = self._fill_empty_word_list(robot_word_list)
        book_word_list = self._fill_empty_word_list(book_word_list)
        kb = len(book_word_list)
        kr = len(robot_word_list)
        sample_word_count = (kr - 1) // sample_loop + 1
        sub_dot_score_list = []
        book_vector = self._word_list_vector_pooling(book_word_list)
        for _ in range(sample_loop):
            sub_robot_word_list = random.sample(robot_word_list,
                                                sample_word_count)
            krp = len(sub_robot_word_list)
            sub_robot_vector = self._word_list_vector_pooling(
                sub_robot_word_list)
            sub_dot_score = np.dot(sub_robot_vector, book_vector)
            sub_dot_score /= (kb * krp)
            sub_dot_score_list.append(sub_dot_score)
        dot_score = max(sub_dot_score_list)
        return dot_score

    def _get_click_drive_score(self,
                               robot_user,
                               book,
                               sample_loop=Args.default_sample_loop):
        # 传入book而非book_def是因为这样后续能让book中pb_def外的字段来参与算分
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_enlighten_info_list = robot_def.ei
        robot_enlighten_word_list = [
            enlighten_info.word for enlighten_info in robot_enlighten_info_list
        ]
        title_word_list = S.sentence_to_string_list(book_def.title)
        synopsis_word_list = S.sentence_to_string_list(book_def.synopsis)
        title_synopsis_word_list = title_word_list + synopsis_word_list
        click_drive_score = self._dot_robot_book(robot_enlighten_word_list,
                                                 synopsis_word_list,
                                                 sample_loop=sample_loop)
        return click_drive_score

    def _get_read_drive_score(self,
                              robot_user,
                              book,
                              chapter,
                              sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_enlighten_info_list = robot_def.ei
        robot_enlighten_word_list = [
            enlighten_info.word for enlighten_info in robot_enlighten_info_list
        ]
        paragraph_all_count = len(book_def.paragraph)
        assert paragraph_all_count >= chapter and chapter > 0, \
            "chapter must in range %s>=chapter>=1 but get %s" % (paragraph_all_count, chapter)
        to_read_paragraph = book_def.paragraph[chapter - 1]
        paragraph_word_list = S.paragraph_to_string_list(to_read_paragraph)
        read_drive_score = self._dot_robot_book(robot_enlighten_word_list,
                                                paragraph_word_list,
                                                sample_loop=sample_loop)
        return read_drive_score

    def _get_title_detest_drive_score(self,
                                      robot_user,
                                      book,
                                      sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_detest_info_list = robot_def.detest_ei
        robot_detest_word_list = [
            enlighten_info.word for enlighten_info in robot_detest_info_list
        ]
        title_word_list = S.sentence_to_string_list(book_def.title)
        synopsis_word_list = S.sentence_to_string_list(book_def.synopsis)
        title_synopsis_word_list = title_word_list + synopsis_word_list
        detest_drive_score = self._dot_robot_book(robot_detest_word_list,
                                                  title_synopsis_word_list,
                                                  sample_loop=sample_loop)
        return detest_drive_score

    def _get_detest_drive_score(self,
                                robot_user,
                                book,
                                chapter,
                                sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_detest_info_list = robot_def.detest_ei
        robot_detest_word_list = [
            enlighten_info.word for enlighten_info in robot_detest_info_list
        ]
        paragraph_all_count = len(book_def.paragraph)
        assert paragraph_all_count >= chapter and chapter > 0, \
            "chapter must in range %s>=chapter>=1 but get %s" % (paragraph_all_count, chapter)
        to_read_paragraph = book.paragraph[chapter - 1]
        paragraph_word_list = S.paragraph_to_string_list(to_read_paragraph)
        detest_drive_score = self._dot_robot_book(robot_detest_word_list,
                                                  paragraph_word_list,
                                                  sample_loop=sample_loop)
        return detest_drive_score

    def _get_addict_drive_score(self,
                                robot_user,
                                book,
                                chapter,
                                sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_addict_info_list = robot_def.addict_ei
        robot_addict_word_list = [
            enlighten_info.word for enlighten_info in robot_addict_info_list
        ]
        paragraph_all_count = len(book_def.paragraph)
        assert paragraph_all_count >= chapter and chapter > 0, \
            "chapter must in range %s>=chapter>=1 but get %s" % (paragraph_all_count, chapter)
        to_read_paragraph = book.paragraph[chapter - 1]
        paragraph_word_list = S.paragraph_to_string_list(to_read_paragraph)
        addict_drive_score = self._dot_robot_book(robot_addict_word_list,
                                                  paragraph_word_list,
                                                  sample_loop=sample_loop)
        return addict_drive_score

    def _get_normal_noise(self, noise_range=1.):
        return noise_range * random.gauss(0., 1.)

    def _drive_click(self, robot_user, book):
        drive_success = False
        click_drive_score = self._get_click_drive_score(robot_user, book)
        click_drive_score += self._get_normal_noise(0.1)
        if click_drive_score > Args.click_drive_threshold:
            drive_success = True
        return (drive_success, click_drive_score)

    def _drive_read(self, robot_user, book, chapter):
        drive_success = False
        read_drive_score = self._get_read_drive_score(robot_user, book,
                                                      chapter)
        read_drive_score += self._get_normal_noise(0.1)
        if read_drive_score > Args.read_drive_threshold:
            drive_success = True
        addict_drive_score = self._get_addict_drive_score(
            robot_user, book, chapter)
        robot_user.addict_value += (addict_drive_score - 0.05)
        return (drive_success, read_drive_score, addict_drive_score)

    def _drive_detest(self, robot_user, book, chapter=0):
        drive_success = False
        if chapter == 0:
            detest_drive_score = self._get_title_detest_drive_score(
                robot_user, book)
        else:
            detest_drive_score = self._get_detest_drive_score(robot_user, book)
        detest_drive_score += self._get_normal_noise(0.1)
        if detest_drive_score > Args.detest_drive_threshold:
            drive_success = True
        robot_user.detest_value += (detest_drive_score - 0.05)
        return (drive_success, detest_drive_score, robot_user.detest_value)

    # 判断机器人是否会(继续)使用app
    def _drive_use_app(self, robot_user):
        use_app = False
        addict_value = robot_uesr.addict_value
        detest_value = robot_user.detest_value
        addict_value += self._get_normal_noise(0.1)
        detest_value += self._get_normal_noise(0.1)
        if addict_value > 0 and detest_value < 0:
            use_app = True
        return (use_app, addict_value, detest_value)

    def judge_robot_finish_chapter(self, robot_user, paragraph):
        score = self._get_robot_finish_chapter_score(robot_user, paragraph)
        if score > Args.ub_dot_chapter_finish_threshold_alpha:
            return True
        return False

    # 驱动分值:读完一章
    def _get_robot_finish_chapter_score(self, robot_user, paragraph):
        paragraph_word_list = S.paragraph_to_string_list(paragraph)
        paragraph_word_list = S.lower_string_list(paragraph_word_list)
        book_word_list = self._fill_no_title(paragraph_word_list)
        user_word_list = [ei.word for ei in robot_user.robot_def.ei]
        book_vector = self._word_list_vector_pooling(book_word_list)
        user_vector = self._word_list_vector_pooling(user_word_list)
        score_sum = np.dot(book_vector, user_vector)
        score = score_sum / (len(book_word_list) * len(user_word_list))
        return score

    def _report_robot_user_status(self):
        report_message_list = []
        report_message_list.append('period: %s' % self._period)
        report_message_list.append('offline: %s' % len(self.offline_uid_set))
        report_message_list.append('recommend: %s' %
                                   len(self.recommend_uid_set))
        report_message_list.append('choosing: %s' % len(self.choosing_uid_set))
        report_message_list.append('click_drive: %s' %
                                   len(self.click_drive_uid_set))
        report_message_list.append('read_drive: %s' %
                                   len(self.read_drive_uid_set))
        report_message_list.append('reading: %s' % len(self.reading_uid_set))
        # report_message_list.append('has_online: %s' % len(self.has_online_set))
        report_message = ','.join(report_message_list)
        logging.error(report_message)
        # self.metrics_writer.record_action_machine()

    # 在关闭app时做的收尾工作
    def _just_shut_down_app(self, robot_user):
        robot_user._addict_value -= 0.5

    # 更新_addict_value恢复速率
    def _update_addict_value_restore_rate(self, robot_user):
        self._addict_value_restore_rate = self._addict_value_restore_rate

    # # offline状态
    # def _run_robot_offline(self, robot_user):
    #     robot_user._addict_value += robot_user._addict_value_restore_rate
    #     addict_value = robot_user._addict_value
    #     if addict_value > 0:
    #         robot_user._status = RobotStatus.RECOMMEND

    # def _run_robot_choosing(self, robot_user):
    #     self.update_addict_value(robot_user, AddictUpdateType.DETEST, 0)
    #     robot_user.choosing_delay -= 1
    #     if robot_user.choosing_delay > 0:
    #         robot_user.status = RobotStatus.CHOOSING
    #     else:
    #         robot_user.status = RobotStatus.CLICK_DRIVE

    # def _run_robot_click_drive(self, robot_user):
    #     # 生成机器人的点击驱动分数,判断是否会发生点击行为
    #     for bid in robot_user.choosing_bid_list:
    #         # 该刷已经点过的book_id,断不会再点
    #         if bid in robot_user.chosen_bid_set:
    #             continue
    #         # 之前展现过,但没点击过的,不会再点(todo: 软性降权,而不是直接不点)
    #         if bid in robot_user.history_impr_map:
    #             continue
    #         book = self.book_map[bid]
    #         click_score = self._get_robot_click_book_score(robot_user, book)
    #         self.update_addict_value(robot_user, AddictUpdateType.CLICK, click_score)
    #         if click_score >= Args.ub_dot_threshold_alpha:
    #             self.event_manager.emit_one_event(uid=robot_user.uid, bid=bid, event='go_detail')
    #             robot_user.reading_bid = bid
    #             robot_user.chosen_bid_set.add(bid)
    #             break
    #     if robot_user.reading_bid is not None:
    #         robot_user.status = RobotStatus.READING
    #     elif self.get_addict_value(robot_user) > 0:
    #         # 如果多次执行到这里,那就是所谓的`狂刷`
    #         robot_user.status = RobotStatus.RECOMMEND
    #     else:
    #         # 执行到这里代表推荐的不好,用户关app了
    #         robot_user.status = RobotStatus.OFFLINE

    # def _run_robot_read_drive(self, robot_user):
    #     read_drive_score = random.random()
    #     drive_chapter = robot_user.reading_chapter + 1
    #     # todo drive_chapter calc
    #     if read_drive_score < 0.1:
    #         robot_user.reading_chapter += 1
    #         robot_user.status = RobotStatus.READING
    #         robot_user.chapter_finish_delay = 3
    #     else:
    #         # 返回列表页选择
    #         robot_user.status = RobotStatus.CLICK_DRIVE

    # def _run_robot_reading(self, robot_user):
    #     self.update_addict_value(robot_user, AddictUpdateType.DETEST, 0)
    #     robot_user.chapter_finish_delay -= 1
    #     if robot_user.chapter_finish_delay <= 0:
    #         robot_user.status = RobotStatus.READ_DRIVE
    #     else:
    #         robot_user.status = RobotStatus.READING
    #     return

    # # 获取用户热情度,用来决定是否继续使用产品
    # def get_addict_value(self, robot_user):
    #     return robot_user.addict_value

    def _init_use_app_info(self, robot_user):
        robot_user._use_app_impulse_value = random.random(
        ) - 0.99  # 有20%的用户会直接开app
        robot_user._addict_rate = random.random()
        robot_user._detest_rate = random.random()

    # 根据_recent_***那些变量俩计算
    def _update_addict_rate(self, robot_user):
        addict_rate_room_decay = .8
        addict_rate_room = 1. - robot_user._addict_rate
        addict_rate_room *= addict_rate_room_decay
        robot_user._addict_rate = 1. - addict_rate_room

    # 根据_recent_***那些变量俩计算
    def _update_detest_rate(self, robot_user):
        detest_rate_decay = .8
        robot_user._detest_rate *= detest_rate_decay

    def _update_use_app_impulse_value(self, robot_user):
        for bid, reading_info in robot_user._history_read_map.items():
            bid_addict_value = reading_info.addict_value
            # 尚未想好bid_addict_value怎么影响_use_app_impulse_value
        delta = (robot_user._addict_rate - robot_user._detest_rate) * 1.
        robot_user._use_app_impulse_value += delta

    def _run_robot_offline(self, robot_user):
        if robot_user._use_app_impulse_value > 0:
            robot_user._status = RobotStatus.RECOMMEND
        else:
            robot_user._status = RobotStatus.OFFLINE
        self._update_use_app_impulse_value(robot_user)
        self._update_addict_rate(robot_user)
        self._update_detest_rate(robot_user)

    def _run_robot_recommend(self, robot_user):
        self.recommend_sys.recommend(robot_user)
        robot_user._chosen_bid_set.clear()
        robot_user._choosing_delay = 2
        robot_user._reading_bid = None
        robot_user._status = RobotStatus.CHOOSING
        for bid in robot_user._choosing_bid_list:
            self.event_manager.emit_one_event(uid=robot_user.uid,
                                              bid=bid,
                                              event='impression')

    def _run_robot_choosing(self, robot_user):
        if robot_user._choosing_delay > 0:
            robot_user._status = RobotStatus.CHOOSING
        else:
            robot_user._status = RobotStatus.CLICK_DRIVE
        robot_user._choosing_delay -= 1

    def _run_robot_click_drive(self, robot_user):
        for bid in robot_user._choosing_bid_list:
            if bid in robot_user._chosen_bid_set:
                continue
            book = self.book_map[bid]
            click_drive_score = self._get_click_drive_score(robot_user,
                                                            book,
                                                            sample_loop=1)
            if bid in robot_user._history_impr_map:
                suppress_rate = .5
            else:
                suppress_rate = 1.
            # sys.stdout.write("\r %s" % click_drive_score)
            click_drive_score *= suppress_rate
            if click_drive_score > Args.click_drive_threshold:
                robot_user._reading_bid = bid
                robot_user._chosen_bid_set.add(bid)
                robot_user._history_read_map[bid] = ReadingInfo(
                    last_period=self._period, chapter=0, addict_value=0)
                break
            if random.random() < 0.1:  # 10%的概率会被机器人记住“给我推过了”
                robot_user._history_impr_map[bid] = ImpressionInfo(
                    last_period=self._period)
        if robot_user._reading_bid:
            robot_user._status = RobotStatus.READ_DRIVE
        else:
            # todo: not only OFFLINE, but also RECOMMEND
            robot_user._status = RobotStatus.OFFLINE

    # chapter记录的是之前读完哪一章了,之后从下一章开始读
    def _run_robot_read_drive(self, robot_user):
        robot_user._status = RobotStatus.OFFLINE
        bid = robot_user._reading_bid
        book = self.book_map[bid]
        reading_info = robot_user._history_read_map[bid]
        reading_info.chapter += 1
        if reading_info.chapter > len(book._book_def.paragraph):
            # 返回第一章继续阅读
            reading_info.chapter = 1
        read_drive_score = self._get_read_drive_score(
            robot_user, book, chapter=reading_info.chapter, sample_loop=1)
        if read_drive_score > Args.read_drive_threshold:
            robot_user._status = RobotStatus.READING
            robot_user._chapter_finish_delay = 3
        else:
            robot_user._status = RobotStatus.CHOOSING

    def _run_robot_reading(self, robot_user):
        if robot_user._chapter_finish_delay > 0:
            robot_user._status = RobotStatus.READING
            robot_user._chapter_finish_delay -= 1
        else:
            robot_user._status = RobotStatus.READ_DRIVE

    def run_one_period(self):
        self._period += 1
        for uid in self.offline_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_offline(robot_user)
        for uid in self.recommend_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_recommend(robot_user)
        for uid in self.choosing_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_choosing(robot_user)
        for uid in self.click_drive_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_click_drive(robot_user)
        for uid in self.read_drive_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_read_drive(robot_user)
        for uid in self.reading_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_reading(robot_user)
        self._update_robot_status()
        self._report_robot_user_status()

    def run(self, k=Args.period_threshold):
        self._period = 0
        while self._period + 1 <= k:
            self.run_one_period()

    # 在每一peroid结束时进行
    def _update_robot_status(self):
        self.offline_uid_set.clear()
        self.recommend_uid_set.clear()
        self.choosing_uid_set.clear()
        self.click_drive_uid_set.clear()
        self.read_drive_uid_set.clear()
        self.reading_uid_set.clear()

        for uid, robot_user in self.user_map.items():
            status = robot_user._status
            # sys.stdout.write('%s'%status)
            if status == RobotStatus.OFFLINE:
                self.offline_uid_set.add(uid)
            elif status == RobotStatus.RECOMMEND:
                self.recommend_uid_set.add(uid)
            elif status == RobotStatus.CHOOSING:
                self.choosing_uid_set.add(uid)
            elif status == RobotStatus.CLICK_DRIVE:
                self.click_drive_uid_set.add(uid)
            elif status == RobotStatus.READ_DRIVE:
                self.read_drive_uid_set.add(uid)
            elif status == RobotStatus.READING:
                self.reading_uid_set.add(uid)
Esempio n. 24
0
from player import Player
from event_manager import EventManager
from db_connector import DbConnector
from data_listener import BookListener

initial_money = 10000
player_name = 'Notroop'

em = EventManager()
conn = DbConnector('postgres', 5432, 'postgres', '127.0.0.1')
file_path = 'D:\\genericbacktesters\\book_data\\file.dat'
bl = BookListener('', 12347, file_path)

player = Player(initial_money, player_name, em, conn)

symbol = 'YESBANK_EQ'
price = 1000
direction = 'SHORT'
quantity = 50
order_id = '9f04581c-a00b-4cd1-bb07-fd25ecf64315'
player.place_new_order(symbol, price, direction, quantity)
Esempio n. 25
0
from event_manager import EventManager

manager = EventManager()

manager.start()
Esempio n. 26
0
class Scenario:


    def __init__(self, background=None):
        
        if background!=None:
            self.set_background(background)

        self.event_manager = EventManager()
        self.subscribe(pygame.QUIT, "quit", self)

        self.items = pygame.sprite.Group()


    def set_background(self, background):
        self.background = pygame.image.load(background)
        self.size = self.background.get_size()
        self.surface = pygame.display.set_mode(self.size)
        self.backgroundRect = self.background.get_rect()
        self.set_limits( (0, 0, self.size[0], self.size[1]))


    def set_limits(self, limits):
        self.limits = limits

    def add(self, item):
        self.items.add(item)
        Logger.log_debug("Added "+ str(item))

    def remove(self, item):
        self.items.remove(item)
        Logger.log_debug("Removed "+ str(item))

    def render(self):
        self.surface.blit(self.background, self.backgroundRect)
        self.items.draw(self.surface)
        pygame.display.flip()

    def subscribe(self, event, action, handler):
        self.event_manager.add(event, action, handler)
        Logger.log_debug("Event "+str(event)+" subscribed "+str(handler)+"#"+str(action))


    def unsubscribe(self, event, action, handler):
        self.event_manager.remove(event, action, handler)
        Logger.log_debug("Event "+str(event)+" unsubscribed "+str(handler)+"#"+str(action))

    def post_event(self, code, info):
        pygame.event.post(pygame.event.Event(code, info))
        
    def update(self):
        self.event_manager.handle(pygame.event.get())
        self.items.update()

    def quit(self, event):
        pygame.quit()
        sys.exit()

    def position_allowed(self, item, position):
        half_size = (item.get_size()[0]/2, item.get_size()[1]/2)
        if position[0] - half_size[0] < self.limits[0]:
            return False
        if position[1] - half_size[0] < self.limits[1]:
            return False
        if position[0] + half_size[0] > self.limits[2]:
            return False
        if position[1] + half_size[1] > self.limits[3]:
            return False
        return True
Esempio n. 27
0
class TapiocaBot(sc2.BotAI):
    def __init__(self, verbose=False, visual_debug=False):
        self.verbose = verbose
        self.visual_debug = visual_debug

        ipdb.launch_ipdb_on_exception()

        # Control Stuff
        self.researched_warpgate = False  # Remove me later

        # Managers and controllers
        self.worker_controller = WorkerController(bot=self,
                                                  verbose=self.verbose)
        self.army_controller = ArmyController(bot=self, verbose=self.verbose)
        self.scouting_controller = ScoutingController(bot=self,
                                                      verbose=self.verbose)
        self.upgrades_controller = UpgradesController(bot=self,
                                                      verbose=self.verbose)
        self.robotics_facility_controller = RoboticsFacilitiyController(
            bot=self,
            verbose=self.verbose,
        )
        self.gateway_controller = GatewayController(
            bot=self, verbose=self.verbose, auto_morph_to_warpgate=True)
        self.building_controller = BuildingController(bot=self,
                                                      verbose=self.verbose)
        self.event_manager = EventManager()
        self.build_order_controller = BuildOrderController(
            verbose=self.verbose, bot=self)
        self.coordinator = Coordinator(bot=self,
                                       verbose=self.verbose,
                                       build_order='three_gate_blink_all_in')

        self.order_queue = []

    def on_start(self):
        self.army_controller.init()

        self.event_manager.add_event(self.worker_controller.step,
                                     0.1,
                                     jitter=0)
        self.event_manager.add_event(
            self.building_controller.update_nexus_list, 2.5)
        self.event_manager.add_event(self.build_order_controller.step, 0.5)
        self.event_manager.add_event(self.army_controller.step, 0.1, jitter=0)
        self.event_manager.add_event(self.coordinator.step, 1)

        self.coordinator.on_start()

    async def on_step(self, iteration):
        if iteration == 0:  # Do nothing on the first iteration to avoid
            # everything being done at the same time
            if self.verbose:
                print('\n------------------------\n')
                print('%8.2f %3d Rise and Shine' %
                      (self.time, self.supply_used))

            await self.chat_send(
                "Cry 'havoc', and let slip the Tapiocas of war!")

            return

        events = self.event_manager.get_current_events(self.time)
        for event in events:
            await event()

        await self.debug()

        await self.execute_order_queue()

        if self.verbose:
            sys.stdout.flush()

    async def do(self, action):
        self.order_queue.append(action)

    async def execute_order_queue(self):
        await self._client.actions(self.order_queue, game_data=self._game_data)
        self.order_queue = []

    async def debug(self):
        if not self.visual_debug:
            return

        # Setup and info

        font_size = 14

        total_units = 0
        for unit_type in self.army_controller.units_available_for_attack.keys(
        ):
            total_units += self.units(unit_type).idle.amount

        # Text

        messages = [
            '       n_workers: %3d' % self.units(UnitTypeId.PROBE).amount,
            '      n_stalkers: %3d' % self.units(UnitTypeId.STALKER).amount,
            '    militia_size: %3d' %
            len(self.worker_controller.militia_controller.militia), '',
            'ememy_structures_nearby: %3d' %
            len(self.worker_controller.militia_controller.
                nearby_enemy_structures_found),
            '   ememy_workers_nearby: %3d' %
            len(self.worker_controller.militia_controller.
                nearby_enemy_workers_found),
            '     ememy_units_nearby: %3d' %
            len(self.worker_controller.militia_controller.
                nearby_enemy_units_found), '',
            '  know_enemy_structures: %3d' % len(self.known_enemy_structures)
        ]

        y = 0
        inc = 0.018

        for message in messages:
            self._client.debug_text_screen(message,
                                           pos=(0.001, y),
                                           size=font_size)
            y += inc

        # 3D text

        debug_army_state = False

        if debug_army_state:
            for tag in self.army_controller.soldiers:
                unit = self.units.find_by_tag(tag)
                if unit is not None:
                    message = self.army_controller.soldiers[tag]['state']
                    self._client.debug_text_world(message,
                                                  pos=unit.position3d,
                                                  size=font_size)

        # Spheres

        debug_army_groups = False

        if debug_army_groups:
            leader_tag = self.army_controller.leader
            for soldier_tag in self.army_controller.soldiers:
                soldier_unit = self.units.find_by_tag(soldier_tag)

                if soldier_unit is not None:
                    if soldier_tag == leader_tag:
                        self._client.debug_sphere_out(soldier_unit,
                                                      r=1,
                                                      color=(255, 0, 0))
                    else:
                        self._client.debug_sphere_out(soldier_unit,
                                                      r=1,
                                                      color=(0, 0, 255))

        # Lines

        if debug_army_groups:
            if self.army_controller.army_size() > 0:
                leader_tag = self.army_controller.leader
                leader_unit = self.units.find_by_tag(leader_tag)

                for soldier_tag in self.army_controller.soldiers:
                    if soldier_tag == leader_tag:
                        continue

                    soldier_unit = self.units.find_by_tag(soldier_tag)
                    if soldier_unit is not None:
                        leader_tag = self.army_controller.leader
                        leader_unit = self.units.find_by_tag(leader_tag)
                        if leader_unit is not None:
                            self._client.debug_line_out(leader_unit,
                                                        soldier_unit,
                                                        color=(0, 255, 255))

        # Attack Lines
        debug_army_attack = False

        if debug_army_attack:
            if self.army_controller.army_size() > 0:
                for soldier_tag in self.army_controller.soldiers:
                    soldier_unit = self.units.find_by_tag(soldier_tag)

                    if soldier_unit is not None and \
                       self.army_controller.soldiers[soldier_tag]['state'] == 'attacking' and \
                       self.army_controller.attack_target is not None:
                        self._client.debug_line_out(
                            soldier_unit,
                            self.army_controller.attack_target,
                            color=(255, 127, 0))

        # Sens the debug info to the game
        await self._client.send_debug()

    def get_unit_info(self, unit, field="food_required"):
        assert isinstance(unit, (Unit, UnitTypeId))

        if isinstance(unit, Unit):
            unit = unit._type_data._proto
        else:
            unit = self._game_data.units[unit.value]._proto

        if hasattr(unit, field):
            return getattr(unit, field)
        else:
            return None
Esempio n. 28
0
 def runTest(self):
     manager = EventManager(["./pkgs/"])
     for i in xrange(30):
         manager.notify()
Esempio n. 29
0
 def __init__(self, window):
     self.window = window
     self.client = Client.get_instance()
     self.event_manager = EventManager.get_instance()
Esempio n. 30
0
def main():
    # Config is a wrapper for a socket, and a channel (for now)
    _config = config.Config(socketutils.connect_to_config(cf), cf.CHANNEL)

    # This is to determine whether we log information or not
    if cf.DO_LOG:
        print("Doing logging.")
        logging.set_logfile_func(cf.get_log_filename)
    if not cf.DO_STDOUT:
        logging.set_print_function(logging.no_print)
    else:
        print("Doing printing.")
    log = logging.log
    log_all = False

    # API takes an object with socket and channel members
    _api = ut.API(_config)
    _manager = EventManager(_api)
    # _manager.add_event(EveryLoopEvent(_callable=None, _api=_api, _manager=_manager, runs_till_event=30,
    #                                   extra_event=functools.partial(_api.send, "There have been 10 loops!")))

    can_make_commanders = [
        'dfolder'
    ]  # This could be loaded from a configuration file & saved back to there
    commanders = ['dfolder']

    log('Starting bot. Information:\n\tSocket:', str(_api.socket),
        '\n\tChannel:', _api.channel)
    while True:
        do_events(_api, _manager)
        full_response = _api.resp()
        _api.extend_resp_list(full_response)
        while full_response is not None and len(full_response) > 0:
            response = full_response.pop(0)
            did_something = False
            # We got a response from the server!
            # First, let's clean it up if we're in full mode.
            # This means that the response looks really ugly & includes a bunch of unnecessary information at the start.
            if _api.full_mode:
                try:
                    full_information = re.search(
                        r'^@badges=[\w/0-9,]*;color=[\w/0-9,]*;display-name=(\w*);.*?user-type=[\w/0-9,]* (.*)',
                        response)
                    log('[FULL RESPONSE]', full_information.strip('\r\n'))
                except TypeError:
                    full_information = None
                    log("Got erroneous response: ")
                    log(str(response))
                if full_information is not None:
                    log('[SENDER]', full_information.group(1))
                    response = full_information.group(2)
            log('[RESPONSE]', response.strip('\r\n'))
            command = re.search(
                r':(\w*)!\1@\1\.tmi\.twitch\.tv PRIVMSG #\w* : *~(.+)$',
                response)
            if command is not None:
                _command = command.group(2).strip('\r\n ')
                _caller = command.group(1)
                _args = None if len(_command.split(
                    ' ', 1)) <= 1 else _command.split(' ', 1)[1]
                _command = _command.split(' ')[0].lower()

                if _caller in can_make_commanders:
                    if log_all:
                        log('[2] Executing command: ' + _command +
                            ' with args: ' + str(_args))
                    if _command == 'conscript':
                        commanders.append(_args.lower())
                        did_something = True
                    elif _command == 'decommission':
                        commanders.remove(_args.lower())
                        did_something = True

                if _caller in commanders:
                    # This should be improved later, but we're going to just check the command here
                    if log_all:
                        log('[1] Executing command: ' + _command +
                            ' with args: ' + str(_args))
                    if _command == 'stop':
                        _manager.add_event_t(
                            SendMessageEvent,
                            message="Why don't you love me...",
                            after_run=functools.partial(
                                ut.safe_exit, _config, 0))
                    elif _command == 'print_full_debug':
                        log_all = True
                        log("Enabled full debugging mode.")
                    elif _command == 'no_full_debug':
                        log_all = False
                    elif _command == 'debug':
                        log("Attempting to print debug messages:")
                        log("Manager debug:")
                        log(_manager.dump_debug())
                        log("Command arguments:")
                        log(str(_args))
                        log("Current commanders:")
                        log(str(commanders))
                        # _manager.add_event_t(GetNoticesEvent)
                    elif _command == 'say' and _args is not None:
                        _manager.add_event_t(SendMessageEvent, message=_args)
                    elif _command == 'cap_req' and _args is not None:
                        _manager.add_event_t(SendCapReqEvent, message=_args)
                    elif _command == 'send_exact' or _command == 'say_exact' and _args is not None:
                        _manager.add_event_t(SendExactEvent, message=_args)
                    elif _command == 'enable_full':
                        _manager.add_event(
                            Event(_event=ut.enable_full,
                                  _api=_api,
                                  _manager=_manager))
                    elif _command == 'flush_log':
                        log("Flushing log.")
                        logging.flush()
                elif not did_something:
                    # We didn't set moderator privileges and we don't have command use
                    if log_all:
                        log('The user ' + _caller + ' attempted to use ' +
                            _command + ' with args: ' + str(_args))
                    _manager.add_event_t(
                        SendMessageEvent,
                        message="Please stop trying to abuse me, " + _caller +
                        ".")

        # This is to avoid making Twitch angry
        time.sleep(0.75)
Esempio n. 31
0
class PokemonGoBot(Datastore):
    @property
    def position(self):
        return self.api.actual_lat, self.api.actual_lng, self.api.actual_alt

    @property
    def noised_position(self):
        return self.api.noised_lat, self.api.noised_lng, self.api.noised_alt

    #@position.setter # these should be called through api now that gps replication is there...
    #def position(self, position_tuple):
    #    self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple

    @property
    def player_data(self):
        """
        Returns the player data as received from the API.
        :return: The player data.
        :rtype: dict
        """
        return self._player

    def __init__(self, config):

        # Database connection MUST be setup before migrations will work
        self.database = _init_database('/data/{}.db'.format(config.username))

        self.config = config
        super(PokemonGoBot, self).__init__()

        self.fort_timeouts = dict()
        self.pokemon_list = json.load(
            open(os.path.join(_base_dir, 'data', 'pokemon.json'))
        )
        self.item_list = json.load(open(os.path.join(_base_dir, 'data', 'items.json')))
        # @var Metrics
        self.metrics = Metrics(self)
        self.latest_inventory = None
        self.cell = None
        self.recent_forts = [None] * config.forts_max_circle_size
        self.tick_count = 0
        self.softban = False
        self.start_position = None
        self.last_map_object = None
        self.last_time_map_object = 0
        self.logger = logging.getLogger(type(self).__name__)
        self.alt = self.config.gps_default_altitude

        # Make our own copy of the workers for this instance
        self.workers = []

        # Theading setup for file writing
        self.web_update_queue = Queue.Queue(maxsize=1)
        self.web_update_thread = threading.Thread(target=self.update_web_location_worker)
        self.web_update_thread.start()

        # Heartbeat limiting
        self.heartbeat_threshold = self.config.heartbeat_threshold
        self.heartbeat_counter = 0
        self.last_heartbeat = time.time()

        self.capture_locked = False  # lock catching while moving to VIP pokemon

        client_id_file_path = os.path.join(_base_dir, 'data', 'mqtt_client_id')
        saved_info = shelve.open(client_id_file_path)
        key = 'client_id'.encode('utf-8')
        if key in saved_info:
            self.config.client_id = saved_info[key]
        else:
            self.config.client_id = str(uuid.uuid4())
            saved_info[key] = self.config.client_id
        saved_info.close()

    def start(self):
        self._setup_event_system()
        self._setup_logging()
        self.sleep_schedule = SleepSchedule(self, self.config.sleep_schedule) if self.config.sleep_schedule else None
        if self.sleep_schedule:
            self.sleep_schedule.work()

        self._setup_api()
        self._load_recent_forts()
        init_inventory(self)
        self.display_player_info()
        self._print_character_info()
        if self.config.pokemon_bag_show_at_start and self.config.pokemon_bag_pokemon_info:
            self._print_list_pokemon()

        random.seed()

    def _setup_event_system(self):
        handlers = []

        if self.config.logging and 'color' in self.config.logging and self.config.logging['color']:
            handlers.append(ColoredLoggingHandler(self))
        else:
            handlers.append(LoggingHandler(self))

        if self.config.enable_social:
            handlers.append(SocialHandler(self))

        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self,
                self.config.websocket_server_url
            )
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()

        # @var EventManager
        self.event_manager = EventManager(self.config.walker_limit_output, *handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)

            # Registering event:
            # self.event_manager.register_event("location", parameters=['lat', 'lng'])
            #
            # Emitting event should be enough to add logging and send websocket
            # message: :
            # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}),

    def _register_events(self):
        self.event_manager.register_event(
            'location_found',
            parameters=('position', 'location')
        )
        self.event_manager.register_event('api_error')
        self.event_manager.register_event('config_error')

        self.event_manager.register_event('login_started')
        self.event_manager.register_event('login_failed')
        self.event_manager.register_event('login_successful')

        self.event_manager.register_event('set_start_location')
        self.event_manager.register_event('load_cached_location')
        self.event_manager.register_event('location_cache_ignored')

        self.event_manager.register_event('debug')

        #  ignore candy above threshold
        self.event_manager.register_event(
            'ignore_candy_above_thresold',
            parameters=(
                'name',
                'amount',
                'threshold'
            )
        )
        self.event_manager.register_event(
            'position_update',
            parameters=(
                'current_position',
                'last_position',
                'distance', # optional
                'distance_unit' # optional
            )
        )
        self.event_manager.register_event(
            'path_lap_update',
            parameters=(
                'number_lap',
                'number_lap_max'
            )
        )
        self.event_manager.register_event(
            'path_lap_end',
            parameters=(
                'duration',
                'resume'
            )
        )

        self.event_manager.register_event('location_cache_error')

        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(
            'next_sleep',
            parameters=(
                'time',
                'duration'
            )
        )
        self.event_manager.register_event(
            'bot_sleep',
            parameters=(
                'time_hms',
                'wake'
            )
        )

        # random pause
        self.event_manager.register_event(
            'next_random_pause',
            parameters=(
                'time',
                'duration'
            )
        )
        self.event_manager.register_event(
            'bot_random_pause',
            parameters=(
                'time_hms',
                'resume'
            )
        )

        # recycle stuff
        self.event_manager.register_event(
            'next_force_recycle',
            parameters=(
                'time'
            )
        )
        self.event_manager.register_event('force_recycle')

        # random alive pause
        self.event_manager.register_event(
            'next_random_alive_pause',
            parameters=(
                'time',
                'duration'
            )
        )
        self.event_manager.register_event(
            'bot_random_alive_pause',
            parameters=(
                'time_hms',
                'resume'
            )
        )

        # fort stuff
        self.event_manager.register_event(
            'spun_fort',
            parameters=(
                'fort_id',
                'latitude',
                'longitude'
            )
        )
        self.event_manager.register_event(
            'lured_pokemon_found',
            parameters=(
                'fort_id',
                'fort_name',
                'encounter_id',
                'latitude',
                'longitude'
            )
        )
        self.event_manager.register_event(
            'moving_to_fort',
            parameters=(
                'fort_name',
                'distance'
            )
        )
        self.event_manager.register_event(
            'moving_to_lured_fort',
            parameters=(
                'fort_name',
                'distance',
                'lure_distance'
            )
        )
        self.event_manager.register_event(
            'spun_pokestop',
            parameters=(
                'pokestop', 'exp', 'items'
            )
        )
        self.event_manager.register_event(
            'pokestop_empty',
            parameters=('pokestop',)
        )
        self.event_manager.register_event(
            'pokestop_out_of_range',
            parameters=('pokestop',)
        )
        self.event_manager.register_event(
            'pokestop_on_cooldown',
            parameters=('pokestop', 'minutes_left')
        )
        self.event_manager.register_event(
            'unknown_spin_result',
            parameters=('status_code',)
        )
        self.event_manager.register_event('pokestop_searching_too_often')
        self.event_manager.register_event('arrived_at_fort')

        # pokemon stuff
        self.event_manager.register_event(
            'catchable_pokemon',
            parameters=(
                'pokemon_id',
                'spawn_point_id',
                'encounter_id',
                'latitude',
                'longitude',
                'expiration_timestamp_ms',
                'pokemon_name'
            )
        )
        self.event_manager.register_event(
            'pokemon_appeared',
            parameters=(
                'pokemon',
                'ncp',
                'cp',
                'iv',
                'iv_display',
                'encounter_id',
                'latitude',
                'longitude',
                'pokemon_id'
            )
        )
        self.event_manager.register_event('no_pokeballs')
        self.event_manager.register_event('enough_ultraballs')
        self.event_manager.register_event(
            'pokemon_catch_rate',
            parameters=(
                'catch_rate',
                'ball_name',
                'berry_name',
                'berry_count'
            )
        )
        self.event_manager.register_event(
            'threw_berry',
            parameters=(
                'berry_name',
                'ball_name',
                'new_catch_rate'
            )
        )
        self.event_manager.register_event(
            'threw_pokeball',
            parameters=(
                'throw_type',
                'spin_label',
                'ball_name',
                'success_percentage',
                'count_left'
            )
        )
        self.event_manager.register_event(
            'pokemon_capture_failed',
            parameters=('pokemon',)
        )
        self.event_manager.register_event(
            'pokemon_vanished',
            parameters=(
                'pokemon',
                'encounter_id',
                'latitude',
                'longitude',
                'pokemon_id'
            )
        )
        self.event_manager.register_event('pokemon_not_in_range')
        self.event_manager.register_event('pokemon_inventory_full')
        self.event_manager.register_event(
            'pokemon_caught',
            parameters=(
                'pokemon',
                'ncp', 'cp', 'iv', 'iv_display', 'exp',
                'encounter_id',
                'latitude',
                'longitude',
                'pokemon_id',
                'daily_catch_limit',
                'caught_last_24_hour',
            )
        )
        self.event_manager.register_event(
            'pokemon_evolved',
            parameters=('pokemon', 'iv', 'cp', 'xp', 'candy')
        )
        self.event_manager.register_event(
            'pokemon_upgraded',
            parameters=('pokemon', 'iv', 'cp', 'candy', 'stardust')
        )
        self.event_manager.register_event('skip_evolve')
        self.event_manager.register_event('threw_berry_failed', parameters=('status_code',))
        self.event_manager.register_event('vip_pokemon')
        self.event_manager.register_event('gained_candy', parameters=('quantity', 'type'))
        self.event_manager.register_event('catch_limit')
        self.event_manager.register_event('show_best_pokemon', parameters=('pokemons'))

        # level up stuff
        self.event_manager.register_event(
            'level_up',
            parameters=(
                'previous_level',
                'current_level'
            )
        )
        self.event_manager.register_event(
            'level_up_reward',
            parameters=('items',)
        )

        # lucky egg
        self.event_manager.register_event(
            'used_lucky_egg',
            parameters=('amount_left',)
        )
        self.event_manager.register_event('lucky_egg_error')

        # softban
        self.event_manager.register_event('softban')
        self.event_manager.register_event('softban_fix')
        self.event_manager.register_event('softban_fix_done')

        # egg incubating
        self.event_manager.register_event(
            'incubate_try',
            parameters=(
                'incubator_id',
                'egg_id'
            )
        )
        self.event_manager.register_event(
            'incubate',
            parameters=('distance_in_km',)
        )
        self.event_manager.register_event(
            'next_egg_incubates',
            parameters=('eggs_left', 'eggs_inc', 'eggs')
        )
        self.event_manager.register_event('incubator_already_used')
        self.event_manager.register_event('egg_already_incubating')
        self.event_manager.register_event(
            'egg_hatched',
            parameters=(
                'pokemon',
                'cp', 'iv', 'exp', 'stardust', 'candy'
            )
        )

        # discard item
        self.event_manager.register_event(
            'item_discarded',
            parameters=(
                'amount', 'item', 'maximum'
            )
        )
        self.event_manager.register_event(
            'item_discard_skipped',
            parameters=('space',)
        )
        self.event_manager.register_event(
            'item_discard_fail',
            parameters=('item',)
        )

        # inventory
        self.event_manager.register_event('inventory_full')

        # release
        self.event_manager.register_event(
            'keep_best_release',
            parameters=(
                'amount', 'pokemon', 'criteria'
            )
        )
        self.event_manager.register_event(
            'future_pokemon_release',
            parameters=(
                'pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic'
            )
        )
        self.event_manager.register_event(
            'pokemon_release',
            parameters=('pokemon', 'iv', 'cp', 'candy')
        )

        # polyline walker
        self.event_manager.register_event(
            'polyline_request',
            parameters=('url',)
        )

        # cluster
        self.event_manager.register_event(
            'found_cluster',
            parameters=(
                'num_points', 'forts', 'radius', 'distance'
            )
        )
        self.event_manager.register_event(
            'arrived_at_cluster',
            parameters=(
                'num_points', 'forts', 'radius'
            )
        )

        # rename
        self.event_manager.register_event(
            'rename_pokemon',
            parameters=('old_name', 'current_name',)
        )
        self.event_manager.register_event(
            'pokemon_nickname_invalid',
            parameters=('nickname',)
        )
        self.event_manager.register_event(
            'unset_pokemon_nickname',
            parameters=('old_name',)
        )

        # Move To map pokemon
        self.event_manager.register_event(
            'move_to_map_pokemon_fail',
            parameters=('message',)
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_updated_map',
            parameters=('lat', 'lon')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_teleport_to',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_encounter',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_move_towards',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_teleport_back',
            parameters=('last_lat', 'last_lon')
        )
        self.event_manager.register_event(
            'moving_to_pokemon_throught_fort',
            parameters=('fort_name', 'distance','poke_name','poke_dist')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon',
            parameters=('message')
        )
        # cached recent_forts
        self.event_manager.register_event('loaded_cached_forts')
        self.event_manager.register_event('cached_fort')
        self.event_manager.register_event(
            'no_cached_forts',
            parameters=('path', )
        )
        self.event_manager.register_event(
            'error_caching_forts',
            parameters=('path', )
        )
        # database shit
        self.event_manager.register_event('catch_log')
        self.event_manager.register_event('evolve_log')
        self.event_manager.register_event('login_log')
        self.event_manager.register_event('transfer_log')
        self.event_manager.register_event('pokestop_log')
        self.event_manager.register_event('softban_log')

        self.event_manager.register_event(
            'badges',
            parameters=('badge', 'level')
        )
        self.event_manager.register_event(
            'player_data',
            parameters=('player_data', )
        )

    def tick(self):
        self.health_record.heartbeat()
        self.cell = self.get_meta_cell()

        if self.sleep_schedule:
            self.sleep_schedule.work()

        now = time.time() * 1000

        for fort in self.cell["forts"]:
            timeout = fort.get("cooldown_complete_timestamp_ms", 0)

            if timeout >= now:
                self.fort_timeouts[fort["id"]] = timeout

        self.tick_count += 1

        # Check if session token has expired
        self.check_session(self.position)

        for worker in self.workers:
            if worker.work() == WorkerResult.RUNNING:
                return

    def get_meta_cell(self):
        location = self.position[0:2]
        cells = self.find_close_cells(*location)

        # Combine all cells into a single dict of the items we care about.
        forts = []
        wild_pokemons = []
        catchable_pokemons = []
        for cell in cells:
            if "forts" in cell and len(cell["forts"]):
                forts += cell["forts"]
            if "wild_pokemons" in cell and len(cell["wild_pokemons"]):
                wild_pokemons += cell["wild_pokemons"]
            if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]):
                catchable_pokemons += cell["catchable_pokemons"]

        # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved
        if len(forts) > 1 or not self.cell:
            return {
                "forts": forts,
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }
        # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells.
        else:
            return {
                "forts": self.cell["forts"],
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }

    def update_web_location(self, cells=[], lat=None, lng=None, alt=None):
        # we can call the function with no arguments and still get the position
        # and map_cells
        if lat is None:
            lat = self.api._position_lat
        if lng is None:
            lng = self.api._position_lng
        if alt is None:
            alt = self.api._position_alt

        # dont cache when teleport_to
        if self.api.teleporting:
            return

        if cells == []:
            location = self.position[0:2]
            cells = self.find_close_cells(*location)

        user_data_cells = os.path.join(_base_dir, 'data', 'cells-%s.json' % self.config.username)
        try:
            with open(user_data_cells, 'w') as outfile:
                json.dump(cells, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

        user_web_location = os.path.join(
            _base_dir, 'web', 'location-%s.json' % self.config.username
        )
        # alt is unused atm but makes using *location easier
        try:
            with open(user_web_location, 'w') as outfile:
                json.dump({
                    'lat': lat,
                    'lng': lng,
                    'alt': alt,
                    'cells': cells
                }, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

        user_data_lastlocation = os.path.join(
            _base_dir, 'data', 'last-location-%s.json' % self.config.username
        )
        try:
            with open(user_data_lastlocation, 'w') as outfile:
                json.dump({'lat': lat, 'lng': lng, 'alt': alt, 'start_position': self.start_position}, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

    def find_close_cells(self, lat, lng):
        cellid = get_cell_ids(lat, lng)
        timestamp = [0, ] * len(cellid)
        response_dict = self.get_map_objects(lat, lng, timestamp, cellid)
        map_objects = response_dict.get(
            'responses', {}
        ).get('GET_MAP_OBJECTS', {})
        status = map_objects.get('status', None)

        map_cells = []
        if status and status == 1:
            map_cells = map_objects['map_cells']
            position = (lat, lng, 0)
            map_cells.sort(
                key=lambda x: distance(
                    lat,
                    lng,
                    x['forts'][0]['latitude'],
                    x['forts'][0]['longitude']) if x.get('forts', []) else 1e6
            )
        return map_cells

    def _setup_logging(self):
        log_level = logging.ERROR

        if self.config.debug:
            log_level = logging.DEBUG

        logging.getLogger("requests").setLevel(log_level)
        logging.getLogger("websocket").setLevel(log_level)
        logging.getLogger("socketio").setLevel(log_level)
        logging.getLogger("engineio").setLevel(log_level)
        logging.getLogger("socketIO-client").setLevel(log_level)
        logging.getLogger("pgoapi").setLevel(log_level)
        logging.getLogger("rpc_api").setLevel(log_level)

        if self.config.logging:
            logging_format = '%(message)s'
            logging_format_options = ''

            if ('show_log_level' not in self.config.logging) or self.config.logging['show_log_level']:
                logging_format = '[%(levelname)s] ' + logging_format
            if ('show_process_name' not in self.config.logging) or self.config.logging['show_process_name']:
                logging_format = '[%(name)10s] ' + logging_format
            if ('show_datetime' not in self.config.logging) or self.config.logging['show_datetime']:
                logging_format = '[%(asctime)s] ' + logging_format
                logging_format_options = '%Y-%m-%d %H:%M:%S'

            formatter = Formatter(logging_format,logging_format_options)
            for handler in logging.root.handlers[:]:
                handler.setFormatter(formatter)

    def check_session(self, position):

        # Check session expiry
        if self.api._auth_provider and self.api._auth_provider._ticket_expire:

            # prevent crash if return not numeric value
            if not self.is_numeric(self.api._auth_provider._ticket_expire):
                self.logger.info("Ticket expired value is not numeric", 'yellow')
                return

            remaining_time = \
                self.api._auth_provider._ticket_expire / 1000 - time.time()

            if remaining_time < 60:
                self.event_manager.emit(
                    'api_error',
                    sender=self,
                    level='info',
                    formatted='Session stale, re-logging in.'
                )
                self.api = ApiWrapper(config=self.config)
                self.api.set_position(*position)
                self.login()
                self.api.activate_signature(self.get_encryption_lib())

    @staticmethod
    def is_numeric(s):
        try:
            float(s)
            return True
        except ValueError:
            return False

    def login(self):
        self.event_manager.emit(
            'login_started',
            sender=self,
            level='info',
            formatted="Login procedure started."
        )
        lat, lng = self.position[0:2]
        self.api.set_position(lat, lng, self.alt)  # or should the alt kept to zero?

        while not self.api.login(
            self.config.auth_service,
            str(self.config.username),
            str(self.config.password)):

            self.event_manager.emit(
                'login_failed',
                sender=self,
                level='info',
                formatted="Login error, server busy. Waiting 10 seconds to try again."
            )
            time.sleep(10)

        with self.database as conn:
            c = conn.cursor()
            c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='login'")

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute('''INSERT INTO login (timestamp, message) VALUES (?, ?)''', (time.time(), 'LOGIN_SUCCESS'))
                break
            else:
                self.event_manager.emit(
                    'login_failed',
                    sender=self,
                    level='info',
                    formatted="Login table not founded, skipping log"
                )
                break

        self.event_manager.emit(
            'login_successful',
            sender=self,
            level='info',
            formatted="Login successful."
        )

    def get_encryption_lib(self):
        if _platform == "Windows" or _platform == "win32":
            # Check if we are on 32 or 64 bit
            if sys.maxsize > 2**32:
                file_name = 'encrypt_64.dll'
            else:
                file_name = 'encrypt.dll'
        else:
            file_name = 'encrypt.so'

        if self.config.encrypt_location == '':
            path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
        else:
            path = self.config.encrypt_location

        full_path = path + '/'+ file_name
        if not os.path.isfile(full_path):
            self.logger.error(file_name + ' is not found! Please place it in the bots root directory or set encrypt_location in config.')
            self.logger.info('Platform: '+ _platform + ' ' + file_name + ' directory: '+ path)
            sys.exit(1)
        else:
            self.logger.info('Found '+ file_name +'! Platform: ' + _platform + ' ' + file_name + ' directory: ' + path)

        return full_path

    def _setup_api(self):
        # instantiate pgoapi @var ApiWrapper
        self.api = ApiWrapper(config=self.config)

        # provide player position on the earth
        self._set_starting_position()

        self.login()
        # chain subrequests (methods) into one RPC call

        self.api.activate_signature(self.get_encryption_lib())
        self.logger.info('')
        # send empty map_cells and then our position
        self.update_web_location()

    def _print_character_info(self):
        # get player profile call
        # ----------------------
        response_dict = self.api.get_player()
        # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2)))
        currency_1 = "0"
        currency_2 = "0"

        if response_dict:
            self._player = response_dict['responses']['GET_PLAYER']['player_data']
            player = self._player
        else:
            self.logger.info(
                "The API didn't return player info, servers are unstable - "
                "retrying.", 'red'
            )
            sleep(5)
            self._print_character_info()

        # @@@ TODO: Convert this to d/m/Y H:M:S
        creation_date = datetime.datetime.fromtimestamp(
            player['creation_timestamp_ms'] / 1e3)
        creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S")

        pokecoins = '0'
        stardust = '0'
        items_inventory = inventory.items()

        if 'amount' in player['currencies'][0]:
            pokecoins = player['currencies'][0]['amount']
        if 'amount' in player['currencies'][1]:
            stardust = player['currencies'][1]['amount']
        self.logger.info('')
        self.logger.info('--- {username} ---'.format(**player))

        self.logger.info(
            'Pokemon Bag: {}/{}'.format(
                inventory.Pokemons.get_space_used(),
                inventory.get_pokemon_inventory_size()
            )
        )
        self.logger.info(
            'Items: {}/{}'.format(
                inventory.Items.get_space_used(),
                inventory.get_item_inventory_size()
            )
        )
        self.logger.info(
            'Stardust: {}'.format(stardust) +
            ' | Pokecoins: {}'.format(pokecoins)
        )
        # Items Output
        self.logger.info(
            'PokeBalls: ' + str(items_inventory.get(1).count) +
            ' | GreatBalls: ' + str(items_inventory.get(2).count) +
            ' | UltraBalls: ' + str(items_inventory.get(3).count) +
            ' | MasterBalls: ' + str(items_inventory.get(4).count))

        self.logger.info(
            'RazzBerries: ' + str(items_inventory.get(701).count) +
            ' | BlukBerries: ' + str(items_inventory.get(702).count) +
            ' | NanabBerries: ' + str(items_inventory.get(703).count))

        self.logger.info(
            'LuckyEgg: ' + str(items_inventory.get(301).count) +
            ' | Incubator: ' + str(items_inventory.get(902).count) +
            ' | TroyDisk: ' + str(items_inventory.get(501).count))

        self.logger.info(
            'Potion: ' + str(items_inventory.get(101).count) +
            ' | SuperPotion: ' + str(items_inventory.get(102).count) +
            ' | HyperPotion: ' + str(items_inventory.get(103).count) +
            ' | MaxPotion: ' + str(items_inventory.get(104).count))

        self.logger.info(
            'Incense: ' + str(items_inventory.get(401).count) +
            ' | IncenseSpicy: ' + str(items_inventory.get(402).count) +
            ' | IncenseCool: ' + str(items_inventory.get(403).count))

        self.logger.info(
            'Revive: ' + str(items_inventory.get(201).count) +
            ' | MaxRevive: ' + str(items_inventory.get(202).count))

        self.logger.info('')

    def _print_list_pokemon(self):
        # get pokemon list
        bag = inventory.pokemons().all()
        id_list =list(set(map(lambda x: x.pokemon_id, bag)))
        id_list.sort()
        pokemon_list = [filter(lambda x: x.pokemon_id == y, bag) for y in id_list]

        show_count = self.config.pokemon_bag_show_count
        show_candies = self.config.pokemon_bag_show_candies
        poke_info_displayed = self.config.pokemon_bag_pokemon_info

        def get_poke_info(info, pokemon):
            poke_info = {
                'cp': 'CP {}'.format(pokemon.cp),
                'iv_ads': 'A/D/S {}/{}/{}'.format(pokemon.iv_attack, pokemon.iv_defense, pokemon.iv_stamina),
                'iv_pct': 'IV {}'.format(pokemon.iv),
                'ivcp': 'IVCP {}'.format(round(pokemon.ivcp,2)),
                'ncp': 'NCP {}'.format(round(pokemon.cp_percent,2)),
                'level': "Level {}".format(pokemon.level),
                'hp': 'HP {}/{}'.format(pokemon.hp, pokemon.hp_max),
                'moveset': 'Moves: {}'.format(pokemon.moveset),
                'dps': 'DPS {}'.format(round(pokemon.moveset.dps, 2))
            }
            if info not in poke_info:
                raise ConfigException("info '{}' isn't available for displaying".format(info))
            return poke_info[info]

        self.logger.info('Pokemon:')

        for pokes in pokemon_list:
            line_p = '#{} {}'.format(pokes[0].pokemon_id, pokes[0].name)
            if show_count:
                line_p += '[{}]'.format(len(pokes))
            if show_candies:
                line_p += '[{} candies]'.format(pokes[0].candy_quantity)
            line_p += ': '

            poke_info = ['({})'.format(', '.join([get_poke_info(x, p) for x in poke_info_displayed])) for p in pokes]
            self.logger.info(line_p + ' | '.join(poke_info))

        self.logger.info('')

    def use_lucky_egg(self):
        return self.api.use_item_xp_boost(item_id=301)

    def _set_starting_position(self):

        self.event_manager.emit(
            'set_start_location',
            sender=self,
            level='info',
            formatted='Setting start location.'
        )

        has_position = False

        if self.config.test:
            # TODO: Add unit tests
            return

        if self.config.location:
            location_str = self.config.location
            location = self.get_pos_by_name(location_str.replace(" ", ""))
            msg = "Location found: {location} {position}"
            self.event_manager.emit(
                'location_found',
                sender=self,
                level='info',
                formatted=msg,
                data={
                    'location': location_str,
                    'position': location
                }
            )

            self.api.set_position(*location)

            self.event_manager.emit(
                'position_update',
                sender=self,
                level='info',
                formatted="Now at {current_position}",
                data={
                    'current_position': self.position,
                    'last_position': '',
                    'distance': '',
                    'distance_unit': ''
                }
            )

            self.start_position = self.position

            has_position = True

        if self.config.location_cache:
            try:
                # save location flag used to pull the last known location from
                # the location.json
                self.event_manager.emit(
                    'load_cached_location',
                    sender=self,
                    level='debug',
                    formatted='Loading cached location...'
                )

                json_file = os.path.join(_base_dir, 'data', 'last-location-%s.json' % self.config.username)

                try:
                    with open(json_file, "r") as infile:
                        location_json = json.load(infile)
                except (IOError, ValueError):
                    # Unable to read json file.
                    # File may be corrupt. Create a new one.
                    location_json = []
                except:
                    raise FileIOException("Unexpected error reading from {}".web_inventory)

                location = (
                    location_json['lat'],
                    location_json['lng'],
                    location_json['alt'],
                )

                # If location has been set in config, only use cache if starting position has not differed
                if has_position and 'start_position' in location_json:
                    last_start_position = tuple(location_json.get('start_position', []))

                    # Start position has to have been set on a previous run to do this check
                    if last_start_position and last_start_position != self.start_position:
                        msg = 'Going to a new place, ignoring cached location.'
                        self.event_manager.emit(
                            'location_cache_ignored',
                            sender=self,
                            level='debug',
                            formatted=msg
                        )
                        return

                self.api.set_position(*location)
                self.event_manager.emit(
                    'position_update',
                    sender=self,
                    level='debug',
                    formatted='Loaded location {current_position} from cache',
                    data={
                        'current_position': location,
                        'last_position': '',
                        'distance': '',
                        'distance_unit': ''
                    }
                )

                has_position = True
            except Exception:
                if has_position is False:
                    sys.exit(
                        "No cached Location. Please specify initial location."
                    )
                self.event_manager.emit(
                    'location_cache_error',
                    sender=self,
                    level='debug',
                    formatted='Parsing cached location failed.'
                )

    def get_pos_by_name(self, location_name):
        # Check if given location name, belongs to favorite_locations
        favorite_location_coords = self._get_pos_by_fav_location(location_name)

        if favorite_location_coords is not None:
            return favorite_location_coords

        # Check if the given location is already a coordinate.
        if ',' in location_name:
            possible_coordinates = re.findall(
                "[-]?\d{1,3}[.]\d{3,7}", location_name
            )
            if len(possible_coordinates) >= 2:
                # 2 matches, this must be a coordinate. We'll bypass the Google
                # geocode so we keep the exact location.
                self.logger.info(
                    '[x] Coordinates found in passed in location, '
                    'not geocoding.'
                )
                return float(possible_coordinates[0]), float(possible_coordinates[1]), (float(possible_coordinates[2]) if len(possible_coordinates) == 3 else self.alt)

        geolocator = GoogleV3(api_key=self.config.gmapkey)
        loc = geolocator.geocode(location_name, timeout=10)

        return float(loc.latitude), float(loc.longitude), float(loc.altitude)

    def _get_pos_by_fav_location(self, location_name):

        location_name = location_name.lower()
        coords = None

        for location in self.config.favorite_locations:
            if location.get('name').lower() == location_name:
                coords = re.findall(
                    "[-]?\d{1,3}[.]\d{3,7}", location.get('coords').strip()
                )
                if len(coords) >= 2:
                    self.logger.info('Favorite location found: {} ({})'.format(location_name, coords))
                break

        #TODO: This is real bad
        if coords is None:
            return coords
        else:
            return float(coords[0]), float(coords[1]), (float(coords[2]) if len(coords) == 3 else self.alt)

    def heartbeat(self):
        # Remove forts that we can now spin again.
        now = time.time()
        self.fort_timeouts = {id: timeout for id, timeout
                              in self.fort_timeouts.iteritems()
                              if timeout >= now * 1000}

        if now - self.last_heartbeat >= self.heartbeat_threshold:
            self.last_heartbeat = now
            request = self.api.create_request()
            request.get_player()
            request.check_awarded_badges()
            responses = request.call()

            if responses['responses']['GET_PLAYER']['success'] == True:
                # we get the player_data anyway, might as well store it
                self._player = responses['responses']['GET_PLAYER']['player_data']
                self.event_manager.emit(
                    'player_data',
                    sender=self,
                    level='debug',
                    formatted='player_data: {player_data}',
                    data={'player_data': self._player}
                )
            if responses['responses']['CHECK_AWARDED_BADGES']['success'] == True:
                # store awarded_badges reponse to be used in a task or part of heartbeat
                self._awarded_badges = responses['responses']['CHECK_AWARDED_BADGES']

            if self._awarded_badges.has_key('awarded_badges'):
                i = 0
                for badge in self._awarded_badges['awarded_badges']:
                    badgelevel = self._awarded_badges['awarded_badge_levels'][i]
                    badgename = BadgeType_pb2._BADGETYPE.values_by_number[badge].name
                    i += 1
                    self.event_manager.emit(
                        'badges',
                        sender=self,
                        level='info',
                        formatted='awarded badge: {badge}, lvl {level}',
                        data={'badge': badgename,
                              'level': badgelevel}
                    )
                    human_behaviour.action_delay(3, 10)

            inventory.refresh_inventory()

        try:
            self.web_update_queue.put_nowait(True)  # do this outside of thread every tick
        except Queue.Full:
            pass

    def update_web_location_worker(self):
        while True:
            self.web_update_queue.get()
            self.update_web_location()

    def display_player_info(self):
            player_stats = player()

            if player_stats:
                nextlvlxp = (int(player_stats.next_level_xp) - int(player_stats.exp))
                self.logger.info(
                    'Level: {}'.format(player_stats.level) +
                    ' (Next Level: {} XP)'.format(nextlvlxp) +
                    ' (Total: {} XP)'
                    ''.format(player_stats.exp))

                self.logger.info(
                    'Pokemon Captured: '
                    '{}'.format(player_stats.pokemons_captured) +
                    ' | Pokestops Visited: '
                    '{}'.format(player_stats.poke_stop_visits))

    def get_forts(self, order_by_distance=False):
        forts = [fort
                 for fort in self.cell['forts']
                 if 'latitude' in fort and 'type' in fort]

        if order_by_distance:
            forts.sort(key=lambda x: distance(
                self.position[0],
                self.position[1],
                x['latitude'],
                x['longitude']
            ))

        return forts

    def get_map_objects(self, lat, lng, timestamp, cellid):
        if time.time() - self.last_time_map_object < self.config.map_object_cache_time:
            return self.last_map_object

        self.last_map_object = self.api.get_map_objects(
            latitude=f2i(lat),
            longitude=f2i(lng),
            since_timestamp_ms=timestamp,
            cell_id=cellid
        )
        self.last_time_map_object = time.time()

        return self.last_map_object

    def _load_recent_forts(self):
        if not self.config.forts_cache_recent_forts:
            return

        cached_forts_path = os.path.join(_base_dir, 'data', 'recent-forts-%s.json' % self.config.username)
        try:
            # load the cached recent forts
            cached_recent_forts = []
            try:
                with open(cached_forts_path) as f:
                    cached_recent_forts = json.load(f)
            except (IOError, ValueError) as e:
                self.logger.info('[x] Error while opening cached forts: %s' % e, 'red')
                pass
            except:
                raise FileIOException("Unexpected error opening {}".cached_forts_path)

            num_cached_recent_forts = len(cached_recent_forts)
            num_recent_forts = len(self.recent_forts)

            # Handles changes in max_circle_size
            if not num_recent_forts:
                self.recent_forts = []
            elif num_recent_forts > num_cached_recent_forts:
                self.recent_forts[-num_cached_recent_forts:] = cached_recent_forts
            elif num_recent_forts < num_cached_recent_forts:
                self.recent_forts = cached_recent_forts[-num_recent_forts:]
            else:
                self.recent_forts = cached_recent_forts

            self.event_manager.emit(
                'loaded_cached_forts',
                sender=self,
                level='debug',
                formatted='Loaded cached forts...'
            )
        except IOError:
            self.event_manager.emit(
                'no_cached_forts',
                sender=self,
                level='debug',
                formatted='Starting new cached forts for {path}',
                data={'path': cached_forts_path}
            )
class ActionMachine(object):
    # DIO
    def __init__(self, novel_helper, robot_army):
        self.nh = novel_helper
        self.novel_helper = novel_helper
        self.book_warehouse = self.nh.book_warehouse
        self.robot_army = robot_army
        self.user_map = dict()
        self.init_user_map()
        self.book_map = dict()
        self.init_book_map()

        self.offline_uid_set = set(list(self.user_map.keys()))
        self.recommend_uid_set = set()
        self.choosing_uid_set = set()
        self.click_drive_uid_set = set()
        self.read_drive_uid_set = set()
        self.reading_uid_set = set()
        self.has_online_set = set()

        self._period = 0

        self.rec_sys = RecSysRandom(self)

        self.event_manager = EventManager(self)
        self.event_manager.clean_all_csv()

        self.metrics_client = MetricsClient(self)

    def get_period(self):
        return self._period

    def reset_timer(self):
        self.st = time.time()

    def timer_cost(self):
        return time.time() - self.st

    def emit_timer(self, query):
        time_cost = self.timer_cost()
        self.metrics_client.emit_timer(query, time_cost)

    # 统计查询,会先检查sqlite-in-memory的建表状态
    def query(self, sql_text, reprepare=True):
        r = self.event_manager.query(sql_text, reprepare)
        return r

    def connect_rec_sys(self, rec_sys):
        self.rec_sys = rec_sys

    def init_user_map(self):
        for regiment in self.robot_army.regiment:
            for robot in regiment.robot:
                uid = robot.uid
                robot_user = RobotUser(robot)
                self._init_use_app_info(robot_user)
                self.user_map[uid] = robot_user

    def init_book_map(self):
        for book_def in self.book_warehouse.book:
            bid = book_def.bid
            self.book_map[bid] = Book(book_def)

    def _fill_empty_word_list(self, word_list):
        word_list = [
            word for word in word_list
            if word in self.novel_helper.wv_word_idx_map
        ]
        if not word_list:
            word_list = ['empty', 'word', 'list']
        return word_list

    # 驱动分值:点击
    # click_drive_score, chapter_read_drive_score, open_app_drive_score, detest_drive_score
    def _get_robot_click_book_score(self, robot_user, book):
        book_def = book.book_def
        title_word = list(book_def.title.word)
        synopsis_word = list(book_def.synopsis.word)
        book_word_list = self._fill_no_title(title_word + synopsis_word)
        user_word_list = [ei.word for ei in robot_user.robot_def.ei]
        book_vector = self._word_list_vector_pooling(book_word_list)
        user_vector = self._word_list_vector_pooling(user_word_list)
        score_sum = np.dot(book_vector, user_vector)
        score = score_sum / (len(book_word_list) * len(user_word_list))
        return score

    # 对word_list取词向量做pooling是常见操作
    def _word_list_vector_pooling(self, word_list):
        wv_word_idx_map = self.nh.wv_word_idx_map
        wv_vector = self.nh.wv_model.vectors
        word_idx_list = [wv_word_idx_map[word.lower()] for word in word_list]
        word_vector_list = [wv_vector[idx] for idx in word_idx_list]
        sum_vector = np.sum(word_vector_list, axis=0)
        return sum_vector

    # book_word多会用到,robot_word会分成多个sub_robot_word_list与book_word_list做
    #  点积,这代表每次阅读机器人用户的关注点是不同的,同时也能引入随机量
    def _dot_robot_book(self, robot_word_list, book_word_list, sample_loop):
        robot_word_list = self._fill_empty_word_list(robot_word_list)
        book_word_list = self._fill_empty_word_list(book_word_list)
        kb = len(book_word_list)
        kr = len(robot_word_list)
        sample_word_count = (kr - 1) // sample_loop + 1
        sub_dot_score_list = []
        book_vector = self._word_list_vector_pooling(book_word_list)
        for _ in range(sample_loop):
            sub_robot_word_list = random.sample(robot_word_list,
                                                sample_word_count)
            krp = len(sub_robot_word_list)
            sub_robot_vector = self._word_list_vector_pooling(
                sub_robot_word_list)
            sub_dot_score = np.dot(sub_robot_vector, book_vector)
            sub_dot_score /= (kb * krp)
            sub_dot_score_list.append(sub_dot_score)
        dot_score = max(sub_dot_score_list)
        return dot_score

    # 机器人的点击驱动值计算
    def _get_click_drive_score(self,
                               robot_user,
                               book,
                               sample_loop=Args.default_sample_loop):
        # 传入book而非book_def是因为这样后续能让book中pb_def外的字段来参与算分
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_enlighten_info_list = robot_def.ei
        robot_enlighten_word_list = [
            enlighten_info.word for enlighten_info in robot_enlighten_info_list
        ]
        title_word_list = S.sentence_to_string_list(book_def.title)
        synopsis_word_list = S.sentence_to_string_list(book_def.synopsis)
        title_synopsis_word_list = title_word_list + synopsis_word_list
        click_drive_score = self._dot_robot_book(robot_enlighten_word_list,
                                                 synopsis_word_list,
                                                 sample_loop=sample_loop)
        return click_drive_score

    # 机器人阅读驱动值计算
    def _get_read_drive_score(self,
                              robot_user,
                              book,
                              chapter,
                              sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_enlighten_info_list = robot_def.ei
        robot_enlighten_word_list = [
            enlighten_info.word for enlighten_info in robot_enlighten_info_list
        ]
        paragraph_all_count = len(book_def.paragraph)
        assert paragraph_all_count >= chapter and chapter > 0, \
            "chapter must in range %s>=chapter>=1 but get %s" % (paragraph_all_count, chapter)
        to_read_paragraph = book_def.paragraph[chapter - 1]
        paragraph_word_list = S.paragraph_to_string_list(to_read_paragraph)
        read_drive_score = self._dot_robot_book(robot_enlighten_word_list,
                                                paragraph_word_list,
                                                sample_loop=sample_loop)
        return read_drive_score

    # 机器人标题厌恶驱动值计算
    def _get_title_detest_drive_score(self,
                                      robot_user,
                                      book,
                                      sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_detest_info_list = robot_def.detest_ei
        robot_detest_word_list = [
            enlighten_info.word for enlighten_info in robot_detest_info_list
        ]
        title_word_list = S.sentence_to_string_list(book_def.title)
        synopsis_word_list = S.sentence_to_string_list(book_def.synopsis)
        title_synopsis_word_list = title_word_list + synopsis_word_list
        detest_drive_score = self._dot_robot_book(robot_detest_word_list,
                                                  title_synopsis_word_list,
                                                  sample_loop=sample_loop)
        return detest_drive_score

    # 机器人厌恶驱动值计算
    def _get_detest_drive_score(self,
                                robot_user,
                                book,
                                chapter,
                                sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_detest_info_list = robot_def.detest_ei
        robot_detest_word_list = [
            enlighten_info.word for enlighten_info in robot_detest_info_list
        ]
        paragraph_all_count = len(book_def.paragraph)
        assert paragraph_all_count >= chapter and chapter > 0, \
            "chapter must in range %s>=chapter>=1 but get %s" % (paragraph_all_count, chapter)
        to_read_paragraph = book_def.paragraph[chapter - 1]
        paragraph_word_list = S.paragraph_to_string_list(to_read_paragraph)
        detest_drive_score = self._dot_robot_book(robot_detest_word_list,
                                                  paragraph_word_list,
                                                  sample_loop=sample_loop)
        return detest_drive_score

    # 机器人addict驱动值计算
    def _get_addict_drive_score(self,
                                robot_user,
                                book,
                                chapter,
                                sample_loop=Args.default_sample_loop):
        robot_def = robot_user._robot_def
        book_def = book._book_def
        robot_addict_info_list = robot_def.addict_ei
        robot_addict_word_list = [
            enlighten_info.word for enlighten_info in robot_addict_info_list
        ]
        paragraph_all_count = len(book_def.paragraph)
        assert paragraph_all_count >= chapter and chapter > 0, \
            "chapter must in range %s>=chapter>=1 but get %s" % (paragraph_all_count, chapter)
        to_read_paragraph = book.paragraph[chapter - 1]
        paragraph_word_list = S.paragraph_to_string_list(to_read_paragraph)
        addict_drive_score = self._dot_robot_book(robot_addict_word_list,
                                                  paragraph_word_list,
                                                  sample_loop=sample_loop)
        return addict_drive_score

    # 计算机器人驱动值时加入的噪音
    def _get_normal_noise(self, noise_range=1.):
        return noise_range * random.gauss(0., 1.)

    # 维护recent_drive_score的队列并推入新值
    def _push_drive_score(self, recent_drive_score_list, drive_score):
        recent_drive_count = 5
        if len(recent_drive_score_list) >= recent_drive_count:
            recent_drive_score_list.pop(0)
        recent_drive_score_list.append(drive_score)

    # 在关闭app时做的收尾工作
    def _just_shut_down_app(self, robot_user):
        robot_user._use_app_impulse_value -= 0.5

    # 初始化机器人用户开关app的驱动信息
    def _init_use_app_info(self, robot_user):
        robot_user._use_app_impulse_value = random.random(
        ) - 0.99  # 有20%的用户会直接开app
        robot_user._addict_rate = random.random()
        robot_user._detest_rate = random.random()

    # 根据_recent_***那些变量俩计算
    def _update_addict_rate(self, robot_user):
        addict_rate_room_decay = .8
        addict_rate_room = 1. - robot_user._addict_rate
        addict_rate_room *= addict_rate_room_decay
        robot_user._addict_rate = 1. - addict_rate_room

    # 根据_recent_***那些变量俩计算
    def _update_detest_rate(self, robot_user):
        detest_rate_decay = .8
        robot_user._detest_rate *= detest_rate_decay

    # 打开app的冲动值
    def _update_offline_use_app_impulse_value(self, robot_user):
        for bid, reading_info in robot_user._history_read_map.items():
            bid_addict_value = reading_info.addict_value
            # 尚未想好bid_addict_value怎么影响_use_app_impulse_value
        delta = (robot_user._addict_rate - robot_user._detest_rate) * 1.
        robot_user._use_app_impulse_value += delta * Args.use_app_impluse_value_update_rate

    def _update_use_app_impulse_value(self, robot_user):
        pass

    # 第一状态——offline
    def _run_robot_offline(self, robot_user):
        if robot_user._use_app_impulse_value > 0:
            self.metrics_client.emit_counter('offline2recommend', 1)
            robot_user._status = RobotStatus.RECOMMEND
        else:
            robot_user._status = RobotStatus.OFFLINE
        self._update_offline_use_app_impulse_value(robot_user)
        self._update_addict_rate(robot_user)
        self._update_detest_rate(robot_user)

    # 第二状态——recommend
    def _run_robot_recommend(self, robot_user):
        self.rec_sys.recommend(robot_user)
        robot_user._chosen_bid_set.clear()
        robot_user._choosing_delay = 2
        robot_user._reading_bid = None
        robot_user._status = RobotStatus.CHOOSING
        for bid in robot_user._choosing_bid_list:
            self.event_manager.emit_one_event(uid=robot_user._uid,
                                              bid=bid,
                                              event='impression')

    # 第三状态——choosing
    def _run_robot_choosing(self, robot_user):
        if robot_user._choosing_delay > 0:
            robot_user._status = RobotStatus.CHOOSING
        else:
            robot_user._status = RobotStatus.CLICK_DRIVE
        robot_user._choosing_delay -= 1

    # 第四状态——click_drive
    def _run_robot_click_drive(self, robot_user):
        self.reset_timer()
        for bid in robot_user._choosing_bid_list:
            if bid in robot_user._chosen_bid_set:
                continue
            book = self.book_map[bid]
            click_drive_score = self._get_click_drive_score(robot_user,
                                                            book,
                                                            sample_loop=1)
            title_detest_drive_score = self._get_title_detest_drive_score(
                robot_user, book, sample_loop=1)
            if bid in robot_user._history_impr_map:
                suppress_rate = .5
            else:
                suppress_rate = 1.
            click_drive_score *= suppress_rate
            if title_detest_drive_score > Args.title_detest_drive_threshold:
                continue
            if click_drive_score > Args.click_drive_threshold:
                robot_user._reading_bid = bid
                robot_user._chosen_bid_set.add(bid)
                robot_user._history_read_map[bid] = ReadingInfo(
                    last_period=self._period, chapter=0, addict_value=0)
                self.event_manager.emit_one_event(uid=robot_user._uid,
                                                  bid=bid,
                                                  event='go_detail')
                break
            if random.random() < 0.1:  # 10%的概率会被机器人记住“给我推过了”
                robot_user._history_impr_map[bid] = ImpressionInfo(
                    last_period=self._period)
        if robot_user._reading_bid:
            robot_user._status = RobotStatus.READ_DRIVE
        else:
            # todo: not only OFFLINE, but also RECOMMEND
            robot_user._status = RobotStatus.OFFLINE
            self._just_shut_down_app(robot_user)
        self._update_use_app_impulse_value(robot_user)
        self.emit_timer('click_drive_delay')

    # 第五状态——read_drive
    def _run_robot_read_drive(self, robot_user):
        robot_user._status = RobotStatus.OFFLINE
        bid = robot_user._reading_bid
        book = self.book_map[bid]
        reading_info = robot_user._history_read_map[bid]
        # chapter记录的是之前读完哪一章了,之后从下一章开始读
        reading_info.chapter += 1
        if reading_info.chapter > len(book._book_def.paragraph):
            # 返回第一章继续阅读
            reading_info.chapter = 1
        read_drive_score = self._get_read_drive_score(
            robot_user, book, chapter=reading_info.chapter, sample_loop=1)
        detest_drive_score = self._get_detest_drive_score(robot_user,
                                                          book,
                                                          reading_info.chapter,
                                                          sample_loop=1)
        if read_drive_score > Args.read_drive_threshold and detest_drive_score < Args.detest_drive_threshold:
            robot_user._status = RobotStatus.READING
            robot_user._chapter_finish_delay = 3
        else:
            robot_user._status = RobotStatus.CHOOSING
        self._update_use_app_impulse_value(robot_user)

    # 第六状态——reading
    def _run_robot_reading(self, robot_user):
        if robot_user._chapter_finish_delay > 0:
            robot_user._status = RobotStatus.READING
            robot_user._chapter_finish_delay -= 1
        else:
            robot_user._status = RobotStatus.READ_DRIVE

    # 一个period内的运行函数
    def run_one_period(self):
        self._period += 1
        for uid in self.offline_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_offline(robot_user)
        for uid in self.recommend_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_recommend(robot_user)
        for uid in self.choosing_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_choosing(robot_user)
        for uid in self.click_drive_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_click_drive(robot_user)
        for uid in self.read_drive_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_read_drive(robot_user)
        for uid in self.reading_uid_set:
            robot_user = self.user_map[uid]
            self._run_robot_reading(robot_user)
        self._update_robot_status()
        self._report_robot_user_status()
        self._metrics_emit_user_status()
        self._metrics_post_to_server()

    def run(self, k=Args.period_threshold):
        self.metrics_client.clear_all()
        self._period = 0
        try:
            while self._period + 1 <= k:
                self.run_one_period()
        except KeyboardInterrupt:
            pass

    # 每个period最后会报告action_machine中各种用户的量
    def _report_robot_user_status(self):
        report_message_list = []
        report_message_list.append('period: %s' % self._period)
        report_message_list.append('offline: %s' % len(self.offline_uid_set))
        report_message_list.append('recommend: %s' %
                                   len(self.recommend_uid_set))
        report_message_list.append('choosing: %s' % len(self.choosing_uid_set))
        report_message_list.append('click_drive: %s' %
                                   len(self.click_drive_uid_set))
        report_message_list.append('read_drive: %s' %
                                   len(self.read_drive_uid_set))
        report_message_list.append('reading: %s' % len(self.reading_uid_set))
        # report_message_list.append('has_online: %s' % len(self.has_online_set))
        report_message = ','.join(report_message_list)
        sys.stdout.write('\r' + report_message + ' ' * 10)
        # logging.error(report_message)

    def _metrics_emit_user_status(self):
        metrics_client = self.metrics_client
        metrics_client.emit_store('offline_uid', len(self.offline_uid_set))
        metrics_client.emit_store('recommend_uid', len(self.recommend_uid_set))
        metrics_client.emit_store('choosing_uid', len(self.choosing_uid_set))
        metrics_client.emit_store('click_drive_uid',
                                  len(self.click_drive_uid_set))
        metrics_client.emit_store('read_drive_uid',
                                  len(self.read_drive_uid_set))
        metrics_client.emit_store('reading_uid', len(self.reading_uid_set))

    def _metrics_post_to_server(self):
        self.metrics_client.post_to_metrics_server(self._period)

    # 在每一peroid结束时进行
    def _update_robot_status(self):
        self.offline_uid_set.clear()
        self.recommend_uid_set.clear()
        self.choosing_uid_set.clear()
        self.click_drive_uid_set.clear()
        self.read_drive_uid_set.clear()
        self.reading_uid_set.clear()

        for uid, robot_user in self.user_map.items():
            status = robot_user._status
            # sys.stdout.write('%s'%status)
            if status == RobotStatus.OFFLINE:
                self.offline_uid_set.add(uid)
            elif status == RobotStatus.RECOMMEND:
                self.recommend_uid_set.add(uid)
            elif status == RobotStatus.CHOOSING:
                self.choosing_uid_set.add(uid)
            elif status == RobotStatus.CLICK_DRIVE:
                self.click_drive_uid_set.add(uid)
            elif status == RobotStatus.READ_DRIVE:
                self.read_drive_uid_set.add(uid)
            elif status == RobotStatus.READING:
                self.reading_uid_set.add(uid)
Esempio n. 33
0
if __name__ == '__main__':
    # init display
    pygame.init()
    game_window = pygame.display.set_mode((SCR_WIDTH, SCR_HEIGHT))
    pygame.display.set_caption("MotherTrucker")

    # init closing conditions and time calculation
    should_close = False
    dt = 1 / 60
    end_time = 0
    start_time = get_time()

    # init first scene
    current_scene = SingleOrMultiScene(game_window)
    events_manager = EventManager.get_instance()

    # main game loop
    while not should_close:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT or events_manager.exit_event:
                client.close_connection()
                should_close = True

        current_scene.draw(events)
        pygame.display.update()
        game_window.fill((120, 110, 100))

        change = events_manager.get_scene_change()
        if change:
def register_events():
    EventManager().register_handler(HandshakingEvent, on_handshake)
    EventManager().register_handler(RoomJoinEvent, on_room_join)
    EventManager().register_handler(UserMessageEvent, on_messaging)
    EventManager().register_handler(RoomExitEvent, on_exit)
Esempio n. 35
0
class PokemonGoBot(object):
    @property
    def position(self):
        return self.api._position_lat, self.api._position_lng, 0

    @position.setter
    def position(self, position_tuple):
        self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple

    @property
    def player_data(self):
        """
        Returns the player data as received from the API.
        :return: The player data.
        :rtype: dict
        """
        return self._player

    def __init__(self, config):
        self.config = config
        self.fort_timeouts = dict()
        self.pokemon_list = json.load(
            open(os.path.join('data', 'pokemon.json'))
        )
        self.item_list = json.load(open(os.path.join('data', 'items.json')))
        self.metrics = Metrics(self)
        self.latest_inventory = None
        self.cell = None
        self.recent_forts = [None] * config.forts_max_circle_size
        self.tick_count = 0
        self.softban = False
        self.start_position = None
        self.last_map_object = None
        self.last_time_map_object = 0
        self.logger = logging.getLogger(type(self).__name__)

        # Make our own copy of the workers for this instance
        self.workers = []

    def start(self):
        self._setup_event_system()
        self._setup_logging()
        self._setup_api()

        random.seed()

    def _setup_event_system(self):
        handlers = [LoggingHandler()]
        if self.config.websocket_server_url:
            if self.config.websocket_start_embedded_server:
                self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
                self.sio_runner.start_listening_async()

            websocket_handler = SocketIoHandler(
                self,
                self.config.websocket_server_url
            )
            handlers.append(websocket_handler)

            if self.config.websocket_remote_control:
                remote_control = WebsocketRemoteControl(self).start()


        self.event_manager = EventManager(*handlers)
        self._register_events()
        if self.config.show_events:
            self.event_manager.event_report()
            sys.exit(1)

        # Registering event:
        # self.event_manager.register_event("location", parameters=['lat', 'lng'])
        #
        # Emitting event should be enough to add logging and send websocket
        # message: :
        # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}),

    def _register_events(self):
        self.event_manager.register_event(
            'location_found',
            parameters=('position', 'location')
        )
        self.event_manager.register_event('api_error')
        self.event_manager.register_event('config_error')

        self.event_manager.register_event('login_started')
        self.event_manager.register_event('login_failed')
        self.event_manager.register_event('login_successful')

        self.event_manager.register_event('set_start_location')
        self.event_manager.register_event('load_cached_location')
        self.event_manager.register_event('location_cache_ignored')
        self.event_manager.register_event(
            'position_update',
            parameters=(
                'current_position',
                'last_position',
                'distance', # optional
                'distance_unit' # optional
            )
        )
        self.event_manager.register_event('location_cache_error')

        self.event_manager.register_event('bot_start')
        self.event_manager.register_event('bot_exit')

        # sleep stuff
        self.event_manager.register_event(
            'next_sleep',
            parameters=('time',)
        )
        self.event_manager.register_event(
            'bot_sleep',
            parameters=('time_in_seconds',)
        )

        # fort stuff
        self.event_manager.register_event(
            'spun_fort',
            parameters=(
                'fort_id',
                'latitude',
                'longitude'
            )
        )
        self.event_manager.register_event(
            'lured_pokemon_found',
            parameters=(
                'fort_id',
                'fort_name',
                'encounter_id',
                'latitude',
                'longitude'
            )
        )
        self.event_manager.register_event(
            'moving_to_fort',
            parameters=(
                'fort_name',
                'distance'
            )
        )
        self.event_manager.register_event(
            'moving_to_lured_fort',
            parameters=(
                'fort_name',
                'distance',
                'lure_distance'
            )
        )
        self.event_manager.register_event(
            'spun_pokestop',
            parameters=(
                'pokestop', 'exp', 'items'
            )
        )
        self.event_manager.register_event(
            'pokestop_empty',
            parameters=('pokestop',)
        )
        self.event_manager.register_event(
            'pokestop_out_of_range',
            parameters=('pokestop',)
        )
        self.event_manager.register_event(
            'pokestop_on_cooldown',
            parameters=('pokestop', 'minutes_left')
        )
        self.event_manager.register_event(
            'unknown_spin_result',
            parameters=('status_code',)
        )
        self.event_manager.register_event('pokestop_searching_too_often')
        self.event_manager.register_event('arrived_at_fort')

        # pokemon stuff
        self.event_manager.register_event(
            'catchable_pokemon',
            parameters=(
                'pokemon_id',
                'spawn_point_id',
                'encounter_id',
                'latitude',
                'longitude',
                'expiration_timestamp_ms'
            )
        )
        self.event_manager.register_event(
            'pokemon_appeared',
            parameters=(
                'pokemon',
                'cp',
                'iv',
                'iv_display',
            )
        )
        self.event_manager.register_event(
            'pokemon_catch_rate',
            parameters=(
                'catch_rate',
                'berry_name',
                'berry_count'
            )
        )
        self.event_manager.register_event(
            'threw_berry',
            parameters=(
                'berry_name',
                'new_catch_rate'
            )
        )
        self.event_manager.register_event(
            'threw_pokeball',
            parameters=(
                'pokeball',
                'success_percentage',
                'count_left'
            )
        )
        self.event_manager.register_event(
            'pokemon_fled',
            parameters=('pokemon',)
        )
        self.event_manager.register_event(
            'pokemon_vanished',
            parameters=('pokemon',)
        )
        self.event_manager.register_event(
            'pokemon_caught',
            parameters=(
                'pokemon',
                'cp', 'iv', 'iv_display', 'exp'
            )
        )
        self.event_manager.register_event(
            'pokemon_evolved',
            parameters=('pokemon', 'iv', 'cp')
        )
        self.event_manager.register_event(
            'pokemon_evolve_fail',
            parameters=('pokemon',)
        )
        self.event_manager.register_event('skip_evolve')
        self.event_manager.register_event('threw_berry_failed', parameters=('status_code',))
        self.event_manager.register_event('vip_pokemon')


        # level up stuff
        self.event_manager.register_event(
            'level_up',
            parameters=(
                'previous_level',
                'current_level'
            )
        )
        self.event_manager.register_event(
            'level_up_reward',
            parameters=('items',)
        )

        # lucky egg
        self.event_manager.register_event(
            'used_lucky_egg',
            parameters=('amount_left',)
        )
        self.event_manager.register_event('lucky_egg_error')

        # softban
        self.event_manager.register_event('softban')
        self.event_manager.register_event('softban_fix')
        self.event_manager.register_event('softban_fix_done')

        # egg incubating
        self.event_manager.register_event(
            'incubate_try',
            parameters=(
                'incubator_id',
                'egg_id'
            )
        )
        self.event_manager.register_event(
            'incubate',
            parameters=('distance_in_km',)
        )
        self.event_manager.register_event(
            'next_egg_incubates',
            parameters=('distance_in_km',)
        )
        self.event_manager.register_event('incubator_already_used')
        self.event_manager.register_event('egg_already_incubating')
        self.event_manager.register_event(
            'egg_hatched',
            parameters=(
                'pokemon',
                'cp', 'iv', 'exp', 'stardust', 'candy'
            )
        )

        # discard item
        self.event_manager.register_event(
            'item_discarded',
            parameters=(
                'amount', 'item', 'maximum'
            )
        )
        self.event_manager.register_event(
            'item_discard_fail',
            parameters=('item',)
        )

        # inventory
        self.event_manager.register_event('inventory_full')

        # release
        self.event_manager.register_event(
            'keep_best_release',
            parameters=(
                'amount', 'pokemon', 'criteria'
            )
        )
        self.event_manager.register_event(
            'future_pokemon_release',
            parameters=(
                'pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic'
            )
        )
        self.event_manager.register_event(
            'pokemon_release',
            parameters=('pokemon', 'cp', 'iv')
        )

        # polyline walker
        self.event_manager.register_event(
            'polyline_request',
            parameters=('url',)
        )

        # cluster
        self.event_manager.register_event(
            'found_cluster',
            parameters=(
                'num_points', 'forts', 'radius', 'distance'
            )
        )
        self.event_manager.register_event(
            'arrived_at_cluster',
            parameters=(
                'forts', 'radius'
            )
        )

        # rename
        self.event_manager.register_event(
            'rename_pokemon',
            parameters=(
                'old_name', 'current_name'
            )
        )
        self.event_manager.register_event(
            'pokemon_nickname_invalid',
            parameters=('nickname',)
        )
        self.event_manager.register_event('unset_pokemon_nickname')

        # Move To map pokemon
        self.event_manager.register_event(
            'move_to_map_pokemon_fail',
            parameters=('message',)
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_updated_map',
            parameters=('lat', 'lon')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_teleport_to',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_encounter',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_move_towards',
            parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon',
                        'disappears_in')
        )
        self.event_manager.register_event(
            'move_to_map_pokemon_teleport_back',
            parameters=('last_lat', 'last_lon')
        )

    def tick(self):
        self.health_record.heartbeat()
        self.cell = self.get_meta_cell()
        self.tick_count += 1

        # Check if session token has expired
        self.check_session(self.position[0:2])

        for worker in self.workers:
            if worker.work() == WorkerResult.RUNNING:
                return

    def open_bag(self):
        response = self.get_inventory()
        self.inventory = list()

        own_pokemon_list = {
            int(x['Number']): {'name': x['Name'], 'candies_needed': x.get('Next Evolution Requirements', {'Amount': 18446744073709551615 }).get('Amount'), 'count': 0}
            for x in self.pokemon_list
        }

        inventory_items = response.get('responses', {}).get('GET_INVENTORY', {}).get(
            'inventory_delta', {}).get('inventory_items', {})
        if inventory_items:
            for item in inventory_items:
                item_info = item.get('inventory_item_data', {})
                if {"pokemon_family"}.issubset(set(item_info.keys())):
                    pokemon_family = item['inventory_item_data']['pokemon_family']
                    pokemon_data = own_pokemon_list[pokemon_family['family_id']]
                    pokemon_data['candies_number'] = pokemon_family['candy']
                if {"pokemon_data"}.issubset(set(item_info.keys())):
                    pokemon_id = item['inventory_item_data']['pokemon_data'].get('pokemon_id')
                    if pokemon_id:
                        pokemon = own_pokemon_list[pokemon_id]
                        pokemon['count'] += 1


        with open('pokemons_process.csv', 'wb') as csvfile:
            spamwriter = csv.writer(
                csvfile,
                delimiter=str(u';'),
                quotechar=str(u'"'),
                quoting=csv.QUOTE_MINIMAL
            )
            spamwriter.writerow([
                'pokemon', 'evolution_cost', 'total_candies', 'total_evolutions',
                'pokemons_available', 'evolutions_available', 'advice'
            ])
            advice = {
                True:'{} evolutions are unused. Catch more. Relax restrictions for this pokemon in config.json ("release" field)',
                False: '{} pokemons are unused. Release more. Enhance restrictions for this pokemon in config.json ("release" field)'
            }
            total_upgrade_count = 0
            for id, pokemon_info in own_pokemon_list.iteritems():
                pokemon_count = pokemon_info['count']
                current_candies_number = pokemon_info.get('candies_number', 0)
                candies_needed = pokemon_info['candies_needed']
                if pokemon_count or (current_candies_number):
                    if candies_needed == 18446744073709551615:
                        spamwriter.writerow([
                            pokemon_info['name'], 'last form', current_candies_number,
                            '-', pokemon_count, '-', advice[False].format(pokemon_count)
                        ])
                    else:
                        # if count and (candies_needed != 18446744073709551615):
                        current_candies_number = pokemon_info.get('candies_number', 0)
                        count_upgrade = current_candies_number/pokemon_info['candies_needed']
                        total_upgrade_count += min(pokemon_count, count_upgrade)
                        candies_left = candies_needed - candies_needed*current_candies_number
                        pokemons_left = pokemon_count - total_upgrade_count
                        if candies_left:
                            advice_string = advice[True].format(candies_left)
                        else:
                            advice_string = advice[False].format(pokemons_left)
                        spamwriter.writerow([
                            pokemon_info['name'], candies_needed, current_candies_number,
                            count_upgrade, pokemon_count, total_upgrade_count, advice_string
                        ])
            spamwriter.writerow([
                'Total evolutons can be made: {}'.format(total_upgrade_count)
            ])



    def get_meta_cell(self):
        location = self.position[0:2]
        cells = self.find_close_cells(*location)

        # Combine all cells into a single dict of the items we care about.
        forts = []
        wild_pokemons = []
        catchable_pokemons = []
        for cell in cells:
            if "forts" in cell and len(cell["forts"]):
                forts += cell["forts"]
            if "wild_pokemons" in cell and len(cell["wild_pokemons"]):
                wild_pokemons += cell["wild_pokemons"]
            if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]):
                catchable_pokemons += cell["catchable_pokemons"]

        # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved
        if len(forts) > 1 or not self.cell:
            return {
                "forts": forts,
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }
        # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells.
        else:
            return {
                "forts": self.cell["forts"],
                "wild_pokemons": wild_pokemons,
                "catchable_pokemons": catchable_pokemons
            }

    def update_web_location(self, cells=[], lat=None, lng=None, alt=None):
        # we can call the function with no arguments and still get the position
        # and map_cells
        if lat is None:
            lat = self.api._position_lat
        if lng is None:
            lng = self.api._position_lng
        if alt is None:
            alt = 0

        if cells == []:
            location = self.position[0:2]
            cells = self.find_close_cells(*location)

            # insert detail info about gym to fort
            for cell in cells:
                if 'forts' in cell:
                    for fort in cell['forts']:
                        if fort.get('type') != 1:
                            response_gym_details = self.api.get_gym_details(
                                gym_id=fort.get('id'),
                                player_latitude=lng,
                                player_longitude=lat,
                                gym_latitude=fort.get('latitude'),
                                gym_longitude=fort.get('longitude')
                            )
                            fort['gym_details'] = response_gym_details.get(
                                'responses', {}
                            ).get('GET_GYM_DETAILS', None)

        user_data_cells = "data/cells-%s.json" % self.config.username
        with open(user_data_cells, 'w') as outfile:
            json.dump(cells, outfile)

        user_web_location = os.path.join(
            'web', 'location-%s.json' % self.config.username
        )
        # alt is unused atm but makes using *location easier
        try:
            with open(user_web_location, 'w') as outfile:
                json.dump({
                    'lat': lat,
                    'lng': lng,
                    'alt': alt,
                    'cells': cells
                }, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

        user_data_lastlocation = os.path.join(
            'data', 'last-location-%s.json' % self.config.username
        )
        try:
            with open(user_data_lastlocation, 'w') as outfile:
                json.dump({'lat': lat, 'lng': lng, 'start_position': self.start_position}, outfile)
        except IOError as e:
            self.logger.info('[x] Error while opening location file: %s' % e)

    def find_close_cells(self, lat, lng):
        cellid = get_cell_ids(lat, lng)
        timestamp = [0, ] * len(cellid)
        response_dict = self.get_map_objects(lat, lng, timestamp, cellid)
        map_objects = response_dict.get(
            'responses', {}
        ).get('GET_MAP_OBJECTS', {})
        status = map_objects.get('status', None)

        map_cells = []
        if status and status == 1:
            map_cells = map_objects['map_cells']
            position = (lat, lng, 0)
            map_cells.sort(
                key=lambda x: distance(
                    lat,
                    lng,
                    x['forts'][0]['latitude'],
                    x['forts'][0]['longitude']) if x.get('forts', []) else 1e6
            )
        return map_cells

    def _setup_logging(self):
        # log settings
        # log format

        if self.config.debug:
            log_level = logging.DEBUG
            logging.getLogger("requests").setLevel(logging.DEBUG)
            logging.getLogger("websocket").setLevel(logging.DEBUG)
            logging.getLogger("socketio").setLevel(logging.DEBUG)
            logging.getLogger("engineio").setLevel(logging.DEBUG)
            logging.getLogger("socketIO-client").setLevel(logging.DEBUG)
            logging.getLogger("pgoapi").setLevel(logging.DEBUG)
            logging.getLogger("rpc_api").setLevel(logging.DEBUG)
        else:
            log_level = logging.ERROR
            logging.getLogger("requests").setLevel(logging.ERROR)
            logging.getLogger("websocket").setLevel(logging.ERROR)
            logging.getLogger("socketio").setLevel(logging.ERROR)
            logging.getLogger("engineio").setLevel(logging.ERROR)
            logging.getLogger("socketIO-client").setLevel(logging.ERROR)
            logging.getLogger("pgoapi").setLevel(logging.ERROR)
            logging.getLogger("rpc_api").setLevel(logging.ERROR)

        logging.basicConfig(
            level=log_level,
            format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s'
        )
    def check_session(self, position):
        # Check session expiry
        if self.api._auth_provider and self.api._auth_provider._ticket_expire:

            # prevent crash if return not numeric value
            if not self.is_numeric(self.api._auth_provider._ticket_expire):
                self.logger.info("Ticket expired value is not numeric", 'yellow')
                return

            remaining_time = \
                self.api._auth_provider._ticket_expire / 1000 - time.time()

            if remaining_time < 60:
                self.event_manager.emit(
                    'api_error',
                    sender=self,
                    level='info',
                    formatted='Session stale, re-logging in.'
                )
                position = self.position
                self.api = ApiWrapper()
                self.position = position
                self.login()
                self.api.activate_signature(self.get_encryption_lib())

    @staticmethod
    def is_numeric(s):
        try:
            float(s)
            return True
        except ValueError:
            return False

    def login(self):
        self.event_manager.emit(
            'login_started',
            sender=self,
            level='info',
            formatted="Login procedure started."
        )
        lat, lng = self.position[0:2]
        self.api.set_position(lat, lng, 0)

        while not self.api.login(
            self.config.auth_service,
            str(self.config.username),
            str(self.config.password)):

            self.event_manager.emit(
                'login_failed',
                sender=self,
                level='info',
                formatted="Login error, server busy. Waiting 10 seconds to try again."
            )
            time.sleep(10)

        self.event_manager.emit(
            'login_successful',
            sender=self,
            level='info',
            formatted="Login successful."
        )

    def get_encryption_lib(self):
        if _platform == "linux" or _platform == "linux2" or _platform == "darwin":
            file_name = 'encrypt.so'
        elif _platform == "Windows" or _platform == "win32":
            # Check if we are on 32 or 64 bit
            if sys.maxsize > 2**32:
                file_name = 'encrypt_64.dll'
            else:
                file_name = 'encrypt.dll'

        if self.config.encrypt_location == '':
            path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
        else:
            path = self.config.encrypt_location

        full_path = path + '/'+ file_name
        if not os.path.isfile(full_path):
            self.logger.error(file_name + ' is not found! Please place it in the bots root directory or set libencrypt_location in config.')
            self.logger.info('Platform: '+ _platform + ' Encrypt.so directory: '+ path)
            sys.exit(1)
        else:
            self.logger.info('Found '+ file_name +'! Platform: ' + _platform + ' Encrypt.so directory: ' + path)

        return full_path

    def _setup_api(self):
        # instantiate pgoapi
        self.api = ApiWrapper()

        # provide player position on the earth
        self._set_starting_position()

        self.login()
        # chain subrequests (methods) into one RPC call

        self._print_character_info()
        self.api.activate_signature(self.get_encryption_lib())
        self.logger.info('')
        self.update_inventory()
        # send empty map_cells and then our position
        self.update_web_location()
        self.open_bag()

    def _print_character_info(self):
        # get player profile call
        # ----------------------
        response_dict = self.api.get_player()
        # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2)))
        currency_1 = "0"
        currency_2 = "0"

        if response_dict:
            self._player = response_dict['responses']['GET_PLAYER']['player_data']
            player = self._player
        else:
            self.logger.info(
                "The API didn't return player info, servers are unstable - "
                "retrying.", 'red'
            )
            sleep(5)
            self._print_character_info()

        # @@@ TODO: Convert this to d/m/Y H:M:S
        creation_date = datetime.datetime.fromtimestamp(
            player['creation_timestamp_ms'] / 1e3)
        creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S")

        pokecoins = '0'
        stardust = '0'
        items_stock = self.current_inventory()

        if 'amount' in player['currencies'][0]:
            pokecoins = player['currencies'][0]['amount']
        if 'amount' in player['currencies'][1]:
            stardust = player['currencies'][1]['amount']
        self.logger.info('')
        self.logger.info('--- {username} ---'.format(**player))
        self.get_player_info()
        self.logger.info(
            'Pokemon Bag: {}/{}'.format(
                self.get_inventory_count('pokemon'),
                player['max_pokemon_storage']
            )
        )
        self.logger.info(
            'Items: {}/{}'.format(
                self.get_inventory_count('item'),
                player['max_item_storage']
            )
        )
        self.logger.info(
            'Stardust: {}'.format(stardust) +
            ' | Pokecoins: {}'.format(pokecoins)
        )
        # Items Output
        self.logger.info(
            'PokeBalls: ' + str(items_stock[1]) +
            ' | GreatBalls: ' + str(items_stock[2]) +
            ' | UltraBalls: ' + str(items_stock[3]))

        self.logger.info(
            'RazzBerries: ' + str(items_stock[701]) +
            ' | BlukBerries: ' + str(items_stock[702]) +
            ' | NanabBerries: ' + str(items_stock[703]))

        self.logger.info(
            'LuckyEgg: ' + str(items_stock[301]) +
            ' | Incubator: ' + str(items_stock[902]) +
            ' | TroyDisk: ' + str(items_stock[501]))

        self.logger.info(
            'Potion: ' + str(items_stock[101]) +
            ' | SuperPotion: ' + str(items_stock[102]) +
            ' | HyperPotion: ' + str(items_stock[103]) +
            ' | MaxPotion: ' + str(items_stock[104]))

        self.logger.info(
            'Incense: ' + str(items_stock[401]) +
            ' | IncenseSpicy: ' + str(items_stock[402]) +
            ' | IncenseCool: ' + str(items_stock[403]))

        self.logger.info(
            'Revive: ' + str(items_stock[201]) +
            ' | MaxRevive: ' + str(items_stock[202]))

        self.logger.info('')

    def use_lucky_egg(self):
        return self.api.use_item_xp_boost(item_id=301)

    def get_inventory(self):
        if self.latest_inventory is None:
            self.latest_inventory = self.api.get_inventory()
        return self.latest_inventory

    def update_inventory(self):
        response = self.get_inventory()
        self.inventory = list()
        inventory_items = response.get('responses', {}).get('GET_INVENTORY', {}).get(
            'inventory_delta', {}).get('inventory_items', {})
        if inventory_items:
            for item in inventory_items:
                item_info = item.get('inventory_item_data', {}).get('item', {})
                if {"item_id", "count"}.issubset(set(item_info.keys())):
                    self.inventory.append(item['inventory_item_data']['item'])

    def current_inventory(self):
        inventory_req = self.get_inventory()
        inventory_dict = inventory_req['responses']['GET_INVENTORY'][
            'inventory_delta']['inventory_items']

        user_web_inventory = 'web/inventory-%s.json' % self.config.username

        with open(user_web_inventory, 'w') as outfile:
            json.dump(inventory_dict, outfile)

        # get player items stock
        # ----------------------
        items_stock = {x.value: 0 for x in list(Item)}

        for item in inventory_dict:
            item_dict = item.get('inventory_item_data', {}).get('item', {})
            item_count = item_dict.get('count')
            item_id = item_dict.get('item_id')

            if item_count and item_id:
                if item_id in items_stock:
                    items_stock[item_id] = item_count
        return items_stock

    def item_inventory_count(self, id):
        inventory_req = self.get_inventory()
        inventory_dict = inventory_req['responses'][
            'GET_INVENTORY']['inventory_delta']['inventory_items']

        if id == 'all':
            return self._all_items_inventory_count(inventory_dict)
        else:
            return self._item_inventory_count_per_id(id, inventory_dict)

    def _item_inventory_count_per_id(self, id, inventory_dict):
        item_count = 0

        for item in inventory_dict:
            item_dict = item.get('inventory_item_data', {}).get('item', {})
            item_id = item_dict.get('item_id', False)
            item_count = item_dict.get('count', False)
            if item_id == int(id) and item_count:
                return item_count
        return 0

    def _all_items_inventory_count(self, inventory_dict):
        item_count_dict = {}

        for item in inventory_dict:
            item_dict = item.get('inventory_item_data', {}).get('item', {})
            item_id = item_dict.get('item_id', False)
            item_count = item_dict.get('count', False)
            if item_id and item_count:
                item_count_dict[item_id] = item_count

        return item_count_dict

    def _set_starting_position(self):

        self.event_manager.emit(
            'set_start_location',
            sender=self,
            level='info',
            formatted='Setting start location.'
        )

        has_position = False

        if self.config.test:
            # TODO: Add unit tests
            return

        if self.config.location:
            location_str = self.config.location
            location = self.get_pos_by_name(location_str.replace(" ", ""))
            msg = "Location found: {location} {position}"
            self.event_manager.emit(
                'location_found',
                sender=self,
                level='info',
                formatted=msg,
                data={
                    'location': location_str,
                    'position': location
                }
            )

            self.api.set_position(*location)

            self.event_manager.emit(
                'position_update',
                sender=self,
                level='info',
                formatted="Now at {current_position}",
                data={
                    'current_position': self.position,
                    'last_position': '',
                    'distance': '',
                    'distance_unit': ''
                }
            )

            self.start_position = self.position

            has_position = True

        if self.config.location_cache:
            try:
                # save location flag used to pull the last known location from
                # the location.json
                self.event_manager.emit(
                    'load_cached_location',
                    sender=self,
                    level='debug',
                    formatted='Loading cached location...'
                )
                with open('data/last-location-%s.json' %
                    self.config.username) as f:
                    location_json = json.load(f)
                location = (
                    location_json['lat'],
                    location_json['lng'],
                    0.0
                )

                # If location has been set in config, only use cache if starting position has not differed
                if has_position and 'start_position' in location_json:
                    last_start_position = tuple(location_json.get('start_position', []))

                    # Start position has to have been set on a previous run to do this check
                    if last_start_position and last_start_position != self.start_position:
                        msg = 'Going to a new place, ignoring cached location.'
                        self.event_manager.emit(
                            'location_cache_ignored',
                            sender=self,
                            level='debug',
                            formatted=msg
                        )
                        return

                self.api.set_position(*location)
                self.event_manager.emit(
                    'position_update',
                    sender=self,
                    level='debug',
                    formatted='Loaded location {current_position} from cache',
                    data={
                        'current_position': location,
                        'last_position': '',
                        'distance': '',
                        'distance_unit': ''
                    }
                )

                has_position = True
            except Exception:
                if has_position is False:
                    sys.exit(
                        "No cached Location. Please specify initial location."
                    )
                self.event_manager.emit(
                    'location_cache_error',
                    sender=self,
                    level='debug',
                    formatted='Parsing cached location failed.'
                )

    def get_pos_by_name(self, location_name):
        # Check if the given location is already a coordinate.
        if ',' in location_name:
            possible_coordinates = re.findall(
                "[-]?\d{1,3}[.]\d{3,7}", location_name
            )
            if len(possible_coordinates) == 2:
                # 2 matches, this must be a coordinate. We'll bypass the Google
                # geocode so we keep the exact location.
                self.logger.info(
                    '[x] Coordinates found in passed in location, '
                    'not geocoding.'
                )
                return float(possible_coordinates[0]), float(possible_coordinates[1]), float("0.0")

        geolocator = GoogleV3(api_key=self.config.gmapkey)
        loc = geolocator.geocode(location_name, timeout=10)

        return float(loc.latitude), float(loc.longitude), float(loc.altitude)

    def heartbeat(self):
        # Remove forts that we can now spin again.
        self.fort_timeouts = {id: timeout for id, timeout
                              in self.fort_timeouts.iteritems()
                              if timeout >= time.time() * 1000}
        request = self.api.create_request()
        request.get_player()
        request.check_awarded_badges()
        request.call()
        self.update_web_location()  # updates every tick

    def get_inventory_count(self, what):
        response_dict = self.get_inventory()
        inventory_items = response_dict.get('responses', {}).get('GET_INVENTORY', {}).get(
            'inventory_delta', {}).get('inventory_items', {})
        if inventory_items:
            pokecount = 0
            itemcount = 1
            for item in inventory_items:
                if 'inventory_item_data' in item:
                    if 'pokemon_data' in item['inventory_item_data']:
                        pokecount += 1
                    itemcount += item['inventory_item_data'].get('item', {}).get('count', 0)
        if 'pokemon' in what:
            return pokecount
        if 'item' in what:
            return itemcount
        return '0'

    def get_player_info(self):
        response_dict = self.get_inventory()
        inventory_items = response_dict.get('responses', {}).get('GET_INVENTORY', {}).get(
            'inventory_delta', {}).get('inventory_items', {})
        if inventory_items:
            pokecount = 0
            itemcount = 1
            for item in inventory_items:
                # print('item {}'.format(item))
                playerdata = item.get('inventory_item_data', {}).get('player_stats')
                if playerdata:
                    nextlvlxp = (int(playerdata.get('next_level_xp', 0)) - int(playerdata.get('experience', 0)))

                    if 'level' in playerdata and 'experience' in playerdata:
                        self.logger.info(
                            'Level: {level}'.format(
                                **playerdata) +
                            ' (Next Level: {} XP)'.format(
                                nextlvlxp) +
                            ' (Total: {experience} XP)'
                            ''.format(**playerdata))

                    if 'pokemons_captured' in playerdata and 'poke_stop_visits' in playerdata:
                        self.logger.info(
                            'Pokemon Captured: '
                            '{pokemons_captured}'.format(
                                **playerdata) +
                            ' | Pokestops Visited: '
                            '{poke_stop_visits}'.format(
                                **playerdata))

    def has_space_for_loot(self):
        number_of_things_gained_by_stop = 5
        enough_space = (
            self.get_inventory_count('item') <
            self._player['max_item_storage'] - number_of_things_gained_by_stop
        )

        return enough_space

    def get_forts(self, order_by_distance=False):
        forts = [fort
             for fort in self.cell['forts']
             if 'latitude' in fort and 'type' in fort]

        if order_by_distance:
            forts.sort(key=lambda x: distance(
                self.position[0],
                self.position[1],
                x['latitude'],
                x['longitude']
            ))

        return forts

    def get_map_objects(self, lat, lng, timestamp, cellid):
        if time.time() - self.last_time_map_object < self.config.map_object_cache_time:
            return self.last_map_object

        self.last_map_object = self.api.get_map_objects(
            latitude=f2i(lat),
            longitude=f2i(lng),
            since_timestamp_ms=timestamp,
            cell_id=cellid
        )
        self.last_time_map_object = time.time()

        return self.last_map_object
Esempio n. 36
0
def main_setup():
    random.seed(0)
    globals_.event_manager = EventManager(None)
    globals_.stats_manager = StatsManager('output')