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
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
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)
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
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
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
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
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)
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
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
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
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
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")
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'])
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
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
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
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")