# ============================================================================== # Author: Simon-Pierre Boucher # File: src/main.py (ka-fansly) # Desc: Profils Fansly via l'API publique apiv3.fansly.com/api/v1/account # (?usernames=) : abonnés (followCount), bio, badge, compteurs de # médias (images/vidéos), avatar/bannière. Lots de 20 handles/requête. # ============================================================================== from __future__ import annotations import asyncio import json from apify import Actor from .net import Fetcher API_URL = ("https://apiv3.fansly.com/api/v1/account" "?usernames={us}&ngsw-bypass=true") def _loc(media: dict | None) -> str | None: """Objet média Fansly → URL de la première variante disponible.""" if not isinstance(media, dict): return None for variant in ([media] + (media.get("variants") or [])): for loc in (variant.get("locations") or []): if loc.get("location"): return loc["location"] return None async def main() -> None: async with Actor: inp = await Actor.get_input() or {} usernames = [u.strip().lstrip("@").lower() for u in (inp.get("usernames") or []) if u and u.strip()] proxy = await Actor.create_proxy_configuration( actor_proxy_input=inp.get("proxyConfiguration")) fetcher = Fetcher(proxy, delay=float(inp.get("requestDelay") or 1)) found: set[str] = set() for i in range(0, len(usernames), 20): batch = usernames[i:i + 20] try: resp = await fetcher.get(API_URL.format(us=",".join(batch)), session_id=f"b{i}", headers={"Accept": "application/json"}) data = (json.loads(resp.text) if resp.status_code == 200 else {}) except Exception as exc: for u in batch: await Actor.push_data( {"kind": "profile", "platform": "fansly", "username": u, "found": False, "error": str(exc)[:200]}) continue for acc in (data.get("response") or []): u = str(acc.get("username") or "").lower() if not u: continue found.add(u) stats = acc.get("timelineStats") or {} tiers = [{"name": t.get("name"), "price": t.get("price")} for t in (acc.get("subscriptionTiers") or []) if isinstance(t, dict) and t.get("name")] await Actor.push_data({ "kind": "profile", "platform": "fansly", "found": True, "id": acc.get("id"), "username": u, "full_name": acc.get("displayName") or u, "biography": (acc.get("about") or "")[:1000], "followers": acc.get("followCount"), "following": acc.get("followingCount"), "is_verified": bool((acc.get("statusInfo") or {}) .get("verified") or acc.get("verified")), "posts_count": acc.get("postCount") or stats.get("postCount"), "images_count": stats.get("imageCount"), "videos_count": stats.get("videoCount"), "likes": acc.get("accountMediaLikes"), "avatar": _loc(acc.get("avatar")), "banner": _loc(acc.get("banner")), "location": acc.get("location"), "account_created_at": acc.get("createdAt"), "subscription_tiers": tiers[:5] or None, "subscription_price": (tiers[0].get("price") if tiers else None), }) for u in batch: if u not in found: await Actor.push_data( {"kind": "profile", "platform": "fansly", "username": u, "found": False, "error": f"not_found_{resp.status_code}"}) Actor.log.info(f"terminé : {len(usernames)} profils, " f"{len(found)} trouvés")