1872 lines
64 KiB
Python
1872 lines
64 KiB
Python
"""Provide classes related to widgets."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from json import JSONEncoder, dumps
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Any, TypeVar
|
|
|
|
from ...const import API_PATH
|
|
from ...util import _deprecate_args
|
|
from ...util.cache import cachedproperty
|
|
from ..base import PRAWBase
|
|
from ..list.base import BaseList
|
|
|
|
if TYPE_CHECKING: # pragma: no cover
|
|
import praw.models
|
|
|
|
WidgetType: TypeVar = TypeVar("WidgetType", bound="Widget")
|
|
|
|
|
|
class Button(PRAWBase):
|
|
"""Class to represent a single button inside a :class:`.ButtonWidget`.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============== =====================================================================
|
|
Attribute Description
|
|
============== =====================================================================
|
|
``color`` The hex color used to outline the button.
|
|
``fillColor`` The hex color for the background of the button.
|
|
``height`` Image height. Only present on image buttons.
|
|
``hoverState`` A ``dict`` describing the state of the button when hovered over.
|
|
Optional.
|
|
``kind`` Either ``"text"`` or ``"image"``.
|
|
``linkUrl`` A link that can be visited by clicking the button. Only present on
|
|
image buttons.
|
|
``text`` The text displayed on the button.
|
|
``textColor`` The hex color for the text of the button.
|
|
``url`` - If the button is a text button, a link that can be visited by
|
|
clicking the button.
|
|
- If the button is an image button, the URL of a Reddit-hosted image.
|
|
``width`` Image width. Only present on image buttons.
|
|
============== =====================================================================
|
|
|
|
"""
|
|
|
|
|
|
class CalendarConfiguration(PRAWBase):
|
|
"""Class to represent the configuration of a :class:`.Calendar`.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
=================== ================================================
|
|
Attribute Description
|
|
=================== ================================================
|
|
``numEvents`` The number of events to display on the calendar.
|
|
``showDate`` Whether to show the dates of events.
|
|
``showDescription`` Whether to show the descriptions of events.
|
|
``showLocation`` Whether to show the locations of events.
|
|
``showTime`` Whether to show the times of events.
|
|
``showTitle`` Whether to show the titles of events.
|
|
=================== ================================================
|
|
|
|
"""
|
|
|
|
|
|
class Hover(PRAWBase):
|
|
"""Class to represent the hover data for a :class:`.ButtonWidget`.
|
|
|
|
These values will take effect when the button is hovered over (the user moves their
|
|
cursor so it's on top of the button).
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``color`` The hex color used to outline the button.
|
|
``fillColor`` The hex color for the background of the button.
|
|
``textColor`` The hex color for the text of the button.
|
|
``height`` Image height. Only present on image buttons.
|
|
``kind`` Either ``text`` or ``image``.
|
|
``text`` The text displayed on the button.
|
|
``url`` - If the button is a text button, a link that can be visited by
|
|
clicking the button.
|
|
- If the button is an image button, the URL of a Reddit-hosted image.
|
|
``width`` Image width. Only present on image buttons.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
|
|
class Image(PRAWBase):
|
|
"""Class to represent an image that's part of a :class:`.ImageWidget`.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
=========== =================================================
|
|
Attribute Description
|
|
=========== =================================================
|
|
``height`` Image height.
|
|
``linkUrl`` A link that can be visited by clicking the image.
|
|
``url`` The URL of the (Reddit-hosted) image.
|
|
``width`` Image width.
|
|
=========== =================================================
|
|
|
|
"""
|
|
|
|
|
|
class ImageData(PRAWBase):
|
|
"""Class for image data that's part of a :class:`.CustomWidget`.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
========== =========================================
|
|
Attribute Description
|
|
========== =========================================
|
|
``height`` The image height.
|
|
``name`` The image name.
|
|
``url`` The URL of the image on Reddit's servers.
|
|
``width`` The image width.
|
|
========== =========================================
|
|
|
|
"""
|
|
|
|
|
|
class MenuLink(PRAWBase):
|
|
"""Class to represent a single link inside a :class:`.Menu` or :class:`.Submenu`.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
========= ====================================
|
|
Attribute Description
|
|
========= ====================================
|
|
``text`` The text of the menu link.
|
|
``url`` The URL that the menu item links to.
|
|
========= ====================================
|
|
|
|
"""
|
|
|
|
|
|
class Styles(PRAWBase):
|
|
"""Class to represent the style information of a widget.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
=================== ========================================================
|
|
Attribute Description
|
|
=================== ========================================================
|
|
``backgroundColor`` The background color of a widget, given as a hexadecimal
|
|
(``0x######``).
|
|
``headerColor`` The header color of a widget, given as a hexadecimal
|
|
(``0x######``).
|
|
=================== ========================================================
|
|
|
|
"""
|
|
|
|
|
|
class Submenu(BaseList):
|
|
r"""Class to represent a submenu of links inside a :class:`.Menu`.
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============ ======================================================================
|
|
Attribute Description
|
|
============ ======================================================================
|
|
``children`` A list of the :class:`.MenuLink`\ s in this submenu. Can be iterated
|
|
over by iterating over the :class:`.Submenu` (e.g., ``for menu_link in
|
|
submenu``).
|
|
``text`` The name of the submenu.
|
|
============ ======================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "children"
|
|
|
|
|
|
class SubredditWidgets(PRAWBase):
|
|
"""Class to represent a :class:`.Subreddit`'s widgets.
|
|
|
|
Create an instance like so:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
|
|
Data will be lazy-loaded. By default, PRAW will not request progressively loading
|
|
images from Reddit. To enable this, instantiate a :class:`.SubredditWidgets` object
|
|
via :meth:`~.Subreddit.widgets`, then set the attribute ``progressive_images`` to
|
|
``True`` before performing any action that would result in a network request.
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
widgets.progressive_images = True
|
|
for widget in widgets.sidebar:
|
|
# do something
|
|
...
|
|
|
|
Access a :class:`.Subreddit`'s widgets with the following attributes:
|
|
|
|
.. code-block:: python
|
|
|
|
print(widgets.id_card)
|
|
print(widgets.moderators_widget)
|
|
print(widgets.sidebar)
|
|
print(widgets.topbar)
|
|
|
|
The attribute :attr:`.id_card` contains the :class:`.Subreddit`'s ID card, which
|
|
displays information like the number of subscribers.
|
|
|
|
The attribute :attr:`.moderators_widget` contains the :class:`.Subreddit`'s
|
|
moderators widget, which lists the moderators of the subreddit.
|
|
|
|
The attribute :attr:`.sidebar` contains a list of widgets which make up the sidebar
|
|
of the subreddit.
|
|
|
|
The attribute :attr:`.topbar` contains a list of widgets which make up the top bar
|
|
of the subreddit.
|
|
|
|
To edit a :class:`.Subreddit`'s widgets, use :attr:`~.SubredditWidgets.mod`. For
|
|
example:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets.mod.add_text_area(
|
|
short_name="My title",
|
|
text="**bold text**",
|
|
styles={"backgroundColor": "#FFFF66", "headerColor": "#3333EE"},
|
|
)
|
|
|
|
For more information, see :class:`.SubredditWidgetsModeration`.
|
|
|
|
To edit a particular widget, use ``.mod`` on the widget. For example:
|
|
|
|
.. code-block:: python
|
|
|
|
for widget in widgets.sidebar:
|
|
widget.mod.update(shortName="Exciting new name")
|
|
|
|
For more information, see :class:`.WidgetModeration`.
|
|
|
|
**Currently available widgets**:
|
|
|
|
- :class:`.ButtonWidget`
|
|
- :class:`.Calendar`
|
|
- :class:`.CommunityList`
|
|
- :class:`.CustomWidget`
|
|
- :class:`.IDCard`
|
|
- :class:`.ImageWidget`
|
|
- :class:`.Menu`
|
|
- :class:`.ModeratorsWidget`
|
|
- :class:`.PostFlairWidget`
|
|
- :class:`.RulesWidget`
|
|
- :class:`.TextArea`
|
|
|
|
"""
|
|
|
|
@cachedproperty
|
|
def id_card(self) -> praw.models.IDCard:
|
|
"""Get this :class:`.Subreddit`'s :class:`.IDCard` widget."""
|
|
return self.items[self.layout["idCardWidget"]]
|
|
|
|
@cachedproperty
|
|
def items(self) -> dict[str, praw.models.Widget]:
|
|
"""Get this :class:`.Subreddit`'s widgets as a dict from ID to widget."""
|
|
items = {}
|
|
for item_name, data in self._raw_items.items():
|
|
data["subreddit"] = self.subreddit
|
|
items[item_name] = self._reddit._objector.objectify(data)
|
|
return items
|
|
|
|
@cachedproperty
|
|
def mod(self) -> praw.models.SubredditWidgetsModeration:
|
|
"""Get an instance of :class:`.SubredditWidgetsModeration`.
|
|
|
|
.. note::
|
|
|
|
Using any of the methods of :class:`.SubredditWidgetsModeration` will likely
|
|
result in the data of this :class:`.SubredditWidgets` being outdated. To
|
|
re-sync, call :meth:`.refresh`.
|
|
|
|
"""
|
|
return SubredditWidgetsModeration(self.subreddit, self._reddit)
|
|
|
|
@cachedproperty
|
|
def moderators_widget(self) -> praw.models.ModeratorsWidget:
|
|
"""Get this :class:`.Subreddit`'s :class:`.ModeratorsWidget`."""
|
|
return self.items[self.layout["moderatorWidget"]]
|
|
|
|
@cachedproperty
|
|
def sidebar(self) -> list[praw.models.Widget]:
|
|
r"""Get a list of :class:`.Widget`\ s that make up the sidebar."""
|
|
return [
|
|
self.items[widget_name] for widget_name in self.layout["sidebar"]["order"]
|
|
]
|
|
|
|
@cachedproperty
|
|
def topbar(self) -> list[praw.models.Menu]:
|
|
r"""Get a list of :class:`.Widget`\ s that make up the top bar."""
|
|
return [
|
|
self.items[widget_name] for widget_name in self.layout["topbar"]["order"]
|
|
]
|
|
|
|
def __getattr__(self, attr: str) -> Any:
|
|
"""Return the value of ``attr``."""
|
|
if not attr.startswith("_") and not self._fetched:
|
|
self._fetch()
|
|
return getattr(self, attr)
|
|
msg = f"{self.__class__.__name__!r} object has no attribute {attr!r}"
|
|
raise AttributeError(msg)
|
|
|
|
def __init__(self, subreddit: praw.models.Subreddit):
|
|
"""Initialize a :class:`.SubredditWidgets` instance.
|
|
|
|
:param subreddit: The :class:`.Subreddit` the widgets belong to.
|
|
|
|
"""
|
|
self._raw_items = None
|
|
self._fetched = False
|
|
self.subreddit = subreddit
|
|
self.progressive_images = False
|
|
|
|
super().__init__(subreddit._reddit, {})
|
|
|
|
def __repr__(self) -> str:
|
|
"""Return an object initialization representation of the instance."""
|
|
return f"SubredditWidgets(subreddit={self.subreddit!r})"
|
|
|
|
def _fetch(self):
|
|
data = self._reddit.get(
|
|
API_PATH["widgets"].format(subreddit=self.subreddit),
|
|
params={"progressive_images": self.progressive_images},
|
|
)
|
|
|
|
self._raw_items = data.pop("items")
|
|
super().__init__(self.subreddit._reddit, data)
|
|
|
|
cached_property_names = [
|
|
"id_card",
|
|
"moderators_widget",
|
|
"sidebar",
|
|
"topbar",
|
|
"items",
|
|
]
|
|
inst_dict_pop = self.__dict__.pop
|
|
for name in cached_property_names:
|
|
inst_dict_pop(name, None)
|
|
|
|
self._fetched = True
|
|
|
|
def refresh(self):
|
|
"""Refresh the :class:`.Subreddit`'s widgets.
|
|
|
|
By default, PRAW will not request progressively loading images from Reddit. To
|
|
enable this, set the attribute ``progressive_images`` to ``True`` prior to
|
|
calling ``refresh()``.
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
widgets.progressive_images = True
|
|
widgets.refresh()
|
|
|
|
"""
|
|
self._fetch()
|
|
|
|
|
|
class Widget(PRAWBase):
|
|
"""Base class to represent a :class:`.Widget`."""
|
|
|
|
@cachedproperty
|
|
def mod(self) -> praw.models.WidgetModeration:
|
|
"""Get an instance of :class:`.WidgetModeration` for this widget.
|
|
|
|
.. note::
|
|
|
|
Using any of the methods of :class:`.WidgetModeration` will likely make the
|
|
data in the :class:`.SubredditWidgets` that this widget belongs to outdated.
|
|
To remedy this, call :meth:`~.SubredditWidgets.refresh`.
|
|
|
|
"""
|
|
return WidgetModeration(self, self.subreddit, self._reddit)
|
|
|
|
def __eq__(self, other: object) -> bool:
|
|
"""Check equality against another object."""
|
|
if isinstance(other, Widget):
|
|
return self.id.lower() == other.id.lower()
|
|
return str(other).lower() == self.id.lower()
|
|
|
|
def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]):
|
|
"""Initialize a :class:`.Widget` instance."""
|
|
self.subreddit = "" # in case it isn't in _data
|
|
self.id = "" # in case it isn't in _data
|
|
super().__init__(reddit, _data=_data)
|
|
self._mod = None
|
|
|
|
|
|
class WidgetEncoder(JSONEncoder):
|
|
"""Class to encode widget-related objects."""
|
|
|
|
def default(self, o: Any) -> Any:
|
|
"""Serialize ``PRAWBase`` objects."""
|
|
if isinstance(o, self._subreddit_class):
|
|
return str(o)
|
|
if isinstance(o, PRAWBase):
|
|
return {key: val for key, val in vars(o).items() if not key.startswith("_")}
|
|
return JSONEncoder.default(self, o)
|
|
|
|
|
|
class ButtonWidget(Widget, BaseList):
|
|
r"""Class to represent a widget containing one or more buttons.
|
|
|
|
Find an existing one:
|
|
|
|
.. code-block:: python
|
|
|
|
button_widget = None
|
|
widgets = reddit.subreddit("test").widgets
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.ButtonWidget):
|
|
button_widget = widget
|
|
break
|
|
|
|
for button in button_widget:
|
|
print(button.text, button.url)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
buttons = [
|
|
{
|
|
"kind": "text",
|
|
"text": "View source",
|
|
"url": "https://github.com/praw-dev/praw",
|
|
"color": "#FF0000",
|
|
"textColor": "#00FF00",
|
|
"fillColor": "#0000FF",
|
|
"hoverState": {
|
|
"kind": "text",
|
|
"text": "ecruos weiV",
|
|
"color": "#000000",
|
|
"textColor": "#FFFFFF",
|
|
"fillColor": "#0000FF",
|
|
},
|
|
},
|
|
{
|
|
"kind": "text",
|
|
"text": "View documentation",
|
|
"url": "https://praw.readthedocs.io",
|
|
"color": "#FFFFFF",
|
|
"textColor": "#FFFF00",
|
|
"fillColor": "#0000FF",
|
|
},
|
|
]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
button_widget = widgets.mod.add_button_widget(
|
|
"Things to click", "Click some of these *cool* links!", buttons, styles
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_button_widget`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
button_widget = button_widget.mod.update(shortName="My fav buttons", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
button_widget.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
==================== ==============================================================
|
|
Attribute Description
|
|
==================== ==============================================================
|
|
``buttons`` A list of :class:`.Button`\ s. These can also be accessed just
|
|
by iterating over the :class:`.ButtonWidget` (e.g., ``for
|
|
button in button_widget``).
|
|
``description`` The description, in Markdown.
|
|
``description_html`` The description, in HTML.
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"button"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and
|
|
``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
==================== ==============================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "buttons"
|
|
|
|
|
|
class Calendar(Widget):
|
|
"""Class to represent a calendar widget.
|
|
|
|
Find an existing one:
|
|
|
|
.. code-block:: python
|
|
|
|
calendar = None
|
|
widgets = reddit.subreddit("test").widgets
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.Calendar):
|
|
calendar = widget
|
|
break
|
|
|
|
print(calendar.googleCalendarId)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
config = {
|
|
"numEvents": 10,
|
|
"showDate": True,
|
|
"showDescription": False,
|
|
"showLocation": False,
|
|
"showTime": True,
|
|
"showTitle": True,
|
|
}
|
|
cal_id = "y6nm89jy427drk8l71w75w9wjn@group.calendar.google.com"
|
|
calendar = widgets.mod.add_calendar(
|
|
short_name="Upcoming Events",
|
|
google_calendar_id=cal_id,
|
|
requires_sync=True,
|
|
configuration=config,
|
|
styles=styles,
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_calendar`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
calendar = calendar.mod.update(shortName="My fav events", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
calendar.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
================= =====================================================
|
|
Attribute Description
|
|
================= =====================================================
|
|
``configuration`` A ``dict`` describing the calendar configuration.
|
|
``data`` A list of dictionaries that represent events.
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"calendar"``).
|
|
``requiresSync`` A ``bool`` representing whether the calendar requires
|
|
synchronization.
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and
|
|
``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
================= =====================================================
|
|
|
|
"""
|
|
|
|
|
|
class CommunityList(Widget, BaseList):
|
|
r"""Class to represent a Related Communities widget.
|
|
|
|
Find an existing one:
|
|
|
|
.. code-block:: python
|
|
|
|
community_list = None
|
|
widgets = reddit.subreddit("test").widgets
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.CommunityList):
|
|
community_list = widget
|
|
break
|
|
|
|
print(community_list)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
subreddits = ["learnpython", reddit.subreddit("test")]
|
|
community_list = widgets.mod.add_community_list(
|
|
short_name="Related subreddits",
|
|
data=subreddits,
|
|
styles=styles,
|
|
description="description",
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_community_list`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
community_list = community_list.mod.update(shortName="My fav subs", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
community_list.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``data`` A list of :class:`.Subreddit`\ s. These can also be iterated over by
|
|
iterating over the :class:`.CommunityList` (e.g., ``for sub in
|
|
community_list``).
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"community-list"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "data"
|
|
|
|
|
|
class CustomWidget(Widget):
|
|
"""Class to represent a custom widget.
|
|
|
|
Find an existing one:
|
|
|
|
.. code-block:: python
|
|
|
|
custom = None
|
|
widgets = reddit.subreddit("test").widgets
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.CustomWidget):
|
|
custom = widget
|
|
break
|
|
|
|
print(custom.text)
|
|
print(custom.css)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
custom = widgets.mod.add_custom_widget(
|
|
short_name="My custom widget",
|
|
text="# Hello world!",
|
|
css="/**/",
|
|
height=200,
|
|
image_data=[],
|
|
styles=styles,
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_custom_widget`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
custom = custom.mod.update(shortName="My fav customization", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
custom.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
================= ============================================================
|
|
Attribute Description
|
|
================= ============================================================
|
|
``css`` The CSS of the widget, as a ``str``.
|
|
``height`` The height of the widget, as an ``int``.
|
|
``id`` The widget ID.
|
|
``imageData`` A ``list`` of :class:`.ImageData` that belong to the widget.
|
|
``kind`` The widget kind (always ``"custom"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and
|
|
``"headerColor"``.
|
|
``stylesheetUrl`` A link to the widget's stylesheet.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
``text`` The text contents, as Markdown.
|
|
``textHtml`` The text contents, as HTML.
|
|
================= ============================================================
|
|
|
|
"""
|
|
|
|
def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]):
|
|
"""Initialize a :class:`.CustomWidget` instance."""
|
|
_data["imageData"] = [
|
|
ImageData(reddit, data) for data in _data.pop("imageData")
|
|
]
|
|
super().__init__(reddit, _data=_data)
|
|
|
|
|
|
class IDCard(Widget):
|
|
"""Class to represent an ID card widget.
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
id_card = widgets.id_card
|
|
print(id_card.subscribersText)
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets.id_card.mod.update(currentlyViewingText="Bots")
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
========================= =======================================================
|
|
Attribute Description
|
|
========================= =======================================================
|
|
``currentlyViewingCount`` The number of redditors viewing the subreddit.
|
|
``currentlyViewingText`` The text displayed next to the view count. For example,
|
|
``"users online"``.
|
|
``description`` The subreddit description.
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"id-card"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and
|
|
``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
``subscribersCount`` The number of subscribers to the subreddit.
|
|
``subscribersText`` The text displayed next to the subscriber count. For
|
|
example, "users subscribed".
|
|
========================= =======================================================
|
|
|
|
"""
|
|
|
|
|
|
class ImageWidget(Widget, BaseList):
|
|
r"""Class to represent an image widget.
|
|
|
|
Find an existing one:
|
|
|
|
.. code-block:: python
|
|
|
|
image_widget = None
|
|
widgets = reddit.subreddit("test").widgets
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.ImageWidget):
|
|
image_widget = widget
|
|
break
|
|
|
|
for image in image_widget:
|
|
print(image.url)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
image_paths = ["/path/to/image1.jpg", "/path/to/image2.png"]
|
|
image_data = [
|
|
{
|
|
"width": 600,
|
|
"height": 450,
|
|
"linkUrl": "",
|
|
"url": widgets.mod.upload_image(img_path),
|
|
}
|
|
for img_path in image_paths
|
|
]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
image_widget = widgets.mod.add_image_widget(
|
|
short_name="My cool pictures", data=image_data, styles=styles
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_image_widget`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
image_widget = image_widget.mod.update(shortName="My fav images", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
image_widget.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``data`` A list of the :class:`.Image`\ s in this widget. Can be iterated over
|
|
by iterating over the :class:`.ImageWidget` (e.g., ``for img in
|
|
image_widget``).
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"image"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "data"
|
|
|
|
|
|
class Menu(Widget, BaseList):
|
|
r"""Class to represent the top menu widget of a :class:`.Subreddit`.
|
|
|
|
Menus can generally be found as the first item in a :class:`.Subreddit`'s top bar.
|
|
|
|
.. code-block:: python
|
|
|
|
topbar = reddit.subreddit("test").widgets.topbar
|
|
if len(topbar) > 0:
|
|
probably_menu = topbar[0]
|
|
assert isinstance(probably_menu, praw.models.Menu)
|
|
for item in probably_menu:
|
|
if isinstance(item, praw.models.Submenu):
|
|
print(item.text)
|
|
for child in item:
|
|
print("\t", child.text, child.url)
|
|
else: # MenuLink
|
|
print(item.text, item.url)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
menu_contents = [
|
|
{"text": "My homepage", "url": "https://example.com"},
|
|
{
|
|
"text": "Python packages",
|
|
"children": [
|
|
{"text": "PRAW", "url": "https://praw.readthedocs.io/"},
|
|
{"text": "requests", "url": "http://python-requests.org"},
|
|
],
|
|
},
|
|
{"text": "Reddit homepage", "url": "https://reddit.com"},
|
|
]
|
|
menu = widgets.mod.add_menu(data=menu_contents)
|
|
|
|
For more information on creation, see :meth:`.add_menu`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
menu_items = list(menu)
|
|
menu_items.reverse()
|
|
menu = menu.mod.update(data=menu_items)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
menu.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= ====================================================================
|
|
Attribute Description
|
|
============= ====================================================================
|
|
``data`` A list of the :class:`.MenuLink`\ s and :class:`.Submenu`\ s in this
|
|
widget. Can be iterated over by iterating over the :class:`.Menu`
|
|
(e.g., ``for item in menu``).
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"menu"``).
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
============= ====================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "data"
|
|
|
|
|
|
class ModeratorsWidget(Widget, BaseList):
|
|
r"""Class to represent a moderators widget.
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
print(widgets.moderators_widget)
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
widgets.moderators_widget.mod.update(styles=new_styles)
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"moderators"``).
|
|
``mods`` A list of the :class:`.Redditor`\ s that moderate the subreddit. Can
|
|
be iterated over by iterating over the :class:`.ModeratorsWidget`
|
|
(e.g., ``for mod in widgets.moderators_widget``).
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
``totalMods`` The total number of moderators in the subreddit.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "mods"
|
|
|
|
def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]):
|
|
"""Initialize a :class:`.ModeratorsWidget` instance."""
|
|
if self.CHILD_ATTRIBUTE not in _data:
|
|
# .mod.update() sometimes returns payload without "mods" field
|
|
_data[self.CHILD_ATTRIBUTE] = []
|
|
super().__init__(reddit, _data=_data)
|
|
|
|
|
|
class PostFlairWidget(Widget, BaseList):
|
|
"""Class to represent a post flair widget.
|
|
|
|
Find an existing one:
|
|
|
|
.. code-block:: python
|
|
|
|
post_flair_widget = None
|
|
widgets = reddit.subreddit("test").widgets
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.PostFlairWidget):
|
|
post_flair_widget = widget
|
|
break
|
|
|
|
for flair in post_flair_widget:
|
|
print(flair)
|
|
print(post_flair_widget.templates[flair])
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
subreddit = reddit.subreddit("test")
|
|
widgets = subreddit.widgets
|
|
flairs = [f["id"] for f in subreddit.flair.link_templates]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
post_flair = widgets.mod.add_post_flair_widget(
|
|
short_name="Some flairs", display="list", order=flairs, styles=styles
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_post_flair_widget`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
post_flair = post_flair.mod.update(shortName="My fav flairs", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
post_flair.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``display`` The display style of the widget, either ``"cloud"`` or ``"list"``.
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"post-flair"``).
|
|
``order`` A list of the flair IDs in this widget. Can be iterated over by
|
|
iterating over the :class:`.PostFlairWidget` (e.g., ``for flair_id in
|
|
post_flair``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
``templates`` A ``dict`` that maps flair IDs to dictionaries that describe flairs.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "order"
|
|
|
|
|
|
class RulesWidget(Widget, BaseList):
|
|
"""Class to represent a rules widget.
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
rules_widget = None
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.RulesWidget):
|
|
rules_widget = widget
|
|
break
|
|
from pprint import pprint
|
|
|
|
pprint(rules_widget.data)
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
rules_widget.mod.update(display="compact", shortName="The LAWS", styles=new_styles)
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``data`` A list of the subreddit rules. Can be iterated over by iterating over
|
|
the :class:`.RulesWidget` (e.g., ``for rule in rules_widget``).
|
|
``display`` The display style of the widget, either ``"full"`` or ``"compact"``.
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"subreddit-rules"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
CHILD_ATTRIBUTE = "data"
|
|
|
|
def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]):
|
|
"""Initialize a :class:`.RulesWidget` instance."""
|
|
if self.CHILD_ATTRIBUTE not in _data:
|
|
# .mod.update() sometimes returns payload without "data" field
|
|
_data[self.CHILD_ATTRIBUTE] = []
|
|
super().__init__(reddit, _data=_data)
|
|
|
|
|
|
class TextArea(Widget):
|
|
"""Class to represent a text area widget.
|
|
|
|
Find a text area in a subreddit:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
text_area = None
|
|
for widget in widgets.sidebar:
|
|
if isinstance(widget, praw.models.TextArea):
|
|
text_area = widget
|
|
break
|
|
print(text_area.text)
|
|
|
|
Create one:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
text_area = widgets.mod.add_text_area(
|
|
short_name="My cool title", text="*Hello* **world**!", styles=styles
|
|
)
|
|
|
|
For more information on creation, see :meth:`.add_text_area`.
|
|
|
|
Update one:
|
|
|
|
.. code-block:: python
|
|
|
|
new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
|
|
text_area = text_area.mod.update(shortName="My fav text", styles=new_styles)
|
|
|
|
Delete one:
|
|
|
|
.. code-block:: python
|
|
|
|
text_area.mod.delete()
|
|
|
|
.. include:: ../../typical_attributes.rst
|
|
|
|
============= =====================================================================
|
|
Attribute Description
|
|
============= =====================================================================
|
|
``id`` The widget ID.
|
|
``kind`` The widget kind (always ``"textarea"``).
|
|
``shortName`` The short name of the widget.
|
|
``styles`` A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
|
|
``subreddit`` The :class:`.Subreddit` the button widget belongs to.
|
|
``text`` The widget's text, as Markdown.
|
|
``textHtml`` The widget's text, as HTML.
|
|
============= =====================================================================
|
|
|
|
"""
|
|
|
|
|
|
class WidgetModeration:
|
|
"""Class for moderating a particular widget.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget = reddit.subreddit("test").widgets.sidebar[0]
|
|
widget.mod.update(shortName="My new title")
|
|
widget.mod.delete()
|
|
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
widget: Widget,
|
|
subreddit: praw.models.Subreddit | str,
|
|
reddit: praw.Reddit,
|
|
):
|
|
"""Initialize a :class:`.WidgetModeration` instance."""
|
|
self.widget = widget
|
|
self._reddit = reddit
|
|
self._subreddit = subreddit
|
|
|
|
def delete(self):
|
|
"""Delete the widget.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget.mod.delete()
|
|
|
|
"""
|
|
path = API_PATH["widget_modify"].format(
|
|
widget_id=self.widget.id, subreddit=self._subreddit
|
|
)
|
|
self._reddit.delete(path)
|
|
|
|
def update(self, **kwargs: Any) -> Widget:
|
|
"""Update the widget. Returns the updated widget.
|
|
|
|
Parameters differ based on the type of widget. See `Reddit documentation
|
|
<https://www.reddit.com/dev/api#PUT_api_widget_{widget_id}>`_ or the document of
|
|
the particular type of widget.
|
|
|
|
:returns: The updated :class:`.Widget`.
|
|
|
|
For example, update a text widget like so:
|
|
|
|
.. code-block:: python
|
|
|
|
text_widget.mod.update(shortName="New text area", text="Hello!")
|
|
|
|
.. note::
|
|
|
|
Most parameters follow the ``lowerCamelCase`` convention. When in doubt,
|
|
check the Reddit documentation linked above.
|
|
|
|
"""
|
|
path = API_PATH["widget_modify"].format(
|
|
widget_id=self.widget.id, subreddit=self._subreddit
|
|
)
|
|
payload = {
|
|
key: value
|
|
for key, value in vars(self.widget).items()
|
|
if not key.startswith("_")
|
|
}
|
|
del payload["subreddit"] # not JSON serializable
|
|
if "mod" in payload:
|
|
del payload["mod"]
|
|
payload.update(kwargs)
|
|
widget = self._reddit.put(
|
|
path, data={"json": dumps(payload, cls=WidgetEncoder)}
|
|
)
|
|
widget.subreddit = self._subreddit
|
|
return widget
|
|
|
|
|
|
class SubredditWidgetsModeration:
|
|
"""Class for moderating a :class:`.Subreddit`'s widgets.
|
|
|
|
Get an instance of this class from :attr:`.SubredditWidgets.mod`.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
reddit.subreddit("test").widgets.mod.add_text_area(
|
|
short_name="My title", text="**bold text**", styles=styles
|
|
)
|
|
|
|
.. note::
|
|
|
|
To use this class's methods, the authenticated user must be a moderator with
|
|
appropriate permissions.
|
|
|
|
"""
|
|
|
|
def __init__(self, subreddit: praw.models.Subreddit, reddit: praw.Reddit):
|
|
"""Initialize a :class:`.SubredditWidgetsModeration` instance."""
|
|
self._subreddit = subreddit
|
|
self._reddit = reddit
|
|
|
|
def _create_widget(self, payload: dict[str, Any]) -> WidgetType:
|
|
path = API_PATH["widget_create"].format(subreddit=self._subreddit)
|
|
widget = self._reddit.post(
|
|
path, data={"json": dumps(payload, cls=WidgetEncoder)}
|
|
)
|
|
widget.subreddit = self._subreddit
|
|
return widget
|
|
|
|
@_deprecate_args("short_name", "description", "buttons", "styles")
|
|
def add_button_widget(
|
|
self,
|
|
*,
|
|
buttons: list[dict[str, dict[str, str | int] | str | int]],
|
|
description: str,
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
**other_settings: Any,
|
|
) -> praw.models.ButtonWidget:
|
|
"""Add and return a :class:`.ButtonWidget`.
|
|
|
|
:param buttons: A list of dictionaries describing buttons, as specified in
|
|
`Reddit docs`_. As of this writing, the format is:
|
|
|
|
Each button is either a text button or an image button. A text button looks
|
|
like this:
|
|
|
|
.. code-block:: text
|
|
|
|
{
|
|
"kind": "text",
|
|
"text": a string no longer than 30 characters,
|
|
"url": a valid URL,
|
|
"color": a 6-digit rgb hex color, e.g., `#AABBCC`,
|
|
"textColor": a 6-digit rgb hex color, e.g., `#AABBCC`,
|
|
"fillColor": a 6-digit rgb hex color, e.g., `#AABBCC`,
|
|
"hoverState": {...}
|
|
}
|
|
|
|
An image button looks like this:
|
|
|
|
.. code-block:: text
|
|
|
|
{
|
|
"kind": "image",
|
|
"text": a string no longer than 30 characters,
|
|
"linkUrl": a valid URL,
|
|
"url": a valid URL of a Reddit-hosted image,
|
|
"height": an integer,
|
|
"width": an integer,
|
|
"hoverState": {...}
|
|
}
|
|
|
|
Both types of buttons have the field ``hoverState``. The field does not have
|
|
to be included (it is optional). If it is included, it can be one of two
|
|
types: ``"text"`` or ``"image"``. A text ``hoverState`` looks like this:
|
|
|
|
.. code-block:: text
|
|
|
|
{
|
|
"kind": "text",
|
|
"text": a string no longer than 30 characters,
|
|
"color": a 6-digit rgb hex color, e.g., `#AABBCC`,
|
|
"textColor": a 6-digit rgb hex color, e.g., `#AABBCC`,
|
|
"fillColor": a 6-digit rgb hex color, e.g., `#AABBCC`
|
|
}
|
|
|
|
An image ``hoverState`` looks like this:
|
|
|
|
.. code-block:: text
|
|
|
|
{
|
|
"kind": "image",
|
|
"url": a valid URL of a Reddit-hosted image,
|
|
"height": an integer,
|
|
"width": an integer
|
|
}
|
|
|
|
.. note::
|
|
|
|
The method :meth:`.upload_image` can be used to upload images to Reddit
|
|
for a ``url`` field that holds a Reddit-hosted image.
|
|
|
|
.. note::
|
|
|
|
An image ``hoverState`` may be paired with a text widget, and a text
|
|
``hoverState`` may be paired with an image widget.
|
|
|
|
:param description: Markdown text to describe the widget.
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
|
|
:returns: The created :class:`.ButtonWidget`.
|
|
|
|
.. _reddit docs: https://www.reddit.com/dev/api#POST_api_widget
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
my_image = widget_moderation.upload_image("/path/to/pic.jpg")
|
|
buttons = [
|
|
{
|
|
"kind": "text",
|
|
"text": "View source",
|
|
"url": "https://github.com/praw-dev/praw",
|
|
"color": "#FF0000",
|
|
"textColor": "#00FF00",
|
|
"fillColor": "#0000FF",
|
|
"hoverState": {
|
|
"kind": "text",
|
|
"text": "ecruos weiV",
|
|
"color": "#FFFFFF",
|
|
"textColor": "#000000",
|
|
"fillColor": "#0000FF",
|
|
},
|
|
},
|
|
{
|
|
"kind": "image",
|
|
"text": "View documentation",
|
|
"linkUrl": "https://praw.readthedocs.io",
|
|
"url": my_image,
|
|
"height": 200,
|
|
"width": 200,
|
|
"hoverState": {"kind": "image", "url": my_image, "height": 200, "width": 200},
|
|
},
|
|
]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
new_widget = widget_moderation.add_button_widget(
|
|
short_name="Things to click",
|
|
description="Click some of these *cool* links!",
|
|
buttons=buttons,
|
|
styles=styles,
|
|
)
|
|
|
|
"""
|
|
button_widget = {
|
|
"buttons": buttons,
|
|
"description": description,
|
|
"kind": "button",
|
|
"shortName": short_name,
|
|
"styles": styles,
|
|
}
|
|
button_widget.update(other_settings)
|
|
return self._create_widget(button_widget)
|
|
|
|
@_deprecate_args(
|
|
"short_name", "google_calendar_id", "requires_sync", "configuration", "styles"
|
|
)
|
|
def add_calendar(
|
|
self,
|
|
*,
|
|
configuration: dict[str, bool | int],
|
|
google_calendar_id: str,
|
|
requires_sync: bool,
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
**other_settings: Any,
|
|
) -> praw.models.Calendar:
|
|
"""Add and return a :class:`.Calendar` widget.
|
|
|
|
:param configuration: A dictionary as specified in `Reddit docs`_. For example:
|
|
|
|
.. code-block:: python
|
|
|
|
{
|
|
"numEvents": 10,
|
|
"showDate": True,
|
|
"showDescription": False,
|
|
"showLocation": False,
|
|
"showTime": True,
|
|
"showTitle": True,
|
|
}
|
|
|
|
:param google_calendar_id: An email-style calendar ID. To share a Google
|
|
Calendar, make it public, then find the "Calendar ID".
|
|
:param requires_sync: Whether the calendar needs synchronization.
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
|
|
:returns: The created :class:`.Calendar`.
|
|
|
|
.. _reddit docs: https://www.reddit.com/dev/api#POST_api_widget
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
config = {
|
|
"numEvents": 10,
|
|
"showDate": True,
|
|
"showDescription": False,
|
|
"showLocation": False,
|
|
"showTime": True,
|
|
"showTitle": True,
|
|
}
|
|
calendar_id = "y6nm89jy427drk8l71w75w9wjn@group.calendar.google.com"
|
|
new_widget = widget_moderation.add_calendar(
|
|
short_name="Upcoming Events",
|
|
google_calendar_id=calendar_id,
|
|
requires_sync=True,
|
|
configuration=config,
|
|
styles=styles,
|
|
)
|
|
|
|
"""
|
|
calendar = {
|
|
"shortName": short_name,
|
|
"googleCalendarId": google_calendar_id,
|
|
"requiresSync": requires_sync,
|
|
"configuration": configuration,
|
|
"styles": styles,
|
|
"kind": "calendar",
|
|
}
|
|
calendar.update(other_settings)
|
|
return self._create_widget(calendar)
|
|
|
|
@_deprecate_args("short_name", "data", "styles", "description")
|
|
def add_community_list(
|
|
self,
|
|
*,
|
|
data: list[str | praw.models.Subreddit],
|
|
description: str = "",
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
**other_settings: Any,
|
|
) -> praw.models.CommunityList:
|
|
"""Add and return a :class:`.CommunityList` widget.
|
|
|
|
:param data: A list of subreddits. Subreddits can be represented as ``str`` or
|
|
as :class:`.Subreddit`. These types may be mixed within the list.
|
|
:param description: A string containing Markdown (default: ``""``).
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
|
|
:returns: The created :class:`.CommunityList`.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
subreddits = ["learnpython", reddit.subreddit("redditdev")]
|
|
new_widget = widget_moderation.add_community_list(
|
|
short_name="My fav subs", data=subreddits, styles=styles, description="description"
|
|
)
|
|
|
|
"""
|
|
community_list = {
|
|
"data": data,
|
|
"kind": "community-list",
|
|
"shortName": short_name,
|
|
"styles": styles,
|
|
"description": description,
|
|
}
|
|
community_list.update(other_settings)
|
|
return self._create_widget(community_list)
|
|
|
|
@_deprecate_args("short_name", "text", "css", "height", "image_data", "styles")
|
|
def add_custom_widget(
|
|
self,
|
|
*,
|
|
css: str,
|
|
height: int,
|
|
image_data: list[dict[str, str | int]],
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
text: str,
|
|
**other_settings: Any,
|
|
) -> praw.models.CustomWidget:
|
|
"""Add and return a :class:`.CustomWidget`.
|
|
|
|
:param css: The CSS for the widget, no longer than 100000 characters.
|
|
|
|
.. note::
|
|
|
|
As of this writing, Reddit will not accept empty CSS. If you wish to
|
|
create a custom widget without CSS, consider using ``"/**/"`` (an empty
|
|
comment) as your CSS.
|
|
|
|
:param height: The height of the widget, between 50 and 500.
|
|
:param image_data: A list of dictionaries as specified in `Reddit docs`_. Each
|
|
dictionary represents an image and has the key ``"url"`` which maps to the
|
|
URL of an image hosted on Reddit's servers. Images should be uploaded using
|
|
:meth:`.upload_image`.
|
|
|
|
For example:
|
|
|
|
.. code-block:: python
|
|
|
|
[
|
|
{
|
|
"url": "https://some.link", # from upload_image()
|
|
"width": 600,
|
|
"height": 450,
|
|
"name": "logo",
|
|
},
|
|
{
|
|
"url": "https://other.link", # from upload_image()
|
|
"width": 450,
|
|
"height": 600,
|
|
"name": "icon",
|
|
},
|
|
]
|
|
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
:param text: The Markdown text displayed in the widget.
|
|
|
|
:returns: The created :class:`.CustomWidget`.
|
|
|
|
.. _reddit docs: https://www.reddit.com/dev/api#POST_api_widget
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
image_paths = ["/path/to/image1.jpg", "/path/to/image2.png"]
|
|
image_urls = [widget_moderation.upload_image(img_path) for img_path in image_paths]
|
|
image_data = [
|
|
{"width": 600, "height": 450, "name": "logo", "url": image_urls[0]},
|
|
{"width": 450, "height": 600, "name": "icon", "url": image_urls[1]},
|
|
]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
new_widget = widget_moderation.add_custom_widget(
|
|
image_short_name="My widget",
|
|
text="# Hello world!",
|
|
css="/**/",
|
|
height=200,
|
|
image_data=image_data,
|
|
styles=styles,
|
|
)
|
|
|
|
"""
|
|
custom_widget = {
|
|
"css": css,
|
|
"height": height,
|
|
"imageData": image_data,
|
|
"kind": "custom",
|
|
"shortName": short_name,
|
|
"styles": styles,
|
|
"text": text,
|
|
}
|
|
custom_widget.update(other_settings)
|
|
return self._create_widget(custom_widget)
|
|
|
|
@_deprecate_args("short_name", "data", "styles")
|
|
def add_image_widget(
|
|
self,
|
|
*,
|
|
data: list[dict[str, str | int]],
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
**other_settings: Any,
|
|
) -> praw.models.ImageWidget:
|
|
"""Add and return an :class:`.ImageWidget`.
|
|
|
|
:param data: A list of dictionaries as specified in `Reddit docs`_. Each
|
|
dictionary has the key ``"url"`` which maps to the URL of an image hosted on
|
|
Reddit's servers. Images should be uploaded using :meth:`.upload_image`.
|
|
|
|
For example:
|
|
|
|
.. code-block:: python
|
|
|
|
[
|
|
{
|
|
"url": "https://some.link", # from upload_image()
|
|
"width": 600,
|
|
"height": 450,
|
|
"linkUrl": "https://github.com/praw-dev/praw",
|
|
},
|
|
{
|
|
"url": "https://other.link", # from upload_image()
|
|
"width": 450,
|
|
"height": 600,
|
|
"linkUrl": "https://praw.readthedocs.io",
|
|
},
|
|
]
|
|
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
|
|
:returns: The created :class:`.ImageWidget`.
|
|
|
|
.. _reddit docs: https://www.reddit.com/dev/api#POST_api_widget
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
image_paths = ["/path/to/image1.jpg", "/path/to/image2.png"]
|
|
image_data = [
|
|
{
|
|
"width": 600,
|
|
"height": 450,
|
|
"linkUrl": "",
|
|
"url": widget_moderation.upload_image(img_path),
|
|
}
|
|
for img_path in image_paths
|
|
]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
new_widget = widget_moderation.add_image_widget(
|
|
short_name="My cool pictures", data=image_data, styles=styles
|
|
)
|
|
|
|
"""
|
|
image_widget = {
|
|
"data": data,
|
|
"kind": "image",
|
|
"shortName": short_name,
|
|
"styles": styles,
|
|
}
|
|
image_widget.update(other_settings)
|
|
return self._create_widget(image_widget)
|
|
|
|
@_deprecate_args("data")
|
|
def add_menu(
|
|
self,
|
|
*,
|
|
data: list[dict[str, list[dict[str, str]] | str]],
|
|
**other_settings: Any,
|
|
) -> praw.models.Menu | Widget:
|
|
"""Add and return a :class:`.Menu` widget.
|
|
|
|
:param data: A list of dictionaries describing menu contents, as specified in
|
|
`Reddit docs`_. As of this writing, the format is:
|
|
|
|
.. code-block:: text
|
|
|
|
[
|
|
{
|
|
"text": a string no longer than 20 characters,
|
|
"url": a valid URL
|
|
},
|
|
|
|
OR
|
|
|
|
{
|
|
"children": [
|
|
{
|
|
"text": a string no longer than 20 characters,
|
|
"url": a valid URL,
|
|
},
|
|
...
|
|
],
|
|
"text": a string no longer than 20 characters,
|
|
},
|
|
...
|
|
]
|
|
|
|
|
|
:returns: The created :class:`.Menu`.
|
|
|
|
.. _reddit docs: https://www.reddit.com/dev/api#POST_api_widget
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
menu_contents = [
|
|
{"text": "My homepage", "url": "https://example.com"},
|
|
{
|
|
"text": "Python packages",
|
|
"children": [
|
|
{"text": "PRAW", "url": "https://praw.readthedocs.io/"},
|
|
{"text": "requests", "url": "https://docs.python-requests.org/"},
|
|
],
|
|
},
|
|
{"text": "Reddit homepage", "url": "https://reddit.com"},
|
|
]
|
|
new_widget = widget_moderation.add_menu(data=menu_contents)
|
|
|
|
"""
|
|
menu = {"data": data, "kind": "menu"}
|
|
menu.update(other_settings)
|
|
return self._create_widget(menu)
|
|
|
|
@_deprecate_args("short_name", "display", "order", "styles")
|
|
def add_post_flair_widget(
|
|
self,
|
|
*,
|
|
display: str,
|
|
order: list[str],
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
**other_settings: Any,
|
|
) -> praw.models.PostFlairWidget | Widget:
|
|
"""Add and return a :class:`.PostFlairWidget`.
|
|
|
|
:param display: Display style. Either ``"cloud"`` or ``"list"``.
|
|
:param order: A list of flair template IDs. You can get all flair template IDs
|
|
in a subreddit with:
|
|
|
|
.. code-block:: python
|
|
|
|
flairs = [f["id"] for f in subreddit.flair.link_templates]
|
|
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
|
|
:returns: The created :class:`.PostFlairWidget`.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
subreddit = reddit.subreddit("test")
|
|
widget_moderation = subreddit.widgets.mod
|
|
flairs = [f["id"] for f in subreddit.flair.link_templates]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
new_widget = widget_moderation.add_post_flair_widget(
|
|
short_name="Some flairs", display="list", order=flairs, styles=styles
|
|
)
|
|
|
|
"""
|
|
post_flair = {
|
|
"kind": "post-flair",
|
|
"display": display,
|
|
"shortName": short_name,
|
|
"order": order,
|
|
"styles": styles,
|
|
}
|
|
post_flair.update(other_settings)
|
|
return self._create_widget(post_flair)
|
|
|
|
@_deprecate_args("short_name", "text", "styles")
|
|
def add_text_area(
|
|
self,
|
|
*,
|
|
short_name: str,
|
|
styles: dict[str, str],
|
|
text: str,
|
|
**other_settings: Any,
|
|
) -> praw.models.TextArea:
|
|
"""Add and return a :class:`.TextArea` widget.
|
|
|
|
:param short_name: A name for the widget, no longer than 30 characters.
|
|
:param styles: A dictionary with keys ``"backgroundColor"`` and
|
|
``"headerColor"``, and values of hex colors. For example,
|
|
``{"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}``.
|
|
:param text: The Markdown text displayed in the widget.
|
|
|
|
:returns: The created :class:`.TextArea`.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widget_moderation = reddit.subreddit("test").widgets.mod
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
new_widget = widget_moderation.add_text_area(
|
|
short_name="My cool title", text="*Hello* **world**!", styles=styles
|
|
)
|
|
|
|
"""
|
|
text_area = {
|
|
"shortName": short_name,
|
|
"text": text,
|
|
"styles": styles,
|
|
"kind": "textarea",
|
|
}
|
|
text_area.update(other_settings)
|
|
return self._create_widget(text_area)
|
|
|
|
@_deprecate_args("new_order", "section")
|
|
def reorder(self, new_order: list[Widget | str], *, section: str = "sidebar"):
|
|
"""Reorder the widgets.
|
|
|
|
:param new_order: A list of widgets. Represented as a list that contains
|
|
:class:`.Widget` objects, or widget IDs as strings. These types may be
|
|
mixed.
|
|
:param section: The section to reorder (default: ``"sidebar"``).
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
widgets = reddit.subreddit("test").widgets
|
|
order = list(widgets.sidebar)
|
|
order.reverse()
|
|
widgets.mod.reorder(order)
|
|
|
|
"""
|
|
order = [
|
|
thing.id if isinstance(thing, Widget) else str(thing) for thing in new_order
|
|
]
|
|
path = API_PATH["widget_order"].format(
|
|
subreddit=self._subreddit, section=section
|
|
)
|
|
self._reddit.patch(path, data={"json": dumps(order), "section": section})
|
|
|
|
def upload_image(self, file_path: str) -> str:
|
|
"""Upload an image to Reddit and get the URL.
|
|
|
|
:param file_path: The path to the local file.
|
|
|
|
:returns: The URL of the uploaded image as a ``str``.
|
|
|
|
This method is used to upload images for widgets. For example, it can be used in
|
|
conjunction with :meth:`.add_image_widget`, :meth:`.add_custom_widget`, and
|
|
:meth:`.add_button_widget`.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
my_sub = reddit.subreddit("test")
|
|
image_url = my_sub.widgets.mod.upload_image("/path/to/image.jpg")
|
|
image_data = [{"width": 300, "height": 300, "url": image_url, "linkUrl": ""}]
|
|
styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
|
|
my_sub.widgets.mod.add_image_widget(
|
|
short_name="My cool pictures", data=image_data, styles=styles
|
|
)
|
|
|
|
"""
|
|
file = Path(file_path)
|
|
img_data = {
|
|
"filepath": file.name,
|
|
"mimetype": "image/jpeg",
|
|
}
|
|
if file_path.lower().endswith(".png"):
|
|
img_data["mimetype"] = "image/png"
|
|
|
|
url = API_PATH["widget_lease"].format(subreddit=self._subreddit)
|
|
# until we learn otherwise, assume this request always succeeds
|
|
upload_lease = self._reddit.post(url, data=img_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()
|
|
|
|
return f"{upload_url}/{upload_data['key']}"
|