From 1d95d5cbbfad56353642f68f22737274cea4c482 Mon Sep 17 00:00:00 2001 From: Kile Date: Tue, 4 May 2021 22:36:30 +0100 Subject: [PATCH] Updating to v0.2.0 --- README.md | 8 +- examples/glitch_discord.markdown | 14 +- pyproject.toml | 2 +- pypxl/__init__.py | 4 +- pypxl/client.py | 301 +++++++++++++++++++++++++++---- pypxl/errors.py | 12 ++ pypxl/pxl_object.py | 38 ++++ 7 files changed, 328 insertions(+), 51 deletions(-) create mode 100644 pypxl/pxl_object.py diff --git a/README.md b/README.md index 85bf9e5..1e99566 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Just use `pip3 install pypxl` to install it # How to use it ```py -from pypxl import Pxlapi +from pypxl import PxlClient #Define yout details here -pxl = Pxlapi(token="Your pxlapi token", stop_on_error=False) +pxl = PxlClient(token="Your pxlapi token", stop_on_error=False) ``` Now you can use a all pxlapi features with just one line of code! @@ -17,9 +17,9 @@ glitch = await pxl.glitch(images=["https://cdn.discordapp.com/avatars/6061626611 ``` # Docs -I have not written any documentation. Generally every enpoint has a function with the same name and the same parameters as the enpoint along with the same default values. For an example in a discord bot, please click [here](https://github.com/Kile/pypxl/blob/main/examples/glitch_discord.markdown) +There is no website offering documentation, however if you hover over a function in your IDE it will give you some info about what it does, you can also just read the source code. For an example in a discord bot, please click [here](https://github.com/Kile/pypxl/blob/main/examples/glitch_discord.markdown) -I have implemented all functions this library offers in commands in my bot. You can find all those commands [here](https://github.com/Kile/Killua/blob/main/killua/cogs/pxlapi.py) +I have implemented most functions this library offers in commands in my bot. You can find those commands [here](https://github.com/Kile/Killua/blob/main/killua/cogs/pxlapi.py) For questions and suggestions, join my discord server or dm me (`Kile#0606`) diff --git a/examples/glitch_discord.markdown b/examples/glitch_discord.markdown index ac8f9ce..be958a7 100644 --- a/examples/glitch_discord.markdown +++ b/examples/glitch_discord.markdown @@ -1,22 +1,24 @@ This would be an example for a small bot command ```py -from pypxl import Pxlapi +from pypxl import PxlClient import discord from discord.ext import commands import io bot = commands.Bot(command_prefix= commands.when_mentioned_or('pxl.'), description="Testing pxlapi wrapper", case_insensitive=True) -pxl = Pxlapi(token="pxlapi-token", stop_on_error=False) +pxl = PxlClient(token="pxlapi-token", stop_on_error=False) @bot.command() async def glitch(ctx, url:str): data = await pxl.glitch(images=[url]) - if isinstance(data, str): - return await ctx.send(':x: '+data) - f = discord.File(io.BytesIO(data), filename="test.gif") - await ctx.send(file=f) + + if data.success: + f = discord.File(io.BytesIO(data), filename=f"glitch.{r.file_type}") + await ctx.send(file=f) + else: + await ctx.send(":x: " + data.error) bot.run("bot-token") diff --git a/pyproject.toml b/pyproject.toml index cb7e171..127a2db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "pypxl" -version = "0.1.8" +version = "0.2.0" description = "An Asynchronos API wrapper for https://pxlapi.dev" license = "MIT" readme = "README.md" diff --git a/pypxl/__init__.py b/pypxl/__init__.py index 43cd068..4668a81 100644 --- a/pypxl/__init__.py +++ b/pypxl/__init__.py @@ -9,6 +9,6 @@ __author__ = "Kile" __license__ = "MIT" __copyright__ = "Copyright 2021 Kile" -__version__ = "0.1.8" +__version__ = "0.2.0" -from .client import Pxlapi \ No newline at end of file +from .client import PxlClient \ No newline at end of file diff --git a/pypxl/client.py b/pypxl/client.py index 059068d..a412462 100644 --- a/pypxl/client.py +++ b/pypxl/client.py @@ -1,10 +1,19 @@ import aiohttp import json from .errors import PxlapiException, InvalidFlag, TooManyCharacters, InvalidSafety, InvalidEyes +from .pxl_object import PxlObject -class Pxlapi(): +class PxlClient(): + """ + The class which allows you to make requests to pxlapi + # Parameters: + `token (string)`: Your pxlapi token + # Optional parameters: + `session (aiohttp client session)`: The session to use for requests + `stop_on_error (boolean)`: If the code should raise an error if something went wrong or return the error text instead + """ def __init__(self, token:str, session=aiohttp.ClientSession(), stop_on_error:bool=False): self.token = token self.session = session @@ -16,47 +25,79 @@ def __init__(self, token:str, session=aiohttp.ClientSession(), stop_on_error:boo self.valid_eyes = ["big", "black", "bloodshot", "blue", "default", "googly", "green", "horror", "illuminati", "money", "pink", "red", "small", "spinner", "spongebob", "white", "yellow", "random"] async def get_img(self, enpoint:str, body:dict): - session = aiohttp.ClientSession() + """ + The function making the request which gets image bytes in return. Not meant to be used outside of this class + + # Parameters: + `endpoint (string)`: The endpoint to make the request to + `body (dictionary)`: The body of the request + + # Returns: + `PxlObject` + """ headers = { 'Content-Type': 'application/json', 'Authorization': f'Application {self.token}' } + try: + r = await self.session.post(f'https://api.pxlapi.dev/{enpoint}', headers=headers, json=body) + except Exception as e: + if self.stop_on_error: + raise e + return PxlObject(success=False, error=str(e)) + if r.status == 200: + image_bytes = await r.read() + return PxlObject(success=True, image_bytes=image_bytes, content_type=r.content_type) + else: + error = str(await r.text()) - async with session.post(f'https://api.pxlapi.dev/{enpoint}', headers=headers, json=body) as r: - if r.status == 200: - image_bytes = await r.read() - await session.close() - return image_bytes - else: - await session.close() - error = str(await r.text()) - - if self.stop_on_error: - raise PxlapiException(error) - return error + if self.stop_on_error: + raise PxlapiException(error) + return PxlObject(success=False, error=error) async def get_text(self, enpoint:str, body:dict): - session = aiohttp.ClientSession() + """ + The function making the request which gets text in return. Not meant to be used outside of this class + + # Parameters: + `endpoint (string)`: The endpoint to make the request to + `body (dictionary)`: The body of the request + + # Returns: + `PxlObject` + """ headers = { 'Content-Type': 'application/json', 'Authorization': f'Application {self.token}' } + try: + r = await self.session.post(f'https://api.pxlapi.dev/{enpoint}', headers=headers, json=body) + except Exception as e: + if self.stop_on_error: + raise e + return PxlObject(success=False, error=str(e)) + if r.status == 200: + data = await r.json() + return PxlObject(data=data, success=True) + else: + error = str(await r.text()) + if self.stop_on_error: + raise PxlapiException(error) - async with session.post(f'https://api.pxlapi.dev/{enpoint}', headers=headers, json=body) as r: - if r.status == 200: - data = await r.json() - await session.close() - return data - else: - await session.close() - error = str(await r.text()) - - if self.stop_on_error: - raise PxlapiException(error) - return error - + return PxlObject(error=error, success=False) async def emojaic(self, images:list, groupSize:int=12, scale:bool=False): + """ + Turns the provided images into images assebled by emojis + + # Parameters: + `images (list)`: The images to proccess + `groupSize (int)`: The group size + `scale (boolean)`: To have the returned image at the same scale or not + + # Returns: + `PxlObject` + """ body = { 'images': images, 'groupSize': groupSize, @@ -65,8 +106,18 @@ async def emojaic(self, images:list, groupSize:int=12, scale:bool=False): return await self.get_img('emojimosaic', body) - async def flag(self, flag:str, images:str, opacity:int=128): + """ + Turns the provided images into images with the flag specified + + # Parameters: + `images (list)`: The images to proccess + `flag (string)`: The name of the flag to use + `opacity (int)`: What opacity to overlay the flag with + + # Returns: + `PxlObject` + """ if not flag.lower() in self.flags: if self.stop_on_error: raise InvalidFlag(f'Flag {flag.lower()} not a valid flag') @@ -79,28 +130,109 @@ async def flag(self, flag:str, images:str, opacity:int=128): } return await self.get_img(f'flag/{flag.lower()}', body) + async def ganimal(self, images:list): + """ + Turns the provided images into images with animal faces + + # Parameters: + `images (list)`: The images to proccess + + # Returns: + `PxlObject` + """ + body = { + 'images': images + } + return await self.get_img('ganimal', body) + + async def flash(self, images:list): + """ + Turns the provided images into flash images - async def glitch(self, images:list, delay:int=100, count:int=10, amount:int=5, iterations:int=10): + # Parameters: + `images (list)`: The images to proccess + + # Returns: + `PxlObject` + """ + body = { + 'images': images + } + return await self.get_img('flash', body) + + async def glitch(self, images:list, delay:int=100, count:int=10, amount:int=5, iterations:int=10, gif:bool=None): + """ + Turns the provided images into images into glitch GIFs and/or images + + # Parameters: + `images (list)`: The images to proccess + `delay (int)`: How long to display each frame for (in ms) + `count (int)`: How many frames to generate + `amount (int)`: Byte chunk length + `iterations (int)`: How many byte chunks to modify, + `gif (boolean)`: Additional information for glitching static images into a GIF + + # Returns: + `PxlObject` + """ body = { 'images': images, 'delay': delay, 'count': count, 'amount': amount, - 'iterations': iterations + 'iterations': iterations, + 'gif': gif } return await self.get_img('glitch', body) - async def lego(self, images:list, scale:bool=False, groupSize:int=8): + """ + Turns the provided images into images into images made up of lego bricks + + # Parameters: + `images (list)`: The images to proccess + `scale (boolean)`: Whether to resize the resulting image to the original images dimensions + `groupSize (int)`: How big of a pixel square to group into one brick. Defaults to a 32x32 brick result + + # Returns: + `PxlObject` + """ body = { 'images': images, 'scale': scale, 'groupSize': groupSize } return await self.get_img('lego', body) + + async def jpeg(self, images:list, quality:int=1): + """ + Turns the provided images into lower quality + # Parameters: + `images (list)`: The images to proccess + `quality (int)`: What JPEG quality to encode the image as + + # Returns: + `PxlObject` + """ + body = { + 'images': images, + 'quality': quality + } + return await self.get_img('jpeg', body) async def snapchat(self, filter:str, images:list, filters:list=None): + """ + Turns the provided images into images with the snap filter provided if a face is detected + + # Parameters: + `images (list)`: The images to proccess + `filter (string)`: The filter to apply + `filters (list)`: What filters to limit "random" to (defaults to all available filters) + + # Returns: + `PxlObject` + """ if not filter.lower() in self.filters: if self.stop_on_error: raise InvalidFilter(f'Flag {filter.lower()} not a valid filter') @@ -114,6 +246,17 @@ async def snapchat(self, filter:str, images:list, filters:list=None): return await self.get_img(f'snapchat/{filter}', body) async def eyes(self, eyes:str, images:list, filters:list=None): + """ + Turns the provided images into images with a filter applied to the eyes of faces detected + + # Parameters: + `images (list)`: The images to proccess + `eyes (string)`: The filter to apply + `filters (list)`: What filters to limit "random" to (defaults to all available filters) + + # Returns: + `PxlObject` + """ if not eyes.lower() in self.valid_eyes: if self.stop_on_error: raise InvalidEyes(f'Flag {eyes.lower()} not a valid eye type') @@ -127,12 +270,30 @@ async def eyes(self, eyes:str, images:list, filters:list=None): return await self.get_img(f'eyes/{eyes}', body) async def thonkify(self, text:str): + """ + Turns the provided text into an image with that text made up of thonks + + # Parameters: + `text (string)`: The text to thonkify + + # Returns: + `PxlObject` + """ body = { 'text': text } return await self.get_img('thonkify', body) async def sonic(self, text:str): + """ + Turns the provided text into an image with sonic saying the provided text + + # Parameters: + `text (string)`: The text to let sonic say + + # Returns: + `PxlObject` + """ if len(text) > 1000: if self.stop_on_error: raise TooManyCharacters("Too many characters used for the sonic endpoint") @@ -144,14 +305,51 @@ async def sonic(self, text:str): } return await self.get_img('sonic', body) - async def jpeg(self, images:list, quality:int=1): + async def imagescript(self, version:str, code:str, inject=None, timeout:int=10000): + """ + Evaluates code + + # Parameters: + `version (string)`: The version of imagescript to use + `code (string)`: The code to evaluate + `inject (object)`: The data to inject as global variables + `timeout (int)`: Maximum run time in ms + + # Returns: + `PxlObject` + """ body = { - 'images': images, - 'quality': quality + 'code': code, + 'inject': inject, + 'timeout': timeout } - return await self.get_img('jpeg', body) + return await self.get_img(f'imagescript/{version}', body) + + async def imagescript_version(self): + """ + Gives you the available versions for imagescript + + # Parameters: + None + + # Returns: + `PxlObject` + """ + body = {} + return await self.get_text('imagescript/versions', body) async def image_search(self, query:str, safeSearch:str='strict', meta:bool=False): + """ + Looks for images with provided query + + # Parameters: + `query (string)`: What to search for + `safeSearch (string)`: What safe search setting to use + `meta (boolean)`: Whether to return meta data (page title and URL) + + # Returns: + `PxlObject` + """ if len(query) > 128: if self.stop_on_error: raise TooManyCharacters("Too many characters used for the image_search endpoint") @@ -170,7 +368,24 @@ async def image_search(self, query:str, safeSearch:str='strict', meta:bool=False } return await self.get_text('image_search', body) - async def screenshot(self, url:str, device:str=None, locale:str='en_US', blocklist:list=[], defaultBlocklist:bool=True, browser:str='chromium', theme:str='dark', timeout:int=30000): + async def screenshot(self, url:str, device:str=None, locale:str='en_US', blocklist:list=[], defaultBlocklist:bool=True, browser:str='chromium', theme:str='dark', timeout:int=30000, fullPage:bool=False): + """ + Screenshots the webpage provided + + # Parameters: + `url (string): The website to screenshot + `device (string)`: The device to emulate. See [list of available devices](https://github.com/microsoft/playwright/blob/17e953c2d8bd19ace20059ffaaa85f3f23cfb19d/src/server/deviceDescriptors.js#L21-L857). Defaults to a non-specific browser with a viewport of 1920x1080 pixels. + `locale (string)`: The locale to set the browser to + `blocklist (list)`: A list of domains to block + `defaultBlocklist (boolean)`: Whether to block a list of predefined, known-bad domains (e.g. NSFW content) + `browser (string)`: What browser engine to use for screenshotting + `theme (string)`: What theme to use + `timout (int)`: The max time to wait until the site has loaded (in ms) + `fullPage (boolean)`: Whether to capture the entire page + + # Returns: + `PxlObject` + """ body = { 'url': url, 'device': device, @@ -184,6 +399,16 @@ async def screenshot(self, url:str, device:str=None, locale:str='en_US', blockli return await self.get_img('screenshot', body) async def web_search(self, query:str, safeSearch:str='strict'): + """ + Searches for the querie provided + + # Parameters: + `url (string): The website to screenshot + `safeSearch (string)`: What safe search setting to use + + # Returns: + `PxlObject` + """ if len(query) > 128: if self.stop_on_error: raise TooManyCharacters("Too many characters used for the web_search endpoint") @@ -199,4 +424,4 @@ async def web_search(self, query:str, safeSearch:str='strict'): 'query': query, 'safeSearch': safeSearch } - return await self.get_text('web_search', body) + return await self.get_text('web_search', body) \ No newline at end of file diff --git a/pypxl/errors.py b/pypxl/errors.py index bb9be26..a3a546b 100644 --- a/pypxl/errors.py +++ b/pypxl/errors.py @@ -1,4 +1,7 @@ class PxlapiException(Exception): + """ + The base exception for anything related to pypxl + """ pass class InvalidFlag(PxlapiException): @@ -14,4 +17,13 @@ class TooManyCharacters(PxlapiException): pass class InvalidSafety(PxlapiException): + pass + +class PxlObjectError(PxlapiException): + """ + A class which all errors originating from using the PxlOnject come from + """ + pass + +class InvalidBytes(PxlObjectError): pass \ No newline at end of file diff --git a/pypxl/pxl_object.py b/pypxl/pxl_object.py new file mode 100644 index 0000000..a44f8ee --- /dev/null +++ b/pypxl/pxl_object.py @@ -0,0 +1,38 @@ +import io +from .errors import InvalidBytes + +class PxlObject(): + """ + An object you will get as a response to any request + + # Properties: + image_bytes (bytes): The image bytes of the response + data (string): The data of the response as a dictionary + success (boolean): If the request was successfull or not + error (string): The error message if the request was not successful + file_type (string): The file ending of the returned image(s) + content_type (string): The content type of the image(s) returned + + # Methods: + `convert_to_ioBytes()` + Converts the image bytes to ioBytes + """ + def __init__(self, image_bytes:bytes=None, data:dict=None, success:bool=True, error:str=None, content_type:str=None): + self.image_bytes = image_bytes + self.success = success + self.ok = self.success # Serves as alias + self.error = error + self.content_type = content_type + self.file_type = self.content_type.split('/')[1] if self.content_type else None + self.data = data + + def convert_to_ioBytes(self): + if not self.success: + raise InvalidBytes("Cannot convert a failed request to io bytes") + if not self.image_bytes: + raise InvalidBytes("This object contains no bytes") + + return io.BytesIO(self.image_bytes) + + + \ No newline at end of file