Exemple #1
0
    def __internal_look_for_button(self, origin, filename, ratiomin, ratiomax,
                                   communicator, upper):
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.debug("lookForButton: Reading lines")
        min_distance_to_middle = None
        try:
            screenshot_read = cv2.imread(filename)
            gray = cv2.cvtColor(screenshot_read, cv2.COLOR_BGR2GRAY)
        except cv2.error:
            origin_logger.error("Screenshot corrupted")
            return False

        if screenshot_read is None:
            origin_logger.error("Screenshot corrupted")
            return False

        height, width, _ = screenshot_read.shape
        _widthold = float(width)
        origin_logger.debug(
            "lookForButton: Determined screenshot scale: {} x {}", height,
            width)

        # resize for better line quality
        height, width = gray.shape
        factor = width / _widthold

        gray = cv2.GaussianBlur(gray, (3, 3), 0)
        edges = cv2.Canny(gray, 50, 200, apertureSize=3)
        # checking for all possible button lines

        max_line_length = (width / ratiomin) + (width * 0.18)
        origin_logger.debug("lookForButton: MaxLineLength: {}",
                            max_line_length)
        min_line_length = (width / ratiomax) - (width * 0.02)
        origin_logger.debug("lookForButton: MinLineLength: {}",
                            min_line_length)

        kernel = np.ones((2, 2), np.uint8)
        edges = cv2.morphologyEx(edges, cv2.MORPH_GRADIENT, kernel)

        num_lines = 0
        lines = []
        lines = cv2.HoughLinesP(edges,
                                rho=1,
                                theta=math.pi / 180,
                                threshold=90,
                                minLineLength=min_line_length,
                                maxLineGap=5)
        if lines is None:
            return False

        lines = self.check_lines(lines, height)

        _last_y = 0
        for line in lines:
            line = [line]
            for x1, y1, x2, y2 in line:

                if y1 == y2 and x2 - x1 <= max_line_length and x2 - x1 >= min_line_length \
                        and y1 > height / 3 \
                        and (x2 - x1) / 2 + x1 < width / 2 + 50 and (x2 - x1) / 2 + x1 > width / 2 - 50:

                    num_lines += 1
                    min_distance_to_middle_tmp = y1 - (height / 2)
                    if upper:
                        if min_distance_to_middle is None:
                            min_distance_to_middle = min_distance_to_middle_tmp
                            click_y = y1 + 50
                            _last_y = y1
                            _x1 = x1
                            _x2 = x2
                        else:
                            if min_distance_to_middle_tmp < min_distance_to_middle:
                                click_y = _last_y + ((y1 - _last_y) / 2)
                                _last_y = y1
                                _x1 = x1
                                _x2 = x2

                    else:
                        click_y = _last_y + ((y1 - _last_y) / 2)
                        _last_y = y1
                        _x1 = x1
                        _x2 = x2
                    origin_logger.debug(
                        "lookForButton: Found Buttonline Nr. {} - Line lenght: {}px Coords - X: {} {} "
                        "Y: {} {}", num_lines, x2 - x1, x1, x2, y1, y1)

        if 1 < num_lines <= 6:
            # recalculate click area for real resolution
            click_x = int(
                ((width - _x2) + ((_x2 - _x1) / 2)) / round(factor, 2))
            click_y = int(click_y)
            origin_logger.debug('lookForButton: found Button - click on it')
            communicator.click(click_x, click_y)
            time.sleep(4)
            return True

        elif num_lines > 6:
            origin_logger.debug(
                'lookForButton: found to much Buttons :) - close it')
            communicator.click(int(width - (width / 7.2)),
                               int(height - (height / 12.19)))
            time.sleep(4)

            return True

        origin_logger.debug('lookForButton: did not found any Button')
        return False
Exemple #2
0
    def __call__(self, *args, **kwargs):
        logger.debug2("HTTP Request from {}", request.remote_addr)
        origin = request.headers.get('Origin')
        origin_logger = get_origin_logger(logger, origin=origin)
        abort = False
        if request.url_rule is not None and str(
                request.url_rule) == '/status/':
            auth = request.headers.get('Authorization', False)
            if self.application_args.mitm_status_password != "" and \
                    (not auth or auth != self.application_args.mitm_status_password):
                self.response = Response(status=500, headers={})
                abort = True
            else:
                abort = False
        elif request.url is not None and str(
                request.url_rule) == '/origin_generator':
            auth = request.headers.get('Authorization', None)
            if auth is None or not check_auth(
                    auth, self.application_args,
                    self.mapping_manager.get_auths()):
                origin_logger.warning("Unauthorized attempt to POST from {}",
                                      request.remote_addr)
                self.response = Response(status=403, headers={})
                abort = True
        else:
            if origin is None:
                origin_logger.warning("Missing Origin header in request")
                self.response = Response(status=500, headers={})
                abort = True
            elif self.mapping_manager.get_all_devicemappings().keys() is not None and \
                    origin not in self.mapping_manager.get_all_devicemappings().keys():
                origin_logger.warning(
                    "MITMReceiver request without Origin or disallowed Origin")
                self.response = Response(status=403, headers={})
                abort = True
            elif self.mapping_manager.get_auths() is not None:
                auth = request.headers.get('Authorization', None)
                if auth is None or not check_auth(
                        origin_logger, auth, self.application_args,
                        self.mapping_manager.get_auths()):
                    origin_logger.warning(
                        "Unauthorized attempt to POST from {}",
                        request.remote_addr)
                    self.response = Response(status=403, headers={})
                    abort = True

        if not abort:
            try:
                content_encoding = request.headers.get('Content-Encoding',
                                                       None)
                if content_encoding and content_encoding == "gzip":
                    # we need to unpack the data first
                    # https://stackoverflow.com/questions/28304515/receiving-gzip-with-flask
                    compressed_data = io.BytesIO(request.data)
                    text_data = gzip.GzipFile(fileobj=compressed_data,
                                              mode='r')
                    request_data = json.loads(text_data.read())
                else:
                    request_data = request.data

                content_type = request.headers.get('Content-Type', None)
                if content_type and content_type == "application/json":
                    request_data = json.loads(request_data)
                else:
                    request_data = request_data
                response_payload = self.action(origin, request_data, *args,
                                               **kwargs)
                if response_payload is None:
                    response_payload = ""
                if type(response_payload) is Response:
                    self.response = response_payload
                else:
                    self.response = Response(
                        status=200,
                        headers={"Content-Type": "application/json"})
                    self.response.data = response_payload
            except Exception as e:  # TODO: catch exact exception
                origin_logger.warning(
                    "Could not get JSON data from request: {}", e)
                self.response = Response(status=500, headers={})
                import traceback
                traceback.print_exc()
        return self.response
Exemple #3
0
    async def __connection_handler(self, websocket_client_connection: websockets.WebSocketClientProtocol,
                                   path: str) -> None:
        if self.__stop_server.is_set():
            return
        # check auth and stuff TODO
        origin: Optional[str]
        success: Optional[bool]
        (origin, success) = await self.__authenticate_connection(websocket_client_connection)
        if success is False:
            # failed auth, stop connection
            await self.__close_websocket_client_connection(origin, websocket_client_connection)
            return
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.info("New connection from {}", websocket_client_connection.remote_address)
        if self.__enable_configmode:
            origin_logger.warning('Connected in ConfigMode.  No mapping will occur in the current mode')
        async with self.__users_connecting_mutex:
            if origin in self.__users_connecting:
                origin_logger.info("Client is already connecting")
                return
            else:
                self.__users_connecting.add(origin)

        continue_register = True
        async with self.__current_users_mutex:
            origin_logger.debug("Checking if an entry is already present")
            entry = self.__current_users.get(origin, None)
            device = None
            use_configmode = self.__enable_configmode
            if not self.__enable_configmode:
                for _, dev in self.__data_manager.search('device', params={'origin': origin}).items():
                    if dev['origin'] == origin:
                        device = dev
                        break
                if not self.__data_manager.is_device_active(device.identifier):
                    origin_logger.warning('Origin is currently paused. Unpause through MADmin to begin working')
                    use_configmode = True
            if entry is None or use_configmode:
                origin_logger.info("Need to start a new worker thread")

                entry = WebsocketConnectedClientEntry(origin=origin,
                                                      websocket_client_connection=websocket_client_connection,
                                                      worker_instance=None,
                                                      worker_thread=None,
                                                      loop_running=self.__loop)
                if not await self.__add_worker_and_thread_to_entry(entry, origin, use_configmode=use_configmode):
                    continue_register = False
            else:
                origin_logger.info("There is a worker thread entry present, handling accordingly")
                if entry.websocket_client_connection.open:
                    origin_logger.error("Old connection open while a new one is attempted to be established, "
                                        "aborting handling of connection")
                    continue_register = False

                entry.websocket_client_connection = websocket_client_connection
                # TODO: also change the worker's Communicator? idk yet
                if entry.worker_thread.is_alive() and not entry.worker_instance.is_stopping():
                    origin_logger.info("Worker thread still alive, continue as usual")
                    # TODO: does this need more handling? probably update communicator or whatever?
                elif not entry.worker_thread.is_alive():
                    origin_logger.info("Old thread is dead, trying to start a new one")
                    if not await self.__add_worker_and_thread_to_entry(entry, origin, use_configmode=use_configmode):
                        continue_register = False
                else:
                    origin_logger.info("Old thread is about to stop. Wait a little and reconnect")
                    # random sleep to not have clients try again in sync
                    continue_register = False
            if continue_register:
                self.__current_users[origin] = entry

        if not continue_register:
            await asyncio.sleep(rand.uniform(3, 15))
            async with self.__users_connecting_mutex:
                origin_logger.debug("Removing from users_connecting")
                self.__users_connecting.remove(origin)
            return

        try:
            if not entry.worker_thread.is_alive():
                entry.worker_thread.start()
            # TODO: we need to somehow check threads and synchronize connection status with worker status?
            async with self.__users_connecting_mutex:
                self.__users_connecting.remove(origin)
            receiver_task = asyncio.ensure_future(
                self.__client_message_receiver(origin, entry))
            await receiver_task
        except Exception as e:
            origin_logger.opt(exception=True).error("Other unhandled exception during registration: {}", e)
        # also check if thread is already running to not start it again. If it is not alive, we need to create it..
        finally:
            origin_logger.info("Awaiting unregister")
            # TODO: cleanup thread is not really desired, I'd prefer to only restart a worker if the route changes :(
            self.__worker_shutdown_queue.put(entry.worker_thread)
        origin_logger.info("Done with connection ({})", websocket_client_connection.remote_address)
Exemple #4
0
    def raids(self, origin: str, map_proto: dict, mitm_mapper):
        """
        Update/Insert raids from a map_proto dict
        """
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.debug3("DbPogoProtoSubmit::raids called with data received")
        cells = map_proto.get("cells", None)
        if cells is None:
            return False
        raid_args = []
        now = datetime.utcfromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S")

        query_raid = (
            "INSERT INTO raid (gym_id, level, spawn, start, end, pokemon_id, cp, move_1, move_2, last_scanned, form, "
            "is_exclusive, gender, costume, evolution) "
            "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) "
            "ON DUPLICATE KEY UPDATE level=VALUES(level), spawn=VALUES(spawn), start=VALUES(start), "
            "end=VALUES(end), pokemon_id=VALUES(pokemon_id), cp=VALUES(cp), move_1=VALUES(move_1), "
            "move_2=VALUES(move_2), last_scanned=VALUES(last_scanned), is_exclusive=VALUES(is_exclusive), "
            "form=VALUES(form), gender=VALUES(gender), costume=VALUES(costume), evolution=VALUES(evolution)"
        )

        for cell in cells:
            for gym in cell["forts"]:
                if gym["type"] == 0 and gym["gym_details"]["has_raid"]:
                    gym_has_raid = gym["gym_details"]["raid_info"]["has_pokemon"]
                    if gym_has_raid:
                        raid_info = gym["gym_details"]["raid_info"]

                        pokemon_id = raid_info["raid_pokemon"]["id"]
                        cp = raid_info["raid_pokemon"]["cp"]
                        move_1 = raid_info["raid_pokemon"]["move_1"]
                        move_2 = raid_info["raid_pokemon"]["move_2"]
                        form = raid_info["raid_pokemon"]["display"]["form_value"]
                        gender = raid_info["raid_pokemon"]["display"]["gender_value"]
                        costume = raid_info["raid_pokemon"]["display"]["costume_value"]
                        evolution = raid_info["raid_pokemon"]["display"].get("current_temp_evolution", 0)
                    else:
                        pokemon_id = None
                        cp = 0
                        move_1 = 1
                        move_2 = 2
                        form = None
                        gender = None
                        costume = None
                        evolution = 0

                    raid_end_sec = int(gym["gym_details"]["raid_info"]["raid_end"] / 1000)
                    raid_spawn_sec = int(gym["gym_details"]["raid_info"]["raid_spawn"] / 1000)
                    raid_battle_sec = int(gym["gym_details"]["raid_info"]["raid_battle"] / 1000)

                    raidend_date = datetime.utcfromtimestamp(
                        float(raid_end_sec)).strftime("%Y-%m-%d %H:%M:%S")
                    raidspawn_date = datetime.utcfromtimestamp(float(raid_spawn_sec)).strftime(
                        "%Y-%m-%d %H:%M:%S")
                    raidstart_date = datetime.utcfromtimestamp(float(raid_battle_sec)).strftime(
                        "%Y-%m-%d %H:%M:%S")

                    is_exclusive = gym["gym_details"]["raid_info"]["is_exclusive"]
                    level = gym["gym_details"]["raid_info"]["level"]
                    gymid = gym["id"]

                    mitm_mapper.collect_raid_stats(origin, gymid)

                    origin_logger.debug3("Adding/Updating gym {} with level {} ending at {}", gymid, level,
                                         raidend_date)

                    raid_args.append(
                        (
                            gymid,
                            level,
                            raidspawn_date,
                            raidstart_date,
                            raidend_date,
                            pokemon_id, cp, move_1, move_2, now,
                            form,
                            is_exclusive,
                            gender,
                            costume,
                            evolution
                        )
                    )
        self._db_exec.executemany(query_raid, raid_args, commit=True)
        origin_logger.debug3("DbPogoProtoSubmit::raids: Done submitting raids with data received")
        return True
Exemple #5
0
    def mon_iv(self, origin: str, timestamp: float, encounter_proto: dict, mitm_mapper):
        """
        Update/Insert a mon with IVs
        """
        origin_logger = get_origin_logger(logger, origin=origin)
        wild_pokemon = encounter_proto.get("wild_pokemon", None)
        if wild_pokemon is None or wild_pokemon.get("encounter_id", 0) == 0 or not str(wild_pokemon["spawnpoint_id"]):
            return

        origin_logger.debug3("Updating IV sent for encounter at {}", timestamp)

        now = datetime.utcfromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S")

        spawnid = int(str(wild_pokemon["spawnpoint_id"]), 16)

        getdetspawntime = self._get_detected_endtime(str(spawnid))
        despawn_time_unix = gen_despawn_timestamp(getdetspawntime, timestamp)
        despawn_time = datetime.utcfromtimestamp(despawn_time_unix).strftime("%Y-%m-%d %H:%M:%S")

        latitude = wild_pokemon.get("latitude")
        longitude = wild_pokemon.get("longitude")
        pokemon_data = wild_pokemon.get("pokemon_data")
        encounter_id = wild_pokemon["encounter_id"]
        shiny = wild_pokemon["pokemon_data"]["display"].get("is_shiny", 0)

        if encounter_id < 0:
            encounter_id = encounter_id + 2 ** 64

        mitm_mapper.collect_mon_iv_stats(origin, encounter_id, int(shiny))

        if getdetspawntime is None:
            origin_logger.debug3("updating IV mon #{} at {}, {}. Despawning at {} (init)", pokemon_data["id"], latitude,
                                 longitude, despawn_time)
        else:
            origin_logger.debug3("updating IV mon #{} at {}, {}. Despawning at {} (non-init)", pokemon_data["id"],
                                 latitude, longitude, despawn_time)

        capture_probability = encounter_proto.get("capture_probability")
        capture_probability_list = capture_probability.get("capture_probability_list")
        if capture_probability_list is not None:
            capture_probability_list = capture_probability_list.replace("[", "").replace("]", "").split(",")

        pokemon_display = pokemon_data.get("display", {})

        # ditto detector
        if is_mon_ditto(origin_logger, pokemon_data):
            # mon must be a ditto :D
            mon_id = 132
            gender = 3
            move_1 = 242
            move_2 = 133
            form = 0
        else:
            mon_id = pokemon_data.get("id")
            gender = pokemon_display.get("gender_value", None)
            move_1 = pokemon_data.get("move_1")
            move_2 = pokemon_data.get("move_2")
            form = pokemon_display.get("form_value", None)

        query = (
            "INSERT INTO pokemon (encounter_id, spawnpoint_id, pokemon_id, latitude, longitude, disappear_time, "
            "individual_attack, individual_defense, individual_stamina, move_1, move_2, cp, cp_multiplier, "
            "weight, height, gender, catch_prob_1, catch_prob_2, catch_prob_3, rating_attack, rating_defense, "
            "weather_boosted_condition, last_modified, costume, form) "
            "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, "
            "%s, %s, %s, %s, %s) "
            "ON DUPLICATE KEY UPDATE last_modified=VALUES(last_modified), disappear_time=VALUES(disappear_time), "
            "individual_attack=VALUES(individual_attack), individual_defense=VALUES(individual_defense), "
            "individual_stamina=VALUES(individual_stamina), move_1=VALUES(move_1), move_2=VALUES(move_2), "
            "cp=VALUES(cp), cp_multiplier=VALUES(cp_multiplier), weight=VALUES(weight), height=VALUES(height), "
            "gender=VALUES(gender), catch_prob_1=VALUES(catch_prob_1), catch_prob_2=VALUES(catch_prob_2), "
            "catch_prob_3=VALUES(catch_prob_3), rating_attack=VALUES(rating_attack), "
            "rating_defense=VALUES(rating_defense), weather_boosted_condition=VALUES(weather_boosted_condition), "
            "costume=VALUES(costume), form=VALUES(form), pokemon_id=VALUES(pokemon_id)"
        )
        insert_values = (
            encounter_id,
            spawnid,
            mon_id,
            latitude, longitude, despawn_time,
            pokemon_data.get("individual_attack"),
            pokemon_data.get("individual_defense"),
            pokemon_data.get("individual_stamina"),
            move_1,
            move_2,
            pokemon_data.get("cp"),
            pokemon_data.get("cp_multiplier"),
            pokemon_data.get("weight"),
            pokemon_data.get("height"),
            gender,
            float(capture_probability_list[0]),
            float(capture_probability_list[1]),
            float(capture_probability_list[2]),
            None, None,
            pokemon_display.get('weather_boosted_value', None),
            now,
            pokemon_display.get("costume_value", None),
            form
        )

        self._db_exec.execute(query, insert_values, commit=True)
        origin_logger.debug3("Done updating mon in DB")
        return True
Exemple #6
0
 def set_injection_status(self, origin, status=True):
     origin_logger = get_origin_logger(logger, origin=origin)
     if origin not in self.__injected or not self.__injected[
             origin] and status is True:
         origin_logger.success("Worker is injected now")
     self.__injected[origin] = status
Exemple #7
0
    def gyms(self, origin: str, map_proto: dict):
        """
        Update/Insert gyms from a map_proto dict
        """
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.debug3("DbPogoProtoSubmit::gyms called with data received from")
        cells = map_proto.get("cells", None)
        if cells is None:
            return False
        gym_args = []
        gym_details_args = []
        now = datetime.utcfromtimestamp(
            time.time()).strftime("%Y-%m-%d %H:%M:%S")

        query_gym = (
            "INSERT INTO gym (gym_id, team_id, guard_pokemon_id, slots_available, enabled, latitude, longitude, "
            "total_cp, is_in_battle, last_modified, last_scanned, is_ex_raid_eligible) "
            "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) "
            "ON DUPLICATE KEY UPDATE "
            "guard_pokemon_id=VALUES(guard_pokemon_id), team_id=VALUES(team_id), "
            "slots_available=VALUES(slots_available), last_scanned=VALUES(last_scanned), "
            "last_modified=VALUES(last_modified), latitude=VALUES(latitude), longitude=VALUES(longitude), "
            "is_ex_raid_eligible=VALUES(is_ex_raid_eligible)"
        )
        query_gym_details = (
            "INSERT INTO gymdetails (gym_id, name, url, last_scanned) "
            "VALUES (%s, %s, %s, %s) "
            "ON DUPLICATE KEY UPDATE last_scanned=VALUES(last_scanned), "
            "url=IF(VALUES(url) IS NOT NULL AND VALUES(url) <> '', VALUES(url), url)"
        )

        for cell in cells:
            for gym in cell["forts"]:
                if gym["type"] == 0:
                    guard_pokemon_id = gym["gym_details"]["guard_pokemon"]
                    gymid = gym["id"]
                    team_id = gym["gym_details"]["owned_by_team"]
                    latitude = gym["latitude"]
                    longitude = gym["longitude"]
                    slots_available = gym["gym_details"]["slots_available"]
                    last_modified_ts = gym["last_modified_timestamp_ms"] / 1000
                    last_modified = datetime.utcfromtimestamp(
                        last_modified_ts).strftime("%Y-%m-%d %H:%M:%S")
                    is_ex_raid_eligible = gym["gym_details"]["is_ex_raid_eligible"]

                    gym_args.append(
                        (
                            gymid, team_id, guard_pokemon_id, slots_available,
                            1,  # enabled
                            latitude, longitude,
                            0,  # total CP
                            0,  # is_in_battle
                            last_modified,  # last_modified
                            now,  # last_scanned
                            is_ex_raid_eligible
                        )
                    )

                    gym_details_args.append(
                        (gym["id"], "unknown", gym["image_url"], now)
                    )
        self._db_exec.executemany(query_gym, gym_args, commit=True)
        self._db_exec.executemany(query_gym_details, gym_details_args, commit=True)
        origin_logger.debug3("submit_gyms done")
        return True
Exemple #8
0
    def spawnpoints(self, origin: str, map_proto: dict, proto_dt: datetime):
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.debug3("DbPogoProtoSubmit::spawnpoints called with data received")
        cells = map_proto.get("cells", None)
        if cells is None:
            return False
        spawnpoint_args, spawnpoint_args_unseen = [], []
        spawn_ids = []

        query_spawnpoints = (
            "INSERT INTO trs_spawn (spawnpoint, latitude, longitude, earliest_unseen, "
            "last_scanned, spawndef, calc_endminsec, eventid) "
            "VALUES (%s, %s, %s, %s, %s, %s, %s, "
            "(select id from trs_event where now() between event_start and "
            "event_end order by event_start desc limit 1)) "
            "ON DUPLICATE KEY UPDATE "
            "last_scanned=VALUES(last_scanned), "
            "earliest_unseen=LEAST(earliest_unseen, VALUES(earliest_unseen)), "
            "spawndef=if(((select id from trs_event where now() between event_start and event_end order "
            "by event_start desc limit 1)=1 and eventid=1) or (select id from trs_event where now() between "
            "event_start and event_end order by event_start desc limit 1)<>1 and eventid<>1, VALUES(spawndef), "
            "spawndef), "
            "calc_endminsec=VALUES(calc_endminsec)"
        )

        query_spawnpoints_unseen = (
            "INSERT INTO trs_spawn (spawnpoint, latitude, longitude, earliest_unseen, last_non_scanned, spawndef, "
            "eventid) VALUES (%s, %s, %s, %s, %s, %s, "
            "(select id from trs_event where now() between event_start and "
            "event_end order by event_start desc limit 1)) "
            "ON DUPLICATE KEY UPDATE "
            "spawndef=if(((select id from trs_event where now() between event_start and event_end order "
            "by event_start desc limit 1)=1 and eventid=1) or (select id from trs_event where now() between "
            "event_start and event_end order by event_start desc limit 1)<>1 and eventid<>1, VALUES(spawndef), "
            "spawndef), "
            "last_non_scanned=VALUES(last_non_scanned)"
        )

        now = proto_dt.strftime("%Y-%m-%d %H:%M:%S")
        dt = proto_dt

        for cell in cells:
            for wild_mon in cell["wild_pokemon"]:
                spawn_ids.append(int(str(wild_mon['spawnpoint_id']), 16))

        spawndef = self._get_spawndef(spawn_ids)

        for cell in cells:
            for wild_mon in cell["wild_pokemon"]:
                spawnid = int(str(wild_mon["spawnpoint_id"]), 16)
                lat, lng, _ = S2Helper.get_position_from_cell(
                    int(str(wild_mon["spawnpoint_id"]) + "00000", 16))
                despawntime = wild_mon["time_till_hidden"]

                minpos = self._get_current_spawndef_pos()
                # TODO: retrieve the spawndefs by a single executemany and pass that...

                spawndef_ = spawndef.get(spawnid, False)
                if spawndef_:
                    newspawndef = self._set_spawn_see_minutesgroup(spawndef_, minpos)
                else:
                    newspawndef = self._set_spawn_see_minutesgroup(self.default_spawndef, minpos)

                last_scanned = None
                last_non_scanned = None

                if 0 <= int(despawntime) <= 90000:
                    fulldate = dt + timedelta(milliseconds=despawntime)
                    earliest_unseen = int(despawntime)
                    last_scanned = now
                    calcendtime = fulldate.strftime("%M:%S")

                    spawnpoint_args.append(
                        (spawnid, lat, lng, earliest_unseen, last_scanned, newspawndef, calcendtime)
                    )
                else:
                    earliest_unseen = 99999999
                    last_non_scanned = now

                    spawnpoint_args_unseen.append(
                        (spawnid, lat, lng, earliest_unseen, last_non_scanned, newspawndef)
                    )

        self._db_exec.executemany(query_spawnpoints, spawnpoint_args, commit=True)
        self._db_exec.executemany(query_spawnpoints_unseen,
                                  spawnpoint_args_unseen, commit=True)
Exemple #9
0
    def mons(self, origin: str, timestamp: float, map_proto: dict, mitm_mapper):
        """
        Update/Insert mons from a map_proto dict
        """
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.debug3("DbPogoProtoSubmit::mons called with data received")
        cells = map_proto.get("cells", None)
        if cells is None:
            return False

        query_mons = (
            "INSERT INTO pokemon (encounter_id, spawnpoint_id, pokemon_id, latitude, longitude, disappear_time, "
            "individual_attack, individual_defense, individual_stamina, move_1, move_2, cp, cp_multiplier, "
            "weight, height, gender, catch_prob_1, catch_prob_2, catch_prob_3, rating_attack, rating_defense, "
            "weather_boosted_condition, last_modified, costume, form) "
            "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, "
            "%s, %s, %s, %s, %s) "
            "ON DUPLICATE KEY UPDATE last_modified=VALUES(last_modified), disappear_time=VALUES(disappear_time)"
        )

        mon_args = []
        for cell in cells:
            for wild_mon in cell["wild_pokemon"]:
                spawnid = int(str(wild_mon["spawnpoint_id"]), 16)
                lat = wild_mon["latitude"]
                lon = wild_mon["longitude"]
                mon_id = wild_mon["pokemon_data"]["id"]
                encounter_id = wild_mon["encounter_id"]

                if encounter_id < 0:
                    encounter_id = encounter_id + 2 ** 64

                mitm_mapper.collect_mon_stats(origin, str(encounter_id))

                now = datetime.utcfromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S")

                # get known spawn end time and feed into despawn time calculation
                getdetspawntime = self._get_detected_endtime(str(spawnid))
                despawn_time_unix = gen_despawn_timestamp(getdetspawntime, timestamp)
                despawn_time = datetime.utcfromtimestamp(despawn_time_unix).strftime("%Y-%m-%d %H:%M:%S")

                if getdetspawntime is None:
                    origin_logger.debug3("adding mon (#{}) at {}, {}. Despawns at {} (init) ({})", mon_id, lat, lon,
                                         despawn_time, spawnid)
                else:
                    origin_logger.debug3("adding mon (#{}) at {}, {}. Despawns at {} (non-init) ({})", mon_id, lat, lon,
                                         despawn_time, spawnid)

                mon_args.append(
                    (
                        encounter_id, spawnid, mon_id, lat, lon,
                        despawn_time,
                        # TODO: consider .get("XXX", None)  # noqa: E800
                        None, None, None, None, None, None, None, None, None,
                        wild_mon["pokemon_data"]["display"]["gender_value"],
                        None, None, None, None, None,
                        wild_mon["pokemon_data"]["display"]["weather_boosted_value"],
                        now, wild_mon["pokemon_data"]["display"]["costume_value"],
                        wild_mon["pokemon_data"]["display"]["form_value"]
                    )
                )

        self._db_exec.executemany(query_mons, mon_args, commit=True)
        return True
Exemple #10
0
    def __read_circle_count(self,
                            filename,
                            identifier,
                            ratio,
                            communicator,
                            xcord=False,
                            crop=False,
                            click=False,
                            canny=False,
                            secondratio=False):
        origin_logger = get_origin_logger(logger, origin=identifier)
        origin_logger.debug2("__read_circle_count: Reading circles")

        try:
            screenshot_read = cv2.imread(filename)
        except Exception:
            origin_logger.error("Screenshot corrupted")
            return -1

        if screenshot_read is None:
            origin_logger.error("Screenshot corrupted")
            return -1

        height, width, _ = screenshot_read.shape

        if crop:
            screenshot_read = screenshot_read[int(height) -
                                              int(int(height / 4)):int(height),
                                              int(int(width) / 2) -
                                              int(int(width) /
                                                  8):int(int(width) / 2) +
                                              int(int(width) / 8)]

        origin_logger.debug(
            "__read_circle_count: Determined screenshot scale: {} x {}",
            height, width)
        gray = cv2.cvtColor(screenshot_read, cv2.COLOR_BGR2GRAY)
        # detect circles in the image

        if not secondratio:
            radius_min = int((width / float(ratio) - 3) / 2)
            radius_max = int((width / float(ratio) + 3) / 2)
        else:
            radius_min = int((width / float(ratio) - 3) / 2)
            radius_max = int((width / float(secondratio) + 3) / 2)
        if canny:
            gray = cv2.GaussianBlur(gray, (3, 3), 0)
            gray = cv2.Canny(gray, 100, 50, apertureSize=3)

        origin_logger.debug(
            "__read_circle_count: Detect radius of circle: Min {} / Max {}",
            radius_min, radius_max)
        circles = cv2.HoughCircles(gray,
                                   cv2.HOUGH_GRADIENT,
                                   1,
                                   width / 8,
                                   param1=100,
                                   param2=15,
                                   minRadius=radius_min,
                                   maxRadius=radius_max)
        circle = 0
        # ensure at least some circles were found
        if circles is not None:
            # convert the (x, y) coordinates and radius of the circles to integers
            circles = np.round(circles[0, :]).astype("int")
            # loop over the (x, y) coordinates and radius of the circles
            for (pos_x, pos_y, radius) in circles:

                if not xcord:
                    circle += 1
                    if click:
                        origin_logger.debug(
                            '__read_circle_count: found Circle - click it')
                        communicator.click(
                            width / 2,
                            ((int(height) - int(height / 4.5))) + pos_y)
                        time.sleep(2)
                else:
                    if pos_x >= (width / 2) - 100 and pos_x <= (
                            width / 2) + 100 and pos_y >= (height -
                                                           (height / 3)):
                        circle += 1
                        if click:
                            origin_logger.debug(
                                '__read_circle_count: found Circle - click on: it'
                            )
                            communicator.click(
                                width / 2,
                                ((int(height) - int(height / 4.5))) + pos_y)
                            time.sleep(2)

            origin_logger.debug(
                "__read_circle_count: Determined screenshot to have {} Circle.",
                circle)
            return circle
        else:
            origin_logger.debug(
                "__read_circle_count: Determined screenshot to have 0 Circle")
            return -1
Exemple #11
0
def trash_image_matching(origin, screen_img, full_screen):
    origin_logger = get_origin_logger(logger, origin=origin)
    clicklist: List[Trash] = []
    screen = cv2.imread(screen_img)
    screen = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY)

    if screen is None:
        origin_logger.error('trash_image_matching: {} appears to be corrupted', screen_img)
        return None

    trash = cv2.imread(os.path.join(mapadroid.MAD_ROOT, 'static/img/trashcan.png'), 0)

    height, width = screen.shape
    _quest_x = get_delete_quest_coords(width)
    _inventory_x = get_delete_item_coords(width)

    if trash.mean() == 255 or trash.mean() == 0:
        return clicklist

    if width < 1080 and width > 720:
        sc_from = 0.5
        sc_till = 1
    elif width == 1080:
        sc_from = 0.5
        sc_till = 1.2
    elif width == 720:
        sc_from = 0.3
        sc_till = 0.9
    elif width == 1440:
        sc_from = 0.5
        sc_till = 1.5
    else:
        sc_from = 0.1
        sc_till = 2

    for scale in np.linspace(sc_from, sc_till, 15)[::-1]:

        resized = imutils.resize(
            trash, width=int(trash.shape[1] * scale))
        (trash_heigh, trash_width) = resized.shape[:2]

        last_y_coord = 0
        res = cv2.matchTemplate(screen, resized, cv2.TM_CCOEFF_NORMED)
        threshold = 0.5
        loc = np.where(res >= threshold)
        boxcount = 0
        for pt in zip(*loc[::-1]):
            screen_height_max = height / 6 * 5
            if full_screen:
                screen_height_max = height
            if pt[0] > width / 4 * 3 and pt[1] < screen_height_max:
                x_coord = int(pt[0] + trash_width / 2)
                y_coord = int(pt[1] + trash_heigh / 2)

                if last_y_coord > 0:
                    if last_y_coord + 100 > y_coord or last_y_coord - 100 > y_coord:
                        if (_inventory_x - 50 < x_coord < _inventory_x + 50) or \
                                (_quest_x - 50 < x_coord < _quest_x + 50):
                            last_y_coord = y_coord
                    else:
                        if (_inventory_x - 50 < x_coord < _inventory_x + 50) or \
                                (_quest_x - 50 < x_coord < _quest_x + 50):
                            clicklist.append(Trash(x_coord, y_coord))
                            last_y_coord = y_coord
                else:
                    if (_inventory_x - 50 < x_coord < _inventory_x + 50) or \
                            (_quest_x - 50 < x_coord < _quest_x + 50):
                        clicklist.append(Trash(x_coord, y_coord))
                        last_y_coord = y_coord
                boxcount += 1

        if boxcount >= 1:
            break

    return clicklist
Exemple #12
0
    def __screendetection_get_type_internal(
        self, image, identifier
    ) -> Optional[Tuple[ScreenType, Optional[dict], int, int, int]]:
        origin_logger = get_origin_logger(logger, origin=identifier)
        returntype: ScreenType = ScreenType.UNDEFINED
        globaldict: Optional[dict] = {}
        diff: int = 1
        origin_logger.debug(
            "__screendetection_get_type_internal: Detecting screen type")

        texts = []
        try:
            with Image.open(image) as frame_org:
                width, height = frame_org.size

                origin_logger.debug("Screensize: W:{} x H:{}", width, height)

                if width < 1080:
                    origin_logger.info('Resize screen ...')
                    frame_org = frame_org.resize(
                        [int(2 * s) for s in frame_org.size], Image.ANTIALIAS)
                    diff: int = 2

                texts = [frame_org]
                for thresh in [200, 175, 150]:
                    fn = lambda x: 255 if x > thresh else 0  # noqa: E731
                    frame = frame_org.convert('L').point(fn, mode='1')
                    texts.append(frame)
                for text in texts:
                    try:
                        globaldict = pytesseract.image_to_data(
                            text,
                            output_type=Output.DICT,
                            timeout=40,
                            config='--dpi 70')
                    except Exception as e:
                        origin_logger.error(
                            "Tesseract Error: {}. Exception: {}", globaldict,
                            e)
                        globaldict = None
                    origin_logger.debug("Screentext: {}", globaldict)
                    if globaldict is None or 'text' not in globaldict:
                        continue
                    n_boxes = len(globaldict['text'])
                    for index in range(n_boxes):
                        if returntype != ScreenType.UNDEFINED:
                            break
                        if len(globaldict['text'][index]) > 3:
                            for screen_elem in self._ScreenType:
                                heightlimit = 0 if screen_elem == 21 else height / 4
                                if globaldict['top'][index] > heightlimit and globaldict['text'][index] in \
                                        self._ScreenType[screen_elem]:
                                    returntype = ScreenType(screen_elem)
                    if returntype != ScreenType.UNDEFINED:
                        break

                del texts
                frame.close()
        except (FileNotFoundError, ValueError) as e:
            origin_logger.error("Failed opening image {} with exception {}",
                                image, e)
            return None

        return returntype, globaldict, width, height, diff
Exemple #13
0
    def process_data(self, received_timestamp, data, origin):
        origin_logger = get_origin_logger(logger, origin=origin)
        data_type = data.get("type", None)
        origin_logger.debug2("Processing received data")
        processed_timestamp = datetime.fromtimestamp(received_timestamp)

        if data_type and not data.get("raw", False):
            self.__mitm_mapper.run_stats_collector(origin)

            origin_logger.debug4("Received data: {}", data)
            if data_type == 106:
                # process GetMapObject
                origin_logger.info("Processing GMO received. Received at {}",
                                   processed_timestamp)

                self.__db_submit.weather(origin, data["payload"],
                                         received_timestamp)

                self.__db_submit.stops(origin, data["payload"])
                self.__db_submit.gyms(origin, data["payload"])
                self.__db_submit.raids(origin, data["payload"],
                                       self.__mitm_mapper)

                self.__db_submit.spawnpoints(origin, data["payload"],
                                             processed_timestamp)
                self.__db_submit.mons(origin, data["payload"],
                                      self.__mitm_mapper)
                self.__db_submit.cells(origin, data["payload"])
                self.__mitm_mapper.submit_gmo_for_location(
                    origin, data["payload"])
                origin_logger.debug2("Done processing GMO")
            elif data_type == 102:
                playerlevel = self.__mitm_mapper.get_playerlevel(origin)
                if playerlevel >= 30:
                    origin_logger.debug("Processing encounter received at {}",
                                        processed_timestamp)
                    self.__db_submit.mon_iv(origin, received_timestamp,
                                            data["payload"],
                                            self.__mitm_mapper)
                    origin_logger.debug2("Done processing encounter")
                else:
                    origin_logger.warning(
                        "Playerlevel lower than 30 - not processing encounter IVs"
                    )
            elif data_type == 101:
                origin_logger.debug2("Processing proto 101")
                self.__db_submit.quest(origin, data["payload"],
                                       self.__mitm_mapper)
                origin_logger.debug2("Done processing proto 101")
            elif data_type == 104:
                origin_logger.debug2("Processing proto 104")
                self.__db_submit.stop_details(data["payload"])
                origin_logger.debug2("Done processing proto 104")
            elif data_type == 4:
                origin_logger.debug2("Processing proto 4")
                self.__mitm_mapper.generate_player_stats(
                    origin, data["payload"])
                origin_logger.debug2("Done processing proto 4")
            elif data_type == 156:
                origin_logger.debug2("Processing proto 156")
                self.__db_submit.gym(origin, data["payload"])
                origin_logger.debug2("Done processing proto 156")
Exemple #14
0
 def flush_level(self) -> None:
     origin_logger = get_origin_logger(logger, origin=self['origin'])
     origin_logger.info('Removing visitation status')
     self._dbc.flush_levelinfo(self['origin'])
Exemple #15
0
    def __check_raid_line(self,
                          filename,
                          identifier,
                          communicator,
                          left_side=False,
                          clickinvers=False):
        origin_logger = get_origin_logger(logger, origin=identifier)
        origin_logger.debug("__check_raid_line: Reading lines")
        if left_side:
            origin_logger.debug("__check_raid_line: Check nearby open ")
        try:
            screenshot_read = cv2.imread(filename)
        except Exception:
            origin_logger.error("Screenshot corrupted")
            return False
        if screenshot_read is None:
            origin_logger.error("Screenshot corrupted")
            return False

        if self.__read_circle_count(os.path.join('', filename),
                                    identifier,
                                    float(11),
                                    communicator,
                                    xcord=False,
                                    crop=True,
                                    click=False,
                                    canny=True) == -1:
            origin_logger.debug("__check_raid_line: Not active")
            return False

        height, width, _ = screenshot_read.shape
        screenshot_read = screenshot_read[int(height / 2) -
                                          int(height / 3):int(height / 2) +
                                          int(height / 3),
                                          int(0):int(width)]
        gray = cv2.cvtColor(screenshot_read, cv2.COLOR_BGR2GRAY)
        gray = cv2.GaussianBlur(gray, (5, 5), 0)
        origin_logger.debug(
            "__check_raid_line: Determined screenshot scale: {} x {}", height,
            width)
        edges = cv2.Canny(gray, 50, 150, apertureSize=3)
        max_line_length = width / 3.30 + width * 0.03
        origin_logger.debug("__check_raid_line: MaxLineLength: {}",
                            max_line_length)
        min_line_length = width / 6.35 - width * 0.03
        origin_logger.debug("__check_raid_line: MinLineLength: {}",
                            min_line_length)
        lines = cv2.HoughLinesP(edges,
                                rho=1,
                                theta=math.pi / 180,
                                threshold=70,
                                minLineLength=min_line_length,
                                maxLineGap=2)
        if lines is None:
            return False
        for line in lines:
            for x1, y1, x2, y2 in line:
                if not left_side:
                    if y1 == y2 and (x2 - x1 <= max_line_length) and (
                            x2 - x1 >= min_line_length
                    ) and x1 > width / 2 and x2 > width / 2 and y1 < (height /
                                                                      2):
                        origin_logger.debug(
                            "__check_raid_line: Raid-tab is active - Line length: {}px "
                            "Coords - x: {} {} Y: {} {}", x2 - x1, x1, x2, y1,
                            y2)
                        return True
                else:
                    if y1 == y2 and (x2 - x1 <= max_line_length) and (
                            x2 - x1 >= min_line_length) and (
                                (x1 < width / 2 and x2 < width / 2) or
                                (x1 < width / 2
                                 and x2 > width / 2)) and y1 < (height / 2):
                        origin_logger.debug(
                            "__check_raid_line: Nearby is active - but not Raid-Tab"
                        )
                        if clickinvers:
                            raidtab_x = int(width - (x2 - x1))
                            raidtab_y = int(
                                (int(height / 2) - int(height / 3) + y1) * 0.9)
                            origin_logger.debug(
                                '__check_raid_line: open Raid-Tab')
                            communicator.click(raidtab_x, raidtab_y)
                            time.sleep(3)
                        return True
        origin_logger.debug("__check_raid_line: Not active")
        return False
Exemple #16
0
    def quest(self, origin: str, quest_proto: dict, mitm_mapper):
        origin_logger = get_origin_logger(logger, origin=origin)
        origin_logger.debug3("DbPogoProtoSubmit::quest called")
        fort_id = quest_proto.get("fort_id", None)
        if fort_id is None:
            return False
        if "challenge_quest" not in quest_proto:
            return False
        protoquest = quest_proto["challenge_quest"]["quest"]
        rewards = protoquest.get("quest_rewards", None)
        if rewards is None or not rewards:
            return False
        reward = rewards[0]
        item = reward['item']
        encounter = reward['pokemon_encounter']
        goal = protoquest['goal']

        quest_type = protoquest.get("quest_type", None)
        quest_template = protoquest.get("template_id", None)

        reward_type = reward.get("type", None)
        item_item = item.get("item", None)
        item_amount = item.get("amount", None)
        pokemon_id = encounter.get("pokemon_id", None)

        if reward_type == 4:
            item_amount = reward.get('candy', {}).get('amount', 0)
            pokemon_id = reward.get('candy', {}).get('pokemon_id', 0)
        elif reward_type == 12:
            item_amount = reward.get('mega_resource', {}).get('amount', 0)
            pokemon_id = reward.get('mega_resource', {}).get('pokemon_id', 0)

        stardust = reward.get("stardust", None)
        form_id = encounter.get("pokemon_display", {}).get("form_value", 0)
        costume_id = encounter.get("pokemon_display", {}).get("costume_value", 0)
        target = goal.get("target", None)
        condition = goal.get("condition", None)

        json_condition = json.dumps(condition)
        task = questtask(int(quest_type), json_condition, int(target), str(quest_template))

        mitm_mapper.collect_quest_stats(origin, fort_id)

        query_quests = (
            "INSERT INTO trs_quest (GUID, quest_type, quest_timestamp, quest_stardust, quest_pokemon_id, "
            "quest_pokemon_form_id, quest_pokemon_costume_id, "
            "quest_reward_type, quest_item_id, quest_item_amount, quest_target, quest_condition, quest_reward, "
            "quest_task, quest_template) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
            "ON DUPLICATE KEY UPDATE quest_type=VALUES(quest_type), quest_timestamp=VALUES(quest_timestamp), "
            "quest_stardust=VALUES(quest_stardust), quest_pokemon_id=VALUES(quest_pokemon_id), "
            "quest_reward_type=VALUES(quest_reward_type), quest_item_id=VALUES(quest_item_id), "
            "quest_item_amount=VALUES(quest_item_amount), quest_target=VALUES(quest_target), "
            "quest_condition=VALUES(quest_condition), quest_reward=VALUES(quest_reward), "
            "quest_task=VALUES(quest_task), quest_template=VALUES(quest_template), "
            "quest_pokemon_form_id=VALUES(quest_pokemon_form_id), "
            "quest_pokemon_costume_id=VALUES(quest_pokemon_costume_id)"
        )
        insert_values = (
            fort_id, quest_type, time.time(), stardust, pokemon_id, form_id, costume_id, reward_type,
            item_item, item_amount, target,
            json_condition, json.dumps(rewards), task, quest_template
        )
        origin_logger.debug3("DbPogoProtoSubmit::quest submitted quest type {} at stop {}", quest_type, fort_id)
        self._db_exec.execute(query_quests, insert_values, commit=True)

        return True
Exemple #17
0
    def __internal_check_close_except_nearby_button(self,
                                                    filename,
                                                    identifier,
                                                    communicator,
                                                    close_raid=False):
        origin_logger = get_origin_logger(logger, origin=identifier)
        origin_logger.debug(
            "__internal_check_close_except_nearby_button: Checking close except nearby with: file {}",
            filename)
        try:
            screenshot_read = cv2.imread(filename)
        except cv2.error:
            origin_logger.error("Screenshot corrupted")
            origin_logger.debug(
                "__internal_check_close_except_nearby_button: Screenshot corrupted..."
            )
            return False
        if screenshot_read is None:
            origin_logger.error(
                "__internal_check_close_except_nearby_button: Screenshot corrupted"
            )
            return False

        if not close_raid:
            origin_logger.debug(
                "__internal_check_close_except_nearby_button: Raid is not to be closed..."
            )
            if not os.path.isfile(filename) \
               or self.__check_raid_line(filename, identifier, communicator) \
               or self.__check_raid_line(filename, identifier, communicator, True):
                # file not found or raid tab present
                origin_logger.debug(
                    "__internal_check_close_except_nearby_button: Not checking for close button (X). "
                    "Input wrong OR nearby window open")
                return False
        origin_logger.debug(
            "__internal_check_close_except_nearby_button: Checking for close button (X). Input wrong "
            "OR nearby window open")

        if self.__check_close_present(filename, identifier, communicator, 10,
                                      True):
            origin_logger.debug(
                "Found close button (X). Closing the window - Ratio: 10")
            return True
        if self.__check_close_present(filename, identifier, communicator, 11,
                                      True):
            origin_logger.debug(
                "Found close button (X). Closing the window - Ratio: 11")
            return True
        elif self.__check_close_present(filename, identifier, communicator, 12,
                                        True):
            origin_logger.debug(
                "Found close button (X). Closing the window - Ratio: 12")
            return True
        elif self.__check_close_present(filename, identifier, communicator, 14,
                                        True):
            origin_logger.debug(
                "Found close button (X). Closing the window - Ratio: 14")
            return True
        elif self.__check_close_present(filename, identifier, communicator, 13,
                                        True):
            origin_logger.debug(
                "Found close button (X). Closing the window - Ratio: 13")
            return True
        else:
            origin_logger.debug("Could not find close button (X).")
            return False
Exemple #18
0
    def get_phonescreens(self):
        if not os.path.exists(os.path.join(mapadroid.MAD_ROOT, self._args.temp_path, "madmin")):
            os.makedirs(os.path.join(self._args.temp_path, "madmin"))

        screens_phone = []
        ws_connected_phones = []
        if self._ws_server is not None:
            phones = self._ws_server.get_reg_origins()
        else:
            phones = []
        devicemappings = self._mapping_manager.get_all_devicemappings()

        # Sort devices by name.
        phones = sorted(phones)
        for phonename in phones:
            ws_connected_phones.append(phonename)
            add_text = ""
            adb_option = False
            adb = devicemappings.get(phonename, {}).get('adb', False)
            origin_logger = get_origin_logger(self._logger, origin=phonename)
            if adb is not None and self._adb_connect.check_adb_status(adb) is not None:
                self._ws_connected_phones.append(adb)
                adb_option = True
                add_text = '<b>ADB</b>'
            else:
                self._ws_connected_phones.append(adb)

            filename = generate_device_screenshot_path(phonename, devicemappings, self._args)
            try:
                screenshot_ending: str = ".jpg"
                image_resize(filename, os.path.join(
                    self._args.temp_path, "madmin"), width=250)
                screen = "screenshot/madmin/screenshot_" + str(phonename) + screenshot_ending
                screens_phone.append(
                    generate_phones(phonename, add_text, adb_option,
                                    screen, filename, self._datetimeformat, dummy=False)
                )
            except IOError:
                screen = "static/dummy.png"
                screens_phone.append(generate_phones(
                    phonename, add_text, adb_option, screen, filename, self._datetimeformat, dummy=True))
                try:
                    os.remove(filename)
                    origin_logger.info("Screenshot {} was corrupted and has been deleted", filename)
                except OSError:
                    pass

        for phonename in self._adb_connect.return_adb_devices():
            if phonename.serial not in self._ws_connected_phones:
                devicemappings = self._mapping_manager.get_all_devicemappings()
                for pho in devicemappings:
                    if phonename.serial == devicemappings[pho].get('adb', False):
                        adb_option = True
                        add_text = '<b>ADB - no WS <i class="fa fa-exclamation-triangle"></i></b>'
                        filename = generate_device_screenshot_path(pho, devicemappings, self._args)
                        if os.path.isfile(filename):
                            image_resize(filename, os.path.join(mapadroid.MAD_ROOT, self._args.temp_path, "madmin"),
                                         width=250)
                            screenshot_ending: str = ".jpg"
                            screen = "screenshot/madmin/screenshot_" + str(pho) + screenshot_ending
                            screens_phone.append(generate_phones(pho, add_text, adb_option, screen, filename,
                                                                 self._datetimeformat, dummy=False))
                        else:
                            screen = "static/dummy.png"
                            screens_phone.append(generate_phones(pho, add_text, adb_option, screen, filename,
                                                                 self._datetimeformat, dummy=True))

        return render_template('phonescreens.html', editform=screens_phone, header="Device control",
                               title="Device control")