diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ab2538e..da22ae1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,7 @@ v1.6.3 ------ * Add server rules API (`instance_rules`) * Add confirmation email resend API (`email_resend_confirmation`) +* Add account lookup API (`account_lookup`) v1.6.2 ------ diff --git a/TODO.md b/TODO.md index 164717a..6964ea3 100644 --- a/TODO.md +++ b/TODO.md @@ -31,7 +31,7 @@ Refer to mastodon changelog and API docs for details when implementing, add or m ----- * [x] Add server rules * [x] Add POST /api/v1/emails/confirmations to REST API -* [ ] Add GET /api/v1/accounts/lookup to REST API +* [x] Add GET /api/v1/accounts/lookup to REST API * [ ] Add policy param to POST /api/v1/push/subscriptions in REST API * [ ] Add details to error response for POST /api/v1/accounts in REST API diff --git a/docs/index.rst b/docs/index.rst index 2318a98..8b47fad 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -906,7 +906,7 @@ is specified, Mastodon.py defaults to https. .. automethod:: Mastodon.auth_request_url .. automethod:: Mastodon.create_account .. automethod:: Mastodon.email_resend_confirmation - + Versioning ---------- Mastodon.py will check if a certain endpoint is available before doing API @@ -999,6 +999,8 @@ their relationships. .. automethod:: Mastodon.account_followers .. automethod:: Mastodon.account_relationships .. automethod:: Mastodon.account_search +.. automethod:: Mastodon.account_lists +.. automethod:: Mastodon.account_lookup Reading data: Featured tags ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index 629a4b2..6e5bc29 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py @@ -1261,6 +1261,19 @@ class Mastodon: url = '/api/v1/accounts/{0}/lists'.format(str(id)) return self.__api_request('GET', url, params) + @api_version("3.4.0", "3.4.0", __DICT_VERSION_ACCOUNT) + def account_lookup(self, acct): + """ + Look up an account from user@instance form (@instance allowed but not required for + local accounts). Will only return accounts that the instance already knows about, + and not do any webfinger requests. Use `account_search` if you need to resolve users + through webfinger from remote. + + Returns an `account dict`_. + """ + return self.__api_request('GET', '/api/v1/accounts/lookup', self.__generate_params(locals())) + + ### # Reading data: Featured hashtags ### diff --git a/tests/cassettes/test_account_lookup.yaml b/tests/cassettes/test_account_lookup.yaml new file mode 100644 index 0000000..01ed5f7 --- /dev/null +++ b/tests/cassettes/test_account_lookup.yaml @@ -0,0 +1,232 @@ +interactions: +- 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":"109366898092282937","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-18T00: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":5,"last_status_at":"2022-11-18","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-3EN7F4MujpHoVrEZ3s8fag==''; + 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/"dc5952ea4f0e073dc114984e11e688fe" + 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: + - b30300c0-b4d2-4d92-a447-90c4343be34f + X-Runtime: + - '0.012757' + 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/lookup?acct=kljadklajsdkljlkjlkjlkjasdasd + response: + body: + string: '{"error":"Record not found"}' + 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-K3+CoL7Djc57eXE+QOhaqw==''; + 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 + 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: + - ad639c09-6937-494b-8d8e-d07c4e57ae17 + X-Runtime: + - '0.007311' + X-XSS-Protection: + - 1; mode=block + status: + code: 404 + message: Not Found +- 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/lookup?acct=mastodonpy_test + response: + body: + string: '{"id":"109366898092282937","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-18T00: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":5,"last_status_at":"2022-11-18","noindex":false,"emojis":[],"fields":[]}' + 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-ahGApVKvUFVrZV/lQw3Ckw==''; + 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/"38e63cb43c9359834b66f41070c8c079" + 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: + - 74fa7552-bb6c-4154-8993-aac7ede92f32 + X-Runtime: + - '0.010043' + 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/lookup?acct=mastodonpy_test%40localhost%3A3000 + response: + body: + string: '{"id":"109366898092282937","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-18T00: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":5,"last_status_at":"2022-11-18","noindex":false,"emojis":[],"fields":[]}' + 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-VIxMxXw1sv2+gz5VzvH8xw==''; + 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/"38e63cb43c9359834b66f41070c8c079" + 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: + - 772bdf24-12ae-4d4b-b4a1-ae3cd452e247 + X-Runtime: + - '0.010063' + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_account.py b/tests/test_account.py index 700433f..6f3b9a5 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -280,3 +280,14 @@ def test_follow_with_notify_reblog(api, api2, api3): api.account_unfollow(api2_id) api3.status_delete(status1) api2.status_delete(status2) + +@pytest.mark.vcr() +def test_account_lookup(api, api3): + id = api.me().id + try: + api.account_lookup("kljadklajsdkljlkjlkjlkjasdasd") + assert False + except: + pass + assert(api.account_lookup("mastodonpy_test").id == id) + assert(api.account_lookup("mastodonpy_test@localhost:3000").id == id) \ No newline at end of file