269 lines
8.7 KiB
Python
269 lines
8.7 KiB
Python
import json
|
|
from dataclasses import dataclass, field
|
|
from typing import ClassVar, Dict, List, Optional
|
|
|
|
import requests
|
|
|
|
from internetarchive import get_session
|
|
from internetarchive.exceptions import AccountAPIError
|
|
from internetarchive.session import ArchiveSession
|
|
|
|
"""
|
|
internetarchive.account
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
:copyright: (C) 2012-2025 by Internet Archive.
|
|
:license: AGPL 3, see LICENSE for more details.
|
|
|
|
This module provides the `Account` class for interacting with user accounts on the
|
|
Internet Archive. It requires administrative privileges.
|
|
"""
|
|
|
|
|
|
@dataclass
|
|
class Account:
|
|
"""
|
|
A class for interacting with user accounts on the Internet Archive.
|
|
|
|
Note:
|
|
This class requires administrative privileges.
|
|
|
|
This class provides methods to:
|
|
- Fetch account details using various identifiers (e.g., email, screenname, itemname).
|
|
- Lock and unlock accounts.
|
|
- Convert account data to a dictionary for serialization.
|
|
|
|
Example Usage:
|
|
>>> from internetarchive.account import Account
|
|
>>> account = Account.from_account_lookup('email', 'foo@example.com')
|
|
>>> account.lock(comment="Locked spam account")
|
|
>>> print(account.to_dict())
|
|
"""
|
|
locked: bool
|
|
verified: bool
|
|
email: str
|
|
canonical_email: str
|
|
itemname: str
|
|
screenname: str
|
|
notifications: List[str]
|
|
has_disability_access: bool
|
|
lastlogin: str
|
|
createdate: str
|
|
session: ArchiveSession = field(default_factory=get_session)
|
|
|
|
API_BASE_URL: str = '/services/xauthn/'
|
|
API_INFO_PARAMS: ClassVar[Dict[str, str]] = {'op': 'info'}
|
|
API_LOCK_UNLOCK_PARAMS: ClassVar[Dict[str, str]] = {'op': 'lock_unlock'}
|
|
|
|
def _get_api_base_url(self) -> str:
|
|
"""Dynamically construct the API base URL using the session's host."""
|
|
return f'https://{self.session.host}{self.API_BASE_URL}' # type: ignore[attr-defined]
|
|
|
|
def _post_api_request(
|
|
self,
|
|
endpoint: str,
|
|
params: Dict[str, str],
|
|
data: Dict[str, str],
|
|
session: Optional[ArchiveSession] = None
|
|
) -> requests.Response:
|
|
"""
|
|
Helper method to make API requests.
|
|
|
|
Args:
|
|
endpoint: The API endpoint to call.
|
|
params: Query parameters for the request.
|
|
data: Data to send in the request body.
|
|
session: Optional session to use for the request. Defaults to self.session.
|
|
|
|
Returns:
|
|
The response from the API.
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails.
|
|
"""
|
|
session = session or self.session
|
|
url = f'https://{session.host}{endpoint}' # type: ignore[attr-defined]
|
|
response = session.post(url, params=params, data=data)
|
|
response.raise_for_status()
|
|
return response
|
|
|
|
@classmethod
|
|
def from_account_lookup(
|
|
cls,
|
|
identifier_type: str,
|
|
identifier: str,
|
|
session: Optional[ArchiveSession] = None
|
|
) -> "Account":
|
|
"""
|
|
Factory method to initialize an Account using an identifier type and value.
|
|
|
|
Args:
|
|
identifier_type: The type of identifier (e.g., 'email', 'screenname').
|
|
identifier: The value of the identifier (e.g., 'foo@example.com').
|
|
session: Optional session to use for the request.
|
|
|
|
Returns:
|
|
An instance of Account.
|
|
"""
|
|
json_data = cls._fetch_account_data_from_api(identifier_type, identifier, session)
|
|
return cls.from_json(json_data, session)
|
|
|
|
@classmethod
|
|
def _fetch_account_data_from_api(
|
|
cls,
|
|
identifier_type: str,
|
|
identifier: str,
|
|
session: Optional[ArchiveSession] = None
|
|
) -> Dict:
|
|
"""
|
|
Fetches account data from the API using an identifier type and value.
|
|
|
|
Args:
|
|
identifier_type: The type of identifier (e.g., 'email', 'screenname').
|
|
identifier: The value of the identifier (e.g., 'foo@example.com').
|
|
session: Optional session to use for the request.
|
|
|
|
Returns:
|
|
A dictionary containing the account data.
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails.
|
|
ValueError: If the API response is invalid or missing required data.
|
|
"""
|
|
data = {identifier_type: identifier}
|
|
session = session or get_session()
|
|
try:
|
|
response = session.post(
|
|
f'https://{session.host}{cls.API_BASE_URL}', # type: ignore[attr-defined]
|
|
params=cls.API_INFO_PARAMS,
|
|
data=json.dumps(data)
|
|
)
|
|
response.raise_for_status()
|
|
j = response.json()
|
|
if j.get("error") or not j.get("values"):
|
|
raise AccountAPIError(j.get("error", "Unknown error"), error_data=j)
|
|
return j["values"]
|
|
except requests.exceptions.RequestException as e:
|
|
raise AccountAPIError(f"Failed to fetch account data: {e}")
|
|
|
|
|
|
@classmethod
|
|
def from_json(
|
|
cls,
|
|
json_data: Dict,
|
|
session: Optional[ArchiveSession] = None
|
|
) -> "Account":
|
|
"""
|
|
Factory method to initialize an Account using JSON data.
|
|
|
|
Args:
|
|
json_data: A dictionary containing account data.
|
|
session: Optional session to use for the request.
|
|
|
|
Returns:
|
|
An instance of Account.
|
|
|
|
Raises:
|
|
ValueError: If required fields are missing in the JSON data.
|
|
"""
|
|
required_fields = [
|
|
"canonical_email",
|
|
"email",
|
|
"has_disability_access",
|
|
"itemname",
|
|
"locked",
|
|
"notifications",
|
|
"screenname",
|
|
"verified",
|
|
"lastlogin",
|
|
"createdate",
|
|
]
|
|
for requried_field in required_fields:
|
|
if requried_field not in json_data:
|
|
raise ValueError(f"Missing required requried_field in JSON data: {requried_field}")
|
|
|
|
# Ensure session is of type ArchiveSession
|
|
if session is None:
|
|
session = get_session() # Default to ArchiveSession
|
|
elif not isinstance(session, ArchiveSession):
|
|
raise TypeError(f"Expected session to be of type ArchiveSession, got {type(session)}")
|
|
|
|
return cls(
|
|
locked=json_data["locked"],
|
|
verified=json_data["verified"],
|
|
email=json_data["email"],
|
|
canonical_email=json_data["canonical_email"],
|
|
itemname=json_data["itemname"],
|
|
screenname=json_data["screenname"],
|
|
notifications=json_data["notifications"],
|
|
has_disability_access=json_data["has_disability_access"],
|
|
lastlogin=json_data["lastlogin"],
|
|
createdate=json_data["createdate"],
|
|
session=session
|
|
)
|
|
|
|
def lock(self,
|
|
comment: Optional[str] = None,
|
|
session: Optional[ArchiveSession] = None) -> requests.Response:
|
|
"""
|
|
Lock the account.
|
|
|
|
Args:
|
|
comment: An optional comment for the lock operation.
|
|
session: Optional session to use for the request.
|
|
|
|
Returns:
|
|
The response from the API.
|
|
"""
|
|
data = {'itemname': self.itemname, 'is_lock': '1'}
|
|
if comment:
|
|
data['comments'] = comment
|
|
return self._post_api_request(
|
|
self.API_BASE_URL,
|
|
params=self.API_LOCK_UNLOCK_PARAMS,
|
|
data=data,
|
|
session=session
|
|
)
|
|
|
|
def unlock(self,
|
|
comment: Optional[str] = None,
|
|
session: Optional[ArchiveSession] = None) -> requests.Response:
|
|
"""
|
|
Unlock the account.
|
|
|
|
Args:
|
|
comment: An optional comment for the unlock operation.
|
|
session: Optional session to use for the request.
|
|
|
|
Returns:
|
|
The response from the API.
|
|
"""
|
|
data = {'itemname': self.itemname, 'is_lock': '0'}
|
|
if comment:
|
|
data['comments'] = comment
|
|
return self._post_api_request(
|
|
self.API_BASE_URL,
|
|
params=self.API_LOCK_UNLOCK_PARAMS,
|
|
data=data,
|
|
session=session
|
|
)
|
|
|
|
def to_dict(self) -> Dict:
|
|
"""
|
|
Converts the Account instance to a dictionary.
|
|
|
|
Returns:
|
|
A dictionary representation of the Account instance.
|
|
"""
|
|
return {
|
|
"locked": self.locked,
|
|
"verified": self.verified,
|
|
"email": self.email,
|
|
"canonical_email": self.canonical_email,
|
|
"itemname": self.itemname,
|
|
"screenname": self.screenname,
|
|
"notifications": self.notifications,
|
|
"has_disability_access": self.has_disability_access,
|
|
"lastlogin": self.lastlogin,
|
|
"createdate": self.createdate,
|
|
}
|