From a17b20cfa142469019cd5982ba200510afc1f884 Mon Sep 17 00:00:00 2001 From: halcy Date: Sun, 13 Nov 2022 14:54:23 +0200 Subject: fix naming for featured tags --- TODO.md | 4 +- docs/index.rst | 12 +- mastodon/Mastodon.py | 12 +- tests/cassettes/test_featured_tags.yaml | 308 +++++++++++++++++++++++++++++++- tests/conftest.py | 2 +- tests/test_account.py | 21 ++- 6 files changed, 344 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index 99e9644..292968e 100644 --- a/TODO.md +++ b/TODO.md @@ -24,8 +24,8 @@ Refer to mastodon changelog and API docs for details when implementing, add or m * [ ] Add duration option to the mute function * [ ] Add ability to block access or limit sign-ups from chosen IPs * [ ] Add support for managing multiple stream subscriptions in a single connection -* [ ] Add support for limiting results by both min_id and max_id at the same time in REST API -* [ ] Add GET /api/v1/accounts/:id/featured_tags to REST API +* [x] Add support for limiting results by both min_id and max_id at the same time in REST API +* [x] Add GET /api/v1/accounts/:id/featured_tags to REST API 3.4.0 ----- diff --git a/docs/index.rst b/docs/index.rst index dad7669..c2caf3a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -130,11 +130,12 @@ allows you to specify the largest id you want. By specifying either min_id or `m (generally, only one, not both, though specifying both is supported starting with Mastodon version 3.3.0) of them you can go through pages forwards and backwards. -On Mastodon mainline, you can, pass datetime objects as IDs, since the IDs used are -Snowflake IDs and dates can be approximately converted to those. This is guaranteed -to work on mainline Mastodon servers and very likely to work on all forks, but will -**not** work on other servers implementing the API, like Pleroma, Misskey or Gotosocial. -You should not use this if you want your application to be universally compatible. +On Mastodon mainline, you can, pass datetime objects as IDs when fetching posts, +since the IDs used are Snowflake IDs and dates can be approximately converted to those. +This is guaranteed to work on mainline Mastodon servers and very likely to work on all +forks, but will **not** work on other servers implementing the API, like Pleroma, Misskey +or Gotosocial. You should not use this if you want your application to be universally +compatible. `limit` allows you to specify how many results you would like returned. Note that an instance may choose to return less results than you requested - by default, Mastodon @@ -1178,6 +1179,7 @@ These functions allow you to interact with other accounts: To (un)follow and .. automethod:: Mastodon.account_unpin .. automethod:: Mastodon.account_update_credentials .. automethod:: Mastodon.account_note_set +.. automethod:: Mastodon.account_featured_tags Writing data: Featured tags ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index c3e3122..c55e006 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py @@ -2265,12 +2265,22 @@ class Mastodon: """ Set a note (visible to the logged in user only) for the given account. - returns a `status dict`_ with the `note` updated. + Returns a `status dict`_ with the `note` updated. """ id = self.__unpack_id(id) params = self.__generate_params(locals(), ["id"]) return self.__api_request('POST', '/api/v1/accounts/{0}/note'.format(str(id)), params) + @api_version("3.3.0", "3.3.0", __DICT_VERSION_HASHTAG) + def account_featured_tags(self, id): + """ + Get an accounts featured hashtags. + + Returns a list of `hashtag dicts`_ (NOT `featured tag dicts`_). + """ + id = self.__unpack_id(id) + return self.__api_request('GET', '/api/v1/accounts/{0}/featured_tags'.format(str(id))) + ### # Writing data: Featured hashtags ### diff --git a/tests/cassettes/test_featured_tags.yaml b/tests/cassettes/test_featured_tags.yaml index f4ea314..955b9d0 100644 --- a/tests/cassettes/test_featured_tags.yaml +++ b/tests/cassettes/test_featured_tags.yaml @@ -20,14 +20,14 @@ interactions: uri: http://localhost:3000/api/v1/featured_tags response: body: - string: '{"id":"1","name":"ringtones","url":"http://localhost:3000/@mastodonpy_test/tagged/ringtones","statuses_count":"0","last_status_at":null}' + string: '{"id":"9","name":"ringtones","url":"http://localhost:3000/@mastodonpy_test/tagged/ringtones","statuses_count":"0","last_status_at":null}' headers: Cache-Control: - no-store Content-Security-Policy: - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; - style-src ''self'' http://localhost:3000 ''nonce-GjXyZZckExmvwzB75bXrRw==''; + style-src ''self'' http://localhost:3000 ''nonce-Ev7aLLklDn9ahF6Z9ITjPw==''; media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 @@ -37,7 +37,7 @@ interactions: Content-Type: - application/json; charset=utf-8 ETag: - - W/"aa2380f6b72962d051f0cd1ca31074a1" + - W/"41bf7bfddac6e1ad15287c517fb9e4ef" Referrer-Policy: - strict-origin-when-cross-origin Transfer-Encoding: @@ -53,9 +53,307 @@ interactions: X-Permitted-Cross-Domain-Policies: - none X-Request-Id: - - 40a41ecb-53f4-4d6a-816b-ea0bdec7283c + - 45375d50-283d-420d-bca7-02f5d80c48e1 X-Runtime: - - '0.262286' + - '0.019871' + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: name=%23coolfree + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + Content-Length: + - '16' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/featured_tags + response: + body: + string: '{"id":"10","name":"coolfree","url":"http://localhost:3000/@mastodonpy_test/tagged/coolfree","statuses_count":"0","last_status_at":null}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src + ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; + style-src ''self'' http://localhost:3000 ''nonce-3E1/Qr00Zf5SwlBjP0neqw==''; + media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' + https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' + data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 + ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; + worker-src ''self'' blob: http://localhost:3000' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"65ef3503d8d2223dcf8599eff3bfba43" + Referrer-Policy: + - strict-origin-when-cross-origin + Transfer-Encoding: + - chunked + Vary: + - Accept, Origin + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - 21ca1fdf-4f9e-43b9-9df8-58f11f9689cc + X-Runtime: + - '0.018429' + X-XSS-Protection: + - 1; mode=block + 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: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/featured_tags/9 + response: + body: + string: '{}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src + ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; + style-src ''self'' http://localhost:3000 ''nonce-hyd1sSf9Too/TRXACKsomg==''; + media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' + https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' + data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 + ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; + worker-src ''self'' blob: http://localhost:3000' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"44136fa355b3678a1146ad16f7e8649e" + Referrer-Policy: + - strict-origin-when-cross-origin + Transfer-Encoding: + - chunked + Vary: + - Accept, Origin + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - 278e1df2-92cc-4580-a15e-1596b5ba1c74 + X-Runtime: + - '0.008709' + X-XSS-Protection: + - 1; mode=block + 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: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/accounts/verify_credentials + response: + body: + string: '{"id":"109336567574114486","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-13T00:00:00.000Z","note":"","url":"http://localhost:3000/@mastodonpy_test","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":0,"last_status_at":null,"noindex":false,"source":{"privacy":"public","sensitive":false,"language":null,"note":"","fields":[],"follow_requests_count":0},"emojis":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src + ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; + style-src ''self'' http://localhost:3000 ''nonce-0+vVDdfw4sA92jsxcIsAaA==''; + media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' + https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' + data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 + ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; + worker-src ''self'' blob: http://localhost:3000' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"9656ddb7266788f144a437312303d9f2" + Referrer-Policy: + - strict-origin-when-cross-origin + Transfer-Encoding: + - chunked + Vary: + - Accept, Origin + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - 62c7f6f3-89aa-4424-acf3-029447793322 + X-Runtime: + - '0.010940' + X-XSS-Protection: + - 1; mode=block + 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: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/accounts/109336567574114486/featured_tags + response: + body: + string: '[{"id":"10","name":"coolfree","url":"http://localhost:3000/@mastodonpy_test/tagged/coolfree","statuses_count":"0","last_status_at":null}]' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src + ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; + style-src ''self'' http://localhost:3000 ''nonce-eCUaxB/0/Z9vIZ+ICDuW9A==''; + media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' + https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' + data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 + ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; + worker-src ''self'' blob: http://localhost:3000' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"48bb4e5567243bbb4d2d33f21aed2597" + Referrer-Policy: + - strict-origin-when-cross-origin + Transfer-Encoding: + - chunked + Vary: + - Accept, Origin + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - e4471af9-a407-4a37-b03a-7f9b3086b163 + X-Runtime: + - '0.007852' + X-XSS-Protection: + - 1; mode=block + 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: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/featured_tags/10 + response: + body: + string: '{}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src + ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; + style-src ''self'' http://localhost:3000 ''nonce-Yp9i56vd+caB7l7es3mkNw==''; + media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' + https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' + data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 + ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; + worker-src ''self'' blob: http://localhost:3000' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"44136fa355b3678a1146ad16f7e8649e" + Referrer-Policy: + - strict-origin-when-cross-origin + Transfer-Encoding: + - chunked + Vary: + - Accept, Origin + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - 6e5dac5e-e1f8-4e09-ae2f-1f4234840d32 + X-Runtime: + - '0.007759' X-XSS-Protection: - 1; mode=block status: diff --git a/tests/conftest.py b/tests/conftest.py index 0d2634c..5978547 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,7 @@ import pytest # Set this to True to debug issues with tests DEBUG_REQUESTS = True -def _api(access_token='__MASTODON_PY_TEST_ACCESS_TOKEN', version="3.1.1", version_check_mode="created"): +def _api(access_token='__MASTODON_PY_TEST_ACCESS_TOKEN', version="4.0.0", version_check_mode="created"): import mastodon return mastodon.Mastodon( api_base_url='http://localhost:3000', diff --git a/tests/test_account.py b/tests/test_account.py index ef48d4e..231f85c 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -232,7 +232,26 @@ def test_suggested_tags(api): @pytest.mark.vcr() def test_featured_tags(api): - featured_tag = api.featured_tag_create("ringtones") + try: + featured_tag = api.featured_tag_create("ringtones") + assert featured_tag + assert featured_tag.name == "ringtones" + + featured_tag_2 = api.featured_tag_create("#coolfree") + assert featured_tag_2 + assert featured_tag_2.name == "coolfree" + + api.featured_tag_delete(featured_tag) + featured_tag = None + + featured_tag_list = api.account_featured_tags(api.account_verify_credentials()) + assert len(featured_tag_list) == 1 + assert featured_tag_list[0].name == "coolfree" + assert "url" in featured_tag_list[0] + finally: + if not featured_tag is None: + api.featured_tag_delete(featured_tag) + api.featured_tag_delete(featured_tag_2) @pytest.mark.vcr() def test_account_notes(api, api2): -- cgit v1.2.3