The Marketing Technologist.

We talk about analytics, code, data science and everything related to marketing technology. Backed by the tech geeks of Greenhouse Group.

Send Slack notifications whenever a Pokémon spawns nearby using a Pokémon GO SlackBot

CURRENT STATUS (04/08/2016): Niantic seems to have made some big changes to the API last night, so currently all API scripts (including PoGoMap and bots) seem to be down. Feel free to share any fixes if you come across anything that could be of help. Meanwhile, I hope that the post is still a good read about how to use slack webhooks in Python.

At the Greenhouse Group we've got quite some colleagues playing Pokémon GO. No wonder we were pretty excited when this reddit post appeared on my Google Now. It let's you run a Google Maps app locally which is filled with Pokémon that are actually visible right now in the game, by pinging the API used by the game itself. Many credits to waishda and contributors!

However, I figured it's not that convenient to continuously look on a map to check whether you're not missing any Pokémon. Just imagine that you just needed that last one and you find out just to late it has been sitting right there on your desk while you were working or in a meeting!

So I decided to clone the repository and simply hook-up to an incoming webhook on Slack, so all the Pokéfans of the Greenhouse Group get notified whenever a new Pokémon spawns within our office. This is the result:

If somebody is interested in the Pokémon, he/she can simply click the distance in the post, which is linking to the exact location of the Pokémon on PokéVision, which is a website hosted version of waishda repository.

If you want to do the same for your colleagues, or you want Slack notifications when Pokémon spawn in your home, follow these steps:

  1. If you like living on the edge, you can use your private (google) account credentials, but I'd suggest making a new Google or Pokémon Club account.
  2. Obtain /services/your/slack_webhook/urlpath for the webhook in the channel you want to post the notifications by creating a new webhook in Slack.
  3. Download zip (and unzip) or git clone my repository (which is just a tweaked clone of waishda's repos).
  4. Move into the directory, e.g. cd ~/Downloads/PokemonGo-SlackBot
  5. Execute in command line sudo pip install -r requirements.txt.
  6. Run python pokeslack.py -u <your_user_name> -p <your_password> -l "<Your location>" -st 1 -r <the range you want in meters> -sw "</services/your/slack_webhook/urlpath>". If you use a Google account for authentication, you should add -a google to the command. For -l "<Your location>" you can input any Google Maps search query, so just try on Google Maps whether your query gives a marker you like. If you want to ping around in a wider range than your exact location (more or less 250 meters radius), set the step argument -st 1 to a higher number like -st 10. If your want to make the map available to your colleagues within your network, add -H <server ip address> -P <port>.
  7. After a while you should start receiving Slack messages every time a Pokémon spawns.
  8. You can also see the original Pokemon-Map when browsing to http://localhost:5000. Note that you'll see more Pokémon on the map, since only those in the specified range will send a notification.

The user_icons will be a :pokeball: by default, however if you upload Pokémon emojis to Slack, you can use those for each specific Pokémon. If you use the smileys without prefix, run python pokeslack.py -u <your_user_name> -p <your_password> -l "<Your location>" -st 1 -r <the range you want in meters> -sw "</services/your/slack_webhook/urlpath>" -pi ':', and if you do use the prefix python pokeslack.py -u <your_user_name> -p <your_password> -l "<Your location>" -st 1 -r <the range you want in meters> -sw "</services/your/slack_webhook/urlpath>" -pi ':pokemon-'. You can also apply filters by adding -i to ignore or -o for only, like: python pokeslack.py -u <your_user_name> -p <your_password> -l "<Your location>" -st 1 -r <the range you want in meters> -sw "</services/your/slack_webhook/urlpath>" -pi ':' -i "zubat, rattata, pidgey, spearow"

Slack webhooks are easy and fun and can be pretty useful for sending any notification. For example, what about monitoring your data pipelines, sending notification about the progress of your data crunching jobs or even automatically send results to your colleagues? So if you're interested in how to do this, I'll explain a bit about the code:

The data is simply send to Slack using a POST request:

import httplib
import urllib

def send_to_slack(text, username, icon_emoji, webhook):
    data = urllib.urlencode({'payload': '{"username": "' + username + '", '
                                        '"icon_emoji": "' + icon_emoji + '", '
                                        '"text": "' + text + '"}'
                             })

    h = httplib.HTTPSConnection('hooks.slack.com')
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}

    h.request('POST', webhook, data, headers)
    r = h.getresponse()

The content of the notification is generated in this way:

spotted_pokemon = {}

        if poke.SpawnPointId in spotted_pokemon.keys():
            if spotted_pokemon[poke.SpawnPointId]['disappear_datetime'] > datetime.now():
                continue
        if poke.TimeTillHiddenMs < 0:
            continue

        disappear_datetime = datetime.fromtimestamp(disappear_timestamp)
        distance = lonlat_to_meters(origin_lat, origin_lon, poke.Latitude, poke.Longitude)
        
        if distance < max_distance:
            time_till_disappears = disappear_datetime - datetime.now()
            disappear_hours, disappear_remainder = divmod(time_till_disappears.seconds, 3600)
            disappear_minutes, disappear_seconds = divmod(disappear_remainder, 60)
            disappear_minutes = str(disappear_minutes)
            disappear_seconds = str(disappear_seconds)
            if len(disappear_seconds) == 1:
                disappear_seconds = str(0) + disappear_seconds
            disappear_time = disappear_datetime.strftime("%H:%M:%S")

            alert_text = 'I\'m just <https://pokevision.com/#/@' + str(poke.Latitude) + \
                        ',' + str(poke.Longitude) + \
                        '|' + "{0:.2f}".format(distance) + \
                        ' m> away until ' + disappear_time + \
                        ' (' + disappear_minutes + ':' + disappear_seconds + ')!'

            if pokemon_icons_prefix != ':pokeball:':
                user_icon = pokemon_icons_prefix + pokename.lower() + ':'
            else:
                user_icon = ':pokeball:'

            send_to_slack(alert_text, pokename, user_icon, slack_webhook_urlpath)

            spotted_pokemon[poke.SpawnPointId] = {'disappear_datetime': disappear_datetime, 
                                                 'pokename': pokename}

Note the 'spotted_pokemon' dict. If you don't check whether a Pokémon is actually newly spawn, you'll generate a lot of spam and probably not so happy colleagues.

Also, there is a little bit of math in calculating lon-lat into meters distance using haversine:

from math import radians, cos, sin, asin, sqrt

def lonlat_to_meters(lat1, lon1, lat2, lon2):
    """
    Calculate the great circle distance between two points
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    # earth radius in meters: 6378100
    m = 6378100 * c
    return m

For more code, please checkout the git repository!

Update #1: Japanese language support

Thanks to several git pull requests from the community, the code is now also working in Japanese. If it happens that the Pokémon names in your language are inconsistent with the names of your emojis in slack, try uploading this list of emojis by pokemon number, using emojipacks, and replace this piece of code:

           if pokemon_icons_prefix != ':pokeball:':
                user_icon = pokemon_icons_prefix + pokename.lower() + ':'
            else:
                user_icon = ':pokeball:'

with this:

           if pokemon_icons_prefix != ':pokeball:':
                user_icon = pokemon_icons_prefix + 'pokemon-' + pokeid + ':'
            else:
                user_icon = ':pokeball:'

Update #2: Homey support by Bart Persoons

After using the Pokemon Slack notifications at work I was wondering if it is possible to send this information to my Homey at home. Homey is my connected home hub which controls my lights for example. It also has a speech function, so how cool would it be if Homey tells me when there is a new Pokémon nearby!
To send the data to Homey I used Homey’s “Webhook Manager” app. With this app it is possible to load an event with 3 data fields. After making some small changes to the Pokeslack script I was able to send the following information to Homey:

{'event': 'pokemon', 'data1': pokename, 'data2': distance}

Within Homey it is now possible to create a flow which triggers on the event “pokemon” and it can use the data parameters as variables.

Watch the result in this video!

The Homey token should be added as an argument, so to run the script you can use:

python pokehomey.py -u <your_user_name> <your_password> -l "<Your location>" -st 1 -r <the range you want in meters> -ht "<homey webhook manager token>"

Update #3: Languages, stability and lured Pokémon!

Several updates:

Message are now available in French and German thanks to Vincent. They can be set by setting the locale. The locale that needs to be used for the emojis can be set separately with -iL, default is English.

As we also see in the app and at Pokevision, server quite often seem to have troubles. Therefore, the script now has a reconnect features instead of stopping the script in cases of issues. From experience, it's advisable to use a Google account, since it's more stable than PTC.

And last but not least, lured Pokémon! Pokevision doesn't show these (yet), so many thanks go to the tip by Daniel. The Pokémon will have (lured) after ttheir names in the Slack messages. If you also want to show the lured Pokéstops on the map, use -dp -ol. If you want to see all Pokéstops on the map, just use -dp without -ol, and for gyms add -dg.