Source code for ogu_api.resources.credits

from __future__ import annotations

import calendar
from datetime import datetime
from typing import Any, Mapping

import tls_client.response

from ..models import RecentTransaction
from ._base import ResourceBase

__all__ = ['CreditsResource']


[docs] class CreditsResource(ResourceBase): """OGU credits: donate, send, and the public stats feed. Paths: - ``/credits.php?action=donate`` — donate form. - ``/credits.php?action=stats`` — recently-sent stats. - ``/credits.php`` (POST) — submit donation. **Requires a solved hCaptcha token; this SDK does not ship a solver.** """
[docs] async def get_donate_page(self) -> tls_client.response.Response: """Fetch the donate form page.""" return await self._http.get('/credits.php?action=donate')
[docs] async def get_stats_page(self) -> tls_client.response.Response: """Fetch the credits stats page (raw HTML; recent transactions table).""" return await self._http.get('/credits.php?action=stats')
[docs] async def recently_sent(self) -> list[RecentTransaction]: """Parse the public "recently sent" feed. Returns: Newest-first list of :class:`~ogu_api.RecentTransaction`. ``date`` is a UTC unix timestamp; ``0`` if the cell couldn't be parsed. Example: >>> for tx in await client.credits.recently_sent(): ... print(f'{tx.sender} -> {tx.recipient}: {tx.amount}') """ response = await self.get_stats_page() return self.extract_recently_sent(response.text)
[docs] async def send( self, username: str, amount: int | float, captcha_token: str, reason: str = '', *, hidden: Mapping[str, Any] | None = None, ) -> tls_client.response.Response: """Send credits to another user. OGU requires a solved hCaptcha token on every donation; you must provide one. The hidden form fields (``donate_token`` and friends) are auto-fetched when ``hidden`` is omitted. Args: username: Recipient username. amount: Number of credits to send. captcha_token: A solved hCaptcha token, sent as both ``g-recaptcha-response`` and ``h-captcha-response``. reason: Optional message attached to the donation. hidden: Pre-fetched hidden form fields. Skips the auto-fetch round-trip. Returns: Raw POST response. ``"You may close this page."`` in the body indicates success. Example: >>> await client.credits.send( ... username = 'recipient', ... amount = 100, ... captcha_token = solved_token, ... ) """ if hidden is None: page = await self.get_donate_page() hidden = self.extract_hidden(page.text) return await self._http.post( '/credits.php?action=donate', data = { **hidden, 'username': username, 'amount': amount, 'reason': reason, 'g-recaptcha-response': captcha_token, 'h-captcha-response': captcha_token, 'submit': 'Submit', }, )
[docs] @staticmethod def extract_hidden(page_html: str) -> dict[str, Any]: """Pull hidden inputs off the donate form (``form[action="credits.php"]``).""" return ResourceBase._extract_hidden(page_html, form_selector = 'form[action="credits.php"]')
[docs] @staticmethod def extract_recently_sent(page_html: str) -> list[RecentTransaction]: """Parse the recently-sent transactions table. Args: page_html: HTML of ``/credits.php?action=stats``. Returns: Newest-first list of :class:`~ogu_api.RecentTransaction`. """ soup = ResourceBase._soup(page_html) transactions: list[RecentTransaction] = [] for row in soup.find_all('tr'): cells = row.find_all('td') if len(cells) < 4: continue if not any(c in (cells[0].get('class') or []) for c in ('trow1', 'trow2')): continue if not cells[2].find('i', class_ = 'fa-coins'): continue sender_link = cells[0].find('a') recipient_link = cells[1].find('a') if not sender_link or not recipient_link: continue amount_text = ''.join(c for c in cells[2].get_text() if c.isdigit()) amount = int(amount_text) if amount_text else 0 date_str = cells[3].get_text(strip = True) try: dt = datetime.strptime(date_str, '%m-%d-%Y, %I:%M %p') date = int(calendar.timegm(dt.timetuple())) except ValueError: date = 0 transactions.append(RecentTransaction( sender = sender_link.get_text(strip = True), recipient = recipient_link.get_text(strip = True), amount = amount, date = date, )) return transactions