コード例 #1
0
 def create(session: Session, steam_id: int):
     d = session.query(Dota).get(steam_id)
     if d is not None:
         raise AlreadyExistingError(repr(d))
     r = requests.get(
         f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(steam_id)}"
     )
     if r.status_code != 200:
         raise RequestError("OpenDota returned {r.status_code}")
     data = r.json()
     if "profile" not in data:
         raise NotFoundError(
             "The specified user has never played Dota or has a private match history"
         )
     r = requests.get(
         f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(steam_id)}/wl"
     )
     if r.status_code != 200:
         raise RequestError("OpenDota returned {r.status_code}")
     wl = r.json()
     new_record = Dota(steam_id=str(steam_id),
                       rank_tier=data["rank_tier"],
                       wins=wl["win"],
                       losses=wl["lose"])
     return new_record
コード例 #2
0
 def create(session: Session, royal_id, battletag, discriminator=None):
     if discriminator is None:
         battletag, discriminator = battletag.split("#", 1)
     o = session.query(Overwatch).filter_by(
         battletag=battletag, discriminator=discriminator).first()
     if o is not None:
         raise AlreadyExistingError(repr(o))
     r = requests.get(
         f"https://owapi.net/api/v3/u/{battletag}-{discriminator}/stats",
         headers={
             "User-Agent": "Royal-Bot/4.0",
             "From": "*****@*****.**"
         })
     if r.status_code != 200:
         raise RequestError(f"OWAPI.net returned {r.status_code}")
     try:
         j = r.json()["eu"]["stats"]["quickplay"]["overall_stats"]
     except TypeError:
         raise RequestError(
             "Something went wrong when retrieving the stats.")
     o = Overwatch(
         royal_id=royal_id,
         battletag=battletag,
         discriminator=discriminator,
         icon=re.search(
             r"https://.+\.cloudfront\.net/game/unlocks/(0x[0-9A-F]+)\.png",
             j["avatar"]).group(1),
         level=j["prestige"] * 100 + j["level"],
         rank=j["comprank"])
     return o
コード例 #3
0
    def login(self, apikey):
        result = self.send_request('login', {'apikey': apikey})
        if result is None:
            raise RequestError('No result after login')

        session = result.get('session')
        if not session:
            raise RequestError('No session in result')

        self.session = session
コード例 #4
0
 def update(self):
     r = requests.get(
         f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(self.steam_id)}"
     )
     if r.status_code != 200:
         raise RequestError("OpenDota returned {r.status_code}")
     data = r.json()
     r = requests.get(
         f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(self.steam_id)}/wl"
     )
     if r.status_code != 200:
         raise RequestError("OpenDota returned {r.status_code}")
     wl = r.json()
     self.rank_tier = data["rank_tier"]
     self.wins = wl["win"]
     self.losses = wl["lose"]
コード例 #5
0
    def _api_request(self, data):
        params = {'id': self.sequence_num}
        self.sequence_num += 1

        if self.sid:
            params.update({'sid': self.sid})

        # ensure input data is a list
        if not isinstance(data, list):
            data = [data]

        url = f'{self.schema}://g.api.{self.domain}/cs'
        req = requests.post(
            url,
            params=params,
            data=json.dumps(data),
            timeout=self.timeout,
        )
        json_resp = json.loads(req.text)
        if isinstance(json_resp, int):
            if json_resp == -3:
                msg = 'Request failed, retrying'
                logger.info(msg)
                raise RuntimeError(msg)
            raise RequestError(json_resp)
        return json_resp[0]
コード例 #6
0
 def create(session: Session,
            royal_id,
            summoner_name=None,
            summoner_id=None):
     if summoner_name:
         lol = session.query(LeagueOfLegends).filter(
             LeagueOfLegends.summoner_name == summoner_name).first()
     elif summoner_id:
         lol = session.query(LeagueOfLegends).get(summoner_id)
     else:
         raise SyntaxError(
             "Neither summoner_name or summoner_id are specified")
     if lol is not None:
         raise AlreadyExistingError(repr(lol))
     # Get the summoner_id
     if summoner_name:
         r = requests.get(
             f"https://euw1.api.riotgames.com/lol/summoner/v3/summoners/by-name/{summoner_name}?api_key={config['League of Legends']['riot_api_key']}"
         )
     else:
         r = requests.get(
             f"https://euw1.api.riotgames.com/lol/summoner/v3/summoners/{summoner_id}?api_key={config['League of Legends']['riot_api_key']}"
         )
     if r.status_code != 200:
         return RequestError(
             f"League of Legends API returned {r.status_code}")
     data = r.json()
     lol = LeagueOfLegends(royal_id=royal_id,
                           summoner_id=data["id"],
                           summoner_name=data["name"],
                           level=data["summonerLevel"])
     lol.update()
     return lol
コード例 #7
0
 def create(session: Session, royal_id, osu_name):
     o = session.query(Osu).filter(Osu.osu_name == osu_name).first()
     if o is not None:
         raise AlreadyExistingError(repr(o))
     r0 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=0"
     )
     r1 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=1"
     )
     r2 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=2"
     )
     r3 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=3"
     )
     if r0.status_code != 200 or r1.status_code != 200 or r2.status_code != 200 or r3.status_code != 200:
         raise RequestError(
             f"Osu! API returned an error ({r0.status_code} {r1.status_code} {r2.status_code} {r3.status_code})"
         )
     j0 = r0.json()[0]
     j1 = r1.json()[0]
     j2 = r2.json()[0]
     j3 = r3.json()[0]
     new_record = Osu(royal_id=royal_id,
                      osu_id=j0["user_id"],
                      osu_name=j0["username"],
                      std_pp=j0["pp_raw"],
                      taiko_pp=j1["pp_raw"],
                      catch_pp=j2["pp_raw"],
                      mania_pp=j3["pp_raw"])
     return new_record
コード例 #8
0
 def update(self):
     r0 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=0"
     )
     r1 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=1"
     )
     r2 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=2"
     )
     r3 = requests.get(
         f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=3"
     )
     if r0.status_code != 200 or r1.status_code != 200 or r2.status_code != 200 or r3.status_code != 200:
         raise RequestError(
             f"Osu! API returned an error ({r0.status_code} {r1.status_code} {r2.status_code} {r3.status_code})"
         )
     j0 = r0.json()[0]
     j1 = r1.json()[0]
     j2 = r2.json()[0]
     j3 = r3.json()[0]
     self.osu_name = j0["username"]
     self.std_pp = j0["pp_raw"]
     self.taiko_pp = j1["pp_raw"]
     self.catch_pp = j2["pp_raw"]
     self.mania_pp = j3["pp_raw"]
コード例 #9
0
    def get_public_file_info(self, file_handle, file_key):
        """
        Get size and name of a public file
        """
        data = self._api_request({'a': 'g', 'p': file_handle, 'ssm': 1})

        #if numeric error code response
        if isinstance(data, int):
            raise RequestError(data)

        if 'at' not in data or 's' not in data:
            raise ValueError("Unexpected result", data)

        key = base64_to_a32(file_key)
        k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6],
             key[3] ^ key[7])

        size = data['s']
        unencrypted_attrs = decrypt_attr(base64_url_decode(data['at']), k)
        if not unencrypted_attrs:
            return None

        result = {'size': size, 'name': unencrypted_attrs['n']}

        return result
コード例 #10
0
 def _parse_url(self, url):
     # parse file id and key from url
     if '!' in url:
         match = re.findall(r'/#!(.*)', url)
         path = match[0]
         return path
     else:
         raise RequestError('Url key missing')
コード例 #11
0
 def _login_user(self, email, password):
     password_aes = prepare_key(str_to_a32(password))
     uh = stringhash(email, password_aes)
     resp = self._api_request({'a': 'us', 'user': email, 'uh': uh})
     #if numeric error code response
     if isinstance(resp, int):
         raise RequestError(resp)
     self._login_process(resp, password_aes)
コード例 #12
0
 def update(self):
     r = requests.get(
         f"https://euw1.api.riotgames.com/lol/summoner/v3/summoners/{self.summoner_id}?api_key={config['League of Legends']['riot_api_key']}"
     )
     if r.status_code != 200:
         return RequestError(
             f"League of Legends API returned {r.status_code}")
     data = r.json()
     r = requests.get(
         f"https://euw1.api.riotgames.com/lol/league/v3/positions/by-summoner/{self.summoner_id}?api_key={config['League of Legends']['riot_api_key']}"
     )
     if r.status_code != 200:
         return RequestError(
             f"League of Legends API returned {r.status_code}")
     rank = r.json()
     solo_rank = None
     flex_rank = None
     twtr_rank = None
     for league in rank:
         if league["queueType"] == "RANKED_SOLO_5x5":
             solo_rank = league
         elif league["queueType"] == "RANKED_FLEX_SR":
             flex_rank = league
         elif league["queueType"] == "RANKED_FLEX_TT":
             twtr_rank = league
     self.summoner_id = data["id"]
     self.summoner_name = data["name"]
     self.level = data["summonerLevel"]
     if solo_rank is not None:
         self.solo_division = LeagueOfLegendsRanks[solo_rank["tier"]]
         self.solo_rank = RomanNumerals[solo_rank["rank"]]
     else:
         self.solo_division = None
         self.solo_rank = None
     if flex_rank is not None:
         self.flex_division = LeagueOfLegendsRanks[flex_rank["tier"]]
         self.flex_rank = RomanNumerals[flex_rank["rank"]]
     else:
         self.flex_division = None
         self.flex_rank = None
     if twtr_rank is not None:
         self.twtr_division = LeagueOfLegendsRanks[twtr_rank["tier"]]
         self.twtr_rank = RomanNumerals[twtr_rank["rank"]]
     else:
         self.twtr_division = None
         self.twtr_rank = None
コード例 #13
0
def _raise_for_remote_status(url, data):
    """Raise an error from the remote API if necessary."""
    if data.get("errorType") and data["errorType"] > 0:
        raise_remote_error(data["errorType"])

    if data.get("statusCode") and data["statusCode"] != 200:
        raise RequestError(
            "Error requesting data from {}: {} {}".format(url, data['statusCode'], data['message'])
        )
コード例 #14
0
 def update(self):
     r = requests.get(
         f"https://owapi.net/api/v3/u/{self.battletag}-{self.discriminator}/stats",
         headers={
             "User-Agent": "Royal-Bot/4.0",
             "From": "*****@*****.**"
         })
     if r.status_code != 200:
         raise RequestError(f"OWAPI.net returned {r.status_code}")
     try:
         j = r.json()["eu"]["stats"]["quickplay"]["overall_stats"]
     except TypeError:
         raise RequestError(
             "Something went wrong when retrieving the stats.")
     try:
         self.icon = re.search(
             r"https://.+\.cloudfront\.net/game/unlocks/(0x[0-9A-F]+)\.png",
             j["avatar"]).group(1)
     except AttributeError:
         pass
     self.level = j["prestige"] * 100 + j["level"]
     self.rank = j["comprank"]
コード例 #15
0
 def update(self):
     r = requests.get(
         f"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={config['Steam']['api_key']}&steamids={self.steam_id}"
     )
     if r.status_code != 200:
         raise RequestError(f"Steam returned {r.status_code}")
     j = r.json()
     self.persona_name = j["response"]["players"][0]["personaname"]
     self.avatar_hex = re.search(
         r"https://steamcdn-a\.akamaihd\.net/steamcommunity/public/images/avatars/../(.+).jpg",
         j["response"]["players"][0]["avatar"]).group(1)
     r = requests.get(
         f"http://api.steampowered.com/IPlayerService/GetRecentlyPlayedGames/v0001/?key={config['Steam']['api_key']}&steamid={self.steam_id}&format=json"
     )
     if r.status_code != 200:
         raise RequestError(f"Steam returned {r.status_code}")
     j = r.json()
     if "response" not in j \
         or "games" not in j["response"] \
         or len(j["response"]["games"]) < 1:
         raise RequestError(f"Game data is private")
     self.most_played_game_id = j["response"]["games"][0]["appid"]
コード例 #16
0
 def get_link(self, file):
     """
     Get a file public link from given file object
     """
     file = file[1]
     if 'h' in file and 'k' in file:
         public_handle = self._api_request({'a': 'l', 'n': file['h']})
         if public_handle == -11:
             raise RequestError("Can't get a public link from that file "
                                "(is this a shared file?)")
         decrypted_key = a32_to_base64(file['key'])
         return (f'{self.schema}://{self.domain}'
                 f'/#!{public_handle}!{decrypted_key}')
     else:
         raise ValidationError('File id and key must be present')
コード例 #17
0
 def get_folder_link(self, file):
     try:
         file = file[1]
     except (IndexError, KeyError):
         pass
     if 'h' in file and 'k' in file:
         public_handle = self._api_request({'a': 'l', 'n': file['h']})
         if public_handle == -11:
             raise RequestError("Can't get a public link from that file "
                                "(is this a shared file?)")
         decrypted_key = a32_to_base64(file['shared_folder_key'])
         return (f'{self.schema}://{self.domain}'
                 f'/#F!{public_handle}!{decrypted_key}')
     else:
         raise ValidationError('File id and key must be present')
コード例 #18
0
    def request(self, method, url, access_token=None, access_token_expiration=None, ssl=True, **kwargs):
        """Make a request against the RainMachine device."""
        if access_token_expiration and datetime.now() >= access_token_expiration:
            raise TokenExpiredError("Long-lived access token has expired")

        kwargs.setdefault('headers', {"Content-Type": "application/json"})
        if access_token:
            kwargs.setdefault('params', {})['access_token'] = access_token

        try:
            resp = requests.request(method, url, verify=ssl, timeout=DEFAULT_TIMEOUT, **kwargs)
            resp.raise_for_status()
            data = resp.json()
            _raise_for_remote_status(url, data)
        except requests.exceptions.HTTPError:
            raise TokenExpiredError("Long-lived access token has expired")
        except requests.exceptions.Timeout:
            raise RequestError("HTTP error occurred using url: {}".format(url))
        return data
コード例 #19
0
 def create(session: Session, royal_id: int, steam_id: str):
     s = session.query(Steam).get(steam_id)
     if s is not None:
         raise AlreadyExistingError(repr(s))
     r = requests.get(
         f"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={config['Steam']['api_key']}&steamids={steam_id}"
     )
     if r.status_code != 200:
         raise RequestError(f"Steam returned {r.status_code}")
     j = r.json()
     if len(j) == 0:
         raise NotFoundError(
             f"The steam_id doesn't match any steam account")
     s = Steam(
         royal_id=royal_id,
         steam_id=steam_id,
         persona_name=j["response"]["players"][0]["personaname"],
         avatar_hex=re.search(
             r"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/../(.+).jpg",
             j["response"]["players"][0]["avatar"]).group(1))
     return s
コード例 #20
0
    def login_anonymous(self):
        logger.info('Logging in anonymous temporary user...')
        master_key = [random.randint(0, 0xFFFFFFFF)] * 4
        password_key = [random.randint(0, 0xFFFFFFFF)] * 4
        session_self_challenge = [random.randint(0, 0xFFFFFFFF)] * 4

        user = self._api_request({
            'a':
            'up',
            'k':
            a32_to_base64(encrypt_key(master_key, password_key)),
            'ts':
            base64_url_encode(
                a32_to_str(session_self_challenge) +
                a32_to_str(encrypt_key(session_self_challenge, master_key)))
        })

        resp = self._api_request({'a': 'us', 'user': user})
        if isinstance(resp, int):
            raise RequestError(resp)
        self._login_process(resp, password_key)
コード例 #21
0
    def _api_request(self, data):
        params = {'id': self.sequence_num}
        self.sequence_num += 1

        if self.sid:
            params.update({'sid': self.sid})

        #ensure input data is a list
        if not isinstance(data, list):
            data = [data]

        req = requests.post('{0}://g.api.{1}/cs'.format(
            self.schema, self.domain),
                            params=params,
                            data=json.dumps(data),
                            timeout=self.timeout)
        json_resp = json.loads(req.text)

        #if numeric error code response
        if isinstance(json_resp, int):
            raise RequestError(json_resp)
        return json_resp[0]
コード例 #22
0
    def make_call(self, request_name, env_name):
        request = self.requests.find(request_name)
        environment = self.environments.find(env_name)

        parsed_req = self._parse_request(request, env_name)

        url = f"{environment['base_url']}{parsed_req['endpoint']}"

        headers = {'content-type': 'application/json'}
        if 'headers' in environment:
            headers = {**headers, **environment['headers']}

        if parsed_req['type'] == 'POST':
            response = requests.post(url,
                                     data=json.dumps(parsed_req['body']),
                                     headers=headers)
        elif parsed_req['type'] == 'PUT':
            response = requests.put(url,
                                    data=json.dumps(parsed_req['body']),
                                    headers=headers)
        elif parsed_req['type'] == 'GET':
            response = requests.get(url,
                                    params=parsed_req['body'],
                                    headers=headers)
        else:
            raise UserError(f'Unknown HTTP method {parsed_req["type"]}')

        response_json = response.json()
        if response.status_code != 200:
            raise RequestError(
                f'{response.status_code} returned when calling {request_name} with response '
                f'{response_json}. Expected status code 200.')

        if self.print_all_responses:
            print(f'Response for call to {request_name}:')
            print(response_json)

        return response_json
コード例 #23
0
 def _login_user(self, email, password):
     logger.info('Logging in user...')
     email = email.lower()
     get_user_salt_resp = self._api_request({'a': 'us0', 'user': email})
     user_salt = None
     try:
         user_salt = base64_to_a32(get_user_salt_resp['s'])
     except KeyError:
         # v1 user account
         password_aes = prepare_key(str_to_a32(password))
         user_hash = stringhash(email, password_aes)
     else:
         # v2 user account
         pbkdf2_key = hashlib.pbkdf2_hmac(hash_name='sha512',
                                          password=password.encode(),
                                          salt=a32_to_str(user_salt),
                                          iterations=100000,
                                          dklen=32)
         password_aes = str_to_a32(pbkdf2_key[:16])
         user_hash = base64_url_encode(pbkdf2_key[-16:])
     resp = self._api_request({'a': 'us', 'user': email, 'uh': user_hash})
     if isinstance(resp, int):
         raise RequestError(resp)
     self._login_process(resp, password_aes)
コード例 #24
0
    def send_request(self, service, args={}, file_args=None):
        '''
        service: string
        args: dict
        '''
        if self.session is not None:
            args.update({'session': self.session})
        print 'Python:', args
        json = python2json(args)
        print 'Sending json:', json
        url = self.get_url(service)
        print 'Sending to URL:', url

        # If we're sending a file, format a multipart/form-data
        if file_args is not None:
            m1 = MIMEBase('text', 'plain')
            m1.add_header('Content-disposition',
                          'form-data; name="request-json"')
            m1.set_payload(json)

            m2 = MIMEApplication(file_args[1], 'octet-stream', encode_noop)
            m2.add_header(
                'Content-disposition',
                'form-data; name="file"; filename="%s"' % file_args[0])

            #msg.add_header('Content-Disposition', 'attachment',
            # filename='bud.gif')
            #msg.add_header('Content-Disposition', 'attachment',
            # filename=('iso-8859-1', '', 'FuSballer.ppt'))

            mp = MIMEMultipart('form-data', None, [m1, m2])

            # Makie a custom generator to format it the way we need.
            from cStringIO import StringIO
            from email.generator import Generator

            class MyGenerator(Generator):
                def __init__(self, fp, root=True):
                    Generator.__init__(self,
                                       fp,
                                       mangle_from_=False,
                                       maxheaderlen=0)
                    self.root = root

                def _write_headers(self, msg):
                    # We don't want to write the top-level headers;
                    # they go into Request(headers) instead.
                    if self.root:
                        return
                    # We need to use \r\n line-terminator, but Generator
                    # doesn't provide the flexibility to override, so we
                    # have to copy-n-paste-n-modify.
                    for h, v in msg.items():
                        print >> self._fp, ('%s: %s\r\n' % (h, v)),
                    # A blank line always separates headers from body
                    print >> self._fp, '\r\n',

                # The _write_multipart method calls "clone" for the
                # subparts.  We hijack that, setting root=False
                def clone(self, fp):
                    return MyGenerator(fp, root=False)

            fp = StringIO()
            g = MyGenerator(fp)
            g.flatten(mp)
            data = fp.getvalue()
            headers = {'Content-type': mp.get('Content-type')}

            if False:
                print 'Sending headers:'
                print ' ', headers
                print 'Sending data:'
                print data[:1024].replace('\n', '\\n\n').replace('\r', '\\r')
                if len(data) > 1024:
                    print '...'
                    print data[-256:].replace('\n',
                                              '\\n\n').replace('\r', '\\r')
                    print

        else:
            # Else send x-www-form-encoded
            data = {'request-json': json}
            print 'Sending form data:', data
            data = urlencode(data)
            print 'Sending data:', data
            headers = {}

        request = Request(url=url, headers=headers, data=data)

        try:
            f = urlopen(request)
            txt = f.read()
            print 'Got json:', txt
            result = json2python(txt)
            print 'Got result:', result
            stat = result.get('status')
            print 'Got status:', stat
            if stat == 'error':
                errstr = result.get('errormessage', '(none)')
                raise RequestError('server error message: ' + errstr)
            return result
        except HTTPError, e:
            print 'HTTPError', e
コード例 #25
0
    def send_request(self, service, args={}, file_args=None):
        if self.session is not None:
            args.update({'session': self.session})

        json = python2json(args)
        url = self.get_url(service)

        # If we're sending a file, format a multipart/form-data
        if file_args is not None:
            m1 = MIMEBase('text', 'plain')
            m1.add_header('Content-disposition', 'form-data; name="request-json"')
            m1.set_payload(json)

            m2 = MIMEApplication(file_args[1], 'octet-stream', encode_noop)
            m2.add_header(
                'Content-disposition',
                'form-data; name="file"; filename="%s"' % file_args[0])

            mp = MIMEMultipart('form-data', None, [m1, m2])

            # Make a custom generator to format it the way we need.
            from cStringIO import StringIO
            from email.generator import Generator

            class MyGenerator(Generator):
                def __init__(self, fp, root=True):
                    Generator.__init__(self, fp, mangle_from_=False, maxheaderlen=0)
                    self.root = root

                def _write_headers(self, msg):
                    # We don't want to write the top-level headers;
                    # they go into Request(headers) instead.
                    if self.root:
                        return

                    # We need to use \r\n line-terminator, but Generator
                    # doesn't provide the flexibility to override, so we
                    # have to copy-n-paste-n-modify.
                    for h, v in msg.items():
                        self._fp.write('%s: %s\r\n' % (h, v))

                    # A blank line always separates headers from body
                    self._fp.write('\r\n')

                # The _write_multipart method calls "clone" for the
                # sub-parts.  We hijack that, setting root=False.
                def clone(self, fp):
                    return MyGenerator(fp, root=False)

            fp = StringIO()
            g = MyGenerator(fp)
            g.flatten(mp)
            data = fp.getvalue()
            headers = {'Content-type': mp.get('Content-type')}
        else:
            # Else send x-www-form-encoded
            data = {'request-json': json}
            data = urlencode(data)
            headers = {}

        request = Request(url=url, headers=headers, data=data)

        response = urlopen(request, timeout=30)
        text = response.read()
        result = json2python(text)
        status = result.get('status')

        if status == 'error':
            error_message = result.get('errormessage', '(none)')
            log.error("Astrometry.net request error: %s" % error_message)
            raise RequestError('Server error message: ' + error_message)

        return result
コード例 #26
0
    def _download_file(self,
                       file_handle,
                       file_key,
                       dest_path=None,
                       dest_filename=None,
                       is_public=False,
                       file=None):
        if file is None:
            if is_public:
                file_key = base64_to_a32(file_key)
                file_data = self._api_request({
                    'a': 'g',
                    'g': 1,
                    'p': file_handle
                })
            else:
                file_data = self._api_request({
                    'a': 'g',
                    'g': 1,
                    'n': file_handle
                })

            k = (file_key[0] ^ file_key[4], file_key[1] ^ file_key[5],
                 file_key[2] ^ file_key[6], file_key[3] ^ file_key[7])
            iv = file_key[4:6] + (0, 0)
            meta_mac = file_key[6:8]
        else:
            file_data = self._api_request({'a': 'g', 'g': 1, 'n': file['h']})
            k = file['k']
            iv = file['iv']
            meta_mac = file['meta_mac']

        # Seems to happens sometime... When this occurs, files are
        # inaccessible also in the official also in the official web app.
        # Strangely, files can come back later.
        if 'g' not in file_data:
            raise RequestError('File not accessible anymore')
        file_url = file_data['g']
        file_size = file_data['s']
        attribs = base64_url_decode(file_data['at'])
        attribs = decrypt_attr(attribs, k)

        if dest_filename is not None:
            file_name = dest_filename
        else:
            file_name = attribs['n']

        input_file = requests.get(file_url, stream=True).raw

        if dest_path is None:
            dest_path = ''
        else:
            dest_path += '/'

        with tempfile.NamedTemporaryFile(mode='w+b',
                                         prefix='megapy_',
                                         delete=False) as temp_output_file:
            k_str = a32_to_str(k)
            counter = Counter.new(128,
                                  initial_value=((iv[0] << 32) + iv[1]) << 64)
            aes = AES.new(k_str, AES.MODE_CTR, counter=counter)

            mac_str = '\0' * 16
            mac_encryptor = AES.new(k_str, AES.MODE_CBC,
                                    mac_str.encode("utf8"))
            iv_str = a32_to_str([iv[0], iv[1], iv[0], iv[1]])

            for chunk_start, chunk_size in get_chunks(file_size):
                chunk = input_file.read(chunk_size)
                chunk = aes.decrypt(chunk)
                temp_output_file.write(chunk)

                encryptor = AES.new(k_str, AES.MODE_CBC, iv_str)
                for i in range(0, len(chunk) - 16, 16):
                    block = chunk[i:i + 16]
                    encryptor.encrypt(block)

                # fix for files under 16 bytes failing
                if file_size > 16:
                    i += 16
                else:
                    i = 0

                block = chunk[i:i + 16]
                if len(block) % 16:
                    block += b'\0' * (16 - (len(block) % 16))
                mac_str = mac_encryptor.encrypt(encryptor.encrypt(block))

                file_info = os.stat(temp_output_file.name)
                logger.info('%s of %s downloaded', file_info.st_size,
                            file_size)
            file_mac = str_to_a32(mac_str)
            # check mac integrity
            if (file_mac[0] ^ file_mac[1],
                    file_mac[2] ^ file_mac[3]) != meta_mac:
                raise ValueError('Mismatched mac')
            output_path = Path(dest_path + file_name)
            shutil.copy(temp_output_file.name, output_path)
            return output_path