Créa·Ka — annuaire public cross-plateforme des créateurs de contenu québécois (crea-ka.com)
Python 73.6%
HTML 13.2%
TypeScript 6%
JavaScript 4.5%
CSS 1.7%
Dockerfile 0.6%
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