Add, test and document last-read markers. Fixes #192
This commit is contained in:
pare
1f36deb119
commit
45908b6f4e
S'han modificat 4 arxius amb 216 adicions i 3 eliminacions
|
@ -773,6 +773,20 @@ Featured tag dicts
|
|||
# (can be None if there are none)
|
||||
}
|
||||
|
||||
Read marker dicts
|
||||
~~~~~~~~~~~~~~~~~
|
||||
.. _read marker dict:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
mastodon.markers_get()["home"]
|
||||
# Returns the following dictionary:
|
||||
{
|
||||
'last_read_id': # ID of the last read object in the timeline
|
||||
'version': # A counter that is incremented whenever the marker is set to a new status
|
||||
'updated_at': # The time the marker was last set, as a datetime object
|
||||
}
|
||||
|
||||
Admin account dicts
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
.. _admin account dict:
|
||||
|
@ -990,11 +1004,20 @@ muted or blocked by the logged in user.
|
|||
.. automethod:: Mastodon.mutes
|
||||
.. automethod:: Mastodon.blocks
|
||||
|
||||
Reading data: Reports (REMOVED IN 2.5.0)
|
||||
----------------------------------------
|
||||
Reading data: Reports
|
||||
---------------------
|
||||
In Mastodon versions before 2.5.0 this function allowed for the retrieval
|
||||
of reports filed by the logged in user. It has since been removed.
|
||||
|
||||
.. automethod:: Mastodon.reports
|
||||
|
||||
|
||||
Writing data: Last-read markers
|
||||
--------------------------
|
||||
This function allows you to set get last read position for timelines.
|
||||
|
||||
.. automethod:: Mastodon.markers_get
|
||||
|
||||
Reading data: Domain blocks
|
||||
---------------------------
|
||||
|
||||
|
@ -1142,6 +1165,14 @@ Writing data: Reports
|
|||
|
||||
.. automethod:: Mastodon.report
|
||||
|
||||
Writing data: Last-read markers
|
||||
--------------------------
|
||||
This function allows you to set the last read position for timelines to
|
||||
allow for persisting where the user was reading a timeline between sessions
|
||||
and clients / devices.
|
||||
|
||||
.. automethod:: Mastodon.markers_set
|
||||
|
||||
Writing data: Domain blocks
|
||||
---------------------------
|
||||
These functions allow you to block and unblock all statuses from a domain
|
||||
|
|
|
@ -213,6 +213,7 @@ class Mastodon:
|
|||
__DICT_VERSION_PREFERENCES = "2.8.0"
|
||||
__DICT_VERSION_ADMIN_ACCOUNT = "2.9.1"
|
||||
__DICT_VERSION_FEATURED_TAG = "3.0.0"
|
||||
__DICT_VERSION_MARKER = "3.0.0"
|
||||
|
||||
###
|
||||
# Registering apps
|
||||
|
@ -1590,6 +1591,25 @@ class Mastodon:
|
|||
"""
|
||||
return self.__api_request('GET', '/api/v1/preferences')
|
||||
|
||||
##
|
||||
# Reading data: Read markers
|
||||
##
|
||||
@api_version("3.0.0", "3.0.0", __DICT_VERSION_MARKER)
|
||||
def markers_get(self, timeline=["home"]):
|
||||
"""
|
||||
Get the last-read-location markers for the specified timelines. Valid timelines
|
||||
are the same as in `timeline()`_
|
||||
|
||||
Note that despite the singular name, `timeline` can be a list.
|
||||
|
||||
Returns a dict of `read marker dicts`_, keyed by timeline name.
|
||||
"""
|
||||
if not isinstance(timeline, (list, tuple)):
|
||||
timeline = [timeline]
|
||||
params = self.__generate_params(locals())
|
||||
|
||||
return self.__api_request('GET', '/api/v1/markers', params)
|
||||
|
||||
###
|
||||
# Writing data: Statuses
|
||||
###
|
||||
|
@ -2450,6 +2470,34 @@ class Mastodon:
|
|||
params = self.__generate_params(locals())
|
||||
self.__api_request('DELETE', '/api/v1/domain_blocks', params)
|
||||
|
||||
##
|
||||
# Writing data: Read markers
|
||||
##
|
||||
@api_version("3.0.0", "3.0.0", __DICT_VERSION_MARKER)
|
||||
def markers_set(self, timelines, last_read_ids):
|
||||
"""
|
||||
Set the "last read" marker(s) for the given timeline(s) to the given id(s)
|
||||
|
||||
Note that if you give an invalid timeline name, this will silently do nothing.
|
||||
|
||||
Returns a dict with the updated `read marker dicts`_, keyed by timeline name.
|
||||
"""
|
||||
if not isinstance(timelines, (list, tuple)):
|
||||
timelines = [timelines]
|
||||
|
||||
if not isinstance(last_read_ids, (list, tuple)):
|
||||
last_read_ids = [last_read_ids]
|
||||
|
||||
if len(last_read_ids) != len(timelines):
|
||||
raise MastodonIllegalArgumentError("Number of specified timelines and ids must be the same")
|
||||
|
||||
params = collections.OrderedDict()
|
||||
for timeline, last_read_id in zip(timelines, last_read_ids):
|
||||
params[timeline] = collections.OrderedDict()
|
||||
params[timeline]["last_read_id"] = self.__unpack_id(last_read_id)
|
||||
|
||||
return self.__api_request('POST', '/api/v1/markers', params, use_json=True)
|
||||
|
||||
###
|
||||
# Writing data: Push subscriptions
|
||||
###
|
||||
|
@ -3071,7 +3119,7 @@ class Mastodon:
|
|||
"""
|
||||
Converts json string numerals to native python bignums.
|
||||
"""
|
||||
for key in ('id', 'week', 'in_reply_to_id', 'in_reply_to_account_id', 'logins', 'registrations', 'statuses', 'day'):
|
||||
for key in ('id', 'week', 'in_reply_to_id', 'in_reply_to_account_id', 'logins', 'registrations', 'statuses', 'day', 'last_read_id'):
|
||||
if (key in json_object and isinstance(json_object[key], six.text_type)):
|
||||
try:
|
||||
json_object[key] = int(json_object[key])
|
||||
|
|
117
tests/cassettes/test_markers.yaml
Normal file
117
tests/cassettes/test_markers.yaml
Normal file
|
@ -0,0 +1,117 @@
|
|||
interactions:
|
||||
- request:
|
||||
body: status=Toot%21
|
||||
headers:
|
||||
Accept: ['*/*']
|
||||
Accept-Encoding: ['gzip, deflate']
|
||||
Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN]
|
||||
Connection: [keep-alive]
|
||||
Content-Length: ['14']
|
||||
Content-Type: [application/x-www-form-urlencoded]
|
||||
User-Agent: [python-requests/2.18.4]
|
||||
method: POST
|
||||
uri: http://localhost:3000/api/v1/statuses
|
||||
response:
|
||||
body: {string: '{"id":"102951400589982753","created_at":"2019-10-12T20:55:05.296Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"ja","uri":"http://localhost/users/mastodonpy_test/statuses/102951400589982753","url":"http://localhost/@mastodonpy_test/102951400589982753","replies_count":0,"reblogs_count":0,"favourites_count":0,"favourited":false,"reblogged":false,"muted":false,"pinned":false,"content":"\u003cp\u003eToot!\u003c/p\u003e","reblog":null,"application":{"name":"Mastodon.py
|
||||
test suite","website":null},"account":{"id":"1234567890123456","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":false,"bot":false,"created_at":"2019-06-22T23:11:52.441Z","note":"\u003cp\u003e\u003c/p\u003e","url":"http://localhost/@mastodonpy_test","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":1,"statuses_count":3,"last_status_at":"2019-10-12T20:55:05.311Z","emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[],"emojis":[],"card":null,"poll":null}'}
|
||||
headers:
|
||||
Cache-Control: ['no-cache, no-store']
|
||||
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: [d6e097f3-e4e8-4d83-8c30-c4fe5ed88a87]
|
||||
X-Runtime: ['0.151706']
|
||||
X-XSS-Protection: [1; mode=block]
|
||||
content-length: ['1280']
|
||||
status: {code: 200, message: OK}
|
||||
- request:
|
||||
body: '{"home": {"last_read_id": 102951400589982753}}'
|
||||
headers:
|
||||
Accept: ['*/*']
|
||||
Accept-Encoding: ['gzip, deflate']
|
||||
Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN]
|
||||
Connection: [keep-alive]
|
||||
Content-Length: ['46']
|
||||
Content-Type: [application/json]
|
||||
User-Agent: [python-requests/2.18.4]
|
||||
method: POST
|
||||
uri: http://localhost:3000/api/v1/markers
|
||||
response:
|
||||
body: {string: '{"home":{"last_read_id":"102951400589982753","version":2,"updated_at":"2019-10-12T20:55:05.457Z"}}'}
|
||||
headers:
|
||||
Cache-Control: ['no-cache, no-store']
|
||||
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: [2e3c3ede-edf6-43c9-9477-fddca87ffed3]
|
||||
X-Runtime: ['0.036295']
|
||||
X-XSS-Protection: [1; mode=block]
|
||||
content-length: ['98']
|
||||
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/markers?timeline%5B%5D=home
|
||||
response:
|
||||
body: {string: '{"home":{"last_read_id":"102951400589982753","version":2,"updated_at":"2019-10-12T20:55:05.457Z"}}'}
|
||||
headers:
|
||||
Cache-Control: ['no-cache, no-store']
|
||||
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: [2d3ddb8a-b93c-48e2-bdda-c4e0edf29adf]
|
||||
X-Runtime: ['0.022064']
|
||||
X-XSS-Protection: [1; mode=block]
|
||||
content-length: ['98']
|
||||
status: {code: 200, message: OK}
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept: ['*/*']
|
||||
Accept-Encoding: ['gzip, deflate']
|
||||
Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN]
|
||||
Connection: [keep-alive]
|
||||
Content-Length: ['0']
|
||||
User-Agent: [python-requests/2.18.4]
|
||||
method: DELETE
|
||||
uri: http://localhost:3000/api/v1/statuses/102951400589982753
|
||||
response:
|
||||
body: {string: '{"id":"102951400589982753","created_at":"2019-10-12T20:55:05.296Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"ja","uri":"http://localhost/users/mastodonpy_test/statuses/102951400589982753","url":"http://localhost/@mastodonpy_test/102951400589982753","replies_count":0,"reblogs_count":0,"favourites_count":0,"favourited":false,"reblogged":false,"muted":false,"pinned":false,"text":"Toot!","reblog":null,"application":{"name":"Mastodon.py
|
||||
test suite","website":null},"account":{"id":"1234567890123456","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":false,"bot":false,"created_at":"2019-06-22T23:11:52.441Z","note":"\u003cp\u003e\u003c/p\u003e","url":"http://localhost/@mastodonpy_test","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":1,"statuses_count":3,"last_status_at":"2019-10-12T20:55:05.311Z","emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[],"emojis":[],"card":null,"poll":null}'}
|
||||
headers:
|
||||
Cache-Control: ['no-cache, no-store']
|
||||
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: [526d76d7-5428-45d2-9a84-4f520b09524c]
|
||||
X-Runtime: ['0.121781']
|
||||
X-XSS-Protection: [1; mode=block]
|
||||
content-length: ['1250']
|
||||
status: {code: 200, message: OK}
|
||||
version: 1
|
17
tests/test_markers.py
Normal file
17
tests/test_markers.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import pytest
|
||||
|
||||
@pytest.mark.vcr()
|
||||
def test_markers(api, status):
|
||||
marker_a = api.markers_set("home", status)
|
||||
assert marker_a
|
||||
assert marker_a["home"]
|
||||
|
||||
marker_b = api.markers_get("home")
|
||||
assert marker_b
|
||||
assert marker_b["home"]
|
||||
|
||||
assert marker_a.home.version == marker_b.home.version
|
||||
assert marker_a.home.last_read_id == status.id
|
||||
assert marker_b.home.last_read_id == status.id
|
||||
|
||||
|
Loading…
Referencia en una nova incidència