kopia lustrzana https://github.com/halcy/Mastodon.py
808 wiersze
42 KiB
Python
808 wiersze
42 KiB
Python
# admin.py - admin / moderation endpoints
|
|
|
|
from mastodon.errors import MastodonIllegalArgumentError
|
|
from mastodon.utility import api_version
|
|
|
|
from mastodon.internals import Mastodon as Internals
|
|
from typing import Optional, List, Union
|
|
from mastodon.return_types import IdType, PrimitiveIdType, Account, AdminAccount, AdminReport, PaginatableList, NonPaginatableList, Status, Tag,\
|
|
PreviewCard, AdminDomainBlock, AdminMeasure, AdminDimension, AdminRetention, AdminCanonicalEmailBlock, AdminDomainAllow, AdminEmailDomainBlock, AdminIpBlock
|
|
from datetime import datetime
|
|
|
|
class Mastodon(Internals):
|
|
###
|
|
# Moderation API
|
|
###
|
|
@api_version("2.9.1", "4.0.0")
|
|
def admin_accounts_v2(self, origin: Optional[str] = None, by_domain: Optional[str] = None, status: Optional[str] = None, username: Optional[str] = None,
|
|
display_name: Optional[str] = None, email: Optional[str] = None, ip: Optional[str] = None, permissions: Optional[str] = None,
|
|
invited_by: Union[Account, IdType] = None, role_ids: Optional[List[IdType]] = None, max_id: Optional[IdType] = None, min_id: Optional[IdType] = None,
|
|
since_id: Optional[IdType] = None, limit: Optional[int] = None) -> List[AdminAccount]:
|
|
"""
|
|
Fetches a list of accounts that match given criteria. By default, local accounts are returned.
|
|
|
|
* Set `origin` to "local" or "remote" to get only local or remote accounts.
|
|
* Set `by_domain` to a domain to get only accounts from that domain.
|
|
* Set `status` to one of "active", "pending", "disabled", "silenced" or "suspended" to get only accounts with that moderation status (default: active)
|
|
* Set `username` to a string to get only accounts whose username contains this string.
|
|
* Set `display_name` to a string to get only accounts whose display name contains this string.
|
|
* Set `email` to an email to get only accounts with that email (this only works on local accounts).
|
|
* Set `ip` to an ip (as a string, standard v4/v6 notation) to get only accounts whose last active ip is that ip (this only works on local accounts).
|
|
* Set `permissions` to "staff" to only get accounts with staff permissions.
|
|
* Set `invited_by` to an account id to get only accounts invited by this user.
|
|
* Set `role_ids` to a list of role IDs to get only accounts with those roles.
|
|
|
|
Pagination on this is a bit weird, so I would recommend not doing that and instead manually fetching.
|
|
"""
|
|
if role_ids is not None:
|
|
if not isinstance(role_ids, list):
|
|
role_ids = [role_ids]
|
|
role_ids = [self.__unpack_id(x) for x in role_ids]
|
|
|
|
if invited_by is not None:
|
|
invited_by = self.__unpack_id(invited_by)
|
|
|
|
if permissions is not None and not permissions in ["staff"]:
|
|
raise MastodonIllegalArgumentError("Permissions must be staff if passed")
|
|
|
|
if origin is not None and not origin in ["local", "remote"]:
|
|
raise MastodonIllegalArgumentError("Origin must be local or remote")
|
|
|
|
if status is not None and not status in ["active", "pending", "disabled", "silenced", "suspended"]:
|
|
raise MastodonIllegalArgumentError("Status must be local or active, pending, disabled, silenced or suspended")
|
|
|
|
if not by_domain is None:
|
|
by_domain = self.__deprotocolize(by_domain)
|
|
|
|
params = self.__generate_params(locals(), dateconv=True)
|
|
return self.__api_request('GET', '/api/v2/admin/accounts', params)
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_accounts(self, remote: bool = False, by_domain: Optional[str] = None, status: str = 'active', username: Optional[str] = None,
|
|
display_name: Optional[str] = None, email: Optional[str] = None, ip: Optional[str] = None, staff_only: bool = False,
|
|
max_id: Optional[IdType] = None, min_id: Optional[IdType] = None, since_id: Optional[IdType] = None,
|
|
limit: Optional[int] = None):
|
|
"""
|
|
Currently a synonym for admin_accounts_v1, now deprecated. You are strongly encouraged to use admin_accounts_v2 instead, since this one is kind of bad.
|
|
|
|
!!!!! This function may be switched to calling the v2 API in the future. This is your warning. If you want to keep using v1, use it explicitly. !!!!!
|
|
|
|
Pagination on this is a bit weird, so I would recommend not doing that and instead manually fetching.
|
|
"""
|
|
return self.admin_accounts_v1(
|
|
remote=remote,
|
|
by_domain=by_domain,
|
|
status=status,
|
|
username=username,
|
|
display_name=display_name,
|
|
email=email,
|
|
ip=ip,
|
|
staff_only=staff_only,
|
|
max_id=max_id,
|
|
min_id=min_id,
|
|
since_id=since_id
|
|
)
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_accounts_v1(self, remote: bool = False, by_domain: Optional[str] = None, status: str = 'active', username: Optional[str] = None,
|
|
display_name: Optional[str] = None, email: Optional[str] = None, ip: Optional[str] = None, staff_only: bool = False,
|
|
max_id: Optional[IdType] = None, min_id: Optional[IdType] = None, since_id: Optional[IdType] = None,
|
|
limit: Optional[int] = None) -> AdminAccount:
|
|
"""
|
|
Fetches a list of accounts that match given criteria. By default, local accounts are returned.
|
|
|
|
* Set `remote` to True to get remote accounts, otherwise local accounts are returned (default: local accounts)
|
|
* Set `by_domain` to a domain to get only accounts from that domain.
|
|
* Set `status` to one of "active", "pending", "disabled", "silenced" or "suspended" to get only accounts with that moderation status (default: active)
|
|
* Set `username` to a string to get only accounts whose username contains this string.
|
|
* Set `display_name` to a string to get only accounts whose display name contains this string.
|
|
* Set `email` to an email to get only accounts with that email (this only works on local accounts).
|
|
* Set `ip` to an ip (as a string, standard v4/v6 notation) to get only accounts whose last active ip is that ip (this only works on local accounts).
|
|
* Set `staff_only` to True to only get staff accounts (this only works on local accounts).
|
|
|
|
Note that setting the boolean parameters to False does not mean "give me users to which this does not apply" but
|
|
instead means "I do not care if users have this attribute".
|
|
|
|
Deprecated in Mastodon version 3.5.0.
|
|
|
|
Pagination on this is a bit weird, so I would recommend not doing that and instead manually fetching.
|
|
"""
|
|
params = self.__generate_params(locals(), ['remote', 'status', 'staff_only'], dateconv=True)
|
|
|
|
if remote:
|
|
params["remote"] = True
|
|
|
|
mod_statuses = ["active", "pending", "disabled", "silenced", "suspended"]
|
|
if not status in mod_statuses:
|
|
raise ValueError("Invalid moderation status requested.")
|
|
|
|
if staff_only:
|
|
params["staff"] = True
|
|
|
|
for mod_status in mod_statuses:
|
|
if status == mod_status:
|
|
params[status] = True
|
|
|
|
if not by_domain is None:
|
|
by_domain = self.__deprotocolize(by_domain)
|
|
|
|
return self.__api_request('GET', '/api/v1/admin/accounts', params)
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Fetches a single admin account for the user with the given id.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/accounts/{id}')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account_enable(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Reenables login for a local account for which login has been disabled.
|
|
|
|
The returned object reflects the updates to the account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/accounts/{id}/enable')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account_approve(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Approves a pending account.
|
|
|
|
The returned object reflects the updates to the account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/accounts/{id}/approve')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account_reject(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Rejects and deletes a pending account.
|
|
|
|
The returned object is that of the now-deleted account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/accounts/{id}/reject')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account_unsilence(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Unsilences an account.
|
|
|
|
The returned object reflects the updates to the account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/accounts/{id}/unsilence')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account_unsuspend(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Unsuspends an account.
|
|
|
|
The returned object reflects the updates to the account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/accounts/{id}/unsuspend')
|
|
|
|
@api_version("3.3.0", "3.3.0")
|
|
def admin_account_delete(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Delete a local user account.
|
|
|
|
The returned object reflects the updates to the account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('DELETE', f'/api/v1/admin/accounts/{id}')
|
|
|
|
@api_version("3.3.0", "3.3.0")
|
|
def admin_account_unsensitive(self, id: Union[Account, AdminAccount, IdType]) -> AdminAccount:
|
|
"""
|
|
Unmark an account as force-sensitive.
|
|
|
|
The returned object reflects the updates to the account.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/accounts/{id}/unsensitive')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_account_moderate(self, id: Union[Account, AdminAccount, IdType], action: Optional[str] = None, report_id: Optional[Union[AdminReport, PrimitiveIdType]] = None,
|
|
warning_preset_id: Optional[PrimitiveIdType] = None, text: Optional[str] = None, send_email_notification: Optional[bool] = True):
|
|
"""
|
|
Perform a moderation action on an account.
|
|
|
|
Valid actions are:
|
|
* "disable" - for a local user, disable login.
|
|
* "silence" - hide the users posts from all public timelines.
|
|
* "suspend" - irreversibly delete all the user's posts, past and future.
|
|
* "sensitive" - forcce an accounts media visibility to always be sensitive.
|
|
|
|
If no action is specified, the user is only issued a warning.
|
|
|
|
Specify the id of a report as `report_id` to close the report with this moderation action as the resolution.
|
|
Specify `warning_preset_id` to use a warning preset as the notification text to the user, or `text` to specify text directly.
|
|
If both are specified, they are concatenated (preset first). Note that there is currently no API to retrieve or create
|
|
warning presets.
|
|
|
|
Set `send_email_notification` to False to not send the user an email notification informing them of the moderation action.
|
|
"""
|
|
if action is None:
|
|
action = "none"
|
|
|
|
if not send_email_notification:
|
|
send_email_notification = None
|
|
|
|
id = self.__unpack_id(id)
|
|
if report_id is not None:
|
|
report_id = self.__unpack_id(report_id)
|
|
|
|
params = self.__generate_params(locals(), ['id', 'action'])
|
|
|
|
params["type"] = action
|
|
|
|
self.__api_request('POST', f'/api/v1/admin/accounts/{id}/action', params)
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_reports(self, resolved: Optional[bool] = False, account_id: Optional[Union[Account, AdminAccount, IdType]] = None,
|
|
target_account_id: Optional[Union[Account, AdminAccount, IdType]] = None, max_id: Optional[IdType] = None,
|
|
min_id: Optional[IdType] = None, since_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[AdminReport]:
|
|
"""
|
|
Fetches the list of reports.
|
|
|
|
Set `resolved` to True to search for resolved reports. `account_id` and `target_account_id`
|
|
can be used to get reports filed by or about a specific user.
|
|
"""
|
|
if account_id is not None:
|
|
account_id = self.__unpack_id(account_id)
|
|
|
|
if target_account_id is not None:
|
|
target_account_id = self.__unpack_id(target_account_id)
|
|
|
|
if not resolved:
|
|
resolved = None
|
|
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/reports', params)
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_report(self, id: Union[AdminReport, IdType]) -> AdminReport:
|
|
"""
|
|
Fetches the report with the given id.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/reports/{id}')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_report_assign(self, id: Union[AdminReport, IdType]) -> AdminReport:
|
|
"""
|
|
Assigns the given report to the logged-in user.
|
|
|
|
The returned object reflects the updates to the report.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/reports/{id}/assign_to_self')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_report_unassign(self, id: Union[AdminReport, IdType]) -> AdminReport:
|
|
"""
|
|
Unassigns the given report from the logged-in user.
|
|
|
|
The returned object reflects the updates to the report.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/reports/{id}/unassign')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_report_reopen(self, id: Union[AdminReport, IdType]) -> AdminReport:
|
|
"""
|
|
Reopens a closed report.
|
|
|
|
The returned object reflects the updates to the report.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/reports/{id}/reopen')
|
|
|
|
@api_version("2.9.1", "2.9.1")
|
|
def admin_report_resolve(self, id: Union[AdminReport, IdType]) -> AdminReport:
|
|
"""
|
|
Marks a report as resolved (without taking any action).
|
|
|
|
The returned object reflects the updates to the report.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/reports/{id}/resolve')
|
|
|
|
@api_version("3.5.0", "3.5.0")
|
|
def admin_trending_tags(self, limit: Optional[int] = None) -> NonPaginatableList[Tag]:
|
|
"""
|
|
Admin version of :ref:`trending_tags() <trending_tags()>`. Includes unapproved tags.
|
|
|
|
The returned list is sorted, descending, by the instance's trending algorithm.
|
|
|
|
Returns a regular Tag without admin attributes between Mastodon.py v4.0.0 and v4.1.0 due to a bug.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/trends/tags', params)
|
|
|
|
@api_version("3.5.0", "3.5.0")
|
|
def admin_trending_statuses(self) -> NonPaginatableList[Status]:
|
|
"""
|
|
Admin version of :ref:`trending_statuses() <trending_statuses()>`. Includes unapproved tags.
|
|
|
|
The returned list is sorted, descending, by the instance's trending algorithm.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/trends/statuses', params)
|
|
|
|
@api_version("3.5.0", "3.5.0")
|
|
def admin_trending_links(self) -> NonPaginatableList[PreviewCard]:
|
|
"""
|
|
Admin version of :ref:`trending_links() <trending_links()>`. Includes unapproved tags.
|
|
|
|
The returned list is sorted, descending, by the instance's trending algorithm.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/trends/links', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_domain_blocks(self, id: Optional[IdType] = None, max_id: Optional[IdType] = None, min_id: Optional[IdType] = None,
|
|
since_id: Optional[IdType] = None, limit: Optional[int] = None) -> Union[AdminDomainBlock, PaginatableList[AdminDomainBlock]]:
|
|
"""
|
|
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.
|
|
|
|
Raises a `MastodonAPIError` if the specified block does not exist.
|
|
"""
|
|
if id is not None:
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/domain_blocks/{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")
|
|
def admin_create_domain_block(self, domain: str, severity: Optional[str] = None, reject_media: Optional[bool] = None,
|
|
reject_reports: Optional[bool] = None, private_comment: Optional[str] = None,
|
|
public_comment: Optional[str] = None, obfuscate: Optional[bool] = None) -> AdminDomainBlock:
|
|
"""
|
|
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.
|
|
* "suspend" - deny interactions with this instance going forward. This action is reversible.
|
|
* "limit" - generally used with reject_media=true to force reject media from an instance without silencing or suspending..
|
|
|
|
If no action is specified, the domain is only silenced.
|
|
`domain` is the domain to block. Note that using the top level domain will also imapct all subdomains. ie, example.com will also impact subdomain.example.com.
|
|
`reject_media` will not download remote media on to your local instance media storage.
|
|
`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` censors some part of the domain name. Useful if the domain name contains unwanted words like slurs.
|
|
"""
|
|
if domain is None:
|
|
raise MastodonIllegalArgumentError("Must provide a domain to block a domain")
|
|
if domain.startswith("http://") or domain.startswith("https://"):
|
|
raise MastodonIllegalArgumentError("Domain should not contain a protocol identifier.")
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('POST', '/api/v1/admin/domain_blocks/', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_update_domain_block(self, id, severity: Optional[str] = None, reject_media: Optional[bool] = None, reject_reports: Optional[bool] = None,
|
|
private_comment: Optional[str] = None, public_comment: Optional[str] = None, obfuscate: Optional[bool] = None) -> AdminDomainBlock:
|
|
"""
|
|
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.
|
|
* "suspend" - deny interactions with this instance going forward. This action is reversible.
|
|
* "limit" - generally used with reject_media=true to force reject media from an instance without silencing or suspending.
|
|
|
|
If no action is specified, the domain is only silenced.
|
|
`domain` is the domain to block. Note that using the top level domain will also imapct all subdomains. ie, example.com will also impact subdomain.example.com.
|
|
`reject_media` will not download remote media on to your local instance media storage.
|
|
`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` censors some part of the domain name. Useful if the domain name contains unwanted words like slurs.
|
|
|
|
Raises a `MastodonAPIError` if the specified block does not exist.
|
|
"""
|
|
if id is None:
|
|
raise MastodonIllegalArgumentError("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', f'/api/v1/admin/domain_blocks/{id}', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_delete_domain_block(self, id = Union[AdminDomainBlock, IdType]):
|
|
"""
|
|
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.
|
|
|
|
Raises a `MastodonAPIError` if the specified block does not exist.
|
|
"""
|
|
if id is not None:
|
|
id = self.__unpack_id(id)
|
|
self.__api_request('DELETE', f'/api/v1/admin/domain_blocks/{id}')
|
|
else:
|
|
raise MastodonIllegalArgumentError("You must provide an id of an existing domain block to remove it.")
|
|
|
|
@api_version("3.5.0", "3.5.0")
|
|
def admin_measures(self, start_at, end_at, active_users: bool = False, new_users: bool = False, interactions: bool = False, opened_reports: bool = False, resolved_reports: bool = False,
|
|
tag_accounts: Optional[Union[Tag, IdType]] = None, tag_uses: Optional[Union[Tag, IdType]] = None, tag_servers: Optional[Union[Tag, IdType]] = None,
|
|
instance_accounts: Optional[str] = None, instance_media_attachments: Optional[str] = None, instance_reports: Optional[str] = None,
|
|
instance_statuses: Optional[str] = None, instance_follows: Optional[str] = None, instance_followers: Optional[str] = None) -> NonPaginatableList[AdminMeasure]:
|
|
"""
|
|
Retrieves numerical instance information for the time period (at day granularity) between `start_at` and `end_at`.
|
|
|
|
* `active_users`: Pass true to retrieve the number of active users on your instance within the time period
|
|
* `new_users`: Pass true to retrieve the number of users who joined your instance within the time period
|
|
* `interactions`: Pass true to retrieve the number of interactions (favourites, boosts, replies) on local statuses within the time period
|
|
* `opened_reports`: Pass true to retrieve the number of reports filed within the time period
|
|
* `resolved_reports` = Pass true to retrieve the number of reports resolved within the time period
|
|
* `tag_accounts`: Pass a tag ID to get the number of accounts which used that tag in at least one status within the time period
|
|
* `tag_uses`: Pass a tag ID to get the number of statuses which used that tag within the time period
|
|
* `tag_servers`: Pass a tag ID to to get the number of remote origin servers for statuses which used that tag within the time period
|
|
* `instance_accounts`: Pass a domain to get the number of accounts originating from that remote domain within the time period
|
|
* `instance_media_attachments`: Pass a domain to get the amount of space used by media attachments from that remote domain within the time period
|
|
* `instance_reports`: Pass a domain to get the number of reports filed against accounts from that remote domain within the time period
|
|
* `instance_statuses`: Pass a domain to get the number of statuses originating from that remote domain within the time period
|
|
* `instance_follows`: Pass a domain to get the number of accounts from a remote domain followed by that local user within the time period
|
|
* `instance_followers`: Pass a domain to get the number of local accounts followed by accounts from that remote domain within the time period
|
|
|
|
This API call is relatively expensive - watch your servers load if you want to get a lot of statistical data. Especially the instance_statuses stats
|
|
might take a long time to compute and, in fact, time out.
|
|
|
|
There is currently no way to get tag IDs implemented in Mastodon.py, because the Mastodon public API does not implement one. This will be fixed in a future
|
|
release.
|
|
"""
|
|
params_init = locals()
|
|
keys = []
|
|
for key in ["active_users", "new_users", "interactions", "opened_reports", "resolved_reports"]:
|
|
if params_init[key] == True:
|
|
keys.append(key)
|
|
|
|
params = {}
|
|
for key in ["tag_accounts", "tag_uses", "tag_servers"]:
|
|
if params_init[key] is not None:
|
|
keys.append(key)
|
|
params[key] = {"id": self.__unpack_id(params_init[key])}
|
|
for key in ["instance_accounts", "instance_media_attachments", "instance_reports", "instance_statuses", "instance_follows", "instance_followers"]:
|
|
if params_init[key] is not None:
|
|
keys.append(key)
|
|
params[key] = {"domain": Mastodon.__deprotocolize(params_init[key]).split("/")[0]}
|
|
|
|
if len(keys) == 0:
|
|
raise MastodonIllegalArgumentError("Must request at least one metric.")
|
|
|
|
params["keys"] = keys
|
|
params["start_at"] = self.__consistent_isoformat_utc(start_at)
|
|
params["end_at"] = self.__consistent_isoformat_utc(end_at)
|
|
|
|
return self.__api_request('POST', '/api/v1/admin/measures', params, use_json=True)
|
|
|
|
@api_version("3.5.0", "3.5.0")
|
|
def admin_dimensions(self, start_at: datetime, end_at: datetime, limit: Optional[int] = None, languages: bool = False, sources: bool = False,
|
|
servers: bool = False, space_usage: bool = False, software_versions: bool = False, tag_servers: Optional[Union[Tag, IdType]] = None,
|
|
tag_languages: Optional[Union[Tag, IdType]] = None, instance_accounts: Optional[str] = None, instance_languages: Optional[str] = None) -> NonPaginatableList[AdminDimension]:
|
|
"""
|
|
Retrieves primarily categorical instance information for the time period (at day granularity) between `start_at` and `end_at`.
|
|
|
|
* `languages`: Pass true to get the most-used languages on this server
|
|
* `sources`: Pass true to get the most-used client apps on this server
|
|
* `servers`: Pass true to get the remote servers with the most statuses
|
|
* `space_usage`: Pass true to get the how much space is used by different components your software stack
|
|
* `software_versions`: Pass true to get the version numbers for your software stack
|
|
* `tag_servers`: Pass a tag ID to get the most-common servers for statuses including a trending tag
|
|
* `tag_languages`: Pass a tag ID to get the most-used languages for statuses including a trending tag
|
|
* `instance_accounts`: Pass a domain to get the most-followed accounts from a remote server
|
|
* `instance_languages`: Pass a domain to get the most-used languages from a remote server
|
|
|
|
Pass `limit` to set how many results you want on queries where that makes sense.
|
|
|
|
This API call is relatively expensive - watch your servers load if you want to get a lot of statistical data.
|
|
|
|
There is currently no way to get tag IDs implemented in Mastodon.py, because the Mastodon public API does not implement one. This will be fixed in a future
|
|
release.
|
|
"""
|
|
params_init = locals()
|
|
keys = []
|
|
for key in ["languages", "sources", "servers", "space_usage", "software_versions"]:
|
|
if params_init[key] == True:
|
|
keys.append(key)
|
|
|
|
params = {}
|
|
for key in ["tag_servers", "tag_languages"]:
|
|
if params_init[key] is not None:
|
|
keys.append(key)
|
|
params[key] = {"id": self.__unpack_id(params_init[key])}
|
|
for key in ["instance_accounts", "instance_languages"]:
|
|
if params_init[key] is not None:
|
|
keys.append(key)
|
|
params[key] = {"domain": Mastodon.__deprotocolize(params_init[key]).split("/")[0]}
|
|
|
|
if len(keys) == 0:
|
|
raise MastodonIllegalArgumentError("Must request at least one dimension.")
|
|
|
|
params["keys"] = keys
|
|
if limit is not None:
|
|
params["limit"] = limit
|
|
params["start_at"] = self.__consistent_isoformat_utc(start_at)
|
|
params["end_at"] = self.__consistent_isoformat_utc(end_at)
|
|
|
|
return self.__api_request('POST', '/api/v1/admin/dimensions', params, use_json=True)
|
|
|
|
@api_version("3.5.0", "3.5.0")
|
|
def admin_retention(self, start_at: datetime, end_at: datetime, frequency: str = "day") -> NonPaginatableList[AdminRetention]:
|
|
"""
|
|
Gets user retention statistics (at `frequency` - "day" or "month" - granularity) between `start_at` and `end_at`.
|
|
"""
|
|
if not frequency in ["day", "month"]:
|
|
raise MastodonIllegalArgumentError("Frequency must be day or month")
|
|
|
|
params = {
|
|
"start_at": self.__consistent_isoformat_utc(start_at),
|
|
"end_at": self.__consistent_isoformat_utc(end_at),
|
|
"frequency": frequency
|
|
}
|
|
return self.__api_request('POST', '/api/v1/admin/retention', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_canonical_email_blocks(self, max_id: Optional[IdType] = None, min_id: Optional[IdType] = None,
|
|
since_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[AdminCanonicalEmailBlock]:
|
|
"""
|
|
Fetches a list of canonical email blocks. Requires scope `admin:read:canonical_email_blocks`.
|
|
|
|
The returned list may be paginated using max_id, min_id, and since_id.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/canonical_email_blocks', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_canonical_email_block(self, id: IdType) -> AdminCanonicalEmailBlock:
|
|
"""
|
|
Fetch a single canonical email block by ID. Requires scope `admin:read:canonical_email_blocks`.
|
|
|
|
Raises `MastodonAPIError` if the email block does not exist.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/canonical_email_blocks/{id}')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_test_canonical_email_block(self, email: str) -> NonPaginatableList[AdminCanonicalEmailBlock]:
|
|
"""
|
|
Canonicalize and hash an email address, returning all matching canonical email blocks. Requires scope `admin:read:canonical_email_blocks`.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('POST', '/api/v1/admin/canonical_email_blocks/test', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_create_canonical_email_block(self, email: Optional[str] = None, canonical_email_hash: Optional[str] = None) -> AdminCanonicalEmailBlock:
|
|
"""
|
|
Block a canonical email. Requires scope `admin:write:canonical_email_blocks`.
|
|
|
|
Either `email` (which will be canonicalized and hashed) or `canonical_email_hash` must be provided.
|
|
|
|
Up do date details about the canonicalization and hashing process can be found here:
|
|
|
|
https://github.com/mastodon/mastodon/blob/main/app/helpers/email_helper.rb
|
|
|
|
As of Mastodon v4.4.0:
|
|
* Everything lowercased
|
|
* Dots are removed from the part before the @
|
|
* Anything after a + is removed
|
|
* The hash in use is SHA256
|
|
"""
|
|
if email is None and canonical_email_hash is None:
|
|
raise MastodonIllegalArgumentError("Either 'email' or 'canonical_email_hash' must be provided.")
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('POST', '/api/v1/admin/canonical_email_blocks', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_delete_canonical_email_block(self, id: IdType) -> AdminCanonicalEmailBlock:
|
|
"""
|
|
Delete a canonical email block by ID. Requires scope `admin:write:canonical_email_blocks`.
|
|
|
|
Raises `MastodonAPIError` if the email block does not exist.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('DELETE', f'/api/v1/admin/canonical_email_blocks/{id}')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_domain_allows(self, max_id: Optional[IdType] = None, min_id: Optional[IdType] = None,
|
|
since_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[AdminDomainAllow]:
|
|
"""
|
|
Fetches a list of allowed domains. Requires scope `admin:read:domain_allows`.
|
|
|
|
The returned list may be paginated using max_id, min_id, and since_id.
|
|
|
|
NB: Untested, since I don't have a Mastodon instance in allowlist mode to test this with.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/domain_allows', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_domain_allow(self, id: Union[AdminDomainAllow, IdType]) -> AdminDomainAllow:
|
|
"""
|
|
Fetch a single allowed domain by ID. Requires scope `admin:read:domain_allows`.
|
|
|
|
Raises `MastodonAPIError` if the domain allow does not exist.
|
|
|
|
NB: Untested, since I don't have a Mastodon instance in allowlist mode to test this with.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/domain_allows/{id}')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_create_domain_allow(self, domain: str) -> AdminDomainAllow:
|
|
"""
|
|
Allow a domain for federation. Requires scope `admin:write:domain_allows`.
|
|
|
|
If the domain is already allowed, returns the existing record.
|
|
|
|
NB: Untested, since I don't have a Mastodon instance in allowlist mode to test this with.
|
|
"""
|
|
params = {"domain": domain}
|
|
return self.__api_request('POST', '/api/v1/admin/domain_allows', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_delete_domain_allow(self, id: Union[AdminDomainAllow, IdType]):
|
|
"""
|
|
Remove a domain from the allowlist. Requires scope `admin:write:domain_allows`.
|
|
|
|
Raises `MastodonAPIError` if the domain allow does not exist.
|
|
|
|
NB: Untested, since I don't have a Mastodon instance in allowlist mode to test this with.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
self.__api_request('DELETE', f'/api/v1/admin/domain_allows/{id}')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_email_domain_blocks(self, max_id: Optional[IdType] = None, min_id: Optional[IdType] = None,
|
|
since_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[AdminEmailDomainBlock]:
|
|
"""
|
|
Fetches a list of blocked email domains. Requires scope `admin:read:email_domain_blocks`.
|
|
|
|
The returned list may be paginated using max_id, min_id, and since_id.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/email_domain_blocks', params)
|
|
|
|
@api_version("4.1.0", "4.1.0")
|
|
def admin_email_domain_block(self, id: IdType) -> AdminEmailDomainBlock:
|
|
"""
|
|
Fetch a single blocked email domain by ID. Requires scope `admin:read:email_domain_blocks`.
|
|
|
|
Raises `MastodonAPIError` if the email domain block does not exist.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/email_domain_blocks/{id}')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_create_email_domain_block(self, domain: str) -> AdminEmailDomainBlock:
|
|
"""
|
|
Block an email domain from signups. Requires scope `admin:write:email_domain_blocks`.
|
|
|
|
If the domain contains invalid characters, a `MastodonAPIError` will be raised.
|
|
"""
|
|
params = {"domain": domain}
|
|
return self.__api_request('POST', '/api/v1/admin/email_domain_blocks', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_delete_email_domain_block(self, id: IdType):
|
|
"""
|
|
Remove an email domain block. Requires scope `admin:write:email_domain_blocks`.
|
|
|
|
Raises `MastodonAPIError` if the email domain block does not exist.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
self.__api_request('DELETE', f'/api/v1/admin/email_domain_blocks/{id}')
|
|
|
|
@api_version("4.2.0", "4.2.0")
|
|
def admin_approve_trending_link(self, id: Union[PreviewCard, IdType]) -> PreviewCard:
|
|
"""
|
|
Approve a trending link. Requires scope `admin:write`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/trends/links/{id}/approve')
|
|
|
|
@api_version("4.2.0", "4.2.0")
|
|
def admin_reject_trending_link(self, id: Union[PreviewCard, IdType]) -> PreviewCard:
|
|
"""
|
|
Reject a trending link. Requires scope `admin:write`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/trends/links/{id}/reject')
|
|
|
|
@api_version("4.2.0", "4.2.0")
|
|
def admin_approve_trending_status(self, id: Union[Status, IdType]) -> Status:
|
|
"""
|
|
Approve a trending status. Requires scope `admin:write`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/trends/statuses/{id}/approve')
|
|
|
|
@api_version("4.2.0", "4.2.0")
|
|
def admin_reject_trending_status(self, id: Union[Status, IdType]) -> Status:
|
|
"""
|
|
Reject a trending status. Requires scope `admin:write`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/trends/statuses/{id}/reject')
|
|
|
|
@api_version("4.2.0", "4.2.0")
|
|
def admin_approve_trending_tag(self, id: Union[Tag, IdType]) -> Tag:
|
|
"""
|
|
Approve a trending tag. Requires scope `admin:write`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/trends/tags/{id}/approve')
|
|
|
|
@api_version("4.2.0", "4.2.0")
|
|
def admin_reject_trending_tag(self, id: Union[Tag, IdType]) -> Tag:
|
|
"""
|
|
Reject a trending tag. Requires scope `admin:write`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('POST', f'/api/v1/admin/trends/tags/{id}/reject')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_ip_blocks(self, max_id: Optional[IdType] = None, min_id: Optional[IdType] = None,
|
|
since_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[AdminIpBlock]:
|
|
"""
|
|
Fetches a list of blocked IP addresses and ranges. Requires scope `admin:read:ip_blocks`.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('GET', '/api/v1/admin/ip_blocks', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_ip_block(self, id: Union[AdminIpBlock, IdType]) -> AdminIpBlock:
|
|
"""
|
|
Fetch a single blocked IP address or range by ID. Requires scope `admin:read:ip_blocks`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
return self.__api_request('GET', f'/api/v1/admin/ip_blocks/{id}')
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_create_ip_block(self, ip: str, severity: str, comment: Optional[str] = None,
|
|
expires_in: Optional[int] = None) -> AdminIpBlock:
|
|
"""
|
|
Block an IP address or range from signups. Requires scope `admin:write:ip_blocks`.
|
|
|
|
Provide the IP address as a CIDR range, e.g. "192.168.1.1/32" to block just that IP address, or
|
|
"8.8.8.8/24" to block all addresses in the 8.8.8.* subnet.
|
|
|
|
severity can be one of three values:
|
|
* "sign_up_requires_approval" - signups from this IP will require manual approval
|
|
* "sign_up_block" - signups from this IP will be blocked
|
|
* "no_access" - all access from this IP will be blocked
|
|
|
|
expires_in is the number of seconds until the block expires. If not provided, the block will be permanent.
|
|
"""
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('POST', '/api/v1/admin/ip_blocks', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_update_ip_block(self, id: Union[AdminIpBlock, IdType], ip: Optional[str] = None, severity: Optional[str] = None,
|
|
comment: Optional[str] = None, expires_in: Optional[int] = None) -> AdminIpBlock:
|
|
"""
|
|
Update an existing IP block. Requires scope `admin:write:ip_blocks`.
|
|
|
|
expires_in is the number of seconds until the block expires. If not provided, the block will be permanent.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
params = self.__generate_params(locals())
|
|
return self.__api_request('PUT', f'/api/v1/admin/ip_blocks/{id}', params)
|
|
|
|
@api_version("4.0.0", "4.0.0")
|
|
def admin_delete_ip_block(self, id: Union[AdminIpBlock, IdType]):
|
|
"""
|
|
Remove an IP block. Requires scope `admin:write:ip_blocks`.
|
|
"""
|
|
id = self.__unpack_id(id)
|
|
self.__api_request('DELETE', f'/api/v1/admin/ip_blocks/{id}')
|