SPB Git forge

spb/crea-ka

Public

Créa·Ka — annuaire public cross-plateforme des créateurs de contenu québécois (crea-ka.com)

52commits 1branches 0releases
11.3 MBsize
maindefault branch
19 days agolast push
Python 73.6% HTML 13.2% TypeScript 6% JavaScript 4.5% CSS 1.7% Dockerfile 0.6%
4.5 KB · 94 lines python
Raw Blame History
1# ==============================================================================2# Author: Simon-Pierre Boucher <contact@spboucher.ai>3# File:   src/main.py (ka-discord)4# Desc:   Serveurs Discord via l'API PUBLIQUE d'invitation5#         /api/v9/invites/{code}?with_counts=true : membres approximatifs,6#         présents en ligne, nom/description/boosts du serveur. Le CODE d'invit7#         est SENSIBLE À LA CASSE → il vient de l'URL, pas du handle minusculé.8# ==============================================================================9from __future__ import annotations1011import asyncio12import json1314from apify import Actor1516from .net import Fetcher1718INVITE_API = ("https://discord.com/api/v9/invites/{code}"19              "?with_counts=true&with_expiration=true")202122async def main() -> None:23    async with Actor:24        inp = await Actor.get_input() or {}25        # ici « usernames » = codes d'invitation (casse préservée par l'appelant)26        codes = [c.strip() for c in (inp.get("usernames") or []) if c.strip()]27        proxy = await Actor.create_proxy_configuration(28            actor_proxy_input=inp.get("proxyConfiguration"))29        fetcher = Fetcher(proxy, delay=float(inp.get("requestDelay") or 1))30        sem = asyncio.Semaphore(int(inp.get("concurrency") or 6))3132        async def one(code: str) -> None:33            async with sem:34                miss = {"kind": "profile", "platform": "discord",35                        "username": code, "found": False}36                try:37                    resp = await fetcher.get(38                        INVITE_API.format(code=code), session_id=code[:12],39                        headers={"Accept": "application/json"})40                except Exception as exc:41                    await Actor.push_data({**miss, "error": str(exc)[:200]})42                    return43                if resp.status_code == 404:44                    await Actor.push_data({**miss, "error": "invite_invalide"})45                    return46                if resp.status_code != 200:47                    await Actor.push_data(48                        {**miss, "error": f"http_{resp.status_code}"})49                    return50                try:51                    d = json.loads(resp.text)52                except Exception:53                    await Actor.push_data({**miss, "error": "bad_json"})54                    return55                guild = d.get("guild") or {}56                icon = guild.get("icon")57                gid = guild.get("id")58                banner_hash = guild.get("banner")59                splash_hash = guild.get("splash")60                await Actor.push_data({61                    "kind": "profile",62                    "platform": "discord",63                    "found": True,64                    "username": code,65                    "guild_id": gid,66                    "full_name": guild.get("name"),67                    "biography": guild.get("description"),68                    "followers": d.get("approximate_member_count"),  # membres69                    "members": d.get("approximate_member_count"),70                    "online": d.get("approximate_presence_count"),71                    "boosts": guild.get("premium_subscription_count"),72                    "verified": ("VERIFIED" in (guild.get("features") or [])),73                    "partnered": ("PARTNERED" in (guild.get("features") or [])),74                    "channel": (d.get("channel") or {}).get("name"),75                    "avatar": (f"https://cdn.discordapp.com/icons/{gid}/{icon}.png"76                               if gid and icon else None),77                    "banner": (f"https://cdn.discordapp.com/banners/{gid}/"78                               f"{banner_hash}.png?size=1024"79                               if gid and banner_hash else None),80                    "splash": (f"https://cdn.discordapp.com/splashes/{gid}/"81                               f"{splash_hash}.jpg?size=1024"82                               if gid and splash_hash else None),83                    "vanity_url_code": guild.get("vanity_url_code"),84                    "features": sorted(guild.get("features") or [])[:12],85                    "nsfw_level": guild.get("nsfw_level"),86                    "premium_tier": guild.get("premium_tier"),87                    "invite_expires_at": d.get("expires_at"),88                    "inviter": ((d.get("inviter") or {}).get("username")89                                or None),90                })9192        await asyncio.gather(*[one(c) for c in codes])93        Actor.log.info(f"terminé : {len(codes)} invitations")94