294 lines
11 KiB
Python
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"])
|