247 lines
8.3 KiB
Python
247 lines
8.3 KiB
Python
"""Provide the Emoji class."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from ...const import API_PATH
|
|
from ...exceptions import ClientException
|
|
from ...util import _deprecate_args
|
|
from .base import RedditBase
|
|
|
|
if TYPE_CHECKING: # pragma: no cover
|
|
import praw
|
|
|
|
|
|
class Emoji(RedditBase):
|
|
"""An individual :class:`.Emoji` object.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
====================== =================================================
|
|
Attribute Description
|
|
====================== =================================================
|
|
``mod_flair_only`` Whether the emoji is restricted for mod use only.
|
|
``name`` The name of the emoji.
|
|
``post_flair_allowed`` Whether the emoji may appear in post flair.
|
|
``url`` The URL of the emoji image.
|
|
``user_flair_allowed`` Whether the emoji may appear in user flair.
|
|
====================== =================================================
|
|
|
|
"""
|
|
|
|
STR_FIELD = "name"
|
|
|
|
def __eq__(self, other: str | Emoji) -> bool:
|
|
"""Return whether the other instance equals the current."""
|
|
if isinstance(other, str):
|
|
return other == str(self)
|
|
if isinstance(other, self.__class__):
|
|
return str(self) == str(other) and other.subreddit == self.subreddit
|
|
return super().__eq__(other)
|
|
|
|
def __hash__(self) -> int:
|
|
"""Return the hash of the current instance."""
|
|
return hash(self.__class__.__name__) ^ hash(str(self)) ^ hash(self.subreddit)
|
|
|
|
def __init__(
|
|
self,
|
|
reddit: praw.Reddit,
|
|
subreddit: praw.models.Subreddit,
|
|
name: str,
|
|
_data: dict[str, Any] | None = None,
|
|
):
|
|
"""Initialize an :class:`.Emoji` instance."""
|
|
self.name = name
|
|
self.subreddit = subreddit
|
|
super().__init__(reddit, _data=_data)
|
|
|
|
def _fetch(self):
|
|
for emoji in self.subreddit.emoji:
|
|
if emoji.name == self.name:
|
|
self.__dict__.update(emoji.__dict__)
|
|
super()._fetch()
|
|
return
|
|
msg = f"r/{self.subreddit} does not have the emoji {self.name}"
|
|
raise ClientException(msg)
|
|
|
|
def delete(self):
|
|
"""Delete an emoji from this subreddit by :class:`.Emoji`.
|
|
|
|
To delete ``"emoji"`` as an emoji on r/test try:
|
|
|
|
.. code-block:: python
|
|
|
|
reddit.subreddit("test").emoji["emoji"].delete()
|
|
|
|
"""
|
|
url = API_PATH["emoji_delete"].format(
|
|
emoji_name=self.name, subreddit=self.subreddit
|
|
)
|
|
self._reddit.delete(url)
|
|
|
|
@_deprecate_args("mod_flair_only", "post_flair_allowed", "user_flair_allowed")
|
|
def update(
|
|
self,
|
|
*,
|
|
mod_flair_only: bool | None = None,
|
|
post_flair_allowed: bool | None = None,
|
|
user_flair_allowed: bool | None = None,
|
|
):
|
|
"""Update the permissions of an emoji in this subreddit.
|
|
|
|
:param mod_flair_only: Indicate whether the emoji is restricted to mod use only.
|
|
Respects pre-existing settings if not provided.
|
|
:param post_flair_allowed: Indicate whether the emoji may appear in post flair.
|
|
Respects pre-existing settings if not provided.
|
|
:param user_flair_allowed: Indicate whether the emoji may appear in user flair.
|
|
Respects pre-existing settings if not provided.
|
|
|
|
.. note::
|
|
|
|
In order to retain pre-existing values for those that are not explicitly
|
|
passed, a network request is issued. To avoid that network request,
|
|
explicitly provide all values.
|
|
|
|
To restrict the emoji ``"emoji"`` in r/test to mod use only, try:
|
|
|
|
.. code-block:: python
|
|
|
|
reddit.subreddit("test").emoji["emoji"].update(mod_flair_only=True)
|
|
|
|
"""
|
|
locals_reference = locals()
|
|
mapping = {
|
|
attribute: locals_reference[attribute]
|
|
for attribute in (
|
|
"mod_flair_only",
|
|
"post_flair_allowed",
|
|
"user_flair_allowed",
|
|
)
|
|
}
|
|
if all(value is None for value in mapping.values()):
|
|
msg = "At least one attribute must be provided"
|
|
raise TypeError(msg)
|
|
|
|
data = {"name": self.name}
|
|
for attribute, value in mapping.items():
|
|
if value is None:
|
|
value = getattr(self, attribute) # noqa: PLW2901
|
|
data[attribute] = value
|
|
url = API_PATH["emoji_update"].format(subreddit=self.subreddit)
|
|
self._reddit.post(url, data=data)
|
|
for attribute, value in data.items():
|
|
setattr(self, attribute, value)
|
|
|
|
|
|
class SubredditEmoji:
|
|
"""Provides a set of functions to a :class:`.Subreddit` for emoji."""
|
|
|
|
def __getitem__(self, name: str) -> Emoji:
|
|
"""Lazily return the :class:`.Emoji` for the subreddit named ``name``.
|
|
|
|
:param name: The name of the emoji.
|
|
|
|
This method is to be used to fetch a specific emoji url, like so:
|
|
|
|
.. code-block:: python
|
|
|
|
emoji = reddit.subreddit("test").emoji["emoji"]
|
|
print(emoji)
|
|
|
|
"""
|
|
return Emoji(self._reddit, self.subreddit, name)
|
|
|
|
def __init__(self, subreddit: praw.models.Subreddit):
|
|
"""Initialize a :class:`.SubredditEmoji` instance.
|
|
|
|
:param subreddit: The subreddit whose emoji are affected.
|
|
|
|
"""
|
|
self.subreddit = subreddit
|
|
self._reddit = subreddit._reddit
|
|
|
|
def __iter__(self) -> list[Emoji]:
|
|
"""Return a list of :class:`.Emoji` for the subreddit.
|
|
|
|
This method is to be used to discover all emoji for a subreddit:
|
|
|
|
.. code-block:: python
|
|
|
|
for emoji in reddit.subreddit("test").emoji:
|
|
print(emoji)
|
|
|
|
"""
|
|
response = self._reddit.get(
|
|
API_PATH["emoji_list"].format(subreddit=self.subreddit)
|
|
)
|
|
subreddit_keys = [
|
|
key
|
|
for key in response
|
|
if key.startswith(self._reddit.config.kinds["subreddit"])
|
|
]
|
|
assert len(subreddit_keys) == 1
|
|
for emoji_name, emoji_data in response[subreddit_keys[0]].items():
|
|
yield Emoji(self._reddit, self.subreddit, emoji_name, _data=emoji_data)
|
|
|
|
def add(
|
|
self,
|
|
*,
|
|
image_path: str,
|
|
mod_flair_only: bool | None = None,
|
|
name: str,
|
|
post_flair_allowed: bool | None = None,
|
|
user_flair_allowed: bool | None = None,
|
|
) -> Emoji:
|
|
"""Add an emoji to this subreddit.
|
|
|
|
:param image_path: A path to a jpeg or png image.
|
|
:param mod_flair_only: When provided, indicate whether the emoji is restricted
|
|
to mod use only (default: ``None``).
|
|
:param name: The name of the emoji.
|
|
:param post_flair_allowed: When provided, indicate whether the emoji may appear
|
|
in post flair (default: ``None``).
|
|
:param user_flair_allowed: When provided, indicate whether the emoji may appear
|
|
in user flair (default: ``None``).
|
|
|
|
:returns: The :class:`.Emoji` added.
|
|
|
|
To add ``"emoji"`` to r/test try:
|
|
|
|
.. code-block:: python
|
|
|
|
reddit.subreddit("test").emoji.add(name="emoji", image_path="emoji.png")
|
|
|
|
"""
|
|
file = Path(image_path)
|
|
data = {
|
|
"filepath": file.name,
|
|
"mimetype": "image/jpeg",
|
|
}
|
|
if image_path.lower().endswith(".png"):
|
|
data["mimetype"] = "image/png"
|
|
url = API_PATH["emoji_lease"].format(subreddit=self.subreddit)
|
|
|
|
# until we learn otherwise, assume this request always succeeds
|
|
upload_lease = self._reddit.post(url, data=data)["s3UploadLease"]
|
|
upload_data = {item["name"]: item["value"] for item in upload_lease["fields"]}
|
|
upload_url = f"https:{upload_lease['action']}"
|
|
|
|
with file.open("rb") as image:
|
|
response = self._reddit._core._requestor._http.post(
|
|
upload_url, data=upload_data, files={"file": image}
|
|
)
|
|
response.raise_for_status()
|
|
|
|
data = {
|
|
"mod_flair_only": mod_flair_only,
|
|
"name": name,
|
|
"post_flair_allowed": post_flair_allowed,
|
|
"s3_key": upload_data["key"],
|
|
"user_flair_allowed": user_flair_allowed,
|
|
}
|
|
url = API_PATH["emoji_upload"].format(subreddit=self.subreddit)
|
|
self._reddit.post(url, data=data)
|
|
return Emoji(self._reddit, self.subreddit, name)
|