Esempio n. 1
0
class PushBulletWSClient(WebSocketBaseClient):

    def init(self, interface):
        """ Initializes the PB WS Client"""

        self.logger = logging.getLogger('PAI').getChild(__name__)
        self.pb = Pushbullet(cfg.PUSHBULLET_KEY, cfg.PUSHBULLET_SECRET)
        self.manager = WebSocketManager()
        self.alarm = None
        self.interface = interface

    def stop(self):
        self.terminate()
        self.manager.stop()

    def set_alarm(self, alarm):
        """ Sets the paradox alarm object """
        self.alarm = alarm

    def handshake_ok(self):
        """ Callback trigger when connection succeeded"""
        self.logger.info("Handshake OK")
        self.manager.add(self)
        self.manager.start()
        for chat in self.pb.chats:
            self.logger.debug("Associated contacts: {}".format(chat))

        # Receiving pending messages
        self.received_message(json.dumps({"type": "tickle", "subtype": "push"}))

        self.send_message("Active")

    def received_message(self, message):
        """ Handle Pushbullet message. It should be a command """
        self.logger.debug("Received Message {}".format(message))

        try:
            message = json.loads(str(message))
        except:
            self.logger.exception("Unable to parse message")
            return

        if self.alarm is None:
            return
        if message['type'] == 'tickle' and message['subtype'] == 'push':
            now = time.time()
            pushes = self.pb.get_pushes(modified_after=int(now) - 20, limit=1, filter_inactive=True)
            self.logger.debug("got pushes {}".format(pushes))
            for p in pushes:
                self.pb.dismiss_push(p.get("iden"))
                self.pb.delete_push(p.get("iden"))

                if p.get('direction') == 'outgoing' or p.get('dismissed'):
                    continue

                if p.get('sender_email_normalized') in cfg.PUSHBULLET_CONTACTS:
                    ret = self.interface.send_command(p.get('body'))

                    if ret:
                        self.logger.info("From {} ACCEPTED: {}".format(p.get('sender_email_normalized'), p.get('body')))
                    else:
                        self.logger.warning("From {} UNKNOWN: {}".format(p.get('sender_email_normalized'), p.get('body')))
                else:
                    self.logger.warning("Command from INVALID SENDER {}: {}".format(p.get('sender_email_normalized'), p.get('body')))

    def unhandled_error(self, error):
        self.logger.error("{}".format(error))

        try:
            self.terminate()
        except Exception:
            self.logger.exception("Closing Pushbullet WS")

        self.close()

    def send_message(self, msg, dstchat=None):
        if dstchat is None:
            dstchat = self.pb.chats

        if not isinstance(dstchat, list):
            dstchat = [dstchat]

        for chat in dstchat:
            if chat.email in cfg.PUSHBULLET_CONTACTS:
                try:
                    self.pb.push_note("paradox", msg, chat=chat)
                except Exception:
                    self.logger.exception("Sending message")
                    time.sleep(5)

    def notify(self, source, message, level):
        try:
            if level.value >= EventLevel.WARN.value:
                self.send_message("{}".format(message))
        except Exception:
            logging.exception("Pushbullet notify")
Esempio n. 2
0
class PushBulletWSClient(WebSocketBaseClient):
    def init(self):
        """ Initializes the PB WS Client"""

        self.pb = Pushbullet(cfg.PUSHBULLET_KEY, cfg.PUSHBULLET_SECRET)
        self.manager = WebSocketManager()
        self.alarm = None

    def set_alarm(self, alarm):
        """ Sets the paradox alarm object """
        self.alarm = alarm

    def handshake_ok(self):
        """ Callback trigger when connection succeeded"""
        logger.info("Handshake OK")
        self.manager.add(self)
        for chat in self.pb.chats:
            logger.debug("Associated contacts: {}".format(chat))

        # Receiving pending messages
        self.received_message(json.dumps({
            "type": "tickle",
            "subtype": "push"
        }))

        self.send_message("Active")

    def handle_message(self, message):
        """ Handle Pushbullet message. It should be a command """

        logger.debug("Received Message {}".format(message))
        try:
            message = json.loads(str(message))
        except:
            logger.exception("Unable to parse message")
            return

        if self.alarm == None:
            return

        if message['type'] == 'tickle' and msg['subtype'] == 'push':
            now = time.time()
            pushes = self.pb.get_pushes(modified_after=int(now) - 10,
                                        limit=1,
                                        filter_inactive=True)

            for p in pushes:
                self.pb.dismiss_push(p.get("iden"))
                self.pb.delete_push(p.get("iden"))

                if p.get('direction') == 'outgoing' or p.get('dismissed'):
                    continue

                if p.get('sender_email_normalized') in PUSHBULLET_CONTACTS:
                    ret = self.send_command(p.get('body'))

                    if ret:
                        logger.info("From {} ACCEPTED: {}".format(
                            p.get('sender_email_normalized'), p.get('body')))
                    else:
                        logger.warning("From {} UNKNOWN: {}".format(
                            p.get('sender_email_normalized'), p.get('body')))
                else:
                    logger.warning("Command from INVALID SENDER {}: {}".format(
                        p.get('sender_email_normalized'), p.get('body')))

    def unhandled_error(self, error):
        logger.error("{}".format(error))

        try:
            self.terminate()
        except:
            logger.exception("Closing Pushbullet WS")

        self.close()

    def send_message(self, msg, dstchat=None):
        for chat in self.pb.chats:
            if chat.email in PUSHBULLET_CONTACTS:
                try:
                    self.pb.push_note("paradox", msg, chat=chat)
                except:
                    logger.exception("Sending message")
                    time.sleep(5)

    def send_command(self, message):
        """Handle message received from the MQTT broker"""
        """Format TYPE LABEL COMMAND """
        tokens = message.split(" ")

        if len(tokens) != 3:
            logger.warning("Message format is invalid")
            return

        if self.alarm == None:
            logger.error("No alarm registered")
            return

        element_type = tokens[0].lower()
        element = tokens[1]
        command = self.normalize_payload(tokens[2])

        # Process a Zone Command
        if element_type == 'zone':
            if command not in ['bypass', 'clear_bypass']:
                logger.error("Invalid command for Zone {}".format(command))
                return

            if not self.alarm.control_zone(element, command):
                logger.warning("Zone command refused: {}={}".format(
                    element, command))

        # Process a Partition Command
        elif element_type == 'partition':
            if command not in ['arm', 'disarm', 'arm_stay', 'arm_sleep']:
                logger.error(
                    "Invalid command for Partition {}".format(command))
                return

            if not self.alarm.control_partition(element, command):
                logger.warning("Partition command refused: {}={}".format(
                    element, command))

        # Process an Output Command
        elif element_type == 'output':
            if command not in ['on', 'off', 'pulse']:
                logger.error("Invalid command for Output {}".format(command))
                return

            if not self.alarm.control_output(element, command):
                logger.warning("Output command refused: {}={}".format(
                    element, command))
        else:
            logger.error("Invalid control property {}".format(element))

    def normalize_payload(self, message):
        message = message.strip().lower()

        if message in ['true', 'on', '1', 'enable']:
            return 'on'
        elif message in ['false', 'off', '0', 'disable']:
            return 'off'
        elif message in [
                'pulse', 'arm', 'disarm', 'arm_stay', 'arm_sleep', 'bypass',
                'clear_bypass'
        ]:
            return message

        return None

    def notify(self, source, message, level):
        if level < logging.INFO:
            return

        try:
            self.send_message("{}".format(message))
        except:
            logging.exception("Pushbullet notify")

    def event(self, raw):
        """Handle Live Event"""
        return

    def change(self, element, label, property, value):
        """Handle Property Change"""
        #logger.debug("Property Change: element={}, label={}, property={}, value={}".format(
        #    element,
        #    label,
        #    property,
        #    value))
        return
Esempio n. 3
0
class PushbulletManager():
    """Manager interface to and from PushBullet.

    Note:
       Requires that the environment variable PUSHBULLET_API_TOKEN is set.
    """
    REMINDER_KEYWORD = r"remi(nder)?\s+"
    last_mod_time = 0.0

    def __init__(self):
        """Create PushBullet listener."""
        self.logger = logging.getLogger(__name__)
        self.logger.info("Starting PushbulletManager...")
        self.pb = Pushbullet(os.environ.get("PUSHBULLET_API_TOKEN"))
        push_list = self.pb.get_pushes(limit=1)
        if push_list:
            self.last_mod_time = push_list[0]["modified"]
        self.pb_listener = Listener(account=self.pb,
                                    on_push=self.pb_on_push,
                                    on_error=self.pb_on_error,
                                    http_proxy_host=HTTP_PROXY_HOST,
                                    http_proxy_port=HTTP_PROXY_PORT)
        self.rmc = ReminderMessageConsumer()
        self.rmc.start()

    def run(self):
        """Listen for messages on PushBullet websocket."""
        try:
            self.logger.info('Waiting for messages. To exit press CTRL+C')
            self.pb_listener.run_forever()
        except KeyboardInterrupt:
            self.rmc.stop()
            self.rmc.join()
            raise
        finally:
            self.pb_listener.close()

    def pb_on_error(self, websocket, exception):
        """Error handler for the PushBullet listener.

        Re-raise any exception that occurs, as otherwise it will be swallowed
        up by the PushBullet Listener.
        """
        # If a KeyboardInterrupt is raised during
        # self.pb_listener.run_forever(), this method is invoked, which is the
        # only way we'll know about it.
        raise exception

    def pb_on_push(self, data):
        """Push handler for the PushBullet listener"""
        try:
            self.logger.debug("Received tickle: %s", str(data))
            push_list = self.pb.get_pushes(limit=1,
                                           modified_after=self.last_mod_time)
            if not push_list:
                self.logger.debug("Ignoring tickle")
                return
            latest = push_list[0]
            self.last_mod_time = latest["modified"]
            self.logger.info("Latest Push: %s", str(latest))
            self.process_push(latest)
        except Exception:
            self.logger.exception("Error processing push message")

    def process_push(self, push):
        """Process PushBullet JSON message.

        Passes the message to the ReminderManager if appropriate
        """
        # Pushbullet can also send Links and Images, which don't have a "body"
        if "body" not in push:
            self.logger.info("Ignoring non-note push: id=%s type=%s",
                             push["iden"], push["type"])
            return
        if push["dismissed"]:
            self.logger.debug("Ignoring dismissed push: id=%s, body=%s",
                              push["iden"], push["body"])
            return
        # We require the reminder keyword at beginning of message, to avoid
        # an infinite loop of pushbullets.
        if not bool(re.match(self.REMINDER_KEYWORD, push["body"], re.I)):
            self.logger.info("Ignoring non-reminder push: id=%s, body=%s",
                             push["iden"], push["body"])
            return
        self.logger.info('Received push: id=%s, body=%s', push["iden"],
                         push["body"])
        # Strip off reminder keyword first
        body = re.sub(r'^\S+\s+', '', push["body"])
        request = {"body": body, "source": "pushbullet"}
        self.logger.info("Sending request to reminder RPC service")
        try:
            reminder_rpc = ReminderRpcClient()
            response = reminder_rpc.call(request)
            self.logger.info("Reminder RPC response: %s", str(response))
        except ResponseTimeout as rto:
            self.logger.exception(str(rto))
            response = {
                "error": {
                    "code": 0,
                    "text": "Unable to connect to server"
                }
            }
        if "error" in response:
            self.logger.error("Reminder RPC returned error code '%d' (%s)",
                              response["error"]["code"],
                              response["error"]["text"])
            self.pb.push_note("Error Setting Reminder",
                              response["error"]["text"])
        else:
            self.logger.info("Received response: %s", str(response))
            self.logger.info("Dismissing push: id=%s", push["iden"])
            self.pb.dismiss_push(push["iden"])
            self.logger.info("Sending response: %s", response["data"]["text"])
            self.pb.push_note("Got it!", response["data"]["text"])
Esempio n. 4
0
def lambda_handler(*args):
    timetable = {
        '1': '08:40',
        '2': '09:40',
        '3': '10:40',
        '4': '11:40',
        '5': '12:40',
        '6': '13:30',
        '7': '14:30',
        '8': '15:30',
        '9': '16:30',
        '10': '17:40',
        '11': '18:40',
        '12': '19:40',
        '13': '20:40'
    }

    school_ip = {
        'futakotamagawa': '125.206.202.147',
        'seijo': '153.150.125.233',
        'shakujii': '221.186.136.107',
        'akitsu': '125.206.199.163',
        'tsunashima': '125.206.214.67'
    }

    IP = school_ip[os.environ['KOYAMA_LOC']]
    LOGIN_URL = 'http://{}/scripts/mtr0010.asp'.format(IP)
    CALENDAR_URL = 'http://{}/scripts/mtr1010.asp'.format(IP)

    browser = mechanize.Browser()
    browser.open(LOGIN_URL)
    browser.form = list(browser.forms())[0]

    un_control = browser.form.find_control('mt0010uid')
    un_control.value = os.environ['KOYAMA_ID']
    password_control = browser.form.find_control('mt0010pwd')
    password_control.value = os.environ['KOYAMA_PW']

    browser.submit()
    browser.open(CALENDAR_URL)

    all_available = {}
    all_reserved = {}
    for i in xrange(0, int(os.environ['MAX_WEEKS'])):
        soup = BeautifulSoup(
            browser.response().read(),
            'html.parser',
            from_encoding='shift-jis')  # doesn't work with lxml/xml
        available = soup.find_all('input', src='/images/ko2_kusya.gif')
        print('week {}: {} available'.format(str(i), str(len(available))))
        for date in available:
            period = date.attrs['name'][1:]
            date_string = date.parent.parent.contents[1].text
            try:
                all_available[date_string].append(timetable[period])
            except KeyError:
                all_available[date_string] = [timetable[period]]
        try:
            browser.form = browser.forms()[0]
            browser.submit('next')
        except:
            break

    print(sorted(all_available))
    message_text = u'\n'.join([
        u'{}: {}'.format(date, ', '.join(times))
        for (date, times) in sorted(all_available.iteritems())
    ])

    if os.environ['KOYAMA_PUSH_MODE'] in ('line', 'both'):
        if all_available:
            message_text += u'\n\n' + CALENDAR_URL
            url = 'https://api.line.me/v2/bot/message/push'
            headers = {
                'Content-Type':
                'application/json',
                'Authorization':
                'Bearer {{{}}}'.format(
                    os.environ['LINE_CHANNEL_TOKEN'])  # {{ → '{'
            }
            payload = {
                'to': os.environ['LINE_MY_ID'],
                'messages': [{
                    'type': 'text',
                    'text': message_text
                }]
            }
            r = requests.post(url, headers=headers, data=json.dumps(payload))
            print(r)
        else:
            print('None available.')

    if os.environ['KOYAMA_PUSH_MODE'] in ('pushbullet', 'both'):
        pb = Pushbullet(os.environ['PUSHBULLET_TOKEN'])
        if all_available:
            pushes = pb.get_pushes()
            try:
                most_recent = [
                    push for push in pushes
                    if push['sender_name'] == 'Koyama Alert'
                ][0]
                if most_recent['body'] != message_text:
                    pb.dismiss_push(most_recent['iden'])
                    push = push_to_pushbullet(pb, message_text)
            except IndexError:
                push = push_to_pushbullet(pb, message_text)
            print(push)
        else:
            undismissed = [
                push['iden'] for push in pb.get_pushes() if
                push['sender_name'] == 'Koyama Alert' and not push['dismissed']
            ]
            for iden in undismissed:
                pb.dismiss_push(iden)
            print('None available. Dismissed pushes.')

    return None