We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
PRAW raises prawcore.exceptions.BadRequest when attempting to call send_removal_message()
## Issue Description

Any time I try to use `send_removal_message()`, PRAW gives me a 400 error. Looking at the official Reddit API, it appears the endpoints for that function have been removed.

**Attempt:**
```python
PS C:\Users\Michael\nobackup\Code\Projects\myproject> py
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import praw
>>> reddit = praw.Reddit(user_agent='[my user agent]')
>>> sub = reddit.comment('[a comment I have the ability to moderate]')
>>> sub
Comment(id='[same as before]')
>>> sub.body
'Test comment'
>>> sub.mod
<praw.models.reddit.comment.CommentModeration object at 0x00F20EF0>
>>> sub.mod.send_removal_message
<bound method ThingModerationMixin.send_removal_message of <praw.models.reddit.comment.CommentModeration object at 0x00F20EF0>>
>>> sub.mod.send_removal_message('hi')
{"item_id": ["t1_ew39cg7"], "message": "hi", "title": "ignored", "type": "public"}  # I added this for debugging
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Michael\AppData\Local\Programs\Python\Python37-32\lib\site-packages\praw\models\reddit\mixins\__init__.py", line 174, in send_removal_message
    return self.thing._reddit.post(url, data={"json": dumps(data)}) or None
  File "C:\Users\Michael\AppData\Local\Programs\Python\Python37-32\lib\site-packages\praw\reddit.py", line 523, in post
    "POST", path, data=data or {}, files=files, params=params
  File "C:\Users\Michael\AppData\Local\Programs\Python\Python37-32\lib\site-packages\praw\reddit.py", line 577, in request
    method, path, data=data, files=files, params=params
  File "C:\Users\Michael\AppData\Local\Programs\Python\Python37-32\lib\site-packages\prawcore\sessions.py", line 185, in request
    params=params, url=url)
  File "C:\Users\Michael\AppData\Local\Programs\Python\Python37-32\lib\site-packages\prawcore\sessions.py", line 130, in _request_with_retries
    raise self.STATUS_EXCEPTIONS[response.status_code](response)
prawcore.exceptions.BadRequest: received 400 HTTP response
>>>
```

## System Information

- PRAW Version: 6.3.1
- Python Version: 3.7.0
- Operating System: Windows 10 x64


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `praw/models/reddit/mixins/__init__.py`
Content:
```
1 """Package providing reddit class mixins."""
2 from json import dumps
3 
4 from ....const import API_PATH
5 from .editable import EditableMixin
6 from .fullname import FullnameMixin
7 from .gildable import GildableMixin
8 from .inboxable import InboxableMixin
9 from .inboxtoggleable import InboxToggleableMixin
10 from .messageable import MessageableMixin
11 from .replyable import ReplyableMixin
12 from .reportable import ReportableMixin
13 from .savable import SavableMixin
14 from .votable import VotableMixin
15 
16 
17 class ThingModerationMixin:
18     """Provides moderation methods for Comments and Submissions."""
19 
20     REMOVAL_MESSAGE_API = None
21 
22     def _add_removal_reason(self, mod_note="", reason_id=None):
23         """Add a removal reason for a Comment or Submission.
24 
25         :param mod_note: A message for the other moderators.
26         :param reason_id: The removal reason ID.
27 
28         It is necessary to first call :meth:`~.remove` on the
29         :class:`~.Comment` or :class:`~.Submission`.
30 
31         If ``reason_id`` is not specified, ``mod_note`` cannot be blank.
32 
33         """
34         if not reason_id and not mod_note:
35             raise ValueError(
36                 "mod_note cannot be blank if reason_id is not specified"
37             )
38         # Only the first element of the item_id list is used.
39         data = {
40             "item_ids": [self.thing.fullname],
41             "mod_note": mod_note,
42             "reason_id": reason_id,
43         }
44         self.thing._reddit.post(
45             API_PATH["removal_reasons"], data={"json": dumps(data)}
46         )
47 
48     def approve(self):
49         """Approve a :class:`~.Comment` or :class:`~.Submission`.
50 
51         Approving a comment or submission reverts a removal, resets the report
52         counter, adds a green check mark indicator (only visible to other
53         moderators) on the website view, and sets the ``approved_by`` attribute
54         to the authenticated user.
55 
56         Example usage:
57 
58         .. code-block:: python
59 
60            # approve a comment:
61            comment = reddit.comment('dkk4qjd')
62            comment.mod.approve()
63            # approve a submission:
64            submission = reddit.submission(id='5or86n')
65            submission.mod.approve()
66 
67         """
68         self.thing._reddit.post(
69             API_PATH["approve"], data={"id": self.thing.fullname}
70         )
71 
72     def distinguish(self, how="yes", sticky=False):
73         """Distinguish a :class:`~.Comment` or :class:`~.Submission`.
74 
75         :param how: One of 'yes', 'no', 'admin', 'special'. 'yes' adds a
76             moderator level distinguish. 'no' removes any distinction. 'admin'
77             and 'special' require special user privileges to use.
78         :param sticky: Comment is stickied if ``True``, placing it at the top
79             of the comment page regardless of score. If thing is not a
80             top-level comment, this parameter is silently ignored.
81 
82         Example usage:
83 
84         .. code-block:: python
85 
86            # distinguish and sticky a comment:
87            comment = reddit.comment('dkk4qjd')
88            comment.mod.distinguish(how='yes', sticky=True)
89            # undistinguish a submission:
90            submission = reddit.submission(id='5or86n')
91            submission.mod.distinguish(how='no')
92 
93         See also :meth:`~.undistinguish`
94 
95         """
96         data = {"how": how, "id": self.thing.fullname}
97         if sticky and getattr(self.thing, "is_root", False):
98             data["sticky"] = True
99         self.thing._reddit.post(API_PATH["distinguish"], data=data)
100 
101     def ignore_reports(self):
102         """Ignore future reports on a :class:`~.Comment` or :class:`~.Submission`.
103 
104         Calling this method will prevent future reports on this Comment or
105         Submission from both triggering notifications and appearing in the
106         various moderation listings. The report count will still increment on
107         the Comment or Submission.
108 
109         Example usage:
110 
111         .. code-block:: python
112 
113            # ignore future reports on a comment:
114            comment = reddit.comment('dkk4qjd')
115            comment.mod.ignore_reports()
116            # ignore future reports on a submission:
117            submission = reddit.submission(id='5or86n')
118            submission.mod.ignore_reports()
119 
120         See also :meth:`~.unignore_reports`
121 
122         """
123         self.thing._reddit.post(
124             API_PATH["ignore_reports"], data={"id": self.thing.fullname}
125         )
126 
127     def lock(self):
128         """Lock a :class:`~.Comment` or :class:`~.Submission`.
129 
130         Example usage:
131 
132         .. code-block:: python
133 
134            # lock a comment:
135            comment = reddit.comment('dkk4qjd')
136            comment.mod.lock()
137            # lock a submission:
138            submission = reddit.submission(id='5or86n')
139            submission.mod.lock()
140 
141         See also :meth:`~.unlock`
142 
143         """
144         self.thing._reddit.post(
145             API_PATH["lock"], data={"id": self.thing.fullname}
146         )
147 
148     def remove(self, spam=False, mod_note="", reason_id=None):
149         """Remove a :class:`~.Comment` or :class:`~.Submission`.
150 
151         :param mod_note: A message for the other moderators.
152         :param spam: When True, use the removal to help train the Subreddit's
153             spam filter (default: False).
154         :param reason_id: The removal reason ID.
155 
156         If either ``reason_id`` or ``mod_note`` are provided, a second API
157         call is made to add the removal reason.
158 
159         Example usage:
160 
161         .. code-block:: python
162 
163            # remove a comment and mark as spam:
164            comment = reddit.comment('dkk4qjd')
165            comment.mod.remove(spam=True)
166            # remove a submission
167            submission = reddit.submission(id='5or86n')
168            submission.mod.remove()
169            # remove a submission with a removal reason
170            reason = reddit.subreddit.mod.removal_reasons["110ni21zo23ql"]
171            submission = reddit.submission(id="5or86n")
172            submission.mod.remove(reason_id=reason.id)
173 
174         """
175         data = {"id": self.thing.fullname, "spam": bool(spam)}
176         self.thing._reddit.post(API_PATH["remove"], data=data)
177         if any([reason_id, mod_note]):
178             self._add_removal_reason(mod_note, reason_id)
179 
180     def send_removal_message(
181         self,
182         message,
183         title="ignored",
184         type="public",  # pylint: disable=redefined-builtin
185     ):
186         """Send a removal message for a :class:`~.Comment` or :class:`~.Submission`.
187 
188         Reddit adds human-readable information about the object to the message.
189 
190         :param type: One of 'public', 'private', 'private_exposed'.
191             'public' leaves a stickied comment on the post.
192             'private' sends a Modmail message with hidden username.
193             'private_exposed' sends a Modmail message without hidden username.
194         :param title: The short reason given in the message.
195             (Ignored if type is 'public'.)
196         :param message: The body of the message.
197 
198         If ``type`` is 'public', the new :class:`~.Comment` is returned.
199         """
200         # The API endpoint used to send removal messages is different
201         # for posts and comments, so the derived classes specify which one.
202         if self.REMOVAL_MESSAGE_API is None:
203             raise NotImplementedError("ThingModerationMixin must be extended.")
204         url = API_PATH[self.REMOVAL_MESSAGE_API]
205 
206         # Only the first element of the item_id list is used.
207         data = {
208             "item_id": [self.thing.fullname],
209             "message": message,
210             "title": title,
211             "type": type,
212         }
213 
214         return self.thing._reddit.post(url, data={"json": dumps(data)}) or None
215 
216     def undistinguish(self):
217         """Remove mod, admin, or special distinguishing from an object.
218 
219         Also unstickies the object if applicable.
220 
221         Example usage:
222 
223         .. code-block:: python
224 
225            # undistinguish a comment:
226            comment = reddit.comment('dkk4qjd')
227            comment.mod.undistinguish()
228            # undistinguish a submission:
229            submission = reddit.submission(id='5or86n')
230            submission.mod.undistinguish()
231 
232         See also :meth:`~.distinguish`
233 
234         """
235         self.distinguish(how="no")
236 
237     def unignore_reports(self):
238         """Resume receiving future reports on a Comment or Submission.
239 
240         Future reports on this :class:`~.Comment` or :class:`~.Submission`
241         will cause notifications, and appear in the various moderation
242         listings.
243 
244         Example usage:
245 
246         .. code-block:: python
247 
248            # accept future reports on a comment:
249            comment = reddit.comment('dkk4qjd')
250            comment.mod.unignore_reports()
251            # accept future reports on a submission:
252            submission = reddit.submission(id='5or86n')
253            submission.mod.unignore_reports()
254 
255         See also :meth:`~.ignore_reports`
256 
257         """
258         self.thing._reddit.post(
259             API_PATH["unignore_reports"], data={"id": self.thing.fullname}
260         )
261 
262     def unlock(self):
263         """Unlock a :class:`~.Comment` or :class:`~.Submission`.
264 
265         Example usage:
266 
267            # unlock a comment:
268            comment = reddit.comment('dkk4qjd')
269            comment.mod.unlock()
270            # unlock a submission:
271            submission = reddit.submission(id='5or86n')
272            submission.mod.unlock()
273 
274         See also :meth:`~.lock`
275 
276         """
277         self.thing._reddit.post(
278             API_PATH["unlock"], data={"id": self.thing.fullname}
279         )
280 
281 
282 class UserContentMixin(
283     EditableMixin,
284     GildableMixin,
285     InboxToggleableMixin,
286     ReplyableMixin,
287     ReportableMixin,
288     SavableMixin,
289     VotableMixin,
290 ):
291     """A convenience mixin that applies to both Comments and Submissions."""
292
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/praw/models/reddit/mixins/__init__.py b/praw/models/reddit/mixins/__init__.py
--- a/praw/models/reddit/mixins/__init__.py
+++ b/praw/models/reddit/mixins/__init__.py
@@ -184,6 +184,11 @@
     ):
         """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
+            ``prawcore.exceptions.BadRequest`` being thrown.
+
         Reddit adds human-readable information about the object to the message.
 
         :param type: One of 'public', 'private', 'private_exposed'.

{"golden_diff": "diff --git a/praw/models/reddit/mixins/__init__.py b/praw/models/reddit/mixins/__init__.py\n--- a/praw/models/reddit/mixins/__init__.py\n+++ b/praw/models/reddit/mixins/__init__.py\n@@ -184,6 +184,11 @@\n     ):\n         \"\"\"Send a removal message for a :class:`~.Comment` or :class:`~.Submission`.\n \n+        .. warning:: The object has to be removed before giving it a removal\n+            reason. Remove the object with :meth:`.remove`. Trying to add a\n+            removal reason without removing the object will result in\n+            ``prawcore.exceptions.BadRequest`` being thrown.\n+\n         Reddit adds human-readable information about the object to the message.\n \n         :param type: One of 'public', 'private', 'private_exposed'.\n", "issue": "PRAW raises prawcore.exceptions.BadRequest when attempting to call send_removal_message()\n## Issue Description\r\n\r\nAny time I try to use `send_removal_message()`, PRAW gives me a 400 error. Looking at the official Reddit API, it appears the endpoints for that function have been removed.\r\n\r\n**Attempt:**\r\n```python\r\nPS C:\\Users\\Michael\\nobackup\\Code\\Projects\\myproject> py\r\nPython 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit (Intel)] on win32\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n>>> import praw\r\n>>> reddit = praw.Reddit(user_agent='[my user agent]')\r\n>>> sub = reddit.comment('[a comment I have the ability to moderate]')\r\n>>> sub\r\nComment(id='[same as before]')\r\n>>> sub.body\r\n'Test comment'\r\n>>> sub.mod\r\n<praw.models.reddit.comment.CommentModeration object at 0x00F20EF0>\r\n>>> sub.mod.send_removal_message\r\n<bound method ThingModerationMixin.send_removal_message of <praw.models.reddit.comment.CommentModeration object at 0x00F20EF0>>\r\n>>> sub.mod.send_removal_message('hi')\r\n{\"item_id\": [\"t1_ew39cg7\"], \"message\": \"hi\", \"title\": \"ignored\", \"type\": \"public\"}  # I added this for debugging\r\nTraceback (most recent call last):\r\n  File \"<stdin>\", line 1, in <module>\r\n  File \"C:\\Users\\Michael\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\praw\\models\\reddit\\mixins\\__init__.py\", line 174, in send_removal_message\r\n    return self.thing._reddit.post(url, data={\"json\": dumps(data)}) or None\r\n  File \"C:\\Users\\Michael\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\praw\\reddit.py\", line 523, in post\r\n    \"POST\", path, data=data or {}, files=files, params=params\r\n  File \"C:\\Users\\Michael\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\praw\\reddit.py\", line 577, in request\r\n    method, path, data=data, files=files, params=params\r\n  File \"C:\\Users\\Michael\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\prawcore\\sessions.py\", line 185, in request\r\n    params=params, url=url)\r\n  File \"C:\\Users\\Michael\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\prawcore\\sessions.py\", line 130, in _request_with_retries\r\n    raise self.STATUS_EXCEPTIONS[response.status_code](response)\r\nprawcore.exceptions.BadRequest: received 400 HTTP response\r\n>>>\r\n```\r\n\r\n## System Information\r\n\r\n- PRAW Version: 6.3.1\r\n- Python Version: 3.7.0\r\n- Operating System: Windows 10 x64\r\n\n", "before_files": [{"content": "\"\"\"Package providing reddit class mixins.\"\"\"\nfrom json import dumps\n\nfrom ....const import API_PATH\nfrom .editable import EditableMixin\nfrom .fullname import FullnameMixin\nfrom .gildable import GildableMixin\nfrom .inboxable import InboxableMixin\nfrom .inboxtoggleable import InboxToggleableMixin\nfrom .messageable import MessageableMixin\nfrom .replyable import ReplyableMixin\nfrom .reportable import ReportableMixin\nfrom .savable import SavableMixin\nfrom .votable import VotableMixin\n\n\nclass ThingModerationMixin:\n    \"\"\"Provides moderation methods for Comments and Submissions.\"\"\"\n\n    REMOVAL_MESSAGE_API = None\n\n    def _add_removal_reason(self, mod_note=\"\", reason_id=None):\n        \"\"\"Add a removal reason for a Comment or Submission.\n\n        :param mod_note: A message for the other moderators.\n        :param reason_id: The removal reason ID.\n\n        It is necessary to first call :meth:`~.remove` on the\n        :class:`~.Comment` or :class:`~.Submission`.\n\n        If ``reason_id`` is not specified, ``mod_note`` cannot be blank.\n\n        \"\"\"\n        if not reason_id and not mod_note:\n            raise ValueError(\n                \"mod_note cannot be blank if reason_id is not specified\"\n            )\n        # Only the first element of the item_id list is used.\n        data = {\n            \"item_ids\": [self.thing.fullname],\n            \"mod_note\": mod_note,\n            \"reason_id\": reason_id,\n        }\n        self.thing._reddit.post(\n            API_PATH[\"removal_reasons\"], data={\"json\": dumps(data)}\n        )\n\n    def approve(self):\n        \"\"\"Approve a :class:`~.Comment` or :class:`~.Submission`.\n\n        Approving a comment or submission reverts a removal, resets the report\n        counter, adds a green check mark indicator (only visible to other\n        moderators) on the website view, and sets the ``approved_by`` attribute\n        to the authenticated user.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # approve a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.approve()\n           # approve a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.approve()\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"approve\"], data={\"id\": self.thing.fullname}\n        )\n\n    def distinguish(self, how=\"yes\", sticky=False):\n        \"\"\"Distinguish a :class:`~.Comment` or :class:`~.Submission`.\n\n        :param how: One of 'yes', 'no', 'admin', 'special'. 'yes' adds a\n            moderator level distinguish. 'no' removes any distinction. 'admin'\n            and 'special' require special user privileges to use.\n        :param sticky: Comment is stickied if ``True``, placing it at the top\n            of the comment page regardless of score. If thing is not a\n            top-level comment, this parameter is silently ignored.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # distinguish and sticky a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.distinguish(how='yes', sticky=True)\n           # undistinguish a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.distinguish(how='no')\n\n        See also :meth:`~.undistinguish`\n\n        \"\"\"\n        data = {\"how\": how, \"id\": self.thing.fullname}\n        if sticky and getattr(self.thing, \"is_root\", False):\n            data[\"sticky\"] = True\n        self.thing._reddit.post(API_PATH[\"distinguish\"], data=data)\n\n    def ignore_reports(self):\n        \"\"\"Ignore future reports on a :class:`~.Comment` or :class:`~.Submission`.\n\n        Calling this method will prevent future reports on this Comment or\n        Submission from both triggering notifications and appearing in the\n        various moderation listings. The report count will still increment on\n        the Comment or Submission.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # ignore future reports on a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.ignore_reports()\n           # ignore future reports on a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.ignore_reports()\n\n        See also :meth:`~.unignore_reports`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"ignore_reports\"], data={\"id\": self.thing.fullname}\n        )\n\n    def lock(self):\n        \"\"\"Lock a :class:`~.Comment` or :class:`~.Submission`.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # lock a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.lock()\n           # lock a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.lock()\n\n        See also :meth:`~.unlock`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"lock\"], data={\"id\": self.thing.fullname}\n        )\n\n    def remove(self, spam=False, mod_note=\"\", reason_id=None):\n        \"\"\"Remove a :class:`~.Comment` or :class:`~.Submission`.\n\n        :param mod_note: A message for the other moderators.\n        :param spam: When True, use the removal to help train the Subreddit's\n            spam filter (default: False).\n        :param reason_id: The removal reason ID.\n\n        If either ``reason_id`` or ``mod_note`` are provided, a second API\n        call is made to add the removal reason.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # remove a comment and mark as spam:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.remove(spam=True)\n           # remove a submission\n           submission = reddit.submission(id='5or86n')\n           submission.mod.remove()\n           # remove a submission with a removal reason\n           reason = reddit.subreddit.mod.removal_reasons[\"110ni21zo23ql\"]\n           submission = reddit.submission(id=\"5or86n\")\n           submission.mod.remove(reason_id=reason.id)\n\n        \"\"\"\n        data = {\"id\": self.thing.fullname, \"spam\": bool(spam)}\n        self.thing._reddit.post(API_PATH[\"remove\"], data=data)\n        if any([reason_id, mod_note]):\n            self._add_removal_reason(mod_note, reason_id)\n\n    def send_removal_message(\n        self,\n        message,\n        title=\"ignored\",\n        type=\"public\",  # pylint: disable=redefined-builtin\n    ):\n        \"\"\"Send a removal message for a :class:`~.Comment` or :class:`~.Submission`.\n\n        Reddit adds human-readable information about the object to the message.\n\n        :param type: One of 'public', 'private', 'private_exposed'.\n            'public' leaves a stickied comment on the post.\n            'private' sends a Modmail message with hidden username.\n            'private_exposed' sends a Modmail message without hidden username.\n        :param title: The short reason given in the message.\n            (Ignored if type is 'public'.)\n        :param message: The body of the message.\n\n        If ``type`` is 'public', the new :class:`~.Comment` is returned.\n        \"\"\"\n        # The API endpoint used to send removal messages is different\n        # for posts and comments, so the derived classes specify which one.\n        if self.REMOVAL_MESSAGE_API is None:\n            raise NotImplementedError(\"ThingModerationMixin must be extended.\")\n        url = API_PATH[self.REMOVAL_MESSAGE_API]\n\n        # Only the first element of the item_id list is used.\n        data = {\n            \"item_id\": [self.thing.fullname],\n            \"message\": message,\n            \"title\": title,\n            \"type\": type,\n        }\n\n        return self.thing._reddit.post(url, data={\"json\": dumps(data)}) or None\n\n    def undistinguish(self):\n        \"\"\"Remove mod, admin, or special distinguishing from an object.\n\n        Also unstickies the object if applicable.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # undistinguish a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.undistinguish()\n           # undistinguish a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.undistinguish()\n\n        See also :meth:`~.distinguish`\n\n        \"\"\"\n        self.distinguish(how=\"no\")\n\n    def unignore_reports(self):\n        \"\"\"Resume receiving future reports on a Comment or Submission.\n\n        Future reports on this :class:`~.Comment` or :class:`~.Submission`\n        will cause notifications, and appear in the various moderation\n        listings.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # accept future reports on a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.unignore_reports()\n           # accept future reports on a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.unignore_reports()\n\n        See also :meth:`~.ignore_reports`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"unignore_reports\"], data={\"id\": self.thing.fullname}\n        )\n\n    def unlock(self):\n        \"\"\"Unlock a :class:`~.Comment` or :class:`~.Submission`.\n\n        Example usage:\n\n           # unlock a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.unlock()\n           # unlock a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.unlock()\n\n        See also :meth:`~.lock`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"unlock\"], data={\"id\": self.thing.fullname}\n        )\n\n\nclass UserContentMixin(\n    EditableMixin,\n    GildableMixin,\n    InboxToggleableMixin,\n    ReplyableMixin,\n    ReportableMixin,\n    SavableMixin,\n    VotableMixin,\n):\n    \"\"\"A convenience mixin that applies to both Comments and Submissions.\"\"\"\n", "path": "praw/models/reddit/mixins/__init__.py"}], "after_files": [{"content": "\"\"\"Package providing reddit class mixins.\"\"\"\nfrom json import dumps\nfrom ....const import API_PATH\nfrom .editable import EditableMixin\nfrom .fullname import FullnameMixin\nfrom .gildable import GildableMixin\nfrom .inboxable import InboxableMixin\nfrom .inboxtoggleable import InboxToggleableMixin\nfrom .messageable import MessageableMixin\nfrom .replyable import ReplyableMixin\nfrom .reportable import ReportableMixin\nfrom .savable import SavableMixin\nfrom .votable import VotableMixin\n\n\nclass ThingModerationMixin:\n    \"\"\"Provides moderation methods for Comments and Submissions.\"\"\"\n\n    REMOVAL_MESSAGE_API = None\n\n    def _add_removal_reason(self, mod_note=\"\", reason_id=None):\n        \"\"\"Add a removal reason for a Comment or Submission.\n\n        :param mod_note: A message for the other moderators.\n        :param reason_id: The removal reason ID.\n\n        It is necessary to first call :meth:`~.remove` on the\n        :class:`~.Comment` or :class:`~.Submission`.\n\n        If ``reason_id`` is not specified, ``mod_note`` cannot be blank.\n\n        \"\"\"\n        if not reason_id and not mod_note:\n            raise ValueError(\n                \"mod_note cannot be blank if reason_id is not specified\"\n            )\n        # Only the first element of the item_id list is used.\n        data = {\n            \"item_ids\": [self.thing.fullname],\n            \"mod_note\": mod_note,\n            \"reason_id\": reason_id,\n        }\n        self.thing._reddit.post(\n            API_PATH[\"removal_reasons\"], data={\"json\": dumps(data)}\n        )\n\n    def approve(self):\n        \"\"\"Approve a :class:`~.Comment` or :class:`~.Submission`.\n\n        Approving a comment or submission reverts a removal, resets the report\n        counter, adds a green check mark indicator (only visible to other\n        moderators) on the website view, and sets the ``approved_by`` attribute\n        to the authenticated user.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # approve a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.approve()\n           # approve a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.approve()\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"approve\"], data={\"id\": self.thing.fullname}\n        )\n\n    def distinguish(self, how=\"yes\", sticky=False):\n        \"\"\"Distinguish a :class:`~.Comment` or :class:`~.Submission`.\n\n        :param how: One of 'yes', 'no', 'admin', 'special'. 'yes' adds a\n            moderator level distinguish. 'no' removes any distinction. 'admin'\n            and 'special' require special user privileges to use.\n        :param sticky: Comment is stickied if ``True``, placing it at the top\n            of the comment page regardless of score. If thing is not a\n            top-level comment, this parameter is silently ignored.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # distinguish and sticky a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.distinguish(how='yes', sticky=True)\n           # undistinguish a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.distinguish(how='no')\n\n        See also :meth:`~.undistinguish`\n\n        \"\"\"\n        data = {\"how\": how, \"id\": self.thing.fullname}\n        if sticky and getattr(self.thing, \"is_root\", False):\n            data[\"sticky\"] = True\n        self.thing._reddit.post(API_PATH[\"distinguish\"], data=data)\n\n    def ignore_reports(self):\n        \"\"\"Ignore future reports on a :class:`~.Comment` or :class:`~.Submission`.\n\n        Calling this method will prevent future reports on this Comment or\n        Submission from both triggering notifications and appearing in the\n        various moderation listings. The report count will still increment on\n        the Comment or Submission.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # ignore future reports on a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.ignore_reports()\n           # ignore future reports on a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.ignore_reports()\n\n        See also :meth:`~.unignore_reports`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"ignore_reports\"], data={\"id\": self.thing.fullname}\n        )\n\n    def lock(self):\n        \"\"\"Lock a :class:`~.Comment` or :class:`~.Submission`.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # lock a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.lock()\n           # lock a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.lock()\n\n        See also :meth:`~.unlock`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"lock\"], data={\"id\": self.thing.fullname}\n        )\n\n    def remove(self, spam=False, mod_note=\"\", reason_id=None):\n        \"\"\"Remove a :class:`~.Comment` or :class:`~.Submission`.\n\n        :param mod_note: A message for the other moderators.\n        :param spam: When True, use the removal to help train the Subreddit's\n            spam filter (default: False).\n        :param reason_id: The removal reason ID.\n\n        If either ``reason_id`` or ``mod_note`` are provided, a second API\n        call is made to add the removal reason.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # remove a comment and mark as spam:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.remove(spam=True)\n           # remove a submission\n           submission = reddit.submission(id='5or86n')\n           submission.mod.remove()\n           # remove a submission with a removal reason\n           reason = reddit.subreddit.mod.removal_reasons[\"110ni21zo23ql\"]\n           submission = reddit.submission(id=\"5or86n\")\n           submission.mod.remove(reason_id=reason.id)\n\n        \"\"\"\n        data = {\"id\": self.thing.fullname, \"spam\": bool(spam)}\n        self.thing._reddit.post(API_PATH[\"remove\"], data=data)\n        if any([reason_id, mod_note]):\n            self._add_removal_reason(mod_note, reason_id)\n\n    def send_removal_message(\n        self,\n        message,\n        title=\"ignored\",\n        type=\"public\",  # pylint: disable=redefined-builtin\n    ):\n        \"\"\"Send a removal message for a :class:`~.Comment` or :class:`~.Submission`.\n\n        .. warning:: The object has to be removed before giving it a removal\n            reason. Remove the object with :meth:`.remove`. Trying to add a\n            removal reason without removing the object will result in\n            ``prawcore.exceptions.BadRequest`` being thrown.\n\n        Reddit adds human-readable information about the object to the message.\n\n        :param type: One of 'public', 'private', 'private_exposed'.\n            'public' leaves a stickied comment on the post.\n            'private' sends a Modmail message with hidden username.\n            'private_exposed' sends a Modmail message without hidden username.\n        :param title: The short reason given in the message.\n            (Ignored if type is 'public'.)\n        :param message: The body of the message.\n\n        If ``type`` is 'public', the new :class:`~.Comment` is returned.\n        \"\"\"\n        # The API endpoint used to send removal messages is different\n        # for posts and comments, so the derived classes specify which one.\n        if self.REMOVAL_MESSAGE_API is None:\n            raise NotImplementedError(\"ThingModerationMixin must be extended.\")\n        url = API_PATH[self.REMOVAL_MESSAGE_API]\n\n        # Only the first element of the item_id list is used.\n        data = {\n            \"item_id\": [self.thing.fullname],\n            \"message\": message,\n            \"title\": title,\n            \"type\": type,\n        }\n\n        return self.thing._reddit.post(url, data={\"json\": dumps(data)}) or None\n\n    def undistinguish(self):\n        \"\"\"Remove mod, admin, or special distinguishing from an object.\n\n        Also unstickies the object if applicable.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # undistinguish a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.undistinguish()\n           # undistinguish a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.undistinguish()\n\n        See also :meth:`~.distinguish`\n\n        \"\"\"\n        self.distinguish(how=\"no\")\n\n    def unignore_reports(self):\n        \"\"\"Resume receiving future reports on a Comment or Submission.\n\n        Future reports on this :class:`~.Comment` or :class:`~.Submission`\n        will cause notifications, and appear in the various moderation\n        listings.\n\n        Example usage:\n\n        .. code-block:: python\n\n           # accept future reports on a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.unignore_reports()\n           # accept future reports on a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.unignore_reports()\n\n        See also :meth:`~.ignore_reports`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"unignore_reports\"], data={\"id\": self.thing.fullname}\n        )\n\n    def unlock(self):\n        \"\"\"Unlock a :class:`~.Comment` or :class:`~.Submission`.\n\n        Example usage:\n\n           # unlock a comment:\n           comment = reddit.comment('dkk4qjd')\n           comment.mod.unlock()\n           # unlock a submission:\n           submission = reddit.submission(id='5or86n')\n           submission.mod.unlock()\n\n        See also :meth:`~.lock`\n\n        \"\"\"\n        self.thing._reddit.post(\n            API_PATH[\"unlock\"], data={\"id\": self.thing.fullname}\n        )\n\n\nclass UserContentMixin(\n    EditableMixin,\n    GildableMixin,\n    InboxToggleableMixin,\n    ReplyableMixin,\n    ReportableMixin,\n    SavableMixin,\n    VotableMixin,\n):\n    \"\"\"A convenience mixin that applies to both Comments and Submissions.\"\"\"\n", "path": "praw/models/reddit/mixins/__init__.py"}]}