Exemplo n.º 1
0
class SlackAlarm(Alarm):

    _defaults = {
        'pokemon': {
            'username': "******",
            'icon_url': get_image_url("monsters/<mon_id_3>_<form_id_3>.png"),
            'title': "A wild <mon_name> has appeared!",
            'url': "<gmaps>",
            'body': "Available until <24h_time> (<time_left>)."
        },
        'pokestop': {
            'username': "******",
            'icon_url': get_image_url("stop/ready.png"),
            'title': "Someone has placed a lure on a Pokestop!",
            'url': "<gmaps>",
            'body': "Lure will expire at <24h_time> (<time_left>)."
        },
        'gym': {
            'username': "******",
            'icon_url': get_image_url("gyms/<new_team_id>.png"),
            'title': "A Team <old_team> gym has fallen!",
            'url': "<gmaps>",
            'body': "It is now controlled by <new_team>."
        },
        'egg': {
            'username': "******",
            'icon_url': get_image_url("eggs/<egg_lvl>.png"),
            'title': "A level <egg_lvl> raid is incoming!",
            'url': "<gmaps>",
            'body': "The egg will hatch <24h_hatch_time> (<hatch_time_left>)."
        },
        'raid': {
            'username':
            "******",
            'icon_url':
            get_image_url("monsters/<mon_id_3>_000.png"),
            'title':
            "Level <raid_lvl> raid is available against <mon_name>!",
            'url':
            "<gmaps>",
            'body':
            "The raid is available until <24h_raid_end> "
            "(<raid_time_left>)."
        }
    }

    # Gather settings and create alarm
    def __init__(self, settings, static_map_key):
        # Required Parameters
        self.__api_key = require_and_remove_key('api_key', settings,
                                                "'Slack' type alarms.")
        self.__default_channel = self.channel_format(
            require_and_remove_key('channel', settings,
                                   "'Slack' type alarms."))
        self.__client = None
        self.__channels = {}

        # Optional Alarm Parameters
        self.__startup_message = parse_boolean(
            settings.pop('startup_message', "True"))
        self.__map = settings.pop('map', {})
        self.__static_map_key = static_map_key

        # Optional Alert Parameters
        self.__pokemon = self.create_alert_settings(
            settings.pop('pokemon', {}), self._defaults['pokemon'])
        self.__pokestop = self.create_alert_settings(
            settings.pop('pokestop', {}), self._defaults['pokestop'])
        self.__gym = self.create_alert_settings(settings.pop('gym', {}),
                                                self._defaults['gym'])
        self.__egg = self.create_alert_settings(settings.pop('egg', {}),
                                                self._defaults['egg'])
        self.__raid = self.create_alert_settings(settings.pop('raid', {}),
                                                 self._defaults['raid'])

        # Warn user about leftover parameters
        reject_leftover_parameters(settings, "'Alarm level in Slack alarm.")

        log.info("Slack Alarm has been created!")

    # Establish connection with Slack
    def connect(self):
        self.__client = Slacker(self.__api_key)
        self.update_channels()

    # Send a message letting the channel know that this alarm started
    def startup_message(self):
        if self.__startup_message:
            self.send_message(self.__default_channel,
                              username="******",
                              text="PokeAlarm activated!")
            log.info("Startup message sent!")

    # Set the appropriate settings for each alert
    def create_alert_settings(self, settings, default):
        alert = {
            'channel':
            settings.pop('channel', self.__default_channel),
            'username':
            settings.pop('username', default['username']),
            'icon_url':
            settings.pop('icon_url', default['icon_url']),
            'title':
            settings.pop('title', default['title']),
            'url':
            settings.pop('url', default['url']),
            'body':
            settings.pop('body', default['body']),
            'map':
            get_static_map_url(settings.pop('map', self.__map),
                               self.__static_map_key)
        }
        reject_leftover_parameters(settings, "'Alert level in Slack alarm.")
        return alert

    # Send Alert to Slack
    def send_alert(self, alert, info):
        coords = {'lat': info['lat'], 'lng': info['lng']}
        attachments = [{
            'fallback': 'Map_Preview',
            'image_url': replace(alert['map'], coords)
        }] if alert['map'] is not None else None
        self.send_message(channel=replace(alert['channel'], info),
                          username=replace(alert['username'], info),
                          text='<{}|{}> - {}'.format(
                              replace(alert['url'], info),
                              replace(alert['title'], info),
                              replace(alert['body'], info)),
                          icon_url=replace(alert['icon_url'], info),
                          attachments=attachments)

    # Trigger an alert based on Pokemon info
    def pokemon_alert(self, pokemon_info):
        self.send_alert(self.__pokemon, pokemon_info)

    # Trigger an alert based on Pokestop info
    def pokestop_alert(self, pokestop_info):
        self.send_alert(self.__pokestop, pokestop_info)

    # Trigger an alert based on Pokestop info
    def gym_alert(self, gym_info):
        self.send_alert(self.__gym, gym_info)

    # Trigger an alert when a raid egg has spawned (UPCOMING raid event)
    def raid_egg_alert(self, raid_info):
        self.send_alert(self.__egg, raid_info)

    # Trigger an alert based on Gym info
    def raid_alert(self, raid_info):
        self.send_alert(self.__raid, raid_info)

    # Get a list of channels from Slack to help
    def update_channels(self):
        self.__channels = {}
        response = self.__client.channels.list(True).body
        for channel in response['channels']:
            self.__channels[channel['name']] = channel['id']
        response = self.__client.groups.list().body
        for channel in response['groups']:
            self.__channels[channel['name']] = channel['id']
        log.debug("Detected the following Slack channnels: {}".format(
            self.__channels))

    # Checks for valid channel, otherwise defaults to general
    def get_channel(self, name):
        channel = SlackAlarm.channel_format(name)
        if channel not in self.__channels:
            log.error("Detected no channel with the name '{}'." +
                      " Trying the default channel '{}' instead.".format(
                          channel, self.__default_channel))
            return self.__default_channel
        return channel

    # Send a message to Slack
    def send_message(self,
                     channel,
                     username,
                     text,
                     icon_url=None,
                     attachments=None):
        args = {
            "channel": self.get_channel(channel),
            "username": username,
            "text": text
        }
        if icon_url is not None:
            args['icon_url'] = icon_url
        if attachments is not None:
            args['attachments'] = attachments
        try_sending(log, self.connect, "Slack",
                    self.__client.chat.post_message, args)

    # Returns a string s that is in proper channel format
    @staticmethod
    def channel_format(name):
        if name[0] == '#':  # Remove # if added
            name = name[1:]
        name = name.replace(u"\u2642", "m").replace(u"\u2640", "f").lower()
        return re.sub("[^_a-z0-9-]+", "", name)
Exemplo n.º 2
0
class Cache(object):
    """ Basic object for caching information.

    This object caches and manages information in Memory. Information will
    be lost between run times if save has not been implemented correctly.
    """

    default_image_url = get_image_url("regular/gyms/0.png"),

    def __init__(self, mgr):
        """ Initializes a new cache object for storing data between events. """
        self._log = mgr.get_child_logger("cache")

        self._mon_hist = {}
        self._stop_hist = {}
        self._egg_hist = {}
        self._raid_hist = {}
        self._quest_hist = {}
        self._grunt_hist = {}
        self._gym_team = {}
        self._gym_name = {}
        self._gym_desc = {}
        self._gym_image = {}
        self._cell_weather_id = {}
        self._severity_id = {}
        self._day_or_night_id = {}
        self._quest_reward = {}
        self._quest_task = {}
        self._quest_last_modified = {}

    def monster_expiration(self, mon_id, expiration=None):
        """ Update and return the datetime that a monster expires."""
        if expiration is not None:
            self._mon_hist[mon_id] = expiration
        return self._mon_hist.get(mon_id)

    def stop_expiration(self, stop_id, expiration=None):
        """ Update and return the datetime that a stop expires."""
        if expiration is not None:
            self._stop_hist[stop_id] = expiration
        return self._stop_hist.get(stop_id)

    def egg_expiration(self, egg_id, expiration=None):
        """ Update and return the datetime that an egg expires."""
        if expiration is not None:
            self._egg_hist[egg_id] = expiration
        return self._egg_hist.get(egg_id)

    def raid_expiration(self, raid_id, expiration=None):
        """ Update and return the datetime that a raid expires."""
        if expiration is not None:
            self._raid_hist[raid_id] = expiration
        return self._raid_hist.get(raid_id)

    def quest_expiration(self, stop_id, last_modified=None):
        """ Update and return the datetime that the stop last had a quest."""
        if last_modified is not None:
            self._quest_hist[stop_id] = last_modified
        return self._quest_hist.get(stop_id)

    def grunt_expiration(self, stop_id, expiration=None):
        """ Update and return the datetime that a stop expires."""
        if expiration is not None:
            self._grunt_hist[stop_id] = expiration
        return self._grunt_hist.get(stop_id)

    def gym_team(self, gym_id, team_id=Unknown.TINY):
        """ Update and return the team_id of a gym. """
        if Unknown.is_not(team_id):
            self._gym_team[gym_id] = team_id
        return self._gym_team.get(gym_id, Unknown.TINY)

    def gym_name(self, gym_id, gym_name=Unknown.REGULAR):
        """ Update and return the gym_name for a gym. """
        if Unknown.is_not(gym_name):
            self._gym_name[gym_id] = gym_name
        return self._gym_name.get(gym_id, Unknown.REGULAR)

    def gym_desc(self, gym_id, gym_desc=Unknown.REGULAR):
        """ Update and return the gym_desc for a gym. """
        if Unknown.is_not(gym_desc):
            self._gym_desc[gym_id] = gym_desc
        return self._gym_desc.get(gym_id, Unknown.REGULAR)

    def gym_image(self, gym_id, gym_image=Unknown.REGULAR):
        """ Update and return the gym_image for a gym. """
        if Unknown.is_not(gym_image):
            self._gym_image[gym_id] = gym_image
        return self._gym_image.get(gym_id, get_image_url('icons/gym_0.png'))

    def cell_weather_id(self, s2_cell_id, cell_weather_id=Unknown.REGULAR):
        """ Update and return weather_id for a cell """
        if Unknown.is_not(cell_weather_id):
            self._cell_weather_id[s2_cell_id] = cell_weather_id
        return self._cell_weather_id.get(s2_cell_id, Unknown.REGULAR)

    def severity_id(self, s2_cell_id, severity_id=Unknown.REGULAR):
        """ Update and return severity_id for a cell """
        if Unknown.is_not(severity_id):
            self._severity_id[s2_cell_id] = severity_id
        return self._severity_id.get(s2_cell_id, Unknown.REGULAR)

    def day_or_night_id(self, s2_cell_id, day_or_night_id=Unknown.REGULAR):
        """ Update and return day_or_night_id for a cell """
        if Unknown.is_not(day_or_night_id):
            self._day_or_night_id[s2_cell_id] = day_or_night_id
        return self._day_or_night_id.get(s2_cell_id, Unknown.REGULAR)

    def quest_reward(self,
                     stop_id,
                     reward=None,
                     task=None,
                     last_modified=None):
        """ Update and return the reward and task for a quest."""
        if Unknown.is_not(reward):
            self._quest_reward[stop_id] = reward
        if Unknown.is_not(task):
            self._quest_task[stop_id] = task
        if Unknown.is_not(last_modified):
            self._quest_last_modified[stop_id] = last_modified
        return self._quest_reward.get(stop_id, Unknown.REGULAR), \
            self._quest_task.get(stop_id, Unknown.REGULAR), \
            self._quest_last_modified.get(stop_id, Unknown.REGULAR)

    def clean_and_save(self):
        """ Cleans the cache and saves the contents if capable. """
        self._clean_hist()
        self._save()

    def _save(self):
        """ Export the data to a more permanent location. """
        pass  # Mem cache isn't backed up.

    def _clean_hist(self):
        """ Clean expired objects to free up memory. """
        for hist in (self._mon_hist, self._stop_hist, self._egg_hist,
                     self._raid_hist, self._quest_hist, self._grunt_hist):
            old = []
            now = datetime.utcnow()
            for key, expiration in hist.iteritems():
                if expiration < now:  # Track expired items
                    old.append(key)
            for key in old:  # Remove expired events
                del hist[key]
        self._log.debug("Cleared %s items from cache.", len(old))
Exemplo n.º 3
0
 def gym_image(self, gym_id, gym_image=Unknown.REGULAR):
     """ Update and return the gym_image for a gym. """
     if Unknown.is_not(gym_image):
         self._gym_image[gym_id] = gym_image
     return self._gym_image.get(gym_id, get_image_url('icons/gym_0.png'))
Exemplo n.º 4
0
class TelegramAlarm(Alarm):

    Alert = namedtuple("Alert", [
        'bot_token', 'chat_id', 'sticker', 'sticker_url', 'sticker_notify',
        'message', 'message_notify', 'venue', 'venue_notify', 'map',
        'map_notify', 'max_attempts', 'web_preview'
    ])

    _defaults = {  # No touchy!!! Edit alarms.json!
        'monsters': {
            'message':
            "*A wild <mon_name> has appeared!*\n"
            "Available until <24h_time> (<time_left>).",
            'sticker_url':
            get_image_url("telegram/monsters/<mon_id_3>_<form_id_3>.webp")
        },
        'stops': {
            'message': "*Someone has placed a lure on a Pokestop!*\n"
            "Lure will expire at <24h_time> (<time_left>).",
            'sticker_url': get_image_url("telegram/stop/ready.webp")
        },
        'gyms': {
            'message': "*A Team <old_team> gym has fallen!*\n"
            "It is now controlled by <new_team>.",
            'sticker_url': get_image_url("telegram/gyms/<new_team_id>.webp"),
        },
        'eggs': {
            'message':
            "*A level <egg_lvl> raid is incoming!*\n"
            "The egg will hatch <24h_hatch_time> "
            "(<hatch_time_left>).",
            'sticker_url':
            get_image_url("telegram/eggs/<egg_lvl>.webp")
        },
        'raids': {
            'message':
            "*A raid is available against <mon_name>!*\n"
            "The raid is available until <24h_raid_end> "
            "(<raid_time_left>).",
            'sticker_url':
            get_image_url("telegram/monsters/<mon_id_3>_000.webp")
        }
    }

    # Gather settings and create alarm
    def __init__(self, settings):
        # Required Parameters
        self._bot_token = require_and_remove_key('bot_token', settings,
                                                 "'Telegram' type alarms.")
        self._chat_id = require_and_remove_key('chat_id', settings,
                                               "'Telegram' type alarms.")

        self._startup_message = self.pop_type(settings, 'startup_message',
                                              utils.parse_bool, True)

        # Optional Alert Parameters
        alert_defaults = {
            'bot_token':
            self._bot_token,
            'chat_id':
            self._chat_id,
            'sticker':
            self.pop_type(settings, 'sticker', utils.parse_bool, True),
            'sticker_notify':
            self.pop_type(settings, 'sticker_notify', utils.parse_bool, False),
            'message_notify':
            self.pop_type(settings, 'message_notify', utils.parse_bool, True),
            'venue':
            self.pop_type(settings, 'venue', utils.parse_bool, False),
            'venue_notify':
            self.pop_type(settings, 'venue_notify', utils.parse_bool, True),
            'map':
            self.pop_type(settings, 'map', utils.parse_bool, True),
            'map_notify':
            self.pop_type(settings, 'map_notify', utils.parse_bool, False),
            'max_attempts':
            self.pop_type(settings, 'max_attempts', int, 3),
            'web_preview':
            self.pop_type(settings, 'web_preview', utils.parse_bool, False)
        }

        # Alert Settings
        self._mon_alert = self.create_alert_settings('monsters', settings,
                                                     alert_defaults)
        self._stop_alert = self.create_alert_settings('stops', settings,
                                                      alert_defaults)
        self._gym_alert = self.create_alert_settings('gyms', settings,
                                                     alert_defaults)
        self._egg_alert = self.create_alert_settings('eggs', settings,
                                                     alert_defaults)
        self._raid_alert = self.create_alert_settings('raids', settings,
                                                      alert_defaults)

        # Reject leftover parameters
        for key in settings:
            raise ValueError("'{}' is not a recognized parameter for the Alarm"
                             " level in a Telegram Alarm".format(key))

        log.info("Telegram Alarm has been created!")

    # (Re)establishes Telegram connection
    def connect(self):
        pass

    # Set the appropriate settings for each alert
    def create_alert_settings(self, kind, settings, alert_defaults):
        default = TelegramAlarm._defaults[kind]
        default.update(alert_defaults)
        settings = Alarm.pop_type(settings, kind, dict, {})

        alert = TelegramAlarm.Alert(
            bot_token=Alarm.pop_type(settings, 'bot_token', unicode,
                                     default['bot_token']),
            chat_id=Alarm.pop_type(settings, 'chat_id', unicode,
                                   default['chat_id']),
            sticker=Alarm.pop_type(settings, 'sticker', utils.parse_bool,
                                   default['sticker']),
            sticker_url=Alarm.pop_type(settings, 'sticker_url', unicode,
                                       default['sticker_url']),
            sticker_notify=Alarm.pop_type(settings, 'sticker_notify',
                                          utils.parse_bool,
                                          default['sticker_notify']),
            message=Alarm.pop_type(settings, 'message', unicode,
                                   default['message']),
            message_notify=Alarm.pop_type(settings, 'message_notify',
                                          utils.parse_bool,
                                          default['message_notify']),
            venue=Alarm.pop_type(settings, 'venue', utils.parse_bool,
                                 default['venue']),
            venue_notify=Alarm.pop_type(settings, 'venue_notify',
                                        utils.parse_bool,
                                        default['venue_notify']),
            map=Alarm.pop_type(settings, 'map', utils.parse_bool,
                               default['map']),
            map_notify=Alarm.pop_type(settings, 'map_notify', utils.parse_bool,
                                      default['map_notify']),
            max_attempts=Alarm.pop_type(settings, 'max_attempts', int,
                                        default['max_attempts']),
            web_preview=Alarm.pop_type(settings, 'web_preview',
                                       utils.parse_bool,
                                       default['web_preview']))

        # Reject leftover parameters
        for key in settings:
            raise ValueError("'{}' is not a recognized parameter for the Alert"
                             " level in a Telegram Alarm".format(key))

        return alert

    # Sends a start up message on Telegram
    def startup_message(self):
        if self._startup_message:
            self.send_message(self._bot_token, self._chat_id,
                              "PokeAlarm activated!")
            log.info("Startup message sent!")

    # Generic Telegram Alert
    def generic_alert(self, alert, dts):
        bot_token = replace(alert.bot_token, dts)
        chat_id = replace(alert.chat_id, dts)
        message = replace(alert.message, dts)
        lat, lng = dts['lat'], dts['lng']
        max_attempts = alert.max_attempts
        sticker_url = replace(alert.sticker_url, dts)
        log.debug(sticker_url)
        # Send Sticker
        if alert.sticker and sticker_url is not None:
            self.send_sticker(bot_token, chat_id, sticker_url, max_attempts)

        # Send Venue
        if alert.venue:
            self.send_venue(bot_token, chat_id, lat, lng, message,
                            max_attempts)
            return  # Don't send message or map

        # Send Message
        self.send_message(bot_token,
                          chat_id,
                          replace(message, dts),
                          web_preview=alert.web_preview)

        # Send Map
        if alert.map:
            self.send_location(bot_token, chat_id, lat, lng, max_attempts)

    # Trigger an alert based on Pokemon info
    def pokemon_alert(self, mon_dts):
        self.generic_alert(self._mon_alert, mon_dts)

    # Trigger an alert based on Pokestop info
    def pokestop_alert(self, stop_dts):
        self.generic_alert(self._stop_alert, stop_dts)

    # Trigger an alert based on Pokestop info
    def gym_alert(self, gym_dts):
        self.generic_alert(self._gym_alert, gym_dts)

    # Trigger an alert when a raid egg has spawned (UPCOMING raid event)
    def raid_egg_alert(self, egg_dts):
        self.generic_alert(self._egg_alert, egg_dts)

    # Trigger an alert based on Raid info
    def raid_alert(self, raid_dts):
        self.generic_alert(self._raid_alert, raid_dts)

    def send_sticker(self,
                     token,
                     chat_id,
                     sticker_url,
                     max_attempts=3,
                     notify=False):
        args = {
            'url': "https://api.telegram.org/bot{}/sendSticker".format(token),
            'payload': {
                'chat_id': chat_id,
                'sticker': sticker_url,
                'disable_notification': not notify
            }
        }
        try_sending(log, self.connect, "Telegram (STKR)", self.send_webhook,
                    args, max_attempts)

    def send_message(self,
                     token,
                     chat_id,
                     message,
                     max_attempts=3,
                     notify=True,
                     web_preview=False):
        args = {
            'url': "https://api.telegram.org/bot{}/sendMessage".format(token),
            'payload': {
                'chat_id': chat_id,
                'text': message,
                'parse_mode': 'Markdown',
                'disable_web_page_preview': not web_preview,
                'disable_notification': not notify
            }
        }
        try_sending(log, self.connect, "Telegram (MSG)", self.send_webhook,
                    args, max_attempts)

    def send_location(self,
                      token,
                      chat_id,
                      lat,
                      lng,
                      max_attempts=3,
                      notify=False):
        args = {
            'url': "https://api.telegram.org/bot{}/sendLocation".format(token),
            'payload': {
                'chat_id': chat_id,
                'latitude': lat,
                'longitude': lng,
                'disable_notification': not notify
            }
        }
        try_sending(log, self.connect, "Telegram (LOC)", self.send_webhook,
                    args, max_attempts)

    def send_venue(self, token, chat_id, lat, lng, message, max_attempts):
        msg = message.split('\n', 1)
        args = {
            'url': "https://api.telegram.org/bot{}/sendVenue".format(token),
            'payload': {
                'chat_id': chat_id,
                'latitude': lat,
                'title': msg[0],
                'address': msg[1] if len(msg) > 1 else '',
                'longitude': lng,
                'disable_notification': False
            }
        }
        try_sending(log, self.connect, "Telegram (VEN)", self.send_webhook,
                    args, max_attempts)

    # Send a payload to the webhook url
    def send_webhook(self, url, payload):
        log.debug(url)
        log.debug(payload)
        resp = requests.post(url, json=payload, timeout=30)
        if resp.ok is True:
            log.debug("Notification successful (returned {})".format(
                resp.status_code))
        else:
            log.debug("Telegram response was {}".format(resp.content))
            raise requests.exceptions.RequestException(
                "Response received {}, webhook not accepted.".format(
                    resp.status_code))
Exemplo n.º 5
0
class DiscordAlarm(Alarm):

    _defaults = {
        'monsters': {
            'username':
            "******",
            'content':
            "",
            'icon_url':
            get_image_url("regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'avatar_url':
            get_image_url("regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'title':
            "A wild <mon_name> has appeared!",
            'url':
            "<gmaps>",
            'body':
            "Available until <24h_time> (<time_left>)."
        },
        'stops': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url("regular/stop/ready.png"),
            'avatar_url': get_image_url("regular/stop/ready.png"),
            'title': "Someone has placed a lure on a Pokestop!",
            'url': "<gmaps>",
            'body': "Lure will expire at <24h_time> (<time_left>)."
        },
        'gyms': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url("regular/gyms/<new_team_id>.png"),
            'avatar_url': get_image_url("regular/gyms/<new_team_id>.png"),
            'title': "A Team <old_team> gym has fallen!",
            'url': "<gmaps>",
            'body': "It is now controlled by <new_team>."
        },
        'eggs': {
            'username':
            "******",
            'content':
            "",
            'icon_url':
            get_image_url("regular/eggs/<egg_lvl>.png"),
            'avatar_url':
            get_image_url("regular/eggs/<egg_lvl>.png"),
            'title':
            "Raid is incoming!",
            'url':
            "<gmaps>",
            'body':
            "A level <egg_lvl> raid will hatch at "
            "<24h_hatch_time> (<hatch_time_left>)."
        },
        'raids': {
            'username':
            "******",
            'content':
            "",
            'icon_url':
            get_image_url("regular/monsters/<mon_id_3>_000.png"),
            'avatar_url':
            get_image_url("regular/monsters/<mon_id_3>_000.png"),
            'title':
            "Level <raid_lvl> raid is available against <mon_name>!",
            'url':
            "<gmaps>",
            'body':
            "The raid is available until "
            "<24h_raid_end> (<raid_time_left>)."
        }
    }

    # Gather settings and create alarm
    def __init__(self, settings, max_attempts, static_map_key):
        # Required Parameters
        self.__webhook_url = require_and_remove_key('webhook_url', settings,
                                                    "'Discord' type alarms.")
        self.__max_attempts = max_attempts

        # Optional Alarm Parameters
        self.__startup_message = parse_boolean(
            settings.pop('startup_message', "True"))
        self.__disable_embed = parse_boolean(
            settings.pop('disable_embed', "False"))
        self.__avatar_url = settings.pop('avatar_url', "")
        self.__map = settings.pop('map', {})
        self.__static_map_key = static_map_key

        # Set Alert Parameters
        self.__monsters = self.create_alert_settings(
            settings.pop('monsters', {}), self._defaults['monsters'])
        self.__stops = self.create_alert_settings(settings.pop('stops', {}),
                                                  self._defaults['stops'])
        self.__gyms = self.create_alert_settings(settings.pop('gyms', {}),
                                                 self._defaults['gyms'])
        self.__eggs = self.create_alert_settings(settings.pop('eggs', {}),
                                                 self._defaults['eggs'])
        self.__raids = self.create_alert_settings(settings.pop('raids', {}),
                                                  self._defaults['raids'])

        # Warn user about leftover parameters
        reject_leftover_parameters(settings, "'Alarm level in Discord alarm.")

        log.info("Discord Alarm has been created!")

    # (Re)connect with Discord
    def connect(self):
        pass

    # Send a message letting the channel know that this alarm has started
    def startup_message(self):
        if self.__startup_message:
            args = {
                'url': self.__webhook_url,
                'payload': {
                    'username': '******',
                    'content': 'PokeAlarm activated!'
                }
            }
            try_sending(log, self.connect, "Discord", self.send_webhook, args,
                        self.__max_attempts)
            log.info("Startup message sent!")

    # Set the appropriate settings for each alert
    def create_alert_settings(self, settings, default):
        alert = {
            'webhook_url':
            settings.pop('webhook_url', self.__webhook_url),
            'username':
            settings.pop('username', default['username']),
            'avatar_url':
            settings.pop('avatar_url', default['avatar_url']),
            'disable_embed':
            parse_boolean(settings.pop('disable_embed', self.__disable_embed)),
            'content':
            settings.pop('content', default['content']),
            'icon_url':
            settings.pop('icon_url', default['icon_url']),
            'title':
            settings.pop('title', default['title']),
            'url':
            settings.pop('url', default['url']),
            'body':
            settings.pop('body', default['body']),
            'map':
            get_static_map_url(settings.pop('map', self.__map),
                               self.__static_map_key)
        }

        reject_leftover_parameters(settings, "'Alert level in Discord alarm.")
        return alert

    # Send Alert to Discord
    def send_alert(self, alert, info):
        log.debug("Attempting to send notification to Discord.")
        payload = {
            # Usernames are limited to 32 characters
            'username': replace(alert['username'], info)[:32],
            'content': replace(alert['content'], info),
            'avatar_url': replace(alert['avatar_url'], info),
        }
        if alert['disable_embed'] is False:
            payload['embeds'] = [{
                'title': replace(alert['title'], info),
                'url': replace(alert['url'], info),
                'description': replace(alert['body'], info),
                'thumbnail': {
                    'url': replace(alert['icon_url'], info)
                }
            }]
            if alert['map'] is not None:
                coords = {'lat': info['lat'], 'lng': info['lng']}
                payload['embeds'][0]['image'] = {
                    'url': replace(alert['map'], coords)
                }
        args = {'url': replace(alert['webhook_url'], info), 'payload': payload}
        try_sending(log, self.connect, "Discord", self.send_webhook, args,
                    self.__max_attempts)

    # Trigger an alert based on Pokemon info
    def pokemon_alert(self, pokemon_info):
        log.debug("Pokemon notification triggered.")
        self.send_alert(self.__monsters, pokemon_info)

    # Trigger an alert based on Pokestop info
    def pokestop_alert(self, pokestop_info):
        log.debug("Pokestop notification triggered.")
        self.send_alert(self.__stops, pokestop_info)

    # Trigger an alert based on Pokestop info
    def gym_alert(self, gym_info):
        log.debug("Gym notification triggered.")
        self.send_alert(self.__gyms, gym_info)

    # Trigger an alert when a raid egg has spawned (UPCOMING raid event)
    def raid_egg_alert(self, raid_info):
        self.send_alert(self.__eggs, raid_info)

    def raid_alert(self, raid_info):
        self.send_alert(self.__raids, raid_info)

    # Send a payload to the webhook url
    def send_webhook(self, url, payload):
        log.debug(payload)
        resp = requests.post(url, json=payload, timeout=5)
        if resp.ok is True:
            log.debug("Notification successful (returned {})".format(
                resp.status_code))
        else:
            log.debug("Discord response was {}".format(resp.content))
            raise requests.exceptions.RequestException(
                "Response received {}, webhook not accepted.".format(
                    resp.status_code))
Exemplo n.º 6
0
class Cache(object):
    """ Basic object for caching information.

    This object caches and manages information in Memory. Information will
    be lost between run times if save has not been implemented correctly.
    """

    default_image_url = get_image_url("regular/gyms/0.png"),

    def __init__(self):
        """ Initializes a new cache object for storing data between events. """
        self._mon_hist = {}
        self._stop_hist = {}
        self._egg_hist = {}
        self._raid_hist = {}
        self._gym_team = {}
        self._gym_name = {}
        self._gym_desc = {}
        self._gym_image = {}

    def monster_expiration(self, mon_id, expiration=None):
        """ Update and return the datetime that a monster expires."""
        if expiration is not None:
            self._mon_hist[mon_id] = expiration
        return self._mon_hist.get(mon_id)

    def stop_expiration(self, stop_id, expiration=None):
        """ Update and return the datetime that a stop expires."""
        if expiration is not None:
            self._stop_hist[stop_id] = expiration
        return self._stop_hist.get(stop_id)

    def egg_expiration(self, egg_id, expiration=None):
        """ Update and return the datetime that an egg expires."""
        if expiration is not None:
            self._egg_hist[egg_id] = expiration
        return self._egg_hist.get(egg_id)

    def raid_expiration(self, raid_id, expiration=None):
        """ Update and return the datetime that a raid expires."""
        if expiration is not None:
            self._raid_hist[raid_id] = expiration
        return self._raid_hist.get(raid_id)

    def gym_team(self, gym_id, team_id=Unknown.TINY):
        """ Update and return the team_id of a gym. """
        if Unknown.is_not(team_id):
            self._gym_team[gym_id] = team_id
        return self._gym_team.get(gym_id, Unknown.TINY)

    def gym_name(self, gym_id, gym_name=Unknown.REGULAR):
        """ Update and return the gym_name for a gym. """
        if Unknown.is_not(gym_name):
            self._gym_name[gym_id] = gym_name
        return self._gym_name.get(gym_id, Unknown.REGULAR)

    def gym_desc(self, gym_id, gym_desc=Unknown.REGULAR):
        """ Update and return the gym_desc for a gym. """
        if Unknown.is_not(gym_desc):
            self._gym_desc[gym_id] = gym_desc
        return self._gym_desc.get(gym_id, Unknown.REGULAR)

    def gym_image(self, gym_id, gym_image=Unknown.REGULAR):
        """ Update and return the gym_image for a gym. """
        if Unknown.is_not(gym_image):
            self._gym_image[gym_id] = gym_image
        return self._gym_image.get(gym_id, get_image_url('icons/gym_0.png'))

    def clean_and_save(self):
        """ Cleans the cache and saves the contents if capable. """
        self._clean_hist()
        self._save()

    def _save(self):
        """ Export the data to a more permanent location. """
        pass  # Mem cache isn't backed up.

    def _clean_hist(self):
        """ Clean expired objects to free up memory. """
        for hist in (self._mon_hist, self._stop_hist, self._egg_hist,
                     self._raid_hist):
            old = []
            now = datetime.utcnow()
            for key, expiration in hist.iteritems():
                if expiration < now:  # Track expired items
                    old.append(key)
            for key in old:  # Remove expired events
                del hist[key]
        log.debug("Cache cleaned!")
Exemplo n.º 7
0
class FacebookPageAlarm(Alarm):

    _defaults = {
        'pokemon': {
            'message': "A wild <mon_name> has appeared!",
            'image': get_image_url(
                "monsters/<mon_id_3>_<form_id_3>.png"),
            'link': "<gmaps>",
            'name': "<mon_name>",
            'description': "Available until <24h_time> (<time_left>).",
            'caption': None
        },
        'pokestop': {
            'message': "Someone has placed a lure on a Pokestop!",
            'image': get_image_url("stop/ready.png"),
            'link': "<gmaps>",
            'name': "Lured Pokestop",
            'description': "Lure will expire at <24h_time> (<time_left>).",
            'caption': None
        },
        'gym': {
            'message': "A Team <old_team> gym has fallen!",
            'image': get_image_url("gyms/<new_team_id>.png"),
            'link': "<gmaps>",
            'name': "<old_team> gym fallen",
            'description': "It is now controlled by <new_team>.",
            'caption': None
        },
        'egg': {
            'message': "A level <egg_lvl> raid is upcoming!",
            'image': get_image_url("eggs/<egg_lvl>.png"),
            'link': "<gmaps>",
            'name': 'Egg',
            'description': "A level <egg_lvl> raid will hatch at "
                           "<24h_hatch_time> (<hatch_time_left>).",
            'caption': None
        },
        'raid': {
            'message': "Level <raid_lvl> raid available against <mon_name>!",
            'image': get_image_url(
                "monsters/<mon_id_3>_000.png"),
            'link': "<gmaps>",
            'name': 'Raid',
            'description':
                "The raid is available until <24h_raid_end>"
                " (<raid_time_left>).",
            'caption': None
        }
    }

    # Gather settings and create alarm
    def __init__(self, settings):
        # Required Parameters
        self.__page_access_token = require_and_remove_key(
            'page_access_token', settings, "'FacebookPage' type alarms.")
        self.__client = None

        # Optional Alarm Parameters
        self.__startup_message = parse_boolean(
            settings.pop('startup_message', "True"))

        # Set Alerts
        self.__pokemon = self.create_alert_settings(
            settings.pop('pokemon', {}), self._defaults['pokemon'])
        self.__pokestop = self.create_alert_settings(
            settings.pop('pokestop', {}), self._defaults['pokestop'])
        self.__gym = self.create_alert_settings(
            settings.pop('gym', {}), self._defaults['gym'])
        self.__egg = self.create_alert_settings(
            settings.pop('egg', {}), self._defaults['egg'])
        self.__raid = self.create_alert_settings(
            settings.pop('raid', {}), self._defaults['raid'])

        #  Warn user about leftover parameters
        reject_leftover_parameters(
            settings, "Alarm level in FacebookPage alarm.")

        log.info("FacebookPage Alarm has been created!")

    # Establish connection with FacebookPage
    def connect(self):
        self.__client = facebook.GraphAPI(self.__page_access_token)

    # Sends a start up message on Facebook
    def startup_message(self):
        if self.__startup_message:
            timestamps = get_time_as_str(datetime.utcnow())
            self.post_to_wall("{} - PokeAlarm has initialized!".format(
                timestamps[2]))
            log.info("Startup message sent!")

    # Set the appropriate settings for each alert
    def create_alert_settings(self, settings, default):
        alert = {
            'message': settings.pop('message', default['message']),
            'link': settings.pop('link', default['link']),
            'caption': settings.pop('caption', default['caption']),
            'description': settings.pop('description', default['description']),
            'image': settings.pop('image', default['image']),
            'name': settings.pop('name', default['name'])
        }
        reject_leftover_parameters(
            settings, "Alert level in FacebookPage alarm.")
        return alert

    # Post Pokemon Message
    def send_alert(self, alert, info):
        attachment = {"link": replace(alert['link'], info)}
        if alert['caption']:
            attachment['caption'] = replace(alert['caption'], info)
        if alert['description']:
            attachment['description'] = replace(alert['description'], info)
        if alert['image']:
            attachment['picture'] = replace(alert['image'], info)
        if alert['name']:
            attachment['name'] = replace(alert['name'], info)
        self.post_to_wall(
            message=replace(alert['message'], info),
            attachment=attachment
        )

    # Trigger an alert based on Pokemon info
    def pokemon_alert(self, pokemon_info):
        self.send_alert(self.__pokemon, pokemon_info)

    # Trigger an alert based on Pokestop info
    def pokestop_alert(self, pokestop_info):
        self.send_alert(self.__pokestop, pokestop_info)

    # Trigger an alert based on Gym info
    def gym_alert(self, gym_info):
        self.send_alert(self.__gym, gym_info)

    # Trigger an alert when a raid egg has spawned (UPCOMING raid event)
    def raid_egg_alert(self, raid_info):
        self.send_alert(self.__egg, raid_info)

    # Trigger an alert based on Raid info
    def raid_alert(self, raid_info):
        self.send_alert(self.__raid, raid_info)

    # Sends a wall post to Facebook
    def post_to_wall(self, message, attachment=None):
        args = {"message": message}
        if attachment is not None:
            args['attachment'] = attachment
        try_sending(log, self.connect, "FacebookPage",
                    self.__client.put_wall_post, args)
Exemplo n.º 8
0
class DiscordAlarm(Alarm):

    _defaults = {
        'monsters': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url(
                "regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'avatar_url': get_image_url(
                "regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'title': "A wild <mon_name> has appeared!",
            'url': "<gmaps>",
            'body': "Available until <24h_time> (<time_left>)."
        },
        'stops': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url("regular/stop/<lure_type_id_3>.png"),
            'avatar_url': get_image_url("regular/stop/<lure_type_id_3>.png"),
            'title': "Someone has placed a lure on a Pokestop!",
            'url': "<gmaps>",
            'body': "Lure will expire at <24h_time> (<time_left>)."
        },
        'gyms': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url("regular/gyms/<new_team_id>.png"),
            'avatar_url': get_image_url("regular/gyms/<new_team_id>.png"),
            'title': "A Team <old_team> gym has fallen!",
            'url': "<gmaps>",
            'body': "It is now controlled by <new_team>."
        },
        'eggs': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url("regular/eggs/<egg_lvl>.png"),
            'avatar_url': get_image_url("regular/eggs/<egg_lvl>.png"),
            'title': "Raid is incoming!",
            'url': "<gmaps>",
            'body': "A level <egg_lvl> raid will hatch at "
                    "<24h_hatch_time> (<hatch_time_left>)."
        },
        'raids': {
            'username': "******",
            'content': "",
            'icon_url':
                get_image_url("regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'avatar_url':
                get_image_url("regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'title': "Level <raid_lvl> raid is available against <mon_name>!",
            'url': "<gmaps>",
            'body': "The raid is available until "
                    "<24h_raid_end> (<raid_time_left>)."
        },
        'weather': {
            'username': "******",
            'content': "",
            "icon_url": get_image_url("regular/weather/<weather_id_3>"
                                      "_<day_or_night_id_3>.png"),
            "avatar_url": get_image_url("regular/weather/<weather_id_3>"
                                        "_<day_or_night_id_3>.png"),
            "title": "The weather has changed!",
            "url": "<gmaps>",
            "body": "The weather around <lat>,<lng> has changed to <weather>!"
        },
        'quests': {
            'username': "******",
            'content': "",
            'icon_url': get_image_url("regular/<quest_image>.png"),
            'avatar_url': get_image_url("regular/<quest_image>.png"),
            'title': "New Quest Found!",
            'url': "<gmaps>",
            'body': "Do this: <quest_task>\nFor this: <reward>"
        },
        'invasions': {
            'username': "******",
            'content': "",
            'icon_url':
                get_image_url("regular/invasions/<type_id_3>.png"),
            'avatar_url':
                get_image_url("regular/invasions/<type_id_3>.png"),
            'title': "This Pokestop has been invaded by Team Rocket!",
            'url': "<gmaps>",
            'body': "Invasion will expire at <24h_time> (<time_left>)."
        }
    }

    # Gather settings and create alarm
    def __init__(self, mgr, settings, max_attempts, static_map_key):
        self._log = mgr.get_child_logger("alarms")
        # Required Parameters
        self.__webhook_url = require_and_remove_key(
            'webhook_url', settings, "'Discord' type alarms.")
        self.__max_attempts = max_attempts

        # Optional Alarm Parameters
        self.__startup_message = parse_boolean(
            settings.pop('startup_message', "True"))
        self.__disable_embed = parse_boolean(
            settings.pop('disable_embed', "False"))
        self.__avatar_url = settings.pop('avatar_url', "")
        self.__map = settings.pop('map', {})
        self.__static_map_key = static_map_key

        # Set Alert Parameters
        self.__monsters = self.create_alert_settings(
            settings.pop('monsters', {}), self._defaults['monsters'])
        self.__stops = self.create_alert_settings(
            settings.pop('stops', {}), self._defaults['stops'])
        self.__gyms = self.create_alert_settings(
            settings.pop('gyms', {}), self._defaults['gyms'])
        self.__eggs = self.create_alert_settings(
            settings.pop('eggs', {}), self._defaults['eggs'])
        self.__raids = self.create_alert_settings(
            settings.pop('raids', {}), self._defaults['raids'])
        self.__weather = self.create_alert_settings(
            settings.pop('weather', {}), self._defaults['weather'])
        self.__quests = self.create_alert_settings(
            settings.pop('quests', {}), self._defaults['quests'])
        self.__invasions = self.create_alert_settings(
            settings.pop('invasions', {}), self._defaults['invasions'])

        # Warn user about leftover parameters
        reject_leftover_parameters(settings, "'Alarm level in Discord alarm.")

        self._log.info("Discord Alarm has been created!")

    # (Re)connect with Discord
    def connect(self):
        pass

    # Send a message letting the channel know that this alarm has started
    def startup_message(self):
        if self.__startup_message:
            args = {
                'url': self.__webhook_url,
                'payload': {
                    'username': '******',
                    'content': 'PokeAlarm activated!'
                }
            }
            try_sending(self._log, self.connect, "Discord",
                        self.send_webhook, args, self.__max_attempts)
            self._log.info("Startup message sent!")

    # Set the appropriate settings for each alert
    def create_alert_settings(self, settings, default):
        map = settings.pop('map', self.__map)
        alert = {
            'webhook_url': settings.pop('webhook_url', self.__webhook_url),
            'username': settings.pop('username', default['username']),
            'avatar_url': settings.pop('avatar_url', default['avatar_url']),
            'disable_embed': parse_boolean(
                settings.pop('disable_embed', self.__disable_embed)),
            'content': settings.pop('content', default['content']),
            'icon_url': settings.pop('icon_url', default['icon_url']),
            'title': settings.pop('title', default['title']),
            'url': settings.pop('url', default['url']),
            'body': settings.pop('body', default['body']),
            'fields': settings.pop('fields', []),
            'map': map if isinstance(map, six.string_types) else
            get_static_map_url(map, self.__static_map_key)
        }

        reject_leftover_parameters(settings, "'Alert level in Discord alarm.")
        return alert

    # Send Alert to Discord
    def send_alert(self, alert, info):
        self._log.debug("Attempting to send notification to Discord.")
        payload = {
            # Usernames are limited to 32 characters
            'username': replace(alert['username'], info)[:32],
            'content': replace(alert['content'], info),
            'avatar_url': replace(alert['avatar_url'], info),
        }
        if alert['disable_embed'] is False:
            payload['embeds'] = [{
                'title': replace(alert['title'], info),
                'url': replace(alert['url'], info),
                'description': replace(alert['body'], info),
                'thumbnail': {'url': replace(alert['icon_url'], info)},
                'fields': self.replace_fields(alert['fields'], info)
            }]
            if alert['map'] is not None:
                coords = {
                    'lat': info['lat'],
                    'lng': info['lng']
                }
                payload['embeds'][0]['image'] = {
                    'url':
                        replace(alert['map'],
                                coords if not
                                isinstance(alert['map'], six.string_types)
                                else info)
                }
        args = {
            'url': replace(alert['webhook_url'], info),
            'payload': payload
        }
        try_sending(self._log, self.connect,
                    "Discord", self.send_webhook, args, self.__max_attempts)

    # Trigger an alert based on Pokemon info
    def pokemon_alert(self, pokemon_info):
        self._log.debug("Pokemon notification triggered.")
        self.send_alert(self.__monsters, pokemon_info)

    # Trigger an alert based on Pokestop info
    def pokestop_alert(self, pokestop_info):
        self._log.debug("Pokestop notification triggered.")
        self.send_alert(self.__stops, pokestop_info)

    # Trigger an alert based on Pokestop info
    def gym_alert(self, gym_info):
        self._log.debug("Gym notification triggered.")
        self.send_alert(self.__gyms, gym_info)

    # Trigger an alert when a raid egg has spawned (UPCOMING raid event)
    def raid_egg_alert(self, raid_info):
        self._log.debug("Raid Egg notification triggered.")
        self.send_alert(self.__eggs, raid_info)

    def raid_alert(self, raid_info):
        self._log.debug("Raid notification triggered.")
        self.send_alert(self.__raids, raid_info)

    # Trigger an alert based on Weather info
    def weather_alert(self, weather_info):
        self._log.debug("Weather notification triggered.")
        self.send_alert(self.__weather, weather_info)

    def quest_alert(self, quest_info):
        self._log.debug("Quest notification triggered.")
        self.send_alert(self.__quests, quest_info)

    def invasion_alert(self, invasion_info):
        self._log.debug("Invasion notification triggered.")
        self.send_alert(self.__invasions, invasion_info)

    # Send a payload to the webhook url
    def send_webhook(self, url, payload):
        self._log.debug(payload)
        resp = requests.post(url, json=payload, timeout=5)
        if resp.ok is True:
            self._log.debug("Notification successful (returned {})".format(
                resp.status_code))
        else:
            self._log.debug("Discord response was {}".format(resp.content))
            raise requests.exceptions.RequestException(
                "Response received {}, webhook not accepted.".format(
                    resp.status_code))

    @staticmethod
    def replace_fields(fields, pkinfo):
        replaced_fields = []
        for field in fields:
            replaced_fields.append({
                'name': replace(field['name'], pkinfo),
                'value': replace(field['value'], pkinfo),
                'inline': field.get('inline', False)
            })
        return replaced_fields
Exemplo n.º 9
0
 def gym_image(self, gym_id, gym_image=Unknown.REGULAR):
     """ Update and return the gym_image for a gym. """
     if Unknown.is_not(gym_image):
         self._gym_image[gym_id] = gym_image
     return self._gym_image.get(gym_id, get_image_url('icons/gym_0.png'))
Exemplo n.º 10
0
class FacebookPageAlarm(Alarm):

    _defaults = {
        'monsters': {
            'message': "A wild <mon_name> has appeared!",
            'image':
            get_image_url("regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'link': "<gmaps>",
            'name': "<mon_name>",
            'description': "Available until <24h_time> (<time_left>).",
            'caption': None
        },
        'stops': {
            'message': "Someone has placed a lure on a Pokestop!",
            'image': get_image_url("regular/stop/<lure_type_id_3>.png"),
            'link': "<gmaps>",
            'name': "Lured Pokestop",
            'description': "Lure will expire at <24h_time> (<time_left>).",
            'caption': None
        },
        'gyms': {
            'message': "A Team <old_team> gym has fallen!",
            'image': get_image_url("regular/gyms/<new_team_id>.png"),
            'link': "<gmaps>",
            'name': "<old_team> gym fallen",
            'description': "It is now controlled by <new_team>.",
            'caption': None
        },
        'eggs': {
            'message': "A level <egg_lvl> raid is upcoming!",
            'image': get_image_url("regular/eggs/<egg_lvl>.png"),
            'link': "<gmaps>",
            'name': 'Egg',
            'description': "A level <egg_lvl> raid will hatch at "
            "<24h_hatch_time> (<hatch_time_left>).",
            'caption': None
        },
        'raids': {
            'message': "Level <raid_lvl> raid available against <mon_name>!",
            'image':
            get_image_url("regular/monsters/<mon_id_3>_<form_id_3>.png"),
            'link': "<gmaps>",
            'name': 'Raid',
            'description': "The raid is available until <24h_raid_end>"
            " (<raid_time_left>).",
            'caption': None
        },
        'weather': {
            'message':
            'The weather has changed!',
            "image":
            get_image_url("regular/weather/<weather_id_3>"
                          "_<day_or_night_id_3>.png"),
            "link":
            "<gmaps>",
            'name':
            "Weather",
            'description':
            "The weather around <lat>,<lng>"
            " has changed to <weather>!",
            'caption':
            None
        },
        'quests': {
            'message': "*New quest for <reward>*",
            'image': get_image_url('regular/<quest_image>.png'),
            'link': '<gmaps>',
            'name': 'Quest',
            'description': '<quest_task>',
            'caption': None
        },
        'invasions': {
            'message': 'This Pokestop has been invaded by Team Rocket!',
            'image': get_image_url("regular/invasions/<type_id_3>.png"),
            'link': '<gmaps>',
            'name': 'Invasion',
            'description': 'Invasion will expire at <24h_time> (<time_left>).',
            'caption': None
        }
    }

    # Gather settings and create alarm
    def __init__(self, mgr, settings):
        self._log = mgr.get_child_logger("alarms")

        # Required Parameters
        self.__page_access_token = require_and_remove_key(
            'page_access_token', settings, "'FacebookPage' type alarms.")
        self.__client = None

        # Optional Alarm Parameters
        self.__startup_message = parse_boolean(
            settings.pop('startup_message', "True"))

        # Set Alerts
        self.__monsters = self.create_alert_settings(
            settings.pop('monsters', {}), self._defaults['monsters'])
        self.__stops = self.create_alert_settings(settings.pop('stops', {}),
                                                  self._defaults['stops'])
        self.__gyms = self.create_alert_settings(settings.pop('gyms', {}),
                                                 self._defaults['gyms'])
        self.__eggs = self.create_alert_settings(settings.pop('eggs', {}),
                                                 self._defaults['eggs'])
        self.__raids = self.create_alert_settings(settings.pop('raids', {}),
                                                  self._defaults['raids'])
        self.__weather = self.create_alert_settings(
            settings.pop('weather', {}), self._defaults['weather'])
        self.__quests = self.create_alert_settings(settings.pop('quests', {}),
                                                   self._defaults['quests'])
        self.__invasions = self.create_alert_settings(
            settings.pop('invasions', {}), self._defaults['invasions'])

        #  Warn user about leftover parameters
        reject_leftover_parameters(settings,
                                   "Alarm level in FacebookPage alarm.")

        self._log.info("FacebookPage Alarm has been created!")

    # Establish connection with FacebookPage
    def connect(self):
        self.__client = facebook.GraphAPI(self.__page_access_token)

    # Sends a start up message on Facebook
    def startup_message(self):
        if self.__startup_message:
            timestamps = get_time_as_str(datetime.utcnow())
            self.post_to_wall("{} - PokeAlarm has initialized!".format(
                timestamps[2]))
            self._log.info("Startup message sent!")

    # Set the appropriate settings for each alert
    def create_alert_settings(self, settings, default):
        alert = {
            'message': settings.pop('message', default['message']),
            'link': settings.pop('link', default['link']),
            'caption': settings.pop('caption', default['caption']),
            'description': settings.pop('description', default['description']),
            'image': settings.pop('image', default['image']),
            'name': settings.pop('name', default['name'])
        }
        reject_leftover_parameters(settings,
                                   "Alert level in FacebookPage alarm.")
        return alert

    # Post Pokemon Message
    def send_alert(self, alert, info):
        attachment = {"link": replace(alert['link'], info)}
        if alert['caption']:
            attachment['caption'] = replace(alert['caption'], info)
        if alert['description']:
            attachment['description'] = replace(alert['description'], info)
        if alert['image']:
            attachment['picture'] = replace(alert['image'], info)
        if alert['name']:
            attachment['name'] = replace(alert['name'], info)
        self.post_to_wall(message=replace(alert['message'], info),
                          attachment=attachment)

    # Trigger an alert based on Pokemon info
    def pokemon_alert(self, pokemon_info):
        self.send_alert(self.__monsters, pokemon_info)

    # Trigger an alert based on Pokestop info
    def pokestop_alert(self, pokestop_info):
        self.send_alert(self.__stops, pokestop_info)

    # Trigger an alert based on Gym info
    def gym_alert(self, gym_info):
        self.send_alert(self.__gyms, gym_info)

    # Trigger an alert when a raid egg has spawned (UPCOMING raid event)
    def raid_egg_alert(self, raid_info):
        self.send_alert(self.__eggs, raid_info)

    # Trigger an alert based on Raid info
    def raid_alert(self, raid_info):
        self.send_alert(self.__raids, raid_info)

    # Trigger an alert based on Weather info
    def weather_alert(self, weather_info):
        self.send_alert(self.__weather, weather_info)

    # Quest Alert
    def quest_alert(self, quest_info):
        self.send_alert(self.__quests, quest_info)

    # Quest Alert
    def invasion_alert(self, invasion_info):
        self.send_alert(self.__invasions, invasion_info)

    # Sends a wall post to Facebook
    def post_to_wall(self, message, attachment=None):
        args = {"message": message}
        if attachment is not None:
            args['attachment'] = attachment
        try_sending(self._log, self.connect, "FacebookPage",
                    self.__client.put_wall_post, args)