from __future__ import annotations
from typing import Any, Mapping
import tls_client.response
from ._base import ResourceBase
__all__ = ['UserCPResource']
[docs]
class UserCPResource(ResourceBase):
"""User Control Panel: account settings, profile, signature, credentials.
Every ``update_*`` / ``change_*`` method auto-fetches its own
``my_post_key`` from the corresponding form page when not provided. POSTs
land on ``/usercp.php`` with the appropriate ``action=do_*`` field.
"""
[docs]
async def get_overview(self) -> tls_client.response.Response:
"""Fetch the UserCP landing page (``/usercp.php``)."""
return await self._http.get('/usercp.php')
[docs]
async def get_options_page(self) -> tls_client.response.Response:
"""Fetch the options form page."""
return await self._http.get('/usercp.php?action=options')
[docs]
async def get_profile_page(self) -> tls_client.response.Response:
"""Fetch the profile-edit form page."""
return await self._http.get('/usercp.php?action=profile')
[docs]
async def get_signature_page(self) -> tls_client.response.Response:
"""Fetch the signature-edit form page."""
return await self._http.get('/usercp.php?action=editsig')
[docs]
async def get_change_username_page(self) -> tls_client.response.Response:
"""Fetch the change-username form page."""
return await self._http.get('/usercp.php?action=changename')
[docs]
async def get_change_password_page(self) -> tls_client.response.Response:
"""Fetch the change-password form page."""
return await self._http.get('/usercp.php?action=password')
[docs]
async def get_change_email_page(self) -> tls_client.response.Response:
"""Fetch the change-email form page."""
return await self._http.get('/usercp.php?action=email')
[docs]
async def update_notepad(
self,
notepad: str,
*,
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Save the UserCP notepad scratch text.
Args:
notepad: New notepad contents.
my_post_key: Pre-fetched CSRF token. Auto-fetched from
``/usercp.php`` when omitted.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_notepad',
'my_post_key': my_post_key,
'notepad': notepad,
'submit': 'Update',
},
)
[docs]
async def update_signature(
self,
signature: str,
*,
update_existing_posts: str = '0',
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Update the forum signature.
Args:
signature: New signature body (BBCode supported).
update_existing_posts: ``'enable'``, ``'disable'``, or ``'0'``.
Whether to re-render the new signature on already-posted
messages.
my_post_key: Pre-fetched CSRF token.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php?action=editsig')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_editsig',
'my_post_key': my_post_key,
'signature': signature,
'updateposts': update_existing_posts,
'submit': 'Update Signature',
},
)
[docs]
async def update_options(
self,
options: Mapping[str, Any],
*,
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Update the UserCP options form (notification prefs, theme, etc.).
Args:
options: Mapping of form fields to values, e.g.
``{'allownotices': '1', 'receivepms': '1', 'tpp': '20'}``.
Field names mirror the page's ``<input name=...>`` attributes
— fetch :meth:`get_options_page` once to enumerate them.
my_post_key: Pre-fetched CSRF token.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php?action=options')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_options',
'my_post_key': my_post_key,
**options,
},
)
[docs]
async def update_profile(
self,
fields: Mapping[str, Any],
*,
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Update the profile-edit form (bio, custom fields, user title, …).
Args:
fields: Mapping of profile field names to values, e.g.
``{'usertitle': 'foo', 'profile_fields[fid5]': 'bar'}``.
my_post_key: Pre-fetched CSRF token.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php?action=profile')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_profile',
'my_post_key': my_post_key,
**fields,
'regsubmit': 'Update Profile',
},
)
[docs]
async def change_username(
self,
new_username: str,
password: str,
*,
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Change your forum username (subject to OGU's cooldown rules).
Args:
new_username: New username.
password: Current password (required by OGU for confirmation).
my_post_key: Pre-fetched CSRF token.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php?action=changename')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_changename',
'my_post_key': my_post_key,
'username': new_username,
'password': password,
'submit': 'Update Username',
},
)
[docs]
async def change_password(
self,
old_password: str,
new_password: str,
*,
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Change your forum password.
Args:
old_password: Current password.
new_password: New password (sent twice as ``password`` and
``password2``).
my_post_key: Pre-fetched CSRF token.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php?action=password')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_password',
'my_post_key': my_post_key,
'oldpassword': old_password,
'password': new_password,
'password2': new_password,
'submit': 'Update Password',
},
)
[docs]
async def change_email(
self,
new_email: str,
password: str,
*,
my_post_key: str | None = None,
) -> tls_client.response.Response:
"""Change your account email.
Args:
new_email: New email address (sent twice as ``email`` and
``email2``).
password: Current password (required by OGU for confirmation).
my_post_key: Pre-fetched CSRF token.
"""
my_post_key = await self._resolve_my_post_key(my_post_key, '/usercp.php?action=email')
return await self._http.post(
'/usercp.php',
data = {
'action': 'do_email',
'my_post_key': my_post_key,
'email': new_email,
'email2': new_email,
'password': password,
'submit': 'Update Email Address',
},
)
async def _resolve_my_post_key(self, key: str | None, page_path: str) -> str:
if key is not None:
return key
response = await self._http.get(page_path)
return self.extract_my_post_key(response.text) or ''