Skip to content

Commit

Permalink
Merge 0c198a2 into 965bbd2
Browse files Browse the repository at this point in the history
  • Loading branch information
LilSpazJoekp committed Dec 29, 2019
2 parents 965bbd2 + 0c198a2 commit f49f733
Show file tree
Hide file tree
Showing 16 changed files with 5,493 additions and 10 deletions.
21 changes: 21 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ Unreleased
* Parameters ``mod_note`` and ``reason_id`` to
:meth:`.ThingModerationMixin.remove` to optionally apply a removal reason on
removal
* Add :class:`.SubredditModerationStream` to enable moderation streams
* Attribute ``stream`` to :class:`.SubredditModeration` to interact with new
moderation streams
* Add :meth:`.SubredditModerationStream.edited` to allow streaming
of :meth:`.SubredditModeration.edited`
* Add :meth:`.SubredditModerationStream.log` to allow streaming
of :meth:`.SubredditModeration.log`
* Add :meth:`.SubredditModerationStream.modmail_conversations` to allow
streaming of :meth:`.Modmail.conversations`
* Add :meth:`.SubredditModerationStream.modqueue` to allow streaming
of :meth:`.SubredditModeration.modqueue`
* Add :meth:`.SubredditModerationStream.reports` to allow streaming
of :meth:`.SubredditModeration.reports`
* Add :meth:`.SubredditModerationStream.spam` to allow streaming
of :meth:`.SubredditModeration.spam`
* Add :meth:`.SubredditModerationStream.unmoderated` to allow streaming
of :meth:`.SubredditModeration.unmoderated`
* Add :meth:`.SubredditModerationStream.unread` to allow streaming
of :meth:`.SubredditModeration.unread`
* Parameter ``exclude_before`` to :func:`.stream_generator` to allow
:meth:`.SubredditModerationStream.modmail_conversations` to work
* Parameters ``allowable_content`` and ``max_emojis`` to
:meth:`~.SubredditRedditorFlairTemplates.add`,
:meth:`~.SubredditLinkFlairTemplates.add`, and
Expand Down
1 change: 1 addition & 0 deletions docs/code_overview/other.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ instances of them bound to an attribute of one of the PRAW models.
other/subredditfilters
other/subredditquarantine
other/subredditstream
other/subredditmoderationstream
other/subredditstylesheet
other/subredditwidgets
other/subredditwiki
Expand Down
5 changes: 5 additions & 0 deletions docs/code_overview/other/subredditmoderationstream.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SubredditModerationStream
=========================

.. autoclass:: praw.models.reddit.subreddit.SubredditModerationStream
:inherited-members:
205 changes: 205 additions & 0 deletions praw/models/reddit/subreddit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,7 @@ def __init__(self, subreddit):
"""
self.subreddit = subreddit
self._stream = None

def accept_invite(self):
"""Accept an invitation as a moderator of the community."""
Expand Down Expand Up @@ -1659,6 +1660,23 @@ def modqueue(self, only=None, **generator_kwargs):
**generator_kwargs
)

@property
def stream(self):
"""Provide an instance of :class:`.SubredditModerationStream`.
Streams can be used to indefinitely retrieve Moderator only items from
:class:`.SubredditModeration` made to moderated subreddits, like:
.. code:: python
for log in reddit.subreddit('mod').mod.stream.log():
print("Mod: {}, Subreddit: {}".format(log.mod, log.subreddit))
"""
if self._stream is None:
self._stream = SubredditModerationStream(self.subreddit)
return self._stream

@cachedproperty
def removal_reasons(self):
"""Provide an instance of :class:`.SubredditRemovalReasons`.
Expand Down Expand Up @@ -1881,6 +1899,193 @@ def update(self, **settings):
)


class SubredditModerationStream:
"""Provides moderator streams."""

def __init__(self, subreddit):
"""Create a SubredditModerationStream instance.
:param subreddit: The moderated subreddit associated with the streams.
"""
self.subreddit = subreddit

def edited(self, only=None, **stream_options):
"""Yield edited comments and submissions as they become available.
:param only: If specified, one of ``'comments'``, or ``'submissions'``
to yield only results of that type.
Keyword arguments are passed to :func:`.stream_generator`.
For example, to retrieve all new edited submissions/commented made
to all moderated subreddits, try:
.. code:: python
for item in reddit.subreddit('mod').mod.stream.edited():
print(item)
"""
return stream_generator(
self.subreddit.mod.edited, only=only, **stream_options
)

def log(self, action=None, mod=None, **stream_options):
"""Yield moderator log entries as they become available.
:param action: If given, only return log entries for the specified
action.
:param mod: If given, only return log entries for actions made by the
passed in Redditor.
For example, to retrieve all new mod actions made to all moderated
subreddits, try:
.. code:: python
for log in reddit.subreddit('mod').mod.stream.log():
print("Mod: {}, Subreddit: {}".format(log.mod, log.subreddit))
"""
return stream_generator(
self.subreddit.mod.log,
action=action,
mod=mod,
attribute_name="id",
**stream_options
)

def modmail_conversations(
self, other_subreddits=None, sort=None, state=None, **stream_options
):
"""Yield unread moderator messages as they become available.
:param other_subreddits: A list of :class:`.Subreddit` instances for
which to fetch conversations (default: None).
:param sort: Can be one of: mod, recent, unread, user
(default: recent).
:param state: Can be one of: all, archived, highlighted, inprogress,
mod, new, notifications, (default: all). "all" does not include
internal or archived conversations.
Keyword arguments are passed to :func:`.stream_generator`.
To print new mail in the unread modmail queue try:
.. code:: python
for message in reddit.subreddit('all').mod.stream.modmail_conversations():
print("From: {}, To: {}".format(message.author, message.dest))
""" # noqa: E501
if self.subreddit.display_name == "mod":
self.subreddit = self.subreddit._reddit.subreddit("all")
return stream_generator(
self.subreddit.modmail.conversations,
other_subreddits=other_subreddits,
sort=sort,
state=state,
attribute_name="id",
exclude_before=True,
**stream_options
)

def modqueue(self, only=None, **stream_options):
"""Yield comments/submissions in the modqueue as they become available.
:param only: If specified, one of ``'comments'``, or ``'submissions'``
to yield only results of that type.
Keyword arguments are passed to :func:`.stream_generator`.
To print all new modqueue items try:
.. code:: python
for item in reddit.subreddit('mod').mod.stream.modqueue():
print(item)
"""
return stream_generator(
self.subreddit.mod.modqueue, only=only, **stream_options
)

def reports(self, only=None, **stream_options):
"""Yield reported comments and submissions as they become available.
:param only: If specified, one of ``'comments'``, or ``'submissions'``
to yield only results of that type.
Keyword arguments are passed to :func:`.stream_generator`.
To print new user and mod report reasons in the report queue try:
.. code:: python
for item in reddit.subreddit('mod').mod.stream.reports():
print(item)
"""
return stream_generator(
self.subreddit.mod.reports, only=only, **stream_options
)

def spam(self, only=None, **stream_options):
"""Yield spam comments and submissions as they become available.
:param only: If specified, one of ``'comments'``, or ``'submissions'``
to yield only results of that type.
Keyword arguments are passed to :func:`.stream_generator`.
To print new items in the spam queue try:
.. code:: python
for item in reddit.subreddit('mod').mod.stream.spam():
print(item)
"""
return stream_generator(
self.subreddit.mod.spam, only=only, **stream_options
)

def unmoderated(self, **stream_options):
"""Yield unmoderated submissions as they become available.
Keyword arguments are passed to :func:`.stream_generator`.
To print new items in the unmoderated queue try:
.. code:: python
for item in reddit.subreddit('mod').mod.stream.unmoderated():
print(item)
"""
return stream_generator(
self.subreddit.mod.unmoderated, **stream_options
)

def unread(self, **stream_options):
"""Yield unread moderator messages as they become available.
Keyword arguments are passed to :func:`.stream_generator`.
See ``inbox`` for all messages.
To print new mail in the unread modmail queue try:
.. code:: python
for message in reddit.subreddit('mod').mod.stream.unread():
print("From: {}, To: {}".format(message.author, message.dest))
"""
return stream_generator(self.subreddit.mod.unread, **stream_options)


class SubredditQuarantine:
"""Provides subreddit quarantine related methods."""

Expand Down
16 changes: 7 additions & 9 deletions praw/models/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def stream_generator(
pause_after: Optional[int] = None,
skip_existing: bool = False,
attribute_name: str = "fullname",
exclude_before: bool = False,
**function_kwargs: Any
) -> Generator[Any, None, None]:
"""Yield new items from ListingGenerators and ``None`` when paused.
Expand All @@ -105,6 +106,9 @@ def stream_generator(
:param attribute_name: The field to use as an id (default: "fullname").
:param exclude_before: When True does not pass ``params`` to ``functions``
(default: False).
Additional keyword arguments will be passed to ``function``.
.. note:: This function internally uses an exponential delay with jitter
Expand Down Expand Up @@ -177,15 +181,9 @@ def stream_generator(
if before_attribute is None:
limit -= without_before_counter
without_before_counter = (without_before_counter + 1) % 30
for item in reversed(
list(
function(
limit=limit,
params={"before": before_attribute},
**function_kwargs
)
)
):
if not exclude_before:
function_kwargs["params"] = {"before": before_attribute}
for item in reversed(list(function(limit=limit, **function_kwargs))):
attribute = getattr(item, attribute_name)
if attribute in seen_attributes:
continue
Expand Down
4 changes: 3 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ def filter_access_token(interaction, current_cassette):
x: env_default(x)
for x in (
"auth_code client_id client_secret password redirect_uri "
"test_subreddit user_agent username"
"test_subreddit user_agent username refresh_token"
).split()
}


placeholders["basic_auth"] = b64_string(
"{}:{}".format(placeholders["client_id"], placeholders["client_secret"])
)
Expand Down
Loading

0 comments on commit f49f733

Please sign in to comment.