2026-02-01 09:31:38 +01:00

294 lines
11 KiB
Python

"""Provides the User class."""
from __future__ import annotations
from typing import TYPE_CHECKING, Iterator
from warnings import warn
from prawcore import Conflict
from ..const import API_PATH
from ..exceptions import ReadOnlyException
from ..models import Preferences
from ..util import _deprecate_args
from ..util.cache import cachedproperty
from .base import PRAWBase
from .listing.generator import ListingGenerator
from .reddit.redditor import Redditor
from .reddit.subreddit import Subreddit
if TYPE_CHECKING: # pragma: no cover
import praw.models
class User(PRAWBase):
"""The :class:`.User` class provides methods for the currently authenticated user."""
@cachedproperty
def preferences(self) -> praw.models.Preferences:
"""Get an instance of :class:`.Preferences`.
The preferences can be accessed as a ``dict`` like so:
.. code-block:: python
preferences = reddit.user.preferences()
print(preferences["show_link_flair"])
Preferences can be updated via:
.. code-block:: python
reddit.user.preferences.update(show_link_flair=True)
The :meth:`.Preferences.update` method returns the new state of the preferences
as a ``dict``, which can be used to check whether a change went through. Changes
with invalid types or parameter names fail silently.
.. code-block:: python
original_preferences = reddit.user.preferences()
new_preferences = reddit.user.preferences.update(invalid_param=123)
print(original_preferences == new_preferences) # True, no change
"""
return Preferences(self._reddit)
def __init__(self, reddit: praw.Reddit):
"""Initialize an :class:`.User` instance.
This class is intended to be interfaced with through ``reddit.user``.
"""
super().__init__(reddit, _data=None)
def blocked(self) -> list[praw.models.Redditor]:
r"""Return a :class:`.RedditorList` of blocked :class:`.Redditor`\ s."""
return self._reddit.get(API_PATH["blocked"])
def contributor_subreddits(
self, **generator_kwargs: str | int | dict[str, str]
) -> Iterator[praw.models.Subreddit]:
r"""Return a :class:`.ListingGenerator` of contributor :class:`.Subreddit`\ s.
These are subreddits in which the user is an approved user.
Additional keyword arguments are passed in the initialization of
:class:`.ListingGenerator`.
To print a list of the subreddits that you are an approved user in, try:
.. code-block:: python
for subreddit in reddit.user.contributor_subreddits(limit=None):
print(str(subreddit))
"""
return ListingGenerator(
self._reddit, API_PATH["my_contributor"], **generator_kwargs
)
@_deprecate_args("user")
def friends(
self, *, user: str | praw.models.Redditor | None = None
) -> list[praw.models.Redditor] | praw.models.Redditor:
r"""Return a :class:`.RedditorList` of friends or a :class:`.Redditor` in the friends list.
:param user: Checks to see if you are friends with the redditor. Either an
instance of :class:`.Redditor` or a string can be given.
:returns: A list of :class:`.Redditor`\ s, or a single :class:`.Redditor` if
``user`` is specified. The :class:`.Redditor` instance(s) returned also has
friend attributes.
:raises: An instance of :class:`.RedditAPIException` if you are not friends with
the specified :class:`.Redditor`.
"""
endpoint = (
API_PATH["friends"]
if user is None
else API_PATH["friend_v1"].format(user=str(user))
)
return self._reddit.get(endpoint)
def karma(self) -> dict[praw.models.Subreddit, dict[str, int]]:
r"""Return a dictionary mapping :class:`.Subreddit`\ s to their karma.
The returned dict contains subreddits as keys. Each subreddit key contains a
sub-dict that have keys for ``comment_karma`` and ``link_karma``. The dict is
sorted in descending karma order.
.. note::
Each key of the main dict is an instance of :class:`.Subreddit`. It is
recommended to iterate over the dict in order to retrieve the values,
preferably through :py:meth:`dict.items`.
"""
karma_map = {}
for row in self._reddit.get(API_PATH["karma"])["data"]:
subreddit = Subreddit(self._reddit, row["sr"])
del row["sr"]
karma_map[subreddit] = row
return karma_map
@_deprecate_args("use_cache")
def me(self, *, use_cache: bool = True) -> praw.models.Redditor | None:
"""Return a :class:`.Redditor` instance for the authenticated user.
:param use_cache: When ``True``, and if this function has been previously
called, returned the cached version (default: ``True``).
.. note::
If you change the :class:`.Reddit` instance's authorization, you might want
to refresh the cached value. Prefer using separate :class:`.Reddit`
instances, however, for distinct authorizations.
.. deprecated:: 7.2
In :attr:`.read_only` mode this method returns ``None``. In PRAW 8 this
method will raise :class:`.ReadOnlyException` when called in
:attr:`.read_only` mode. To operate in PRAW 8 mode, set the config variable
``praw8_raise_exception_on_me`` to ``True``.
"""
if self._reddit.read_only:
if not self._reddit.config.custom.get("praw8_raise_exception_on_me"):
warn(
"The 'None' return value is deprecated, and will raise a"
" ReadOnlyException beginning with PRAW 8. See documentation for"
" forward compatibility options.",
category=DeprecationWarning,
stacklevel=2,
)
return None
msg = "`user.me()` does not work in read_only mode"
raise ReadOnlyException(msg)
if "_me" not in self.__dict__ or not use_cache:
user_data = self._reddit.get(API_PATH["me"])
self._me = Redditor(self._reddit, _data=user_data)
return self._me
def moderator_subreddits(
self, **generator_kwargs: str | int | dict[str, str]
) -> Iterator[praw.models.Subreddit]:
"""Return a :class:`.ListingGenerator` subreddits that the user moderates.
Additional keyword arguments are passed in the initialization of
:class:`.ListingGenerator`.
To print a list of the names of the subreddits you moderate, try:
.. code-block:: python
for subreddit in reddit.user.moderator_subreddits(limit=None):
print(str(subreddit))
.. seealso::
:meth:`.Redditor.moderated`
"""
return ListingGenerator(
self._reddit, API_PATH["my_moderator"], **generator_kwargs
)
def multireddits(self) -> list[praw.models.Multireddit]:
r"""Return a list of :class:`.Multireddit`\ s belonging to the user."""
return self._reddit.get(API_PATH["my_multireddits"])
def pin(
self, submission: praw.models.Submission, *, num: int = None, state: bool = True
) -> praw.models.Submission:
"""Set the pin state of a submission on the authenticated user's profile.
:param submission: An instance of :class:`.Submission` that will be
pinned/unpinned.
:param num: If specified, the slot in which the submission will be pinned into.
If there is a submission already in the specified slot, it will be replaced.
If ``None`` or there is not a submission in the specified slot, the first
available slot will be used (default: ``None``). If all slots are used the
following will occur:
- Old Reddit:
1. The submission in the last slot will be unpinned.
2. The remaining pinned submissions will be shifted down a slot.
3. The new submission will be pinned in the first slot.
- New Reddit:
1. The submission in the first slot will be unpinned.
2. The remaining pinned submissions will be shifted up a slot.
3. The new submission will be pinned in the last slot.
.. note::
At the time of writing (10/22/2021), there are 4 pin slots available and
pins are in reverse order on old Reddit. If ``num`` is an invalid value,
Reddit will ignore it and the same behavior will occur as if ``num`` is
``None``.
:param state: ``True`` pins the submission, ``False`` unpins (default:
``True``).
:returns: The pinned submission.
:raises: ``prawcore.BadRequest`` when pinning a removed or deleted submission.
:raises: ``prawcore.Forbidden`` when pinning a submission the authenticated user
is not the author of.
.. code-block:: python
submission = next(reddit.user.me().submissions.new())
reddit.user.pin(submission)
"""
data = {
"id": submission.fullname,
"num": num,
"state": state,
"to_profile": True,
}
try:
return self._reddit.post(API_PATH["sticky_submission"], data=data)
except Conflict:
pass
def subreddits(
self, **generator_kwargs: str | int | dict[str, str]
) -> Iterator[praw.models.Subreddit]:
r"""Return a :class:`.ListingGenerator` of :class:`.Subreddit`\ s the user is subscribed to.
Additional keyword arguments are passed in the initialization of
:class:`.ListingGenerator`.
To print a list of the subreddits that you are subscribed to, try:
.. code-block:: python
for subreddit in reddit.user.subreddits(limit=None):
print(str(subreddit))
"""
return ListingGenerator(
self._reddit, API_PATH["my_subreddits"], **generator_kwargs
)
def trusted(self) -> list[praw.models.Redditor]:
r"""Return a :class:`.RedditorList` of trusted :class:`.Redditor`\ s.
To display the usernames of your trusted users and the times at which you
decided to trust them, try:
.. code-block:: python
trusted_users = reddit.user.trusted()
for user in trusted_users:
print(f"User: {user.name}, time: {user.date}")
"""
return self._reddit.get(API_PATH["trusted"])