示例#1
0
    def _get_router_service_description(cls, url_scheme, base_url, root_url):
        """ Examines the given router to find the control URL, serial number, and UUID """

        from xml.etree import ElementTree

        response = http_request(url_scheme, base_url, root_url)

        # Parse the returned XML and find the <URLBase> and <controlURL> elements
        xml = ElementTree.fromstring(response)

        serial_number = next((x.text for x in xml.findall(
            ".//{urn:schemas-upnp-org:device-1-0}serialNumber")), None)

        # The UUID field contains the text "uuid:" before the actual UUID value. This is removed
        # and just the actual UUID is returned.
        # Example: uuid:11111111-2222-3333-4444-555555555555 becomes 11111111-2222-3333-4444-555555555555
        uuid = next(
            (x.text
             for x in xml.findall(".//{urn:schemas-upnp-org:device-1-0}UDN")),
            None)

        if uuid:
            uuid = uuid.split(":")[1]

        for svc in xml.findall(".//{urn:schemas-upnp-org:device-1-0}service"):
            svc_type = svc.find(
                ".//{urn:schemas-upnp-org:device-1-0}serviceType").text
            control_url = svc.find(
                ".//{urn:schemas-upnp-org:device-1-0}controlURL").text

            if SSDP._is_wanip_service(svc_type):
                return (serial_number, control_url, uuid, svc_type)

        return (None, None, None, None)
示例#2
0
    def delete_port_mapping(cls, router, protocol, public_port):
        """ Deletes a port mapping from a router """

        log.add_debug("Deleting port mapping (%s, %s, %s)",
                      (router, protocol, public_port))

        url = '{}{}'.format(router.base_url, router.control_url)
        log.add_debug('Deleting port mapping (%s/%s) at url "%s"',
                      (public_port, protocol, url))

        headers = {
            'Host': '{}:{}'.format(router.ip, router.port),
            'Content-Type': 'text/xml; charset=utf-8',
            'SOAPACTION': '{}#DeletePortMapping'.format(router.type)
        }

        data = UPnp._delete_port_mapping_template.format(public_port, protocol)
        log.add_debug('UPnP: Delete port mapping request: %s', data)

        response = http_request(router.url_scheme,
                                router.base_url,
                                router.control_url,
                                request_type="POST",
                                body=data,
                                headers=headers)

        log.add_debug('UPnP: Delete port mapping response: %s',
                      response.encode('utf-8'))
示例#3
0
    def _request_port_mapping(self, router, protocol, public_port, private_ip,
                              private_port, mapping_description,
                              lease_duration):
        """
        Function that adds a port mapping to the router.
        If a port mapping already exists, it is updated with a lease period of 24 hours.
        """

        from xml.etree import ElementTree

        url = '%s%s' % (router.base_url, router.control_url)
        log.add_debug(
            "UPnP: Adding port mapping (%s %s/%s, %s) at url '%s'",
            (private_ip, private_port, protocol, router.search_target, url))

        headers = {
            "Host": router.base_url,
            "Content-Type": "text/xml; charset=utf-8",
            "SOAPACTION": '"%s#AddPortMapping"' % router.service_type
        }

        body = (
            self.request_body %
            (router.service_type, public_port, protocol, private_port,
             private_ip, mapping_description, lease_duration)).encode('utf-8')

        log.add_debug("UPnP: Add port mapping request headers: %s", headers)
        log.add_debug("UPnP: Add port mapping request contents: %s", body)

        response = http_request(router.url_scheme,
                                router.base_url,
                                router.control_url,
                                request_type="POST",
                                body=body,
                                headers=headers)

        xml = ElementTree.fromstring(response)

        if xml.find(
                ".//{http://schemas.xmlsoap.org/soap/envelope/}Body") is None:
            raise Exception(
                _("Invalid response: %s") % response.encode('utf-8'))

        log.add_debug("UPnP: Add port mapping response: %s",
                      response.encode('utf-8'))

        error_code = xml.findtext(
            ".//{urn:schemas-upnp-org:control-1-0}errorCode")
        error_description = xml.findtext(
            ".//{urn:schemas-upnp-org:control-1-0}errorDescription")

        if error_code or error_description:
            raise Exception(
                _("Error code %(code)s: %(description)s") % {
                    "code": error_code,
                    "description": error_description
                })
示例#4
0
    def lastfm(self, user):
        """ Function to get the last song played via Last.fm API """

        try:
            user, apikey = user.split(';')

        except ValueError:
            log.add_important_error(
                _("Last.fm: Please provide both your Last.fm username and API key"
                  ))
            return None

        try:
            response = http_request(
                "https",
                "ws.audioscrobbler.com",
                "/2.0/?method=user.getrecenttracks&user="******"&api_key=" + apikey + "&limit=1&format=json",
                headers={"User-Agent": self.config.application_name})

        except Exception as error:
            log.add_important_error(
                _("Last.fm: Could not connect to Audioscrobbler: %(error)s"),
                {"error": error})
            return None

        try:
            json_api = json.loads(response)
            lastplayed = json_api["recenttracks"]["track"]

            try:
                # In most cases, a list containing a single track dictionary is sent
                lastplayed = lastplayed[0]

            except KeyError:
                # On rare occasions, the track dictionary is not wrapped in a list
                pass

            self.title["artist"] = lastplayed["artist"]["#text"]
            self.title["title"] = lastplayed["name"]
            self.title["album"] = lastplayed["album"]["#text"]
            self.title["nowplaying"] = "%s: %s - %s - %s" % (
                _("Last played"), self.title["artist"], self.title["album"],
                self.title["title"])

        except Exception:
            log.add_important_error(
                _("Last.fm: Could not get recent track from Audioscrobbler: %(error)s"
                  ), {"error": response})
            return None

        return True
示例#5
0
    def add_port_mapping(cls, router, protocol, public_port, private_ip,
                         private_port, mapping_description, lease_duration):
        """ Adds a port mapping to a router """

        from xml.etree import ElementTree

        log.add_debug(
            "Adding port mapping (%s, %s, %s, %s, %s)",
            (router.uuid, protocol, public_port, private_ip, private_port))

        url = '{}{}'.format(router.base_url, router.control_url)
        log.add_debug('Adding port mapping (%s %s/%s) at url "%s"',
                      (private_ip, private_port, protocol, url))

        headers = {
            'Host': '{}:{}'.format(router.ip, router.port),
            'Content-Type': 'text/xml; charset=utf-8',
            'SOAPACTION': '"{}#AddPortMapping"'.format(router.svc_type)
        }

        data = UPnp._add_port_mapping_template.format(public_port, protocol,
                                                      private_port, private_ip,
                                                      mapping_description,
                                                      lease_duration)

        log.add_debug('UPnP: Add port mapping request headers: %s', headers)
        log.add_debug('UPnP: Add port mapping request contents: %s', data)

        response = http_request(router.url_scheme,
                                router.base_url,
                                router.control_url,
                                request_type="POST",
                                body=data,
                                headers=headers)

        log.add_debug('UPnP: Add port mapping response: %s',
                      response.encode('utf-8'))

        xml = ElementTree.fromstring(response)

        error_code = next((x.text for x in xml.findall(
            ".//{urn:schemas-upnp-org:control-1-0}errorCode")), None)

        error_description = next((x.text for x in xml.findall(
            ".//{urn:schemas-upnp-org:control-1-0}errorDescription")), None)

        if error_code or error_description:
            raise Exception('Error code %(code)s: %(description)s' % {
                'code': error_code,
                'description': error_description
            })
示例#6
0
    def listenbrainz(self, username):
        """ Function to get the currently playing song via ListenBrainz API """

        if not username:
            log.add_important_error(
                _("ListenBrainz: Please provide your ListenBrainz username"))
            return None

        try:
            response = http_request(
                'https',
                'api.listenbrainz.org',
                '/1/user/{}/playing-now'.format(username),
                headers={'User-Agent': self.config.application_name})

        except Exception as error:
            log.add_important_error(
                _("ListenBrainz: Could not connect to ListenBrainz: %(error)s"
                  ), {'error': error})
            return None

        try:
            json_api = json.loads(response)['payload']

            if not json_api['playing_now']:
                log.add_important_error(
                    _("ListenBrainz: You don\'t seem to be listening to anything right now"
                      ))
                return None

            track = json_api['listens'][0]['track_metadata']

            self.title['artist'] = track['artist_name']
            self.title['title'] = track['track_name']
            self.title['album'] = track['release_name']
            self.title['nowplaying'] = '%s: %s - %s - %s' % (
                _('Playing now'), self.title['artist'], self.title['album'],
                self.title['title'])

            return True

        except Exception:
            log.add_important_error(
                _("ListenBrainz: Could not get current track from ListenBrainz: %(error)s"
                  ), {'error': str(response)})
        return None
示例#7
0
    def list_port_mappings(cls, router):
        """ Lists the port mappings for a router """

        log.add_debug('Listing existing port mappings...')

        headers = {
            'Host': '{}:{}'.format(router.ip, router.port),
            'Content-Type': 'text/xml; charset=utf-8',
            'SOAPACTION':
            '"{}#GetGenericPortMappingEntry"'.format(router.svc_type)
        }

        index = -1
        portmap_found = True
        portmaps = []

        while portmap_found:
            index += 1
            data = UPnp._list_port_mappings_template.format(index)
            log.add_debug('UPnP: List port mappings request headers: %s',
                          headers)
            log.add_debug('UPnP: List port mappings request contents: %s',
                          data)

            response = http_request(router.url_scheme,
                                    router.base_url,
                                    router.control_url,
                                    request_type="POST",
                                    body=data,
                                    headers=headers)

            log.add_debug('UPnP: List port mappings response: %s',
                          response.encode('utf-8'))

            portmap = PortMapping.parse_port_map_xml(response, router.svc_type)

            if not portmap:
                portmap_found = False
            else:
                portmaps.append(portmap)

        log.add_debug('Existing port mappings: %s', portmaps)

        return portmaps
示例#8
0
    def incoming_public_chat_notification(self, room, user, line):
        line = line.lower().strip()

        if not line.startswith(self.plugin_command) or " " not in line:
            return

        subreddit = line.split(" ")[1].strip("/")

        if not self.responder.ok_to_respond(room, user, subreddit):
            return

        try:
            response = http_request(
                'https',
                'www.reddit.com',
                '/r/' + subreddit + '/.json',
                headers={"User-Agent": self.config.application_name})

        except Exception as error:
            self.log("Could not connect to Reddit: %(error)s",
                     {"error": error})
            return

        try:
            response = json.loads(response)

            for post in islice(response['data']['children'],
                               self.settings['reddit_links']):
                post_data = post['data']
                self.send_public(
                    room, "/me {}: {}".format(post_data['title'],
                                              post_data['url']))

        except Exception as error:
            self.log("Failed to parse response from Reddit: %(error)s",
                     {"error": error})
            return

        self.responder.responded()
示例#9
0
    def get_router_control_url(url_scheme, base_url, root_url):

        service_type = None
        control_url = None

        try:
            from xml.etree import ElementTree

            response = http_request(url_scheme, base_url, root_url, timeout=2)
            log.add_debug(
                "UPnP: Device description response from %s://%s%s: %s",
                (url_scheme, base_url, root_url, response.encode('utf-8')))

            xml = ElementTree.fromstring(response)

            for service in xml.findall(
                    ".//{urn:schemas-upnp-org:device-1-0}service"):
                found_service_type = service.find(
                    ".//{urn:schemas-upnp-org:device-1-0}serviceType").text

                if found_service_type in (
                        "urn:schemas-upnp-org:service:WANIPConnection:1",
                        "urn:schemas-upnp-org:service:WANPPPConnection:1",
                        "urn:schemas-upnp-org:service:WANIPConnection:2"):
                    # We found a router with UPnP enabled
                    service_type = found_service_type
                    control_url = service.find(
                        ".//{urn:schemas-upnp-org:device-1-0}controlURL").text
                    break

        except Exception as error:
            # Invalid response
            log.add_debug(
                "UPnP: Invalid device description response from %s://%s%s: %s",
                (url_scheme, base_url, root_url, error))

        return service_type, control_url
示例#10
0
    def parse_response(self, video_id):

        if not self.settings['api_key']:
            self.log('No API key specified')
            return None

        try:
            response = http_request(
                'https',
                'www.googleapis.com',
                '/youtube/v3/videos?part=snippet,statistics,contentDetails&id={}&key={}'
                .format(video_id, self.settings['api_key']),
                headers={'User-Agent': self.config.application_name})

        except Exception as error:
            self.log('Failed to connect to www.googleapis.com: %s', error)
            return None

        try:
            data = json.loads(response)

        except Exception as error:
            self.log('Failed to parse response from www.googleapis.com: %s',
                     str(error))
            return None

        if 'error' in data:
            error_message = data['error'].get('message', False)
            if not error_message:
                # This should not occur
                error_message = str(data['error'])
            self.log(error_message)
            return None

        total_results = data.get('pageInfo', {}).get('totalResults', False)

        if not total_results:
            if isinstance(total_results, int):
                # Video removed / invalid id
                self.log('Video unavailable')

            elif isinstance(total_results, bool):
                # This should not occur
                self.log('Youtube API appears to be broken')
            return None

        try:
            data = data['items'][0]

            title = data['snippet']['title']
            description = data['snippet']['description']
            channel = data['snippet']['channelTitle']
            live = data['snippet']['liveBroadcastContent']

            duration = data['contentDetails']['duration']
            quality = data['contentDetails']['definition'].upper()

            views = data['statistics'].get('viewCount', 'RESTRICTED')
            likes = data['statistics'].get('likeCount', 'LIKES')

        except KeyError:
            # This should not occur
            self.log('An error occurred while parsing id "%s"', video_id)
            return None

        if likes != 'LIKES':
            likes = humanize(int(likes))

        if views != 'RESTRICTED':
            views = humanize(int(views))

        if live in ('live', 'upcoming'):
            duration = live.upper()
        else:
            duration = self.get_duration(duration)

        return {
            '%title%': title,
            '%description%': description,
            '%duration%': duration,
            '%quality%': quality,
            '%channel%': channel,
            '%views%': views,
            '%likes%': likes
        }