diff --git a/docs/index.rst b/docs/index.rst index bb6e5b1..95ae0aa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -884,9 +884,10 @@ Admin account dicts Admin domain block dicts ~~~~~~~~~~~~~~~~~~~~~~~~ -.. _domain dicts +.. _admin domain block dict: .. code-block::python + mastodon.domain_blocks(id=1) #Returns the following dictionary: { @@ -1466,7 +1467,7 @@ have admin: scopes attached with a lot of care, but be extra careful with those .. automethod:: Mastodon.admin_trending_statuses .. automethod:: Mastodon.admin_trending_links .. automethod:: Mastodon.admin_domain_blocks -.. automethod:: Mastodon.admin_domain_block +.. automethod:: Mastodon.admin_create_domain_block .. automethod:: Mastodon.admin_update_domain_block .. automethod:: Mastodon.admin_delete_domain_block diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index c519a0d..82e058c 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py @@ -210,10 +210,20 @@ class Mastodon: 'admin:read': [ 'admin:read:accounts', 'admin:read:reports', + 'admin:read:domain_allows', + 'admin:read:domain_blocks', + 'admin:read:ip_blocks', + 'admin:read:email_domain_blocks', + 'admin:read:canonical_email_blocks', ], 'admin:write': [ 'admin:write:accounts', 'admin:write:reports', + 'admin:write:domain_allows', + 'admin:write:domain_blocks', + 'admin:write:ip_blocks', + 'admin:write:email_domain_blocks', + 'admin:write:canonical_email_blocks', ], } __VALID_SCOPES = ['read', 'write', 'follow', 'push', 'admin:read', 'admin:write'] + \ @@ -253,6 +263,7 @@ class Mastodon: __DICT_VERSION_REACTION = "3.1.0" __DICT_VERSION_ANNOUNCEMENT = bigger_version("3.1.0", __DICT_VERSION_REACTION) __DICT_VERSION_STATUS_EDIT = "3.5.0" + __DICT_VERSION_ADMIN_DOMAIN_BLOCK = "4.0.0" ### # Registering apps @@ -3363,26 +3374,26 @@ class Mastodon: params = self.__generate_params(locals()) return self.__api_request('GET', '/api/v1/admin/trends/links', params) - @api_version("4.0.0","4.0.0","4.0.0") - def admin_domain_blocks(self, id:str=None, limit:int=None): + @api_version("4.0.0", "4.0.0", __DICT_VERSION_ADMIN_DOMAIN_BLOCK) + def admin_domain_blocks(self, id=None, limit:int=None): """ Fetches a list of blocked domains. Requires scope `admin:read:domain_blocks`. Provide an `id` to fetch a specific domain block based on its database id. - Returns a list of `domain dicts`_, or 404 if a domain is queried for and not found. + Returns a list of `admin domain block dicts`_, raises a `MastodonAPIError` if the specified block does not exist. """ - id = self.__unpack_id(id) if id is not None: + id = self.__unpack_id(id) return self.__api_request('GET', '/api/v1/admin/domain_blocks/{0}'.format(id)) else: params = self.__generate_params(locals(),['limit']) return self.__api_request('GET', '/api/v1/admin/domain_blocks/', params) - @api_version("4.0.0","4.0.0","4.0.0") - def admin_domain_block(self, domain:str, severity:str=None, reject_media:bool=None, reject_reports:bool=None, private_comment:str=None, public_comment:str=None, obfuscate:bool=None): + @api_version("4.0.0", "4.0.0", __DICT_VERSION_ADMIN_DOMAIN_BLOCK) + def admin_create_domain_block(self, domain:str, severity:str=None, reject_media:bool=None, reject_reports:bool=None, private_comment:str=None, public_comment:str=None, obfuscate:bool=None): """ - Perform a moderation action on a domain. + Perform a moderation action on a domain. Requires scope `admin:write:domain_blocks`. Valid severities are: * "silence" - hide all posts from federated timelines and do not show notifications to local users from the remote instance's users unless they are following the remote user. @@ -3395,20 +3406,19 @@ class Mastodon: `reject_reports` ignores all reports from the remote instance. `private_comment` sets a private admin comment for the domain. `public_comment` sets a publicly available comment for this domain, which will be available to local users and may be available to everyone depending on your settings. - `obfuscate` sensors some part of the domain name. Useful if the domain name contains unwanted words like slurs. + `obfuscate` censors some part of the domain name. Useful if the domain name contains unwanted words like slurs. + + Returns the new domain block as an `admin domain block dict`_. """ if domain is None: raise AttributeError("Must provide a domain to block a domain") - params = self.__generate_params(locals()) + return self.__api_request('POST', '/api/v1/admin/domain_blocks/', params) - - self.__api_request('POST', '/api/v1/admin/domain_blocks/', params) - - @api_version("4.0.0","4.0.0","4.0.0") - def admin_update_domain_block(self, id:str, severity:str=None, reject_media:bool=None, reject_reports:bool=None, private_comment:str=None, public_comment:str=None, obfuscate:bool=None): + @api_version("4.0.0", "4.0.0", __DICT_VERSION_ADMIN_DOMAIN_BLOCK) + def admin_update_domain_block(self, id, severity:str=None, reject_media:bool=None, reject_reports:bool=None, private_comment:str=None, public_comment:str=None, obfuscate:bool=None): """ - Modify existing moderation action on a domain. + Modify existing moderation action on a domain. Requires scope `admin:write:domain_blocks`. Valid severities are: * "silence" - hide all posts from federated timelines and do not show notifications to local users from the remote instance's users unless they are following the remote user. @@ -3421,27 +3431,28 @@ class Mastodon: `reject_reports` ignores all reports from the remote instance. `private_comment` sets a private admin comment for the domain. `public_comment` sets a publicly available comment for this domain, which will be available to local users and may be available to everyone depending on your settings. - `obfuscate` sensors some part of the domain name. Useful if the domain name contains unwanted words like slurs. + `obfuscate` censors some part of the domain name. Useful if the domain name contains unwanted words like slurs. + + Returns the modified domain block as an `admin domain block dict`_, raises a `MastodonAPIError` if the specified block does not exist. """ if id is None: raise AttributeError("Must provide an id to modify the existing moderation actions on a given domain.") + id = self.__unpack_id(id) + params = self.__generate_params(locals(), ["id"]) + return self.__api_request('PUT', '/api/v1/admin/domain_blocks/{0}'.format(id), params) - params = self.__generate_params(locals()) - - self.__api_request('PUT', '/api/v1/admin/domain_blocks/', params) - - @api_version("4.0.0","4.0.0","4.0.0") - def admin_delete_domain_blocks(self, id:str=None): + @api_version("4.0.0", "4.0.0", __DICT_VERSION_ADMIN_DOMAIN_BLOCK) + def admin_delete_domain_block(self, id=None): """ Removes moderation action against a given domain. Requires scope `admin:write:domain_blocks`. Provide an `id` to remove a specific domain block based on its database id. - Returns 200 OK if successful. + Raises a `MastodonAPIError` if the specified block does not exist. """ - id = self.__unpack_id(id) if id is not None: - return self.__api_request('DELETE', '/api/v1/admin/domain_blocks/{0}'.format(id)) + id = self.__unpack_id(id) + self.__api_request('DELETE', '/api/v1/admin/domain_blocks/{0}'.format(id)) else: raise AttributeError("You must provide an id of an existing domain block to remove it.") diff --git a/tests/cassettes/test_admin_domain_blocks.yaml b/tests/cassettes/test_admin_domain_blocks.yaml new file mode 100644 index 0000000..742a8b0 --- /dev/null +++ b/tests/cassettes/test_admin_domain_blocks.yaml @@ -0,0 +1,366 @@ +interactions: +- request: + body: domain=https%3A%2F%2Fchitter.xyz%2F&severity=suspend&public_comment=sicko+behaviour + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '83' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/admin/domain_blocks/ + response: + body: + string: '{"id":"8","domain":"https:chitter.xyz","created_at":"2022-11-25T22:59:08.008Z","severity":"suspend","reject_media":false,"reject_reports":false,"private_comment":null,"public_comment":"sicko + behaviour","obfuscate":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-Imbtm+KUEpnqJvOJ45mutA==''; + 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/"1db810d68fb2778f381897c8eb7c01b1" + 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: + - 2411dfa5-38f0-4e57-942c-1cd2673fe831 + X-Runtime: + - '0.022290' + 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_2 + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/admin/domain_blocks/ + response: + body: + string: '[{"id":"8","domain":"https:chitter.xyz","created_at":"2022-11-25T22:59:08.008Z","severity":"suspend","reject_media":false,"reject_reports":false,"private_comment":null,"public_comment":"sicko + behaviour","obfuscate":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-cmboU6G0U/fNuxkj3razsg==''; + 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/"e807730146b189a5a31148fb29e433da" + Link: + - ; rel="prev" + 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: + - c5bb9784-47fc-4e9a-ab37-5bdcadfdccc2 + X-Runtime: + - '0.010368' + 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_2 + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/admin/domain_blocks/8 + response: + body: + string: '{"id":"8","domain":"https:chitter.xyz","created_at":"2022-11-25T22:59:08.008Z","severity":"suspend","reject_media":false,"reject_reports":false,"private_comment":null,"public_comment":"sicko + behaviour","obfuscate":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-y+xczE/ywMzzq9Ae5Shj3A==''; + 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/"1db810d68fb2778f381897c8eb7c01b1" + 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: + - ea5739bc-7ebd-40c8-9692-db2f4bbdbde4 + X-Runtime: + - '0.009239' + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: severity=silence&private_comment=jk+ilu+%3C3 + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '44' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: PUT + uri: http://localhost:3000/api/v1/admin/domain_blocks/8 + response: + body: + string: '{"id":"8","domain":"https:chitter.xyz","created_at":"2022-11-25T22:59:08.008Z","severity":"silence","reject_media":false,"reject_reports":false,"private_comment":"jk + ilu \u003c3","public_comment":"sicko behaviour","obfuscate":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-MH0zeyGASxwMnqSQO8XnVw==''; + 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/"56e34924f74cdfe16875883a46f8959b" + 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: + - cb8d039e-d10c-403d-a1e7-6e43c3ac3f6d + X-Runtime: + - '0.019215' + 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_2 + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/admin/domain_blocks/8 + 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-ICuLgest9NO+qhP7YVWYXw==''; + 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: + - 299ea94d-2646-42c4-8bde-def40fd3858a + X-Runtime: + - '0.017753' + 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_2 + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/admin/domain_blocks/ + 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-f4x2CzcoPSq9zb0LNrS1pA==''; + 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/"4f53cda18c2baa0c0354bb5f9a3ecbe5" + 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: + - 1f76071e-2199-4c73-8744-e1d87cadc4d7 + X-Runtime: + - '0.009019' + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_admin.py b/tests/test_admin.py index 6a72ed7..887ed14 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -120,3 +120,17 @@ def test_admin_trends(api2): def test_admin_accountrequests(api2): pass +@pytest.mark.vcr() +def test_admin_domain_blocks(api2): + block = api2.admin_create_domain_block(domain = "https://chitter.xyz/", public_comment="sicko behaviour", severity="suspend") + assert isinstance(api2.admin_domain_blocks(), list) + block2 = api2.admin_domain_blocks(block) + assert block.severity == "suspend" + assert block.public_comment == "sicko behaviour" + assert block.severity == block2.severity + block3 = api2.admin_update_domain_block(block, severity="silence", private_comment="jk ilu <3") + assert block3.severity == "silence" + assert block3.public_comment == "sicko behaviour" + assert block3.private_comment == "jk ilu <3" + api2.admin_delete_domain_block(block2) + assert not block3.id in map(lambda x: x.id, api2.admin_domain_blocks())