From 09c03296db1671e80f5e7814045e826f1975fb0b Mon Sep 17 00:00:00 2001 From: Lorenz Diener Date: Sun, 28 Apr 2019 23:12:27 +0200 Subject: [PATCH] Polls --- docs/index.rst | 41 ++++- mastodon/Mastodon.py | 97 +++++++++-- tests/cassettes/test_poll_illegal_vote.yaml | 96 ++++++++++ tests/cassettes/test_polls.yaml | 184 ++++++++++++++++++++ 4 files changed, 401 insertions(+), 17 deletions(-) create mode 100644 tests/cassettes/test_poll_illegal_vote.yaml create mode 100644 tests/cassettes/test_polls.yaml diff --git a/docs/index.rst b/docs/index.rst index a98eb23..6e798d7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -314,6 +314,7 @@ Toot dicts 'replies_count': # The number of replies to this status. 'card': # A preview card for links from the status, if present at time of delivery, # as card dict. + 'poll': # A poll dict if a poll is attached to this status. } Mention dicts @@ -355,7 +356,29 @@ Scheduled toot dicts }, 'media_attachments': # Array of media dicts for the attachments to the scheduled toot } - + +Poll dicts +~~~~~~~~~~ +.. _poll dict: + +.. code-block:: python + + # Returns the following dictionary: + mastodon.poll(id) + { + 'id': # The polls ID + 'expires_at': # The time at which the poll is set to expire + 'expired': # Boolean denoting whether you can still vote in this poll + 'multiple': # Boolean indicating whether it is allowed to vote for more than one option + 'votes_count': # Total number of votes cast in this poll + 'voted': # Boolean indicating whether the logged-in user has already voted in this poll + 'options': # The poll options as a list of dicts, each option with a `title` and a + # `votes_count` field. `votes_count` can be None if the poll creator has + # chosen to hide vote totals until the poll expires and it hasn't yet. + 'emojis': # List of emoji dicts for all emoji used in answer strings + } + + Conversation dicts ~~~~~~~~~~~~~~~~~~ .. _conversation dict: @@ -797,13 +820,19 @@ These functions allow you to get information about single statuses. .. automethod:: Mastodon.status_favourited_by .. automethod:: Mastodon.status_card -Writing data: Scheduled statuses +Reading data: Scheduled statuses ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These functions allow you to get information about scheduled statuses. .. automethod:: Mastodon.scheduled_statuses .. automethod:: Mastodon.scheduled_status +Reading data: Polls +~~~~~~~~~~~~~~~~~~~ +This function allows you to get and refresh information about polls. + +.. automethod:: Mastodon.poll + Reading data: Notifications --------------------------- This function allows you to get information about a users notifications. @@ -913,6 +942,8 @@ interact with already posted statuses. .. automethod:: Mastodon.status_post .. automethod:: Mastodon.status_reply .. automethod:: Mastodon.toot +.. _make_poll(): +.. automethod:: Mastodon.make_poll .. automethod:: Mastodon.status_reblog .. automethod:: Mastodon.status_unreblog .. automethod:: Mastodon.status_favourite @@ -932,6 +963,12 @@ scheduled statuses. .. automethod:: Mastodon.scheduled_status_update .. automethod:: Mastodon.scheduled_status_delete +Writing data: Polls +~~~~~~~~~~~~~~~~~~~ +This function allows you to vote in polls. + +.. automethod:: Mastodon.poll_vote + Writing data: Notifications --------------------------- These functions allow you to clear all or some notifications. diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index 6b3de9a..27acbaa 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py @@ -161,8 +161,9 @@ class Mastodon: __DICT_VERSION_MENTION = "1.0.0" __DICT_VERSION_MEDIA = "2.3.0" __DICT_VERSION_ACCOUNT = "2.4.0" - __DICT_VERSION_STATUS = bigger_version(bigger_version(bigger_version(bigger_version("2.5.0", - __DICT_VERSION_MEDIA), __DICT_VERSION_ACCOUNT), __DICT_VERSION_APPLICATION), __DICT_VERSION_MENTION) + __DICT_VERSION_POLL = "2.8.0" + __DICT_VERSION_STATUS = bigger_version(bigger_version(bigger_version(bigger_version(bigger_version("2.8.0", + __DICT_VERSION_MEDIA), __DICT_VERSION_ACCOUNT), __DICT_VERSION_APPLICATION), __DICT_VERSION_MENTION), __DICT_VERSION_POLL) __DICT_VERSION_INSTANCE = bigger_version("2.7.2", __DICT_VERSION_ACCOUNT) __DICT_VERSION_HASHTAG = "2.3.4" __DICT_VERSION_EMOJI = "2.1.0" @@ -826,6 +827,20 @@ class Mastodon: url = '/api/v1/scheduled_statuses/{0}'.format(str(id)) return self.__api_request('GET', url) + ### + # Reading data: Polls + ### + @api_version("2.8.0", "2.8.0", __DICT_VERSION_POLL) + def poll(self, id): + """ + Fetch information about the poll with the given id + + Returns a `poll dict`_. + """ + id = self.__unpack_id(id) + url = '/api/v1/polls/{0}'.format(str(id)) + return self.__api_request('GET', url) + ### # Reading data: Notifications ### @@ -1367,11 +1382,11 @@ class Mastodon: ### # Writing data: Statuses ### - @api_version("1.0.0", "2.7.0", __DICT_VERSION_STATUS) + @api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS) def status_post(self, status, in_reply_to_id=None, media_ids=None, sensitive=False, visibility=None, spoiler_text=None, language=None, idempotency_key=None, content_type=None, - scheduled_at=None): + scheduled_at=None, poll=None): """ Post a status. Can optionally be in reply to another status and contain media. @@ -1412,6 +1427,9 @@ class Mastodon: (the time must be at least 5 minutes into the future). If this is passed, status_post returns a `scheduled toot dict`_ instead. + Pass `poll` to attach a poll to the status. An appropriate object can be + constructed using `make_poll()`_ + Specify `content_type` to set the content type of your post on Pleroma. It accepts 'text/plain' (default), 'text/markdown', and 'text/html'. This parameter is not supported on Mastodon servers, but will be @@ -1466,10 +1484,14 @@ class Mastodon: if params_initial['content_type'] == None: del params_initial['content_type'] - params = self.__generate_params(params_initial, ['idempotency_key']) - return self.__api_request('POST', '/api/v1/statuses', params, headers = headers) + use_json = False + if not poll is None: + use_json = True - @api_version("1.0.0", "2.7.0", __DICT_VERSION_STATUS) + params = self.__generate_params(params_initial, ['idempotency_key']) + return self.__api_request('POST', '/api/v1/statuses', params, headers = headers, use_json = use_json) + + @api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS) def toot(self, status): """ Synonym for `status_post()`_ that only takes the status text as input. @@ -1480,10 +1502,10 @@ class Mastodon: """ return self.status_post(status) - @api_version("1.0.0", "2.7.0", __DICT_VERSION_STATUS) + @api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS) def status_reply(self, to_status, status, media_ids=None, sensitive=False, visibility=None, spoiler_text=None, language=None, idempotency_key=None, content_type=None, - scheduled_at=None, untag=False): + scheduled_at=None, poll=None, untag=False): """ Helper function - acts like status_post, but prepends the name of all the users that are being replied to to the status text and retains @@ -1516,8 +1538,22 @@ class Mastodon: return self.status_post(status, in_reply_to_id = to_status.id, media_ids = media_ids, sensitive = sensitive, visibility = visibility, spoiler_text = spoiler_text, language = language, idempotency_key = idempotency_key, content_type = content_type, - scheduled_at = scheduled_at) + scheduled_at = scheduled_at, poll = poll) + + @api_version("2.8.0", "2.8.0", __DICT_VERSION_POLL) + def make_poll(self, options, expires_in, multiple=False, hide_totals=False): + """ + Generate a poll object that can be passed as the `poll` option when posting a status. + options is an array of strings with the poll options (Maximum, by default: 4), + expires_in is the time in seconds for which the poll should be open. + Set multiple to True to allow people to choose more than one answer. Set + hide_totals to True to hide the results of the poll until it has expired. + """ + poll_params = locals() + del poll_params["self"] + return poll_params + @api_version("1.0.0", "1.0.0", "1.0.0") def status_delete(self, id): """ @@ -1653,6 +1689,34 @@ class Mastodon: url = '/api/v1/scheduled_statuses/{0}'.format(str(id)) self.__api_request('DELETE', url) + ### + # Writing data: Polls + ### + @api_version("2.8.0", "2.8.0", __DICT_VERSION_POLL) + def poll_vote(self, id, choices): + """ + Vote in the given poll. + + `choices` is the index of the choice you wish to register a vote for + (i.e. its index in the corresponding polls `options` field. In case + of a poll that allows selection of more than one option, a list of + indices can be passed. + + You can only submit choices for any given poll once in case of + single-option polls, or only once per option in case of multi-option + polls. + + Returns the updated `poll dict`_ + """ + id = self.__unpack_id(id) + if not isinstance(choices, list): + choices = [choices] + params = self.__generate_params(locals(), ['id']) + + url = '/api/v1/polls/{0}/votes'.format(id) + self.__api_request('POST', url, params) + + ### # Writing data: Notifications ### @@ -2480,7 +2544,7 @@ class Mastodon: isotime = isotime[:-2] + ":" + isotime[-2:] return isotime - def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, do_ratelimiting=True): + def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, do_ratelimiting=True, use_json = False): """ Internal API request helper. """ @@ -2527,11 +2591,14 @@ class Mastodon: try: kwargs = dict(headers=headers, files=files, timeout=self.request_timeout) - if method == 'GET': - kwargs['params'] = params + if use_json == False: + if method == 'GET': + kwargs['params'] = params + else: + kwargs['data'] = params else: - kwargs['data'] = params - + kwargs['json'] = params + response_object = self.session.request( method, self.api_base_url + endpoint, **kwargs) except Exception as e: diff --git a/tests/cassettes/test_poll_illegal_vote.yaml b/tests/cassettes/test_poll_illegal_vote.yaml new file mode 100644 index 0000000..72482b9 --- /dev/null +++ b/tests/cassettes/test_poll_illegal_vote.yaml @@ -0,0 +1,96 @@ +interactions: +- request: + body: '{"poll": {"hide_totals": false, "multiple": false, "expires_in": 300, "options": + ["four twenty", "sixty-nine"]}, "status": "nice"}' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2] + Connection: [keep-alive] + Content-Length: ['130'] + Content-Type: [application/json] + User-Agent: [python-requests/2.18.4] + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: {string: '{"id":"102005835304968136","created_at":"2019-04-28T21:05:24.088Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost/users/admin/statuses/102005835304968136","content":"\u003cp\u003enice\u003c/p\u003e","url":"http://localhost/@admin/102005835304968136","replies_count":0,"reblogs_count":0,"favourites_count":0,"favourited":false,"reblogged":false,"muted":false,"pinned":false,"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"1","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2019-04-27T18:52:42.626Z","note":"\u003cp\u003e\u003c/p\u003e","url":"http://localhost/@admin","avatar":"http://localhost/avatars/original/missing.png","avatar_static":"http://localhost/avatars/original/missing.png","header":"http://localhost/headers/original/missing.png","header_static":"http://localhost/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":25,"emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[],"emojis":[],"card":null,"poll":{"id":"16","expires_at":"2019-04-28T21:10:24.070Z","expired":false,"multiple":false,"votes_count":0,"voted":true,"options":[{"title":"four + twenty","votes_count":0},{"title":"sixty-nine","votes_count":0}],"emojis":[]}}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"cb8935a9ac8d3dea6609462bbce81c7d"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [13adc429-bd63-4a6c-8afa-e52db1b58762] + X-Runtime: ['0.342656'] + X-XSS-Protection: [1; mode=block] + content-length: ['1383'] + status: {code: 200, message: OK} +- request: + body: choices%5B%5D=1 + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN] + Connection: [keep-alive] + Content-Length: ['15'] + Content-Type: [application/x-www-form-urlencoded] + User-Agent: [python-requests/2.18.4] + method: POST + uri: http://localhost:3000/api/v1/polls/16/votes + response: + body: {string: '{"id":"16","expires_at":"2019-04-28T21:10:24.070Z","expired":false,"multiple":false,"votes_count":1,"voted":true,"options":[{"title":"four + twenty","votes_count":0},{"title":"sixty-nine","votes_count":1}],"emojis":[]}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"96d62a6ad640c2c992e83b4bffc10c7b"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [d7715baa-8b11-47b3-bc72-401b22f6296c] + X-Runtime: ['0.167098'] + X-XSS-Protection: [1; mode=block] + content-length: ['216'] + status: {code: 200, message: OK} +- request: + body: choices%5B%5D=0 + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN] + Connection: [keep-alive] + Content-Length: ['15'] + Content-Type: [application/x-www-form-urlencoded] + User-Agent: [python-requests/2.18.4] + method: POST + uri: http://localhost:3000/api/v1/polls/16/votes + response: + body: {string: "{\"error\":\"\u30D0\u30EA\u30C7\u30FC\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F: + \u3053\u306E\u30A2\u30F3\u30B1\u30FC\u30C8\u306B\u306F\u6295\u7968\u6E08\u307F\u3067\u3059\"}"} + headers: + Cache-Control: [no-cache] + Content-Type: [application/json; charset=utf-8] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [846275ac-e205-4fb2-8132-edeef42a2ff2] + X-Runtime: ['0.101521'] + X-XSS-Protection: [1; mode=block] + content-length: ['101'] + status: {code: 422, message: Unprocessable Entity} +version: 1 diff --git a/tests/cassettes/test_polls.yaml b/tests/cassettes/test_polls.yaml new file mode 100644 index 0000000..6132074 --- /dev/null +++ b/tests/cassettes/test_polls.yaml @@ -0,0 +1,184 @@ +interactions: +- request: + body: '{"poll": {"hide_totals": false, "multiple": true, "expires_in": 300, "options": + ["four twenty", "sixty-nine"]}, "status": "nice"}' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2] + Connection: [keep-alive] + Content-Length: ['129'] + Content-Type: [application/json] + User-Agent: [python-requests/2.18.4] + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: {string: '{"id":"102005835265128615","created_at":"2019-04-28T21:05:23.464Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost/users/admin/statuses/102005835265128615","content":"\u003cp\u003enice\u003c/p\u003e","url":"http://localhost/@admin/102005835265128615","replies_count":0,"reblogs_count":0,"favourites_count":0,"favourited":false,"reblogged":false,"muted":false,"pinned":false,"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"1","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2019-04-27T18:52:42.626Z","note":"\u003cp\u003e\u003c/p\u003e","url":"http://localhost/@admin","avatar":"http://localhost/avatars/original/missing.png","avatar_static":"http://localhost/avatars/original/missing.png","header":"http://localhost/headers/original/missing.png","header_static":"http://localhost/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":25,"emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[],"emojis":[],"card":null,"poll":{"id":"15","expires_at":"2019-04-28T21:10:23.457Z","expired":false,"multiple":true,"votes_count":0,"voted":true,"options":[{"title":"four + twenty","votes_count":0},{"title":"sixty-nine","votes_count":0}],"emojis":[]}}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"9b51d6b8d66f9e0eb1c833ed6f67df71"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [f11d8fa0-461a-446e-a0f5-20b81af7f6cc] + X-Runtime: ['0.157309'] + X-XSS-Protection: [1; mode=block] + content-length: ['1382'] + status: {code: 200, message: OK} +- request: + body: choices%5B%5D=1 + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN] + Connection: [keep-alive] + Content-Length: ['15'] + Content-Type: [application/x-www-form-urlencoded] + User-Agent: [python-requests/2.18.4] + method: POST + uri: http://localhost:3000/api/v1/polls/15/votes + response: + body: {string: '{"id":"15","expires_at":"2019-04-28T21:10:23.457Z","expired":false,"multiple":true,"votes_count":1,"voted":true,"options":[{"title":"four + twenty","votes_count":0},{"title":"sixty-nine","votes_count":1}],"emojis":[]}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"5b2a849b672ccadf70a2620d4dc1cacb"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [9bfef151-c9a3-44cc-94d4-6ed33e9e7c24] + X-Runtime: ['0.079564'] + X-XSS-Protection: [1; mode=block] + content-length: ['215'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN] + Connection: [keep-alive] + User-Agent: [python-requests/2.18.4] + method: GET + uri: http://localhost:3000/api/v1/polls/15 + response: + body: {string: '{"id":"15","expires_at":"2019-04-28T21:10:23.457Z","expired":false,"multiple":true,"votes_count":1,"voted":true,"options":[{"title":"four + twenty","votes_count":0},{"title":"sixty-nine","votes_count":1}],"emojis":[]}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"5b2a849b672ccadf70a2620d4dc1cacb"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [f6a54825-bd65-49b0-a83f-39ef5124a362] + X-Runtime: ['0.043202'] + X-XSS-Protection: [1; mode=block] + content-length: ['215'] + status: {code: 200, message: OK} +- request: + body: choices%5B%5D=0 + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN] + Connection: [keep-alive] + Content-Length: ['15'] + Content-Type: [application/x-www-form-urlencoded] + User-Agent: [python-requests/2.18.4] + method: POST + uri: http://localhost:3000/api/v1/polls/15/votes + response: + body: {string: '{"id":"15","expires_at":"2019-04-28T21:10:23.457Z","expired":false,"multiple":true,"votes_count":2,"voted":true,"options":[{"title":"four + twenty","votes_count":1},{"title":"sixty-nine","votes_count":1}],"emojis":[]}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"567f9562c02e1a5533bf95d1d4bbdede"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [209f0950-c9c0-4ab8-978f-de3239546a97] + X-Runtime: ['0.062952'] + X-XSS-Protection: [1; mode=block] + content-length: ['215'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN] + Connection: [keep-alive] + User-Agent: [python-requests/2.18.4] + method: GET + uri: http://localhost:3000/api/v1/polls/15 + response: + body: {string: '{"id":"15","expires_at":"2019-04-28T21:10:23.457Z","expired":false,"multiple":true,"votes_count":2,"voted":true,"options":[{"title":"four + twenty","votes_count":1},{"title":"sixty-nine","votes_count":1}],"emojis":[]}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"567f9562c02e1a5533bf95d1d4bbdede"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [7caa443c-5605-436f-bae1-f7088dc7db1d] + X-Runtime: ['0.037629'] + X-XSS-Protection: [1; mode=block] + content-length: ['215'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2] + Connection: [keep-alive] + Content-Length: ['0'] + User-Agent: [python-requests/2.18.4] + method: DELETE + uri: http://localhost:3000/api/v1/statuses/102005835265128615 + response: + body: {string: '{}'} + headers: + Cache-Control: ['max-age=0, private, must-revalidate'] + Content-Type: [application/json; charset=utf-8] + ETag: [W/"6e856267241253766a52970611e7487c"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept-Encoding, Origin'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [9a5591f2-fbaa-4f0e-832f-86f5d1b8e6b1] + X-Runtime: ['0.044707'] + X-XSS-Protection: [1; mode=block] + content-length: ['2'] + status: {code: 200, message: OK} +version: 1