Initial commit

This commit is contained in:
2026-02-01 09:31:38 +01:00
commit e02db93960
4396 changed files with 1511612 additions and 0 deletions

View File

@@ -0,0 +1,322 @@
"""Package providing reddit class mixins."""
from __future__ import annotations
from json import dumps
from typing import TYPE_CHECKING, Optional
from ....const import API_PATH
from ....util import _deprecate_args
from .editable import EditableMixin
from .fullname import FullnameMixin
from .gildable import GildableMixin
from .inboxable import InboxableMixin
from .inboxtoggleable import InboxToggleableMixin
from .messageable import MessageableMixin
from .modnote import ModNoteMixin
from .replyable import ReplyableMixin
from .reportable import ReportableMixin
from .savable import SavableMixin
from .votable import VotableMixin
if TYPE_CHECKING: # pragma: no cover
import praw.models
class ThingModerationMixin(ModNoteMixin):
r"""Provides moderation methods for :class:`.Comment`\ s and :class:`.Submission`\ s."""
REMOVAL_MESSAGE_API = None
def _add_removal_reason(self, *, mod_note: str = "", reason_id: str | None = None):
"""Add a removal reason for a :class:`.Comment` or :class:`.Submission`.
:param mod_note: A message for the other moderators.
:param reason_id: The removal reason ID.
It is necessary to first call :meth:`.remove` on the :class:`.Comment` or
:class:`.Submission`.
If ``reason_id`` is not specified, ``mod_note`` cannot be blank.
"""
if not reason_id and not mod_note:
msg = "mod_note cannot be blank if reason_id is not specified"
raise ValueError(msg)
# Only the first element of the item_id list is used.
data = {
"item_ids": [self.thing.fullname],
"mod_note": mod_note,
"reason_id": reason_id,
}
self.thing._reddit.post(API_PATH["removal_reasons"], data={"json": dumps(data)})
def approve(self):
"""Approve a :class:`.Comment` or :class:`.Submission`.
Approving a comment or submission reverts a removal, resets the report counter,
adds a green check mark indicator (only visible to other moderators) on the
website view, and sets the ``approved_by`` attribute to the authenticated user.
Example usage:
.. code-block:: python
# approve a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.approve()
# approve a submission:
submission = reddit.submission("5or86n")
submission.mod.approve()
"""
self.thing._reddit.post(API_PATH["approve"], data={"id": self.thing.fullname})
@_deprecate_args("how", "sticky")
def distinguish(self, *, how: str = "yes", sticky: bool = False):
"""Distinguish a :class:`.Comment` or :class:`.Submission`.
:param how: One of ``"yes"``, ``"no"``, ``"admin"``, or ``"special"``. ``"yes"``
adds a moderator level distinguish. ``"no"`` removes any distinction.
``"admin"`` and ``"special"`` require special user privileges to use
(default ``"yes"``).
:param sticky: :class:`.Comment` is stickied if ``True``, placing it at the top
of the comment page regardless of score. If thing is not a top-level
comment, this parameter is silently ignored (default ``False``).
Example usage:
.. code-block:: python
# distinguish and sticky a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.distinguish(sticky=True)
# undistinguish a submission:
submission = reddit.submission("5or86n")
submission.mod.distinguish(how="no")
.. seealso::
:meth:`.undistinguish`
"""
data = {"how": how, "id": self.thing.fullname}
if sticky and getattr(self.thing, "is_root", False):
data["sticky"] = True
self.thing._reddit.post(API_PATH["distinguish"], data=data)
def ignore_reports(self):
"""Ignore future reports on a :class:`.Comment` or :class:`.Submission`.
Calling this method will prevent future reports on this :class:`.Comment` or
:class:`.Submission` from both triggering notifications and appearing in the
various moderation listings. The report count will still increment on the
:class:`.Comment` or :class:`.Submission`.
Example usage:
.. code-block:: python
# ignore future reports on a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.ignore_reports()
# ignore future reports on a submission:
submission = reddit.submission("5or86n")
submission.mod.ignore_reports()
.. seealso::
:meth:`.unignore_reports`
"""
self.thing._reddit.post(
API_PATH["ignore_reports"], data={"id": self.thing.fullname}
)
def lock(self):
"""Lock a :class:`.Comment` or :class:`.Submission`.
Example usage:
.. code-block:: python
# lock a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.lock()
# lock a submission:
submission = reddit.submission("5or86n")
submission.mod.lock()
.. seealso::
:meth:`.unlock`
"""
self.thing._reddit.post(API_PATH["lock"], data={"id": self.thing.fullname})
@_deprecate_args("spam", "mod_note", "reason_id")
def remove(
self, *, mod_note: str = "", spam: bool = False, reason_id: str | None = None
):
"""Remove a :class:`.Comment` or :class:`.Submission`.
:param mod_note: A message for the other moderators.
:param spam: When ``True``, use the removal to help train the
:class:`.Subreddit`'s spam filter (default: ``False``).
:param reason_id: The removal reason ID.
If either ``reason_id`` or ``mod_note`` are provided, a second API call is made
to add the removal reason.
Example usage:
.. code-block:: python
# remove a comment and mark as spam:
comment = reddit.comment("dkk4qjd")
comment.mod.remove(spam=True)
# remove a submission
submission = reddit.submission("5or86n")
submission.mod.remove()
# remove a submission with a removal reason
reason = reddit.subreddit.mod.removal_reasons["110ni21zo23ql"]
submission = reddit.submission("5or86n")
submission.mod.remove(reason_id=reason.id)
"""
data = {"id": self.thing.fullname, "spam": bool(spam)}
self.thing._reddit.post(API_PATH["remove"], data=data)
if any([reason_id, mod_note]):
self._add_removal_reason(mod_note=mod_note, reason_id=reason_id)
@_deprecate_args("message", "title", "type")
def send_removal_message(
self,
*,
message: str,
title: str = "ignored",
type: str = "public",
) -> praw.models.Comment | None:
"""Send a removal message for a :class:`.Comment` or :class:`.Submission`.
.. warning::
The object has to be removed before giving it a removal reason. Remove the
object with :meth:`.remove`. Trying to add a removal reason without removing
the object will result in :class:`.RedditAPIException` being thrown with an
``INVALID_ID`` error_type.
Reddit adds human-readable information about the object to the message.
:param type: One of ``"public"``, ``"public_as_subreddit"``, ``"private"``, or
``"private_exposed"``. ``"public"`` leaves a stickied comment on the post.
``"public_as_subreddit"`` leaves a stickied comment on the post with the
u/subreddit-ModTeam account. ``"private"`` sends a modmail message with
hidden username. ``"private_exposed"`` sends a modmail message without
hidden username (default: ``"public"``).
:param title: The short reason given in the message. Ignored if type is
``"public"`` or ``"public_as_subreddit"``.
:param message: The body of the message.
:returns: The new :class:`.Comment` if ``type`` is ``"public"`` or
``"public_as_subreddit"``.
"""
# The API endpoint used to send removal messages is different for posts and
# comments, so the derived classes specify which one.
if self.REMOVAL_MESSAGE_API is None:
msg = "ThingModerationMixin must be extended."
raise NotImplementedError(msg)
url = API_PATH[self.REMOVAL_MESSAGE_API]
# Only the first element of the item_id list is used.
data = {
"item_id": [self.thing.fullname],
"message": message,
"title": title,
"type": type,
}
return self.thing._reddit.post(url, data={"json": dumps(data)}) or None
def undistinguish(self):
"""Remove mod, admin, or special distinguishing from an object.
Also unstickies the object if applicable.
Example usage:
.. code-block:: python
# undistinguish a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.undistinguish()
# undistinguish a submission:
submission = reddit.submission("5or86n")
submission.mod.undistinguish()
.. seealso::
:meth:`.distinguish`
"""
self.distinguish(how="no")
def unignore_reports(self):
"""Resume receiving future reports on a :class:`.Comment` or :class:`.Submission`.
Future reports on this :class:`.Comment` or :class:`.Submission` will cause
notifications, and appear in the various moderation listings.
Example usage:
.. code-block:: python
# accept future reports on a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.unignore_reports()
# accept future reports on a submission:
submission = reddit.submission("5or86n")
submission.mod.unignore_reports()
.. seealso::
:meth:`.ignore_reports`
"""
self.thing._reddit.post(
API_PATH["unignore_reports"], data={"id": self.thing.fullname}
)
def unlock(self):
"""Unlock a :class:`.Comment` or :class:`.Submission`.
Example usage:
.. code-block:: python
# unlock a comment:
comment = reddit.comment("dkk4qjd")
comment.mod.unlock()
# unlock a submission:
submission = reddit.submission("5or86n")
submission.mod.unlock()
.. seealso::
:meth:`.lock`
"""
self.thing._reddit.post(API_PATH["unlock"], data={"id": self.thing.fullname})
class UserContentMixin(
EditableMixin,
GildableMixin,
InboxToggleableMixin,
ReplyableMixin,
ReportableMixin,
SavableMixin,
VotableMixin,
):
"""A convenience mixin that applies to both Comments and Submissions."""

View File

@@ -0,0 +1,67 @@
"""Provide the EditableMixin class."""
from __future__ import annotations
from typing import TYPE_CHECKING
from ....const import API_PATH
if TYPE_CHECKING: # pragma: no cover
import praw.models
class EditableMixin:
"""Interface for classes that can be edited and deleted."""
def delete(self):
"""Delete the object.
Example usage:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
comment.delete()
submission = reddit.submission("8dmv8z")
submission.delete()
"""
self._reddit.post(API_PATH["del"], data={"id": self.fullname})
def edit(self, body: str) -> praw.models.Comment | praw.models.Submission:
"""Replace the body of the object with ``body``.
:param body: The Markdown formatted content for the updated object.
:returns: The current instance after updating its attributes.
Example usage:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
# construct the text of an edited comment
# by appending to the old body:
edited_body = comment.body + "Edit: thanks for the gold!"
comment.edit(edited_body)
"""
data = {
"text": body,
"thing_id": self.fullname,
"validate_on_submit": self._reddit.validate_on_submit,
}
updated = self._reddit.post(API_PATH["edit"], data=data)[0]
for attribute in [
"_fetched",
"_reddit",
"_submission",
"replies",
"subreddit",
]:
if attribute in updated.__dict__:
delattr(updated, attribute)
self.__dict__.update(updated.__dict__)
return self

View File

@@ -0,0 +1,19 @@
"""Provide the FullnameMixin class."""
class FullnameMixin:
"""Interface for classes that have a fullname."""
_kind = None
@property
def fullname(self) -> str:
"""Return the object's fullname.
A fullname is an object's kind mapping like ``t3`` followed by an underscore and
the object's base36 ID, e.g., ``t1_c5s96e0``.
"""
if "_" in self.id:
return self.id
return f"{self._kind}_{self.id}"

View File

@@ -0,0 +1,118 @@
"""Provide the GildableMixin class."""
from warnings import warn
from ....const import API_PATH
from ....util import _deprecate_args
class GildableMixin:
"""Interface for classes that can be gilded."""
@_deprecate_args("gild_type", "is_anonymous", "message")
def award(
self,
*,
gild_type: str = "gid_2",
is_anonymous: bool = True,
message: str = None,
) -> dict:
"""Award the author of the item.
:param gild_type: Type of award to give. See table below for currently know
global award types.
:param is_anonymous: If ``True``, the authenticated user's username will not be
revealed to the recipient.
:param message: Message to include with the award.
:returns: A dict containing info similar to what is shown below:
.. code-block:: python
{
"subreddit_balance": 85260,
"treatment_tags": [],
"coins": 8760,
"gildings": {"gid_1": 0, "gid_2": 1, "gid_3": 0},
"awarder_karma_received": 4,
"all_awardings": [
{
"giver_coin_reward": 0,
"subreddit_id": None,
"is_new": False,
"days_of_drip_extension": 0,
"coin_price": 75,
"id": "award_9663243a-e77f-44cf-abc6-850ead2cd18d",
"penny_donate": 0,
"coin_reward": 0,
"icon_url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_512.png",
"days_of_premium": 0,
"icon_height": 512,
"tiers_by_required_awardings": None,
"icon_width": 512,
"static_icon_width": 512,
"start_date": None,
"is_enabled": True,
"awardings_required_to_grant_benefits": None,
"description": "For an especially amazing showing.",
"end_date": None,
"subreddit_coin_reward": 0,
"count": 1,
"static_icon_height": 512,
"name": "Bravo Grande!",
"icon_format": "APNG",
"award_sub_type": "PREMIUM",
"penny_price": 0,
"award_type": "global",
"static_icon_url": "https://i.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png",
}
],
}
.. warning::
Requires the authenticated user to own Reddit Coins. Calling this method
will consume Reddit Coins.
To award the gold award anonymously do:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
comment.award()
submission = reddit.submission("8dmv8z")
submission.award()
To award the platinum award with the message 'Nice!' and reveal your username to
the recipient do:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
comment.award(gild_type="gild_3", message="Nice!", is_anonymous=False)
submission = reddit.submission("8dmv8z")
submission.award(gild_type="gild_3", message="Nice!", is_anonymous=False)
.. include:: awards.txt
"""
params = {
"api_type": "json",
"gild_type": gild_type,
"is_anonymous": is_anonymous,
"thing_id": self.fullname,
"message": message,
}
return self._reddit.post(API_PATH["award_thing"], params=params)
def gild(self) -> dict:
"""Alias for :meth:`.award` to maintain backwards compatibility."""
warn(
"'.gild' has been renamed to '.award'.",
category=DeprecationWarning,
stacklevel=2,
)
return self.award()

View File

@@ -0,0 +1,153 @@
"""Provide the InboxableMixin class."""
from ....const import API_PATH
class InboxableMixin:
"""Interface for :class:`.RedditBase` subclasses that originate from the inbox."""
def block(self):
"""Block the user who sent the item.
.. note::
This method pertains only to objects which were retrieved via the inbox.
Example usage:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
comment.block()
# or, identically:
comment.author.block()
"""
self._reddit.post(API_PATH["block"], data={"id": self.fullname})
def collapse(self):
"""Mark the item as collapsed.
.. note::
This method pertains only to objects which were retrieved via the inbox.
Example usage:
.. code-block:: python
inbox = reddit.inbox()
# select first inbox item and collapse it message = next(inbox)
message.collapse()
.. seealso::
:meth:`.uncollapse`
"""
self._reddit.inbox.collapse([self])
def mark_read(self):
"""Mark a single inbox item as read.
.. note::
This method pertains only to objects which were retrieved via the inbox.
Example usage:
.. code-block:: python
inbox = reddit.inbox.unread()
for message in inbox:
# process unread messages
...
.. seealso::
:meth:`.mark_unread`
To mark the whole inbox as read with a single network request, use
:meth:`.Inbox.mark_all_read`
"""
self._reddit.inbox.mark_read([self])
def mark_unread(self):
"""Mark the item as unread.
.. note::
This method pertains only to objects which were retrieved via the inbox.
Example usage:
.. code-block:: python
inbox = reddit.inbox(limit=10)
for message in inbox:
# process messages
...
.. seealso::
:meth:`.mark_read`
"""
self._reddit.inbox.mark_unread([self])
def unblock_subreddit(self):
"""Unblock a subreddit.
.. note::
This method pertains only to objects which were retrieved via the inbox.
For example, to unblock all blocked subreddits that you can find by going
through your inbox:
.. code-block:: python
from praw.models import SubredditMessage
subs = set()
for item in reddit.inbox.messages(limit=None):
if isinstance(item, SubredditMessage):
if (
item.subject == "[message from blocked subreddit]"
and str(item.subreddit) not in subs
):
item.unblock_subreddit()
subs.add(str(item.subreddit))
"""
self._reddit.post(API_PATH["unblock_subreddit"], data={"id": self.fullname})
def uncollapse(self):
"""Mark the item as uncollapsed.
.. note::
This method pertains only to objects which were retrieved via the inbox.
Example usage:
.. code-block:: python
inbox = reddit.inbox()
# select first inbox item and uncollapse it
message = next(inbox)
message.uncollapse()
.. seealso::
:meth:`.collapse`
"""
self._reddit.inbox.uncollapse([self])

View File

@@ -0,0 +1,59 @@
"""Provide the InboxToggleableMixin class."""
from ....const import API_PATH
class InboxToggleableMixin:
"""Interface for classes that can optionally receive inbox replies."""
def disable_inbox_replies(self):
"""Disable inbox replies for the item.
.. note::
This can only apply to items created by the authenticated user.
Example usage:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
comment.disable_inbox_replies()
submission = reddit.submission("8dmv8z")
submission.disable_inbox_replies()
.. seealso::
:meth:`.enable_inbox_replies`
"""
self._reddit.post(
API_PATH["sendreplies"], data={"id": self.fullname, "state": False}
)
def enable_inbox_replies(self):
"""Enable inbox replies for the item.
.. note::
This can only apply to items created by the authenticated user.
Example usage:
.. code-block:: python
comment = reddit.comment("dkk4qjd")
comment.enable_inbox_replies()
submission = reddit.submission("8dmv8z")
submission.enable_inbox_replies()
.. seealso::
:meth:`.disable_inbox_replies`
"""
self._reddit.post(
API_PATH["sendreplies"], data={"id": self.fullname, "state": True}
)

View File

@@ -0,0 +1,67 @@
"""Provide the MessageableMixin class."""
from __future__ import annotations
from typing import TYPE_CHECKING
from ....const import API_PATH
from ....util import _deprecate_args
if TYPE_CHECKING: # pragma: no cover
import praw
class MessageableMixin:
"""Interface for classes that can be messaged."""
@_deprecate_args("subject", "message", "from_subreddit")
def message(
self,
*,
from_subreddit: praw.models.Subreddit | str | None = None,
message: str,
subject: str,
):
"""Send a message to a :class:`.Redditor` or a :class:`.Subreddit`'s moderators (modmail).
:param from_subreddit: A :class:`.Subreddit` instance or string to send the
message from. When provided, messages are sent from the subreddit rather
than from the authenticated user.
.. note::
The authenticated user must be a moderator of the subreddit and have the
``mail`` moderator permission.
:param message: The message content.
:param subject: The subject of the message.
For example, to send a private message to u/spez, try:
.. code-block:: python
reddit.redditor("spez").message(subject="TEST", message="test message from PRAW")
To send a message to u/spez from the moderators of r/test try:
.. code-block:: python
reddit.redditor("spez").message(
subject="TEST", message="test message from r/test", from_subreddit="test"
)
To send a message to the moderators of r/test, try:
.. code-block:: python
reddit.subreddit("test").message(subject="TEST", message="test PM from PRAW")
"""
data = {
"subject": subject,
"text": message,
"to": f"{getattr(self.__class__, 'MESSAGE_PREFIX', '')}{self}",
}
if from_subreddit:
data["from_sr"] = str(from_subreddit)
self._reddit.post(API_PATH["compose"], data=data)

View File

@@ -0,0 +1,61 @@
"""Provide the ModNoteMixin class."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Generator
if TYPE_CHECKING: # pragma: no cover
import praw.models
class ModNoteMixin:
"""Interface for classes that can have a moderator note set on them."""
def author_notes(
self, **generator_kwargs: Any
) -> Generator[praw.models.ModNote, None, None]:
"""Get the moderator notes for the author of this object in the subreddit it's posted in.
:param generator_kwargs: Additional keyword arguments are passed in the
initialization of the moderator note generator.
:returns: A generator of :class:`.ModNote`.
For example, to list all notes the author of a submission, try:
.. code-block:: python
for note in reddit.submission("92dd8").mod.author_notes():
print(f"{note.label}: {note.note}")
"""
return self.thing.subreddit.mod.notes.redditors(
self.thing.author, **generator_kwargs
)
def create_note(
self, *, label: str | None = None, note: str, **other_settings: Any
) -> praw.models.ModNote:
"""Create a moderator note on the author of this object in the subreddit it's posted in.
:param label: The label for the note. As of this writing, this can be one of the
following: ``"ABUSE_WARNING"``, ``"BAN"``, ``"BOT_BAN"``,
``"HELPFUL_USER"``, ``"PERMA_BAN"``, ``"SOLID_CONTRIBUTOR"``,
``"SPAM_WARNING"``, ``"SPAM_WATCH"``, or ``None`` (default: ``None``).
:param note: The content of the note. As of this writing, this is limited to 250
characters.
:param other_settings: Additional keyword arguments are passed to
:meth:`~.BaseModNotes.create`.
:returns: The new :class:`.ModNote` object.
For example, to create a note on a :class:`.Submission`, try:
.. code-block:: python
reddit.submission("92dd8").mod.create_note(label="HELPFUL_USER", note="Test note")
"""
return self.thing.subreddit.mod.notes.create(
label=label, note=note, thing=self.thing, **other_settings
)

View File

@@ -0,0 +1,48 @@
"""Provide the ReplyableMixin class."""
from __future__ import annotations
from typing import TYPE_CHECKING
from ....const import API_PATH
if TYPE_CHECKING: # pragma: no cover
import praw.models
class ReplyableMixin:
"""Interface for :class:`.RedditBase` classes that can be replied to."""
def reply(self, body: str) -> praw.models.Comment | praw.models.Message | None:
"""Reply to the object.
:param body: The Markdown formatted content for a comment.
:returns: A :class:`.Comment` or :class:`.Message` object for the newly created
comment or message or ``None`` if Reddit doesn't provide one.
:raises: ``prawcore.exceptions.Forbidden`` when attempting to reply to some
items, such as locked submissions/comments or non-replyable messages.
A ``None`` value can be returned if the target is a comment or submission in a
quarantined subreddit and the authenticated user has not opt-ed into viewing the
content. When this happens the comment will be successfully created on Reddit
and can be retried by drawing the comment from the user's comment history.
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.reply("reply")
comment = reddit.comment("dxolpyc")
comment.reply("reply")
"""
data = {"text": body, "thing_id": self.fullname}
comments = self._reddit.post(API_PATH["comment"], data=data)
try:
return comments[0]
except IndexError:
return None

View File

@@ -0,0 +1,30 @@
"""Provide the ReportableMixin class."""
from ....const import API_PATH
class ReportableMixin:
"""Interface for :class:`.RedditBase` classes that can be reported."""
def report(self, reason: str):
"""Report this object to the moderators of its subreddit.
:param reason: The reason for reporting.
:raises: :class:`.RedditAPIException` if ``reason`` is longer than 100
characters.
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.report("report reason")
comment = reddit.comment("dxolpyc")
comment.report("report reason")
"""
self._reddit.post(
API_PATH["report"], data={"id": self.fullname, "reason": reason}
)

View File

@@ -0,0 +1,56 @@
"""Provide the SavableMixin class."""
from __future__ import annotations
from ....const import API_PATH
from ....util import _deprecate_args
class SavableMixin:
"""Interface for :class:`.RedditBase` classes that can be saved."""
@_deprecate_args("category")
def save(self, *, category: str | None = None):
"""Save the object.
:param category: The category to save to. If the authenticated user does not
have Reddit Premium this value is ignored by Reddit (default: ``None``).
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.save(category="view later")
comment = reddit.comment("dxolpyc")
comment.save()
.. seealso::
:meth:`.unsave`
"""
self._reddit.post(
API_PATH["save"], data={"category": category, "id": self.fullname}
)
def unsave(self):
"""Unsave the object.
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.unsave()
comment = reddit.comment("dxolpyc")
comment.unsave()
.. seealso::
:meth:`.save`
"""
self._reddit.post(API_PATH["unsave"], data={"id": self.fullname})

View File

@@ -0,0 +1,94 @@
"""Provide the VotableMixin class."""
from __future__ import annotations
from ....const import API_PATH
class VotableMixin:
"""Interface for :class:`.RedditBase` classes that can be voted on."""
def _vote(self, direction: int):
self._reddit.post(
API_PATH["vote"], data={"dir": str(direction), "id": self.fullname}
)
def clear_vote(self):
"""Clear the authenticated user's vote on the object.
.. note::
Votes must be cast by humans. That is, API clients proxying a human's action
one-for-one are OK, but bots deciding how to vote on content or amplifying a
human's vote are not. See the reddit rules for more details on what
constitutes vote manipulation. [`Ref
<https://www.reddit.com/dev/api#POST_api_vote>`_]
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.clear_vote()
comment = reddit.comment("dxolpyc")
comment.clear_vote()
"""
self._vote(direction=0)
def downvote(self):
"""Downvote the object.
.. note::
Votes must be cast by humans. That is, API clients proxying a human's action
one-for-one are OK, but bots deciding how to vote on content or amplifying a
human's vote are not. See the reddit rules for more details on what
constitutes vote manipulation. [`Ref
<https://www.reddit.com/dev/api#POST_api_vote>`_]
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.downvote()
comment = reddit.comment("dxolpyc")
comment.downvote()
.. seealso::
:meth:`.upvote`
"""
self._vote(direction=-1)
def upvote(self):
"""Upvote the object.
.. note::
Votes must be cast by humans. That is, API clients proxying a human's action
one-for-one are OK, but bots deciding how to vote on content or amplifying a
human's vote are not. See the reddit rules for more details on what
constitutes vote manipulation. [`Ref
<https://www.reddit.com/dev/api#POST_api_vote>`_]
Example usage:
.. code-block:: python
submission = reddit.submission("5or86n")
submission.upvote()
comment = reddit.comment("dxolpyc")
comment.upvote()
.. seealso::
:meth:`.downvote`
"""
self._vote(direction=1)