diff --git a/docs/index.rst b/docs/index.rst index f65fe1e..0ec0f75 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -844,6 +844,7 @@ current instance. .. automethod:: Mastodon.instance_activity .. automethod:: Mastodon.instance_peers .. automethod:: Mastodon.instance_health +.. automethod:: Mastodon.instance_nodeinfo Reading data: Timelines ----------------------- diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index f0063e5..1561fa1 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py @@ -660,7 +660,35 @@ class Mastodon: Basic health check. Returns True if healthy, False if not. """ return self.__api_request('GET', '/health', parse=False).decode("utf-8") == "success" - + + @api_version("3.0.0", "3.0.0", "3.0.0") + def instance_nodeinfo(self, schema = "http://nodeinfo.diaspora.software/ns/schema/2.0"): + """ + Retrieves the instances nodeinfo information. + + For information on what the nodeinfo can contain, see the nodeinfo + specification: https://github.com/jhass/nodeinfo . By default, + Mastodon.py will try to retrieve the version 2.0 schema nodeinfo. + + To override the schema, specify the desired schema with the `schema` + parameter. + """ + links = self.__api_request('GET', '/.well-known/nodeinfo')["links"] + + schema_url = None + for available_schema in links: + if available_schema.rel == schema: + schema_url = available_schema.href + + if schema_url is None: + raise MastodonIllegalArgumentError("Requested nodeinfo schema is not available.") + + try: + return self.__api_request('GET', schema_url, base_url_override="") + except MastodonNotFoundError: + parse = urlparse(schema_url) + return self.__api_request('GET', parse.path + parse.params + parse.query + parse.fragment) + ### # Reading data: Timelines ## diff --git a/tests/cassettes/test_nodeinfo.yaml b/tests/cassettes/test_nodeinfo.yaml new file mode 100644 index 0000000..38f8d01 --- /dev/null +++ b/tests/cassettes/test_nodeinfo.yaml @@ -0,0 +1,81 @@ +interactions: +- 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/.well-known/nodeinfo + response: + body: {string: '{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"http://localhost/nodeinfo/2.0"}]}'} + headers: + Cache-Control: ['max-age=259200, public'] + Content-Type: [application/json; charset=utf-8] + Date: ['Sat, 12 Oct 2019 18:50:31 GMT'] + ETag: [W/"e1fd8a5cbe12d2ef42c3146d5319c8af"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept, 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: [38a2a5fc-0a7e-44f1-bf4d-85c90c39b2bb] + X-Runtime: ['0.026451'] + X-XSS-Protection: [1; mode=block] + content-length: ['108'] + 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/nodeinfo/2.0 + response: + body: {string: "\r\n404 Not Found\r\n\r\n

404 + Not Found

\r\n
nginx/1.14.0 (Ubuntu)
\r\n\r\n\r\n"} + headers: + Connection: [keep-alive] + Content-Type: [text/html] + Date: ['Sat, 12 Oct 2019 18:50:31 GMT'] + Server: [nginx/1.14.0 (Ubuntu)] + Transfer-Encoding: [chunked] + content-length: ['178'] + 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: [python-requests/2.18.4] + method: GET + uri: http://localhost:3000/nodeinfo/2.0 + response: + body: {string: '{"version":"2.0","software":{"name":"mastodon","version":"3.0.1"},"protocols":["activitypub"],"usage":{"users":{"total":3,"activeMonth":3,"activeHalfyear":3},"localPosts":32},"openRegistrations":true}'} + headers: + Cache-Control: ['max-age=1800, public'] + Content-Type: [application/json; charset=utf-8] + Date: ['Sat, 12 Oct 2019 18:50:31 GMT'] + ETag: [W/"a09a04c6f9dc121c4b70f446e88a2686"] + Referrer-Policy: [strict-origin-when-cross-origin] + Transfer-Encoding: [chunked] + Vary: ['Accept,Accept-Encoding'] + X-Content-Type-Options: [nosniff] + X-Download-Options: [noopen] + X-Frame-Options: [SAMEORIGIN] + X-Permitted-Cross-Domain-Policies: [none] + X-Request-Id: [5d4c66c1-efad-4b9f-85b0-d67755adb967] + X-Runtime: ['0.020737'] + X-XSS-Protection: [1; mode=block] + content-length: ['200'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_instance.py b/tests/test_instance.py index a304585..343af18 100644 --- a/tests/test_instance.py +++ b/tests/test_instance.py @@ -36,3 +36,10 @@ def test_emoji(api): @pytest.mark.vcr() def test_health(api): assert api.instance_health() == True + +@pytest.mark.vcr() +def test_nodeinfo(api): + nodeinfo = api.instance_nodeinfo() + assert nodeinfo + assert nodeinfo.version == '2.0' +