Skip to content

Instantly share code, notes, and snippets.

@Gobot1234
Last active June 7, 2020 21:52
Show Gist options
  • Save Gobot1234/5df6fb67a1f835cbbbd00a439a295966 to your computer and use it in GitHub Desktop.
Save Gobot1234/5df6fb67a1f835cbbbd00a439a295966 to your computer and use it in GitHub Desktop.
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