async def on_ready(): try: log.info( "Connected to Discord. Generating Nest messages and sending them." ) emote_refs = await get_emotes(bot, nesting_mons, config) for d, area in discord_message_data: try: channel = await bot.fetch_channel(d) found = False embed_dict, _ = area.get_nest_text(config, emote_refs, last_regular_migration, config.time_format) embed = discord.Embed().from_dict(embed_dict) async for message in channel.history(): if message.author == bot.user: embeds = message.embeds if len(embeds) > 0: if embeds[0].title == embed.title: found = True break if found: log.success( f"Found existing Nest message for {area.name} and editing it" ) await message.edit(embed=embed) else: log.success( f"Sending a new Nest message for {area.name}") await channel.send(embed=embed) except Exception as e: log.exception(e) except Exception as e: log.exception(e) await bot.logout()
def get_osm_data(bbox, date, osm_file_name): got_data = False while not got_data: free_slot = False while not free_slot: r = requests.get("http://overpass-api.de/api/status").text if "available now" in r: free_slot = True else: if "Slot available after" in r: rate_seconds = int( r.split(", in ")[1].split(" seconds.")[0]) + 15 log.warning( f"Overpass is rate-limiting you. Gonna have to wait {rate_seconds} seconds before continuing" ) time.sleep(rate_seconds) else: log.warning( "Had trouble finding out about your overpass status. Waiting 1 minute before trying again" ) time.sleep(60) log.info( "Getting OSM data. This will take ages if this is your first run.") osm_time_start = timeit.default_timer() nest_json = fetch_data(bbox, date) osm_time_stop = timeit.default_timer() seconds = round(osm_time_stop - osm_time_start, 1) if len(nest_json.get("elements", [])) == 0: log.error( f"Overpass did not return any data in {seconds} seconds. This could have different causes. Check if your geofence doesn't have its lat/lon switched up. Sleeping 5 minutes and trying again.\nIf you want, you can share the below log entry in Discord" ) log.error(nest_json.get("remark")) time.sleep(60 * 5) else: got_data = True with open(osm_file_name, mode='w', encoding="utf-8") as osm_file: osm_file.write(json.dumps(nest_json, indent=4)) log.success( f"Done. Got all OSM data in {seconds} seconds and saved it.") return nest_json
async def get_emotes(bot, nesting_mons, config): guilds = [] for guild in bot.guilds: if guild.name == "Nest Emotes": guilds.append(guild) emote_names = [f"m{mid}" for mid in nesting_mons] # emote removing for guild in guilds: for emoji in guild.emojis: if emoji.name not in emote_names: log.info( f"Found emoji {emoji.name} not being used anymore - deleting" ) await emoji.delete() # emote creation final_emotes = {} for monid in nesting_mons: emote_name = f"m{monid}" existing = existing_emotes(guilds, emote_name) if existing: final_emotes[int(monid)] = existing continue free_guild = None for guild in guilds: if len(guild.emojis) < guild.emoji_limit: free_guild = guild if not free_guild: try: free_guild = await bot.create_guild("Nest Emotes") channel = await free_guild.create_text_channel("hello") invite = await channel.create_invite() log.info( f"Created new emote server. Invite code: {invite.code}") guilds.append(free_guild) except Exception as e: log.error("Exception while trying to create a guild. Aborting") log.exception(e) return final_emotes image_url = config.icon_repo + f"pokemon_icon_{monid.zfill(3)}_00.png" image = requests.get(image_url).content emoji = await free_guild.create_custom_emoji(name=emote_name, image=image) log.info(f"Created emoji {emote_name}") final_emotes[int(monid)] = emoji.id return final_emotes
continue event_start = timestr_to_datetime(event["start"]) if event_start > local_time: continue event_end = timestr_to_datetime(event["end"]) if event_end <= last_migration: continue if (event_start <= last_migration) and (event_end > local_time): continue if event_end < local_time: last_migration = event_end log.info( f"Overwriting nest migration with the end time of {event['name']}" ) else: last_migration = event_start log.info( f"Overwriting nest migration with the start time of {event['name']}" ) log.success(f"Last migration: {last_migration}") else: hours_since_migration = config.hours_since_change last_regular_migration = None if args.hours is not None: hours_since_migration = int(args.hours) log.info(
def analyze_nests(config, area, nest_mons, queries, reset_time, nodelete): OSM_DATE = osm_date() # Getting OSM/overpass data osm_file_name = f"data/osm_data/{area.name} {OSM_DATE.replace(':', '')}.json" try: with open(osm_file_name, mode="r", encoding="utf-8") as osm_file: nest_json = json.load(osm_file) except: nest_json = get_osm_data(area.bbox, OSM_DATE, osm_file_name) # Getting area data area_file_name = f"data/area_data/{area.name}.json" area_file_data = {} try: with open(area_file_name, mode="r", encoding="utf-8") as area_file: log.info( "Found area data file. Reading and using data from it now") area_file_data_raw = json.load(area_file) for k, v in area_file_data_raw.items(): area_file_data[int(k)] = v except FileNotFoundError: pass """db_file_name = f"data/db_data/{area.name}.json" try: with open(db_file_name, mode="r", encoding="utf-8") as db_file: db_data = json.load(db_file) except FileNotFoundError: db_data = {}""" if not nodelete: queries.nest_delete(area.sql_fence, str(reset_time)) if config.poracle: poracle_data = [] log.info( f"Got all relevant information. Searching for nests in {area.name} now" ) nodes = {} ways = [] relations = [] for element in nest_json['elements']: if not "type" in element: continue if element["type"] == "node": nodes[element["id"]] = { "lat": element["lat"], "lon": element["lon"] } elif element["type"] == "way": if "nodes" not in element and not element["nodes"]: continue ways.append(WayPark(element, config)) elif element["type"] == "relation": if "members" not in element and not element["members"]: continue relations.append(RelPark(element, config)) parks = ways + relations # Check Relations failed_nests = defaultdict(int) failed_nests["Total Nests found"] = 0 double_ways = [] start = timeit.default_timer() if config.less_queries: log.info("Getting DB data") all_spawns = [(str(_id), geometry.Point(lon, lat)) for _id, lat, lon in queries.spawns(area.sql_fence)] all_mons = queries.all_mons(str(tuple(nest_mons)), str(reset_time), area.sql_fence) all_mons = [(_id, geometry.Point(lon, lat)) for _id, lat, lon in all_mons] with Progress() as progress: #check_rels_task = progress.add_task("Generating Polygons", total=len(parks)) for park in relations: double_ways = park.get_polygon(nodes, ways, double_ways) #progress.update(check_rels_task, advance=1) for park in ways: park.get_polygon(nodes) #progress.update(check_rels_task, advance=1) for osm_id, data in area_file_data.items(): for connect_id in data["connect"]: for i, park in enumerate(parks): if park.id == osm_id: big_park = park big_park_i = i if park.id == connect_id: small_park = park small_park_i = i parks[big_park_i].connect.append(connect_id) parks[big_park_i].polygon = cascaded_union( [big_park.polygon, small_park.polygon]) parks.pop(small_park_i) # NOW CHECK ALL AREAS ONE AFTER ANOTHER check_nest_task = progress.add_task("Nests found: 0", total=len(parks)) nests = [] for park in parks: progress.update( check_nest_task, advance=1, description=f"Nests found: {failed_nests['Total Nests found']}" ) if not park.is_valid: failed_nests["Geometry is not valid"] += 1 continue if not area.polygon.contains(park.polygon): failed_nests["Not in Geofence"] += 1 continue if park.id in double_ways: failed_nests["Avoiding double nests"] += 1 continue pokestop_in = None stops = [] if config.pokestop_pokemon: # Get all Pokestops with id, lat and lon for pkstp in queries.stops(park.sql_fence): stops.append(str(pkstp[0])) pokestop_in = "'{}'".format("','".join(stops)) if config.less_queries: spawns = [ s[0] for s in all_spawns if park.polygon.contains(s[1]) ] else: spawns = [str(s[0]) for s in queries.spawns(park.sql_fence)] if not stops and not spawns: failed_nests["No Stops or Spawnpoints"] += 1 continue if (len(stops) < 1) and (len(spawns) < area.settings['min_spawnpoints']): failed_nests["Not enough Spawnpoints"] += 1 continue spawnpoint_in = "'{}'".format("','".join(spawns)) if spawnpoint_in == "''": spawnpoint_in = "NULL" # This will handle the SQL warning since a blank string shouldn't be used for a number if config.less_queries: mons = [s[0] for s in all_mons if park.polygon.contains(s[1])] if len(mons) == 0: failed_nests["No Pokemon"] += 1 continue most_id = max(set(mons), key=mons.count) poke_data = [most_id, mons.count(most_id)] else: poke_data = queries.mons(spawnpoint_in, str(tuple(nest_mons)), str(reset_time), pokestop_in) if poke_data is None: failed_nests["No Pokemon"] += 1 continue park.mon_data(poke_data[0], poke_data[1], area.settings['scan_hours_per_day'], len(spawns) + len(stops)) if park.mon_count < area.settings['min_pokemon']: failed_nests["Not enough Pokemon"] += 1 continue if park.mon_avg < area.settings['min_average']: failed_nests["Average spawnrate too low"] += 1 continue if park.mon_ratio < area.settings['min_ratio']: failed_nests["Average spawn ratio too low"] += 1 continue try: park.generate_details(area_file_data, failed_nests["Total Nests found"]) except TopologicalError: failed_nests["Geometry is not valid"] += 1 # Insert Nest data to db insert_args = { "nest_id": park.id, "name": park.name, "form": park.mon_form, "lat": park.lat, "lon": park.lon, "pokemon_id": park.mon_id, "type": 0, "pokemon_count": park.mon_count, "pokemon_avg": park.mon_avg, "pokemon_ratio": park.mon_ratio, "poly_path": json.dumps(park.path), "poly_type": 1 if isinstance(park, RelPark) else 0, "current_time": int(time.time()), "nest_submitted_by": config.submitted_by } failed_nests["Total Nests found"] += 1 nests.append(park) queries.nest_insert(insert_args) if config.poracle: insert_args["reset_time"] = int(reset_time) poracle_data.append({"type": "nest", "message": insert_args}) stop = timeit.default_timer() log.success( f"Done finding nests in {area.name} ({round(stop - start, 1)} seconds)" ) for k, v in failed_nests.items(): log.info(f" - {k}: {v}") def sort_avg(nest): return nest.mon_avg new_area_data = {} for nest in sorted(nests, key=sort_avg, reverse=True): new_area_data[nest.id] = { "name": nest.name, "center": [nest.lat, nest.lon], "connect": nest.connect } for oid, data in area_file_data.items(): if oid not in [n.id for n in nests]: new_area_data[oid] = { "name": data["name"], "center": data["center"], "connect": data["connect"] } with open(area_file_name, mode="w+") as area_file: area_file.write(json.dumps(new_area_data, indent=4)) log.info("Saved area data") if config.poracle: r = requests.post(config.poracle, json=poracle_data) log.info(f"Sent data to Poracle with status code {r.status_code}") log.success(f"All done with {area.name}\n") return nests