Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

saveData() and other modificative functions not supported? #18

Open
RokeJulianLockhart opened this issue Jun 8, 2023 · 1 comment
Open

Comments

@RokeJulianLockhart
Copy link

RokeJulianLockhart commented Jun 8, 2023

GravatarXMLRPC(gravatar_username, password=gravatar_password).saveData('/picture.png')

doesn't work:

>>> from libgravatar import GravatarXMLRPC
>>> GravatarXMLRPC(gravatar_username, password=gravatar_password).saveData('/picture.png')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'GravatarXMLRPC' object has no attribute 'saveData'
>>>

https://libgravatar.readthedocs.io/en/latest/ doesn't mention any of them

get_image(size=80, default='', force_default=False, rating='', filetype_extension=False, use_ssl=True)

        Returns an URL to the user profile image.

        >>> g = Gravatar('[email protected]')
        >>> g.get_image()
        'https://www.gravatar.com/avatar/0bc83cb571cd1c50ba6f3e8a78ef1346'

        With size you can request a specific image size, by default, images are presented at 80px by 80px. You may request image anywhere from 1px up to 2048px.

        The default parameter is used to supply a default image when an email address has no match Gravatar image. default can be an URL or one of the built in options 404, mm, mp, identicon, monsterid, wavatar, robohash, retro or blank.

        force_default force the default image to always load.

        rating can be used to request images by audience type. Possible values are g, pg, r or x. By default, only g rated images are displayed.

        filetype_extension add an optional .jpg extension.

        use_ssl can be used to request images via SSL.

        See more details on [Gravatar Image Requests](http://en.gravatar.com/site/implement/images/).

    get_profile(data_format='', use_ssl=True)

        Returns an URL to the profile information associated with the Gravatar account.

        >>> g = Gravatar('[email protected]')
        >>> g.get_profile()
        'https://www.gravatar.com/0bc83cb571cd1c50ba6f3e8a78ef1346'

        See more details on [Gravatar Profile Requests](http://en.gravatar.com/site/implement/profiles/).

class libgravatar.GravatarXMLRPC(email, apikey='', password='')

    This class encapsulates all the authenticated methods from the XML-RPC API.

    API details: http://en.gravatar.com/site/implement/xmlrpc

    addresses()

        Gets a list of addresses for this account.

    exists(hashes)

        Checks whether a hash has a gravatar.

    test()

        Test the API.

    userimages()

        Returns a dict of userimages for this account.

libgravatar.default_url_is_valid(url)

    Gravatar conditions for valid default URLs.

    >>> default_url_is_valid('http://example.com/images/avatar.jpg')
    True

libgravatar.md5_hash(string)

    Returns a md5 hash from a string.

    >>> md5_hash('[email protected]')
    '0bc83cb571cd1c50ba6f3e8a78ef1346'

libgravatar.sanitize_email(email)

    Returns an e-mail address in lower-case and strip leading and trailing whitespaces.

    >>> sanitize_email(' [email protected] ')
    '[email protected]'

and $HOME/.local/lib/python3.10/site-packages/libgravatar/__init__.py doesn't appear to include any of them:

    def __init__(self, email):
        self.email = sanitize_email(email)
        self.email_hash = md5_hash(self.email)

    def get_image(
        self,
        size=DEFAULT_IMAGE_SIZE,
        default="",
        force_default=False,
        rating="",
        filetype_extension=False,
        use_ssl=True,
    ):
        """
        Returns an URL to the user profile image.

        >>> g = Gravatar('[email protected]')
        >>> g.get_image()
        'https://www.gravatar.com/avatar/0bc83cb571cd1c50ba6f3e8a78ef1346'

        With *size* you can request a specific image size, by default, images are presented at 80px by 80px.
        You may request image anywhere from 1px up to 2048px.

        The *default* parameter is used to supply a default image when an email address has no match Gravatar image.
        *default* can be an URL or one of the built in options *404*, *mm*, *mp*, *identicon*, *monsterid*, *wavatar*, *robohash*, *retro* or *blank*.

        *force_default* force the default image to always load.

        *rating* can be used to request images by audience type. Possible values are *g*, *pg*, *r* or *x*.
        By default, only *g* rated images are displayed.

        *filetype_extension* add an optional `.jpg` extension.

        *use_ssl* can be used to request images via SSL.

        See more details on `Gravatar Image Requests <http://en.gravatar.com/site/implement/images/>`_.

        """
        base_url = "{protocol}://www.gravatar.com/avatar/" "{hash}{extension}{params}"

        params_dict = {
            "size": size,
            "default": default,
            "forcedefault": force_default,
            "rating": rating,
        }

        if params_dict["size"] == self.DEFAULT_IMAGE_SIZE:
            del params_dict["size"]
        else:
            if not (0 < params_dict["size"] < 2048):
                raise ValueError("Invalid image size.")
        if params_dict["default"] == "":
            del params_dict["default"]
        else:
            if not params_dict["default"] in self.DEFAULT_IMAGE:
                if not default_url_is_valid(params_dict["default"]):
                    raise ValueError("Your URL for the default image is not valid.")
        if params_dict["forcedefault"]:
            params_dict["forcedefault"] = "y"
        else:
            del params_dict["forcedefault"]
        if params_dict["rating"] == "":
            del params_dict["rating"]
        else:
            if not params_dict["rating"] in self.RATINGS:
                raise ValueError("Invalid rating value.")

        params = urlencode(params_dict)

        protocol = "https"
        if not use_ssl:
            protocol = "http"

        extension = ".jpg" if filetype_extension else ""
        params = "?%s" % params if params else ""
        data = {
            "protocol": protocol,
            "hash": self.email_hash,
            "extension": extension,
            "params": params,
        }
        return base_url.format(**data)

    def get_profile(self, data_format="", use_ssl=True):
        """
        Returns an URL to the profile information associated with the Gravatar account.

        >>> g = Gravatar('[email protected]')
        >>> g.get_profile()
        'https://www.gravatar.com/0bc83cb571cd1c50ba6f3e8a78ef1346'

        See more details on `Gravatar Profile Requests <http://en.gravatar.com/site/implement/profiles/>`_.

        """
        protocol = "https"
        if not use_ssl:
            protocol = "http"

        base_url = "{protocol}://www.gravatar.com/{hash}{data_format}"

        if data_format and data_format in self.PROFILE_FORMATS:
            data_format = ".%s" % data_format

        data = {
            "protocol": protocol,
            "hash": self.email_hash,
            "data_format": data_format,
        }
        return base_url.format(**data)


class GravatarXMLRPC(object):
    """
    This class encapsulates all the authenticated methods from the XML-RPC API.

    API details: http://en.gravatar.com/site/implement/xmlrpc
    """

    API_URI = "https://secure.gravatar.com/xmlrpc?user={0}"

    def __init__(self, email, apikey="", password=""):
        self.apikey = apikey
        self.password = password
        self.email = sanitize_email(email)
        self.email_hash = md5_hash(self.email)
        self._server = ServerProxy(self.API_URI.format(self.email_hash))

    def exists(self, hashes):
        """Checks whether a hash has a gravatar."""
        response = self._call("exists", params={"hashes": hashes})
        results = {}
        for key, value in response.items():
            results[key] = True if value else False
        return results

    def addresses(self):
        """Gets a list of addresses for this account."""
        return self._call("addresses")

    def userimages(self):
        """Returns a dict of userimages for this account."""
        return self._call("userimages")

    def test(self):
        """Test the API."""
        return self._call("test")

    def _call(self, method, params=None):
        """Call a method from the API, gets 'grav.' prepended to it."""

        if params is None:
            params = {}

        args = {
            "apikey": self.apikey,
            "password": self.password,
        }
        args.update(params)

        try:
            return getattr(self._server, "grav." + method, None)(args)
        except Fault as error:
            error_msg = "Server error: {1} (error code: {0})"
            print(error_msg.format(error.faultCode, error.faultString))


def sanitize_email(email):
    """
    Returns an e-mail address in lower-case and strip leading and trailing
    whitespaces.

    >>> sanitize_email(' [email protected] ')
    '[email protected]'

    """
    return email.lower().strip()


def md5_hash(string):
    """
    Returns a md5 hash from a string.

    >>> md5_hash('[email protected]')
    '0bc83cb571cd1c50ba6f3e8a78ef1346'

    """
    try:
        # On security-restricted systems, indicate we are fingerprinting only:
        return md5(string.encode("utf-8"), usedforsecurity=False).hexdigest()
    except TypeError:
        # On systems without usedforsecurity:
        return md5(string.encode("utf-8")).hexdigest()


def default_url_is_valid(url):
    """
    Gravatar conditions for valid default URLs.

    >>> default_url_is_valid('http://example.com/images/avatar.jpg')
    True

    """
    result = urlparse(url)

    if result.scheme == "http" or result.scheme == "https":
        path = result.path.lower()
        if (
            path.endswith(".jpg")
            or path.endswith(".jpeg")
            or path.endswith(".gif")
            or path.endswith(".png")
        ):
            if not result.query:
                return True
    return False

despite https://en.gravatar.com/site/implement/xmlrpc/ clearly mentioning them:

Methods

grav.exists - check whether a hash has a gravatar 
	@param  (array)$args['hashes'] an array of hashes to check 
	@param	(string)$args['password'] for authentication 
	@return array ( 
		hash => (bool)exists,
	)

grav.addresses - get a list of addresses for this account 
	@param  (string)$args['password'] for authentication 
	@return array ( 
		address => array (
			rating        => (int)rating, 
			userimage     => (int)userimage, 
			userimage_url => (int)userimage_url
		) 
	)

grav.userimages - return an array of userimages for this account 
	@param  (string)$args['password'] for authentication 
	@return array (
		userimage => array( 
			(int)rating, // 0:g, 1:pg, 2:r, 3:x
			(string)url,
		)
	) 

grav.saveData - Save binary image data as a userimage for this account 
	@param  (string)$args['data'] a base64_encode()d image
	@param  (int)$args['rating'] 0:g, 1:pg, 2:r, 3:x
	@param  (string)$args['password'] for authentication 
	@return (bool)false on failure, (string)userimage on success 

grav.saveUrl - Read an image via its URL and save that as a userimage for this account 
	@param  (string)$args['url'] a full url to an image 
	@param  (int)$args['rating'] 0:g, 1:pg, 2:r, 3:x
	@param  (string)$args['password'] for authentication 
	@return (bool)false on failure, (string)userimage on success 

grav.useUserimage - use a userimage as a gravatar for one of more addresses on this account 
	@param  (string)$args['userimage'] The userimage you wish to use 
	@param  (array)$args['addresses'] A list of the email addresses you wish to use this userimage for 
	@param  (string)$args['password'] for authentication 
	@return array(
		address => (bool)status
	)

grav.removeImage - remove the userimage associated with one or more email addresses 
	@param  (array)$args['addresses'] A list of the email addresses you wish to use this userimage for 
	@param  (string)$args['password'] for authentication 
	@return array(
		address => (bool)status
	)

grav.deleteUserimage - remove a userimage from the account and any email addresses with which it is associated 
	@param  (string)$args['userimage'] The userimage you wish to remove from the account 
	@param  (string)$args['password'] for authentication 
	@return (bool)status

grav.test - a test function
	@param  (string)$args['password'] for authentication 
	@return (mixed)$args
@pabluk
Copy link
Owner

pabluk commented Jun 8, 2023

Thanks @RokeJulianLockhart for reporting this issue. It seems that these are new Gravatar methods that need to be implemented.
I'll try to add them in my free time but if you or anyone want to open a pull request that will more than welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants