131 lines
4.2 KiB
Python
131 lines
4.2 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timedelta, timezone
|
|
from typing import Any
|
|
|
|
from .config import SourceSettings
|
|
from .fluxer_api import FluxerAPI
|
|
|
|
PAGE_SIZE = 200
|
|
MAX_PAGES = 50
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Item:
|
|
key: str
|
|
title: str
|
|
detail: str
|
|
path: str | None = None
|
|
urgent: bool = False
|
|
opened_at: datetime | None = None
|
|
reminder_after: timedelta | None = None
|
|
|
|
|
|
FetchFunc = Callable[[FluxerAPI, SourceSettings], list[Item]]
|
|
|
|
|
|
def _parse_time(value: Any) -> datetime | None:
|
|
if not isinstance(value, str):
|
|
return None
|
|
try:
|
|
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
except ValueError:
|
|
return None
|
|
if parsed.tzinfo is None:
|
|
parsed = parsed.replace(tzinfo=timezone.utc)
|
|
return parsed
|
|
|
|
|
|
def fetch_pending_reports(api: FluxerAPI, cfg: SourceSettings) -> list[Item]:
|
|
items: list[Item] = []
|
|
reminder_after = timedelta(hours=cfg.stale_after_hours) if cfg.stale_after_hours else None
|
|
offset = 0
|
|
for _ in range(MAX_PAGES):
|
|
data = api.get(
|
|
"/admin/reports",
|
|
{"status": "pending", "limit": PAGE_SIZE, "offset": offset},
|
|
)
|
|
reports = data.get("reports", [])
|
|
for report in reports:
|
|
report_id = str(report["report_id"])
|
|
reason = report.get("reason_label") or report.get("category") or "no reason given"
|
|
items.append(
|
|
Item(
|
|
key=f"report:{report_id}",
|
|
title="Report waiting for review",
|
|
detail=f"Reason: {reason}",
|
|
urgent=bool(report.get("reason_highest_priority")),
|
|
opened_at=_parse_time(report.get("reported_at")),
|
|
reminder_after=reminder_after,
|
|
)
|
|
)
|
|
offset += len(reports)
|
|
if not reports or offset >= int(data.get("total", 0)):
|
|
break
|
|
return items
|
|
|
|
|
|
def fetch_pending_registrations(api: FluxerAPI, cfg: SourceSettings) -> list[Item]:
|
|
data = api.get("/admin/instance/pending-registrations")
|
|
items: list[Item] = []
|
|
for entry in data.get("pending_registrations", []):
|
|
user_id = str(entry["user_id"])
|
|
name = entry.get("username") or user_id
|
|
items.append(
|
|
Item(
|
|
key=f"registration:{user_id}",
|
|
title="Account waiting for approval",
|
|
detail=f"User: {name}",
|
|
)
|
|
)
|
|
return items
|
|
|
|
|
|
def fetch_discovery_applications(api: FluxerAPI, cfg: SourceSettings) -> list[Item]:
|
|
data = api.get("/admin/discovery/applications")
|
|
applications = data if isinstance(data, list) else data.get("applications", [])
|
|
items: list[Item] = []
|
|
for entry in applications:
|
|
guild_id = str(entry["guild_id"])
|
|
name = entry.get("guild_name") or guild_id
|
|
items.append(
|
|
Item(
|
|
key=f"discovery:{guild_id}",
|
|
title="Community waiting for discovery approval",
|
|
detail=f"Community: {name}",
|
|
)
|
|
)
|
|
return items
|
|
|
|
|
|
def fetch_enforcement_review(api: FluxerAPI, cfg: SourceSettings) -> list[Item]:
|
|
data = api.get("/admin/enforcement-decisions", {"review_queue": "true", "limit": 100})
|
|
items: list[Item] = []
|
|
for entry in data.get("decisions", []):
|
|
decision_id = str(entry["decision_id"])
|
|
kind = entry.get("kind", "decision")
|
|
subject = entry.get("subject_name") or entry.get("subject_id") or "unknown"
|
|
items.append(
|
|
Item(
|
|
key=f"enforcement:{decision_id}",
|
|
title="Automated enforcement needs review",
|
|
detail=f"{kind} on {entry.get('subject_type', 'unknown')} {subject}",
|
|
)
|
|
)
|
|
return items
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Source:
|
|
name: str
|
|
fetch: FetchFunc
|
|
|
|
|
|
SOURCES: tuple[Source, ...] = (
|
|
Source("pending_reports", fetch_pending_reports),
|
|
Source("pending_registrations", fetch_pending_registrations),
|
|
Source("discovery_applications", fetch_discovery_applications),
|
|
Source("enforcement_review", fetch_enforcement_review),
|
|
)
|