Example #1
0
 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()
Example #2
0
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
Example #3
0
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
Example #4
0
            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(
Example #5
0
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