Last active
June 7, 2020 21:52
-
-
Save Gobot1234/5df6fb67a1f835cbbbd00a439a295966 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async def request(self, method: str, url: Union[APIRoute, CRoute, str], | |
**kwargs) -> Optional[Any]: # adapted from d.py | |
kwargs['headers'] = { | |
"User-Agent": self.user_agent, | |
**kwargs.get('headers', {}) | |
} | |
async with self._lock: | |
for tries in range(5): | |
async with self._session.request(method, str(url), **kwargs) as r: | |
payload = kwargs.get('json') | |
log.debug(self.REQUEST_LOG.format( | |
method=method, | |
url=url, | |
payload=f'PAYLOAD: {payload}', | |
status=r.status) | |
) | |
# even errors have text involved in them so this is safe to call | |
data = await json_or_text(r) | |
# the request was successful so just return the text/json | |
if 300 > r.status >= 200: | |
if method == 'PUT': | |
print(r.status, 'PUT') | |
print(r.request_info) | |
log.debug(f'{method} {url} has received {data}') | |
return data | |
# we are being rate limited | |
if r.status == 429: | |
# I haven't been able to get any X-Retry-After headers | |
# from the API but we should probably still handle it | |
try: | |
await asyncio.sleep(float(r.headers['X-Retry-After'])) | |
except KeyError: # steam being un-helpful as usual | |
await asyncio.sleep(2 ** tries) | |
continue | |
# we've received a 500 or 502, an unconditional retry | |
if r.status in {500, 502}: | |
await asyncio.sleep(1 + tries * 3) | |
continue | |
if r.status == 401: | |
# api key either got revoked or it was never valid | |
if not data: | |
raise errors.HTTPException(r, data) | |
if 'Access is denied. Retrying will not help. Please verify your <pre>key=</pre>' in data: | |
# time to fetch a new key | |
self.api_key = kwargs['key'] = await self.get_api_key() | |
continue | |
# retry with our new key | |
# the usual error cases | |
if r.status == 403: | |
raise errors.Forbidden(r, data) | |
if r.status == 404: | |
raise errors.NotFound(r, data) | |
else: | |
raise errors.HTTPException(r, data) | |
# we've run out of retries, raise | |
raise errors.HTTPException(r, data) | |
async def send_image(self, user_id64: int, image: 'Image') -> None: | |
referer = {"Referer": f'{URL.COMMUNITY}/chat'} | |
params = {"l": 'english'} | |
payload = { | |
"sessionid": self.session_id, | |
"l": 'english', | |
"file_size": len(image), | |
"file_name": image.name, | |
"file_sha": image.hash, | |
"file_image_width": image.width, | |
"file_image_height": image.height, | |
"file_type": f'image/{image.type}', | |
} | |
resp = await self.request('POST', CRoute('/chat/beginfileupload'), headers=referer, data=payload, params=params) | |
result = resp['result'] | |
url = f'{"https" if result["use_https"] else "http"}://{result["url_host"]}{result["url_path"]}' | |
headers = {header['name'].lower(): header['value'] for header in result['request_headers']} | |
file = { | |
"body": image.read() | |
} | |
await self.request('PUT', url=url, headers=headers, data=file) | |
del payload['file_size'] | |
payload.update({ | |
'success': 1, | |
'ugcid': result['ugcid'], | |
'timestamp': resp['timestamp'], | |
'hmac': resp['hmac'], | |
'friend_steamid': user_id64, | |
'spoiler': int(image.spoiler) | |
}) | |
print(await self.request('POST', CRoute('/chat/commitfileupload'), data=payload, headers=referer)) | |
class Image: | |
__slots__ = ('fp', 'spoiler', 'name', 'width', 'height', 'type', 'hash') | |
def __init__(self, fp: Union[io.IOBase, aiohttp.StreamReader, str], *, spoiler: bool = False): | |
self.fp = fp | |
self.spoiler = spoiler | |
if isinstance(fp, io.IOBase): | |
if not (fp.seekable() and fp.readable()): | |
raise ValueError(f'file buffer {fp!r} must be seekable and readable') | |
self.fp = fp | |
elif isinstance(fp, aiohttp.StreamReader): | |
exc = fp.exception() | |
if exc is not None: | |
raise ValueError('aiohttp.StreamReader cannot be read due to an exception') from exc | |
self.fp = fp | |
else: | |
self.fp = open(fp, 'rb') | |
if len(self) > 10485760: | |
raise ValueError('file is too large to upload') | |
# from https://stackoverflow.com/questions/8032642 | |
head = self.fp.read(24) | |
if len(head) != 24: | |
raise ValueError('opened file has no headers') | |
self.type = imghdr.what(None, head) | |
if self.type == 'png': | |
check = struct.unpack('>i', head[4:8])[0] | |
if check != 0x0d0a1a0a: | |
raise ValueError("opened file's headers do not match a standard PNG's headers") | |
width, height = struct.unpack('>ii', head[16:24]) | |
elif self.type == 'gif': | |
width, height = struct.unpack('<HH', head[6:10]) | |
elif self.type == 'jpeg': | |
try: | |
self.fp.seek(0) # read 0xff next | |
size = 2 | |
ftype = 0 | |
while not 0xc0 <= ftype <= 0xcf or ftype in (0xc4, 0xc8, 0xcc): | |
self.fp.seek(size, 1) | |
byte = self.fp.read(1) | |
while ord(byte) == 0xff: | |
byte = self.fp.read(1) | |
ftype = ord(byte) | |
size = struct.unpack('>H', self.fp.read(2))[0] - 2 | |
# we are at a SOFn block | |
self.fp.seek(1, 1) # skip `precision' byte. | |
height, width = struct.unpack('>HH', self.fp.read(4)) | |
except Exception as exc: | |
raise ValueError from exc | |
else: | |
raise TypeError('unsupported file type passed') | |
self.width = width | |
self.height = height | |
self.hash = hashlib.sha1(self.read()).hexdigest() | |
self.name = f'{int(time())}_image.{self.type}' | |
def __len__(self): | |
self.fp.seek(0) | |
size = len(bytes(self.fp.read())) | |
self.fp.seek(0) | |
return size | |
def read(self) -> bytes: | |
self.fp.seek(0) | |
read = self.fp.read() | |
self.fp.seek(0) | |
return read |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment