crea-ka: 12 connecteurs Apify maison (enrichissement multi-plateforme ultra détaillé)
Acteurs KA (compte Apify gorgeous_thistle, proxy résidentiel) branchés comme connecteurs denrichissement: - instagram/tiktok/x/youtube/twitch/kick/facebook/threads/snapchat/fansly-apify - profils détaillés: abonnés, bio, badges, engagement, cadence + contenu récent (posts/vidéos/tweets avec likes/commentaires/vues), liens croisés (cross_link) - rotation par fraîcheur (revisit_days), garde-fous Scrapfly (_fresh 72h) - onlyfans-apify + instagram-decouverte présents mais hors ordre auto (SPA / topsearch fermé aux sessions anonymes) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
37 changed files +1,187 −22
added
creaka/connectors/apify_social.py
+461 −0
@@ -0,0 +1,461 @@ | ||
| 1 | +# ============================================================================== | |
| 2 | +# Author: Simon-Pierre Boucher <contact@spboucher.ai> | |
| 3 | +# File: creaka/connectors/apify_social.py | |
| 4 | +# Desc: Connecteurs Apify MAISON — acteurs KA (~/Desktop/ka-apify-actors, | |
| 5 | +# compte gorgeous_thistle) exécutés sur la plateforme Apify avec proxy | |
| 6 | +# RÉSIDENTIEL : enrichissement ULTRA DÉTAILLÉ (abonnés, bio, badges, | |
| 7 | +# contenu récent avec métriques, engagement) pour Instagram, TikTok, | |
| 8 | +# X, Facebook, Threads et Snapchat + découverte Instagram (topsearch). | |
| 9 | +# Un passage = UN run d'acteur par plateforme (lot de handles), au lieu | |
| 10 | +# d'une requête Scrapfly par profil — moins cher, plus fiable (IP | |
| 11 | +# résidentielles + empreinte TLS Chrome côté acteur). | |
| 12 | +# ============================================================================== | |
| 13 | +"""Enrichissement multi-plateforme via les acteurs Apify KA. | |
| 14 | + | |
| 15 | +Chaque connecteur d'enrichissement : | |
| 16 | +1. sélectionne les comptes de SA plateforme dont `last_checked` est plus vieux | |
| 17 | + que `revisit_days` (rotation douce = maîtrise du coût proxy) ; | |
| 18 | +2. lance l'acteur `gorgeous_thistle/ka-<plateforme>` avec le lot de handles | |
| 19 | + (proxy résidentiel Apify forcé) et attend le dataset ; | |
| 20 | +3. applique aux fiches : abonnés/badge (§13), métriques étendues + CONTENU | |
| 21 | + récent (posts/vidéos/tweets avec likes, commentaires, vues) dans | |
| 22 | + `account.metrics`, avatar/bio manquants, liens de bio → `cross_link` (§12.1). | |
| 23 | + | |
| 24 | +Nécessite APIFY_TOKEN (.env). Sans clé : SkipSource propre (§16/§18). | |
| 25 | +Uniquement des profils PUBLICS (§15) ; un profil privé/introuvable est ignoré. | |
| 26 | +""" | |
| 27 | +from __future__ import annotations | |
| 28 | + | |
| 29 | +import os | |
| 30 | +import time | |
| 31 | +from datetime import datetime, timedelta, timezone | |
| 32 | + | |
| 33 | +import requests | |
| 34 | + | |
| 35 | +from ..identity import account, merge_accounts | |
| 36 | +from ..normalize import parse_count, platform_from_url | |
| 37 | +from ..schema import Creator, now_iso | |
| 38 | +from .base import BaseConnector, SkipSource | |
| 39 | +from .linkinbio import is_supported as is_linkinbio | |
| 40 | + | |
| 41 | +APIFY_API = "https://api.apify.com/v2" | |
| 42 | +ACTOR_OWNER = "gorgeous_thistle" | |
| 43 | +RESIDENTIAL = {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"]} | |
| 44 | + | |
| 45 | + | |
| 46 | +def run_actor(name: str, run_input: dict, *, timeout_s: int = 2400, | |
| 47 | + memory_mb: int = 1024) -> list[dict]: | |
| 48 | + """Lance un acteur KA sur Apify et retourne les items de son dataset. | |
| 49 | + | |
| 50 | + Run asynchrone + polling (run-sync plafonne à 300 s, trop court pour les | |
| 51 | + gros lots). Le run est avorté si `timeout_s` est dépassé. | |
| 52 | + """ | |
| 53 | + token = os.environ.get("APIFY_TOKEN") | |
| 54 | + if not token: | |
| 55 | + raise SkipSource("APIFY_TOKEN manquant (voir .env)") | |
| 56 | + headers = {"Authorization": f"Bearer {token}"} | |
| 57 | + resp = requests.post( | |
| 58 | + f"{APIFY_API}/acts/{ACTOR_OWNER}~{name}/runs", | |
| 59 | + json=run_input, headers=headers, | |
| 60 | + params={"memory": memory_mb}, timeout=60) | |
| 61 | + resp.raise_for_status() | |
| 62 | + run_id = resp.json()["data"]["id"] | |
| 63 | + t0 = time.time() | |
| 64 | + status: dict = {} | |
| 65 | + while True: | |
| 66 | + time.sleep(15) | |
| 67 | + r = requests.get(f"{APIFY_API}/actor-runs/{run_id}", | |
| 68 | + headers=headers, timeout=60) | |
| 69 | + r.raise_for_status() | |
| 70 | + status = r.json()["data"] | |
| 71 | + if status["status"] in ("SUCCEEDED", "FAILED", "ABORTED", | |
| 72 | + "TIMED-OUT"): | |
| 73 | + break | |
| 74 | + if time.time() - t0 > timeout_s: | |
| 75 | + requests.post(f"{APIFY_API}/actor-runs/{run_id}/abort", | |
| 76 | + headers=headers, timeout=60) | |
| 77 | + raise RuntimeError(f"acteur {name} : délai {timeout_s}s dépassé") | |
| 78 | + if status["status"] != "SUCCEEDED": | |
| 79 | + raise RuntimeError(f"acteur {name} : run {status['status']}") | |
| 80 | + dataset = status["defaultDatasetId"] | |
| 81 | + items: list[dict] = [] | |
| 82 | + offset = 0 | |
| 83 | + while True: | |
| 84 | + r = requests.get(f"{APIFY_API}/datasets/{dataset}/items", | |
| 85 | + params={"offset": offset, "limit": 1000, | |
| 86 | + "clean": "true"}, | |
| 87 | + headers=headers, timeout=120) | |
| 88 | + r.raise_for_status() | |
| 89 | + chunk = r.json() | |
| 90 | + items.extend(chunk) | |
| 91 | + if len(chunk) < 1000: | |
| 92 | + return items | |
| 93 | + offset += 1000 | |
| 94 | + | |
| 95 | + | |
| 96 | +class _ApifySocialEnrich(BaseConnector): | |
| 97 | + """Base commune des enrichissements Apify (un acteur par plateforme).""" | |
| 98 | + | |
| 99 | + kind = "enrichment" | |
| 100 | + platform = "" # plateforme canonique (§6.2) | |
| 101 | + actor = "" # nom court de l'acteur (ka-instagram…) | |
| 102 | + cap = 300 # comptes max par run (maîtrise du coût) | |
| 103 | + revisit_days = 5 # ne pas re-sonder un compte plus frais que ça | |
| 104 | + content_key = "" # clé du contenu récent dans l'item acteur | |
| 105 | + # clés d'item copiées telles quelles dans account.metrics si non nulles | |
| 106 | + metric_map: dict[str, str] = {} | |
| 107 | + | |
| 108 | + def _stale(self, acc) -> bool: | |
| 109 | + if not acc.last_checked: | |
| 110 | + return True | |
| 111 | + try: | |
| 112 | + seen = datetime.fromisoformat( | |
| 113 | + acc.last_checked.replace("Z", "+00:00")) | |
| 114 | + except ValueError: | |
| 115 | + return True | |
| 116 | + return (datetime.now(timezone.utc) - seen | |
| 117 | + > timedelta(days=self.revisit_days)) | |
| 118 | + | |
| 119 | + def extra_input(self) -> dict: | |
| 120 | + return {} | |
| 121 | + | |
| 122 | + def enrich(self, creators: list[Creator]) -> list[Creator]: | |
| 123 | + targets: dict[str, list] = {} | |
| 124 | + for cr in creators: | |
| 125 | + acc = next((a for a in cr.platforms | |
| 126 | + if a.platform == self.platform and a.handle), None) | |
| 127 | + if acc is None or not self._stale(acc): | |
| 128 | + continue | |
| 129 | + targets.setdefault(acc.handle, []).append((cr, acc)) | |
| 130 | + if len(targets) >= self.cap: | |
| 131 | + break | |
| 132 | + if not targets: | |
| 133 | + return [] | |
| 134 | + items = run_actor(self.actor, { | |
| 135 | + "usernames": sorted(targets), | |
| 136 | + "proxyConfiguration": RESIDENTIAL, | |
| 137 | + **self.extra_input(), | |
| 138 | + }) | |
| 139 | + by_handle = {str(it.get("username") or "").lower(): it | |
| 140 | + for it in items if it.get("kind") == "profile"} | |
| 141 | + enriched: list[Creator] = [] | |
| 142 | + self.errors = 0 | |
| 143 | + for handle, pairs in targets.items(): | |
| 144 | + it = by_handle.get(handle) | |
| 145 | + if it is None: | |
| 146 | + continue | |
| 147 | + if not it.get("found"): | |
| 148 | + if str(it.get("error", "")).startswith(("http_5", "shell")): | |
| 149 | + self.errors += 1 | |
| 150 | + continue | |
| 151 | + for cr, acc in pairs: | |
| 152 | + self.apply(cr, acc, it) | |
| 153 | + enriched.append(cr) | |
| 154 | + return enriched | |
| 155 | + | |
| 156 | + # -- application aux fiches ------------------------------------------------ | |
| 157 | + def apply(self, cr: Creator, acc, it: dict) -> None: | |
| 158 | + acc.followers = parse_count(it.get("followers")) or acc.followers | |
| 159 | + if it.get("is_verified") is not None: | |
| 160 | + acc.verified = bool(it["is_verified"]) | |
| 161 | + acc.last_checked = now_iso() | |
| 162 | + metrics = {dst: it.get(src) for src, dst in self.metric_map.items() | |
| 163 | + if it.get(src) is not None} | |
| 164 | + if self.content_key and it.get(self.content_key): | |
| 165 | + metrics[self.content_key] = it[self.content_key][:12] | |
| 166 | + acc.metrics.update(metrics) | |
| 167 | + if not cr.avatar_url and it.get("avatar"): | |
| 168 | + cr.avatar_url = it["avatar"] | |
| 169 | + if not cr.bio and it.get("biography"): | |
| 170 | + cr.bio = it["biography"] | |
| 171 | + self.cross_links(cr, it) | |
| 172 | + | |
| 173 | + def cross_links(self, cr: Creator, it: dict) -> None: | |
| 174 | + """Liens de bio → link-in-bio ou compte `cross_link` (§12.1).""" | |
| 175 | + for url in self.bio_urls(it): | |
| 176 | + url = (url or "").strip() | |
| 177 | + if url and not url.startswith("http"): | |
| 178 | + url = "https://" + url | |
| 179 | + if not url: | |
| 180 | + continue | |
| 181 | + if is_linkinbio(url) and not cr.link_in_bio_url: | |
| 182 | + cr.link_in_bio_url = url | |
| 183 | + continue | |
| 184 | + hit = platform_from_url(url) | |
| 185 | + if hit and hit[0] != self.platform: | |
| 186 | + cr.platforms = merge_accounts(cr.platforms, [ | |
| 187 | + account(hit[0], hit[1], "cross_link", | |
| 188 | + url=url).finalize()]) | |
| 189 | + | |
| 190 | + def bio_urls(self, it: dict) -> list[str]: | |
| 191 | + return [] | |
| 192 | + | |
| 193 | + | |
| 194 | +class ApifyInstagram(_ApifySocialEnrich): | |
| 195 | + source_id = "instagram-apify" | |
| 196 | + platform = "instagram" | |
| 197 | + actor = "ka-instagram" | |
| 198 | + cap = 400 | |
| 199 | + content_key = "recent_posts" | |
| 200 | + metric_map = {"following": "following", "posts_count": "posts", | |
| 201 | + "category": "category", "is_business": "is_business", | |
| 202 | + "business_category": "business_category", | |
| 203 | + "highlight_reels": "highlight_reels", | |
| 204 | + "avg_likes": "avg_likes", "avg_comments": "avg_comments", | |
| 205 | + "avg_video_views": "avg_video_views", | |
| 206 | + "engagement_rate_pct": "engagement_rate_pct", | |
| 207 | + "posts_per_week": "posts_per_week", | |
| 208 | + "last_post_at": "last_post_at", | |
| 209 | + "video_share_pct": "video_share_pct", | |
| 210 | + "top_post": "top_post"} | |
| 211 | + | |
| 212 | + def bio_urls(self, it: dict) -> list[str]: | |
| 213 | + return [it.get("external_url") or ""] + (it.get("bio_links") or []) | |
| 214 | + | |
| 215 | + def apply(self, cr, acc, it): | |
| 216 | + super().apply(cr, acc, it) | |
| 217 | + related = [r.get("username") for r in (it.get("related_profiles") | |
| 218 | + or []) if r.get("username")] | |
| 219 | + if related: # matière première de découvertes futures (§12.1 mention) | |
| 220 | + acc.metrics["related_profiles"] = related[:10] | |
| 221 | + | |
| 222 | + | |
| 223 | +class ApifyTikTok(_ApifySocialEnrich): | |
| 224 | + source_id = "tiktok-apify" | |
| 225 | + platform = "tiktok" | |
| 226 | + actor = "ka-tiktok" | |
| 227 | + cap = 300 | |
| 228 | + content_key = "recent_videos" | |
| 229 | + metric_map = {"following": "following", "total_likes": "likes", | |
| 230 | + "videos_count": "videos", "region": "region", | |
| 231 | + "friends": "friends", "is_seller": "is_seller", | |
| 232 | + "is_live_now": "is_live_now", | |
| 233 | + "commerce_category": "commerce_category", | |
| 234 | + "account_created_at": "account_created_at", | |
| 235 | + "avg_views": "avg_views", "avg_likes": "avg_likes", | |
| 236 | + "avg_comments": "avg_comments", "avg_shares": "avg_shares", | |
| 237 | + "engagement_rate_pct": "engagement_rate_pct", | |
| 238 | + "top_video": "top_video"} | |
| 239 | + | |
| 240 | + def bio_urls(self, it: dict) -> list[str]: | |
| 241 | + return [it.get("bio_link") or ""] | |
| 242 | + | |
| 243 | + | |
| 244 | +class ApifyX(_ApifySocialEnrich): | |
| 245 | + source_id = "x-apify" | |
| 246 | + platform = "x" | |
| 247 | + actor = "ka-x" | |
| 248 | + cap = 300 | |
| 249 | + content_key = "recent_tweets" | |
| 250 | + metric_map = {"following": "following", "tweets_count": "tweets", | |
| 251 | + "location": "location", "created_at": "created_at", | |
| 252 | + "listed_count": "listed", "account_age_days": "account_age_days", | |
| 253 | + "avg_likes": "avg_likes", "avg_retweets": "avg_retweets", | |
| 254 | + "avg_replies": "avg_replies", | |
| 255 | + "engagement_rate_pct": "engagement_rate_pct", | |
| 256 | + "tweets_per_week": "tweets_per_week", | |
| 257 | + "last_tweet_at": "last_tweet_at", | |
| 258 | + "media_share_pct": "media_share_pct", | |
| 259 | + "top_hashtags": "top_hashtags", "top_tweet": "top_tweet"} | |
| 260 | + | |
| 261 | + def extra_input(self) -> dict: | |
| 262 | + return {"maxTweets": 20} | |
| 263 | + | |
| 264 | + def bio_urls(self, it: dict) -> list[str]: | |
| 265 | + return [it.get("website") or ""] | |
| 266 | + | |
| 267 | + | |
| 268 | +class ApifyFacebook(_ApifySocialEnrich): | |
| 269 | + source_id = "facebook-apify" | |
| 270 | + platform = "facebook" | |
| 271 | + actor = "ka-facebook" | |
| 272 | + cap = 100 | |
| 273 | + revisit_days = 7 | |
| 274 | + metric_map = {"category": "category"} | |
| 275 | + | |
| 276 | + | |
| 277 | +class ApifyThreads(_ApifySocialEnrich): | |
| 278 | + source_id = "threads-apify" | |
| 279 | + platform = "threads" | |
| 280 | + actor = "ka-threads" | |
| 281 | + cap = 60 | |
| 282 | + revisit_days = 7 | |
| 283 | + content_key = "recent_posts" | |
| 284 | + metric_map = {"avg_likes": "avg_likes", "top_post": "top_post"} | |
| 285 | + | |
| 286 | + | |
| 287 | +class ApifySnapchat(_ApifySocialEnrich): | |
| 288 | + source_id = "snapchat-apify" | |
| 289 | + platform = "snapchat" | |
| 290 | + actor = "ka-snapchat" | |
| 291 | + cap = 60 | |
| 292 | + revisit_days = 7 | |
| 293 | + metric_map = {"category": "category", "has_story": "has_story", | |
| 294 | + "has_spotlight": "has_spotlight", "address": "address", | |
| 295 | + "subcategory": "subcategory", | |
| 296 | + "publisher_type": "publisher_type", | |
| 297 | + "story_snaps_count": "story_snaps", | |
| 298 | + "lenses_count": "lenses", | |
| 299 | + "spotlight_highlights_count": "spotlight_highlights"} | |
| 300 | + | |
| 301 | + def bio_urls(self, it: dict) -> list[str]: | |
| 302 | + return [it.get("website") or ""] | |
| 303 | + | |
| 304 | + | |
| 305 | +class ApifyYouTube(_ApifySocialEnrich): | |
| 306 | + source_id = "youtube-apify" | |
| 307 | + platform = "youtube" | |
| 308 | + actor = "ka-youtube" | |
| 309 | + cap = 300 | |
| 310 | + content_key = "recent_videos" | |
| 311 | + metric_map = {"videos_count": "videos", "country": "country", | |
| 312 | + "keywords": "keywords", "channel_id": "channel_id", | |
| 313 | + "avg_views": "avg_views", "top_video": "top_video", | |
| 314 | + "last_video_published": "last_video_published"} | |
| 315 | + | |
| 316 | + | |
| 317 | +class ApifyTwitch(_ApifySocialEnrich): | |
| 318 | + source_id = "twitch-apify" | |
| 319 | + platform = "twitch" | |
| 320 | + actor = "ka-twitch" | |
| 321 | + cap = 300 | |
| 322 | + content_key = "recent_videos" | |
| 323 | + metric_map = {"is_partner": "is_partner", "is_affiliate": "is_affiliate", | |
| 324 | + "team": "team", "created_at": "created_at", | |
| 325 | + "is_live_now": "is_live_now", "live_viewers": "live_viewers", | |
| 326 | + "live_game": "live_game", | |
| 327 | + "last_broadcast_at": "last_broadcast_at", | |
| 328 | + "last_broadcast_game": "last_broadcast_game", | |
| 329 | + "avg_video_views": "avg_video_views", | |
| 330 | + "recent_games": "recent_games"} | |
| 331 | + | |
| 332 | + | |
| 333 | +class ApifyKick(_ApifySocialEnrich): | |
| 334 | + source_id = "kick-apify" | |
| 335 | + platform = "kick" | |
| 336 | + actor = "ka-kick" | |
| 337 | + cap = 120 | |
| 338 | + metric_map = {"is_live_now": "is_live_now", "live_viewers": "live_viewers", | |
| 339 | + "subscription_enabled": "subscription_enabled", | |
| 340 | + "recent_categories": "recent_categories"} | |
| 341 | + | |
| 342 | + def cross_links(self, cr: Creator, it: dict) -> None: | |
| 343 | + """Liens sociaux AUTO-DÉCLARÉS de la fiche Kick → cross_link (§12.1).""" | |
| 344 | + socials = it.get("social_links") or {} | |
| 345 | + tmpl = {"instagram": "https://instagram.com/{}", | |
| 346 | + "twitter": "https://x.com/{}", | |
| 347 | + "youtube": "https://youtube.com/{}", | |
| 348 | + "tiktok": "https://www.tiktok.com/@{}", | |
| 349 | + "facebook": "https://facebook.com/{}", | |
| 350 | + "discord": "https://discord.gg/{}"} | |
| 351 | + for key, val in socials.items(): | |
| 352 | + val = (val or "").strip() | |
| 353 | + if not val: | |
| 354 | + continue | |
| 355 | + url = val if val.startswith("http") else \ | |
| 356 | + tmpl.get(key, "{}").format(val.lstrip("@")) | |
| 357 | + hit = platform_from_url(url) | |
| 358 | + if hit and hit[0] != self.platform: | |
| 359 | + cr.platforms = merge_accounts(cr.platforms, [ | |
| 360 | + account(hit[0], hit[1], "cross_link", | |
| 361 | + url=url).finalize()]) | |
| 362 | + | |
| 363 | + | |
| 364 | +class ApifyOnlyFans(_ApifySocialEnrich): | |
| 365 | + source_id = "onlyfans-apify" | |
| 366 | + platform = "onlyfans" | |
| 367 | + actor = "ka-onlyfans" | |
| 368 | + cap = 100 | |
| 369 | + revisit_days = 7 | |
| 370 | + metric_map = {"posts_count": "posts", "photos_count": "photos", | |
| 371 | + "videos_count": "videos", "likes": "likes", | |
| 372 | + "streams_count": "streams", | |
| 373 | + "subscribe_price_usd": "subscribe_price_usd", | |
| 374 | + "is_free": "is_free", "location": "location"} | |
| 375 | + | |
| 376 | + def bio_urls(self, it: dict) -> list[str]: | |
| 377 | + return [it.get("website") or ""] | |
| 378 | + | |
| 379 | + | |
| 380 | +class ApifyFansly(_ApifySocialEnrich): | |
| 381 | + source_id = "fansly-apify" | |
| 382 | + platform = "fansly" | |
| 383 | + actor = "ka-fansly" | |
| 384 | + cap = 200 | |
| 385 | + revisit_days = 7 | |
| 386 | + metric_map = {"posts_count": "posts", "images_count": "images", | |
| 387 | + "videos_count": "videos", "likes": "likes", | |
| 388 | + "following": "following", "location": "location"} | |
| 389 | + | |
| 390 | + | |
| 391 | +# --- découverte Instagram (topsearch, mots-clés QC) --------------------------- | |
| 392 | + | |
| 393 | +# thèmes × marqueurs QC — mêmes familles que youtube-recherche, adaptées à la | |
| 394 | +# recherche d'UTILISATEURS Instagram (courtes, telles qu'on tape dans l'app) | |
| 395 | +_THEMES = ["humoriste", "gamer", "gaming", "maquillage", "beauté", "fitness", | |
| 396 | + "cuisine", "chef", "mode", "voyage", "musique", "chanteuse", | |
| 397 | + "chanteur", "danse", "maman", "papa", "plein air", "chasse", | |
| 398 | + "pêche", "photographe", "artiste", "peintre", "tatoueur", | |
| 399 | + "coiffure", "esthétique", "auto", "moto", "déco", "immobilier", | |
| 400 | + "entrepreneure", "podcast", "comédien", "comédienne", "drag", | |
| 401 | + "twitch", "youtubeur", "youtubeuse", "tiktokeuse", "influenceuse", | |
| 402 | + "créateur de contenu", "créatrice de contenu"] | |
| 403 | +_MARKERS = ["québec", "qc", "montréal", "mtl"] | |
| 404 | + | |
| 405 | + | |
| 406 | +class InstagramDecouverteApify(BaseConnector): | |
| 407 | + """Découverte Instagram par recherche de mots-clés QC (acteur ka-instagram). | |
| 408 | + | |
| 409 | + L'endpoint topsearch du web public retourne les meilleurs comptes pour un | |
| 410 | + mot-clé (nom, handle, abonnés approximatifs, badge). Requêtes tournantes | |
| 411 | + (~40/passage sur ~160 combinaisons) pour accumuler sans re-payer les mêmes | |
| 412 | + résultats chaque jour. Signal : profil_source (0.98) — le compte découvert | |
| 413 | + EST la source de la fiche. | |
| 414 | + """ | |
| 415 | + | |
| 416 | + source_id = "instagram-decouverte" | |
| 417 | + kind = "discovery" | |
| 418 | + queries_per_pass = 40 | |
| 419 | + | |
| 420 | + def build_queries(self) -> list[str]: | |
| 421 | + combos = [f"{t} {m}" for t in _THEMES for m in _MARKERS] | |
| 422 | + day = datetime.now(timezone.utc).timetuple().tm_yday | |
| 423 | + start = (day * self.queries_per_pass) % len(combos) | |
| 424 | + rotated = combos[start:] + combos[:start] | |
| 425 | + return rotated[:self.queries_per_pass] | |
| 426 | + | |
| 427 | + def fetch(self) -> list[Creator]: | |
| 428 | + # NOTE 2026-08-18 : l'endpoint topsearch du web public exige désormais | |
| 429 | + # une session authentifiée (401 « Server Error » depuis toute IP non | |
| 430 | + # connectée). La découverte par mots-clés est donc suspendue proprement | |
| 431 | + # jusqu'à ce qu'une voie publique existe ; l'enrichissement Instagram | |
| 432 | + # (instagram-apify) alimente `related_profiles` comme graine future. | |
| 433 | + raise SkipSource("topsearch Instagram exige une session connectée " | |
| 434 | + "(voie publique fermée le 2026-08-18)") | |
| 435 | + items = run_actor("ka-instagram", { # noqa: unreachable (repli futur) | |
| 436 | + "usernames": [], | |
| 437 | + "queries": self.build_queries(), | |
| 438 | + "proxyConfiguration": RESIDENTIAL, | |
| 439 | + }) | |
| 440 | + creators: list[Creator] = [] | |
| 441 | + seen: set[str] = set() | |
| 442 | + for it in items: | |
| 443 | + if it.get("kind") != "search_user" or not it.get("username"): | |
| 444 | + continue | |
| 445 | + handle = str(it["username"]).lower() | |
| 446 | + if handle in seen: | |
| 447 | + continue | |
| 448 | + seen.add(handle) | |
| 449 | + acc = account("instagram", handle, "profil_source", | |
| 450 | + followers=parse_count(it.get("followers")), | |
| 451 | + verified=it.get("is_verified"), | |
| 452 | + url=f"https://www.instagram.com/{handle}/") | |
| 453 | + creators.append(Creator( | |
| 454 | + source=self.source_id, | |
| 455 | + external_id=handle, | |
| 456 | + display_name=it.get("full_name") or handle, | |
| 457 | + avatar_url=it.get("avatar") or None, | |
| 458 | + platforms=[acc], | |
| 459 | + notes=f"découvert via recherche Instagram « {it.get('query')} »", | |
| 460 | + )) | |
| 461 | + return creators | |
modified
creaka/connectors/instagram.py
+15 −0
@@ -22,6 +22,8 @@ Uniquement des profils publics ; un profil privé/introuvable est ignoré. | ||
| 22 | 22 | """ |
| 23 | 23 | from __future__ import annotations |
| 24 | 24 | |
| 25 | +from datetime import datetime, timedelta, timezone | |
| 26 | + | |
| 25 | 27 | import json |
| 26 | 28 | |
| 27 | 29 | from ..identity import account, merge_accounts |
@@ -35,6 +37,17 @@ PROFILE_API = ("https://i.instagram.com/api/v1/users/web_profile_info/" | ||
| 35 | 37 | IG_APP_ID = "936619743392459" # app id du web public instagram.com |
| 36 | 38 | |
| 37 | 39 | |
| 40 | + | |
| 41 | +def _fresh(last_checked: str, hours: int = 72) -> bool: | |
| 42 | + """Compte déjà rafraîchi récemment (acteur Apify) — épargner Scrapfly.""" | |
| 43 | + if not last_checked: | |
| 44 | + return False | |
| 45 | + try: | |
| 46 | + seen = datetime.fromisoformat(last_checked.replace("Z", "+00:00")) | |
| 47 | + except ValueError: | |
| 48 | + return False | |
| 49 | + return datetime.now(timezone.utc) - seen < timedelta(hours=hours) | |
| 50 | + | |
| 38 | 51 | class InstagramConnector(BaseConnector): |
| 39 | 52 | source_id = "instagram-profil" |
| 40 | 53 | kind = "enrichment" |
@@ -64,6 +77,8 @@ class InstagramConnector(BaseConnector): | ||
| 64 | 77 | ig = next((a for a in cr.platforms if a.platform == "instagram"), None) |
| 65 | 78 | if ig is None or fetched >= self.max_profiles: |
| 66 | 79 | continue |
| 80 | + if _fresh(ig.last_checked): | |
| 81 | + continue | |
| 67 | 82 | try: |
| 68 | 83 | user = self.fetch_profile(ig.handle) |
| 69 | 84 | except Exception: |
modified
creaka/connectors/tiktok.py
+15 −0
@@ -15,6 +15,8 @@ Scrapfly. Lien de bio → page link-in-bio ou compte croisé (signal cross_link) | ||
| 15 | 15 | """ |
| 16 | 16 | from __future__ import annotations |
| 17 | 17 | |
| 18 | +from datetime import datetime, timedelta, timezone | |
| 19 | + | |
| 18 | 20 | import json |
| 19 | 21 | import re |
| 20 | 22 | |
@@ -43,6 +45,17 @@ def parse_profile(html: str) -> dict | None: | ||
| 43 | 45 | return None |
| 44 | 46 | |
| 45 | 47 | |
| 48 | + | |
| 49 | +def _fresh(last_checked: str, hours: int = 72) -> bool: | |
| 50 | + """Compte déjà rafraîchi récemment (acteur Apify) — épargner Scrapfly.""" | |
| 51 | + if not last_checked: | |
| 52 | + return False | |
| 53 | + try: | |
| 54 | + seen = datetime.fromisoformat(last_checked.replace("Z", "+00:00")) | |
| 55 | + except ValueError: | |
| 56 | + return False | |
| 57 | + return datetime.now(timezone.utc) - seen < timedelta(hours=hours) | |
| 58 | + | |
| 46 | 59 | class TikTokConnector(BaseConnector): |
| 47 | 60 | source_id = "tiktok-profil" |
| 48 | 61 | kind = "enrichment" |
@@ -56,6 +69,8 @@ class TikTokConnector(BaseConnector): | ||
| 56 | 69 | tk = next((a for a in cr.platforms if a.platform == "tiktok"), None) |
| 57 | 70 | if tk is None or fetched >= self.max_profiles: |
| 58 | 71 | continue |
| 72 | + if _fresh(tk.last_checked): | |
| 73 | + continue | |
| 59 | 74 | try: |
| 60 | 75 | result = self.scrapfly(PROFILE_URL.format(h=tk.handle), |
| 61 | 76 | render_js=False) |
modified
creaka/ingest.py
+5 −1
@@ -30,7 +30,11 @@ from .dedup import dedupe | ||
| 30 | 30 | # ordre d'exécution des connecteurs d'enrichissement — link-in-bio vers la |
| 31 | 31 | # fin (il consomme les link_in_bio_url des étapes précédentes) ; bio-liens en |
| 32 | 32 | # tout dernier (il relit les bios enrichies, sans aucune requête) |
| 33 | −ENRICH_ORDER = ["instagram-profil", "tiktok-profil", "youtube", "twitch", | |
| 33 | +ENRICH_ORDER = ["instagram-apify", "tiktok-apify", "x-apify", | |
| 34 | + "facebook-apify", "threads-apify", "snapchat-apify", | |
| 35 | + "youtube-apify", "twitch-apify", "kick-apify", | |
| 36 | + "fansly-apify", | |
| 37 | + "instagram-profil", "tiktok-profil", "youtube", "twitch", | |
| 34 | 38 | "x-profil", "balados-rss", "podcastindex", |
| 35 | 39 | "link-in-bio", "bio-liens", "region-bio"] |
| 36 | 40 | |
modified
data/sources.json
+108 −0
@@ -175,6 +175,114 @@ | ||
| 175 | 175 | "signal_identite": "n/a (ne touche pas aux comptes)", |
| 176 | 176 | "cadence": "quotidienne (watch), tout dernier du pipeline", |
| 177 | 177 | "notes": "vague 4 — liste de ~70 villes SÛRES seulement (pas d'Alma/Waterloo/Varennes : prénoms, homonymes hors QC, particules) ; abstention si villes de régions différentes ; ne remplit que les champs vides" |
| 178 | + }, | |
| 179 | + { | |
| 180 | + "id": "instagram-apify", | |
| 181 | + "famille": "enrichissement", | |
| 182 | + "palier": 4, | |
| 183 | + "acces": "acteur Apify maison ka-instagram (endpoint web public web_profile_info, proxy résidentiel)", | |
| 184 | + "signal_identite": "cross_link (0.90) pour les liens de bio", | |
| 185 | + "cadence": "quotidienne (rotation 5 j, lot <=400)", | |
| 186 | + "notes": "profil ultra détaillé : abonnés, bio, liens, 12 derniers posts (likes/commentaires/vues/musique/lieu), engagement, cadence, profils reliés" | |
| 187 | + }, | |
| 188 | + { | |
| 189 | + "id": "tiktok-apify", | |
| 190 | + "famille": "enrichissement", | |
| 191 | + "palier": 4, | |
| 192 | + "acces": "acteur Apify maison ka-tiktok (page publique @handle, proxy résidentiel)", | |
| 193 | + "signal_identite": "cross_link (0.90) pour le lien de bio", | |
| 194 | + "cadence": "quotidienne (rotation 5 j, lot <=300)", | |
| 195 | + "notes": "abonnés, coeurs, vidéos, bio, badge, commerce/live + vidéos récentes si SSR" | |
| 196 | + }, | |
| 197 | + { | |
| 198 | + "id": "x-apify", | |
| 199 | + "famille": "enrichissement", | |
| 200 | + "palier": 3, | |
| 201 | + "acces": "acteur Apify maison ka-x (syndication officielle des widgets, sans clé)", | |
| 202 | + "signal_identite": "cross_link (0.90) pour le site du profil", | |
| 203 | + "cadence": "quotidienne (rotation 5 j, lot <=300)", | |
| 204 | + "notes": "profil complet + 20 tweets récents avec métriques, hashtags, cadence ; remplace la voie locale 429 (TLS)" | |
| 205 | + }, | |
| 206 | + { | |
| 207 | + "id": "facebook-apify", | |
| 208 | + "famille": "enrichissement", | |
| 209 | + "palier": 4, | |
| 210 | + "acces": "acteur Apify maison ka-facebook (HTML public de page, proxy résidentiel) — EXPÉRIMENTAL", | |
| 211 | + "signal_identite": "—", | |
| 212 | + "cadence": "hebdomadaire (rotation 7 j, lot <=100)", | |
| 213 | + "notes": "abonnés/catégorie/avatar de pages publiques ; found=false fréquent (mur de connexion)" | |
| 214 | + }, | |
| 215 | + { | |
| 216 | + "id": "threads-apify", | |
| 217 | + "famille": "enrichissement", | |
| 218 | + "palier": 4, | |
| 219 | + "acces": "acteur Apify maison ka-threads (JSON embarqué de la page publique, proxy résidentiel)", | |
| 220 | + "signal_identite": "—", | |
| 221 | + "cadence": "hebdomadaire (rotation 7 j, lot <=60)", | |
| 222 | + "notes": "abonnés, bio, badge + posts récents si embarqués" | |
| 223 | + }, | |
| 224 | + { | |
| 225 | + "id": "snapchat-apify", | |
| 226 | + "famille": "enrichissement", | |
| 227 | + "palier": 3, | |
| 228 | + "acces": "acteur Apify maison ka-snapchat (__NEXT_DATA__ de snapchat.com/add)", | |
| 229 | + "signal_identite": "—", | |
| 230 | + "cadence": "hebdomadaire (rotation 7 j, lot <=60)", | |
| 231 | + "notes": "abonnés, bio, badge, catégorie, story/spotlight/lenses, comptes reliés" | |
| 232 | + }, | |
| 233 | + { | |
| 234 | + "id": "instagram-decouverte", | |
| 235 | + "famille": "decouverte", | |
| 236 | + "palier": 4, | |
| 237 | + "acces": "acteur Apify maison ka-instagram mode recherche (topsearch web public, proxy résidentiel)", | |
| 238 | + "signal_identite": "profil_source (0.98)", | |
| 239 | + "cadence": "quotidienne (40 requêtes tournantes sur ~160 combinaisons thème x QC)", | |
| 240 | + "notes": "découverte de créateurs IG par mots-clés québécois" | |
| 241 | + }, | |
| 242 | + { | |
| 243 | + "id": "youtube-apify", | |
| 244 | + "famille": "enrichissement", | |
| 245 | + "palier": 3, | |
| 246 | + "acces": "acteur Apify maison ka-youtube (ytInitialData de /videos, sans clé)", | |
| 247 | + "signal_identite": "—", | |
| 248 | + "cadence": "quotidienne (rotation 5 j, lot <=300)", | |
| 249 | + "notes": "abonnés, nb vidéos, pays, description + vidéos récentes avec vues ; complète le connecteur youtube (API) quand la clé manque" | |
| 250 | + }, | |
| 251 | + { | |
| 252 | + "id": "twitch-apify", | |
| 253 | + "famille": "enrichissement", | |
| 254 | + "palier": 3, | |
| 255 | + "acces": "acteur Apify maison ka-twitch (GQL public Client-ID web, sans clé)", | |
| 256 | + "signal_identite": "—", | |
| 257 | + "cadence": "quotidienne (rotation 5 j, lot <=300)", | |
| 258 | + "notes": "abonnés, partenaire/affilié, live en cours, dernière diffusion, jeux + vidéos récentes" | |
| 259 | + }, | |
| 260 | + { | |
| 261 | + "id": "kick-apify", | |
| 262 | + "famille": "enrichissement", | |
| 263 | + "palier": 4, | |
| 264 | + "acces": "acteur Apify maison ka-kick (API v2 publique, proxy résidentiel pour Cloudflare)", | |
| 265 | + "signal_identite": "cross_link (0.90) pour les liens sociaux auto-déclarés", | |
| 266 | + "cadence": "quotidienne (rotation 5 j, lot <=120)", | |
| 267 | + "notes": "abonnés, live, catégories + liens sociaux déclarés → découverte croisée" | |
| 268 | + }, | |
| 269 | + { | |
| 270 | + "id": "onlyfans-apify", | |
| 271 | + "famille": "enrichissement", | |
| 272 | + "palier": 4, | |
| 273 | + "acces": "acteur Apify maison ka-onlyfans (page publique, proxy résidentiel) — EXPÉRIMENTAL", | |
| 274 | + "signal_identite": "—", | |
| 275 | + "cadence": "hebdomadaire (rotation 7 j, lot <=100)", | |
| 276 | + "notes": "posts/photos/vidéos/J'aime, prix d'abonnement, bio ; found=false fréquent (mur Cloudflare)" | |
| 277 | + }, | |
| 278 | + { | |
| 279 | + "id": "fansly-apify", | |
| 280 | + "famille": "enrichissement", | |
| 281 | + "palier": 3, | |
| 282 | + "acces": "acteur Apify maison ka-fansly (API v1 publique, lots de 20)", | |
| 283 | + "signal_identite": "—", | |
| 284 | + "cadence": "hebdomadaire (rotation 7 j, lot <=200)", | |
| 285 | + "notes": "abonnés, bio, badge, compteurs de médias, avatar" | |
| 178 | 286 | } |
| 179 | 287 | ] |
| 180 | 288 | } |
| \ No newline at end of file | ||
modified
docs/connecteurs/INDEX.md
+14 −2
@@ -1,8 +1,8 @@ | ||
| 1 | 1 | # Créa-Ka — Index des connecteurs |
| 2 | 2 | |
| 3 | −_Généré automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Généré automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | −**19 sources au registre** · **9437 créateurs** et **10512 comptes** en BD. | |
| 5 | +**31 sources au registre** · **9437 créateurs** et **10512 comptes** en BD. | |
| 6 | 6 | |
| 7 | 7 | | Source | Famille | Palier | Type d'accès | Backend | Découverts | Comptes (plateforme) | État | Dernier passage | |
| 8 | 8 | |---|---|---|---|---|---|---|---|---| |
@@ -25,3 +25,15 @@ _Généré automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14: | ||
| 25 | 25 | | [`twitch-decouverte`](twitch-decouverte.md) | decouverte | P3 | pages HTML publiques | direct | 262 | — | actif | 2026-08-18T14:33… | |
| 26 | 26 | | [`kick-decouverte`](kick-decouverte.md) | decouverte | P4 | pages HTML publiques | Scrapfly | 119 | — | actif | 2026-08-18T14:42… | |
| 27 | 27 | | [`region-bio`](region-bio.md) | enrichissement | P1 | pages HTML publiques | direct | 0 | — | actif | 2026-08-18T17:29… | |
| 28 | +| [`instagram-apify`](instagram-apify.md) | enrichissement | P4 | — | — | 0 | — | actif | — | | |
| 29 | +| [`tiktok-apify`](tiktok-apify.md) | enrichissement | P4 | — | — | 0 | — | actif | — | | |
| 30 | +| [`x-apify`](x-apify.md) | enrichissement | P3 | — | — | 0 | — | actif | — | | |
| 31 | +| [`facebook-apify`](facebook-apify.md) | enrichissement | P4 | — | — | 0 | — | actif | — | | |
| 32 | +| [`threads-apify`](threads-apify.md) | enrichissement | P4 | — | — | 0 | — | actif | — | | |
| 33 | +| [`snapchat-apify`](snapchat-apify.md) | enrichissement | P3 | — | — | 0 | — | actif | — | | |
| 34 | +| [`instagram-decouverte`](instagram-decouverte.md) | decouverte | P4 | API JSON | direct | 0 | — | actif | — | | |
| 35 | +| [`youtube-apify`](youtube-apify.md) | enrichissement | P3 | — | — | 0 | — | actif | — | | |
| 36 | +| [`twitch-apify`](twitch-apify.md) | enrichissement | P3 | — | — | 0 | — | actif | — | | |
| 37 | +| [`kick-apify`](kick-apify.md) | enrichissement | P4 | — | — | 0 | — | actif | — | | |
| 38 | +| [`onlyfans-apify`](onlyfans-apify.md) | enrichissement | P4 | — | — | 0 | — | actif | — | | |
| 39 | +| [`fansly-apify`](fansly-apify.md) | enrichissement | P3 | — | — | 0 | — | actif | — | | |
modified
docs/connecteurs/balados-itunes.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `balados-itunes` — connecteur decouverte (palier 2) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 460 · touchés : 460 |
| 6 | 6 | |
modified
docs/connecteurs/balados-rss.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `balados-rss` — connecteur enrichissement (palier 1) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
modified
docs/connecteurs/bio-liens.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `bio-liens` — connecteur enrichissement (palier 1) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
added
docs/connecteurs/facebook-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `facebook-apify` — connecteur enrichissement (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés/catégorie/avatar de pages publiques ; found=false fréquent (mur de connexion) | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-facebook (HTML public de page, proxy résidentiel) — EXPÉRIMENTAL | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : hebdomadaire (rotation 7 j, lot <=100) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync facebook-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='facebook-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-facebook (HTML public de page, proxy résidentiel) — EXPÉRIMENTAL | |
| 40 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
added
docs/connecteurs/fansly-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `fansly-apify` — connecteur enrichissement (palier 3) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, bio, badge, compteurs de médias, avatar | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-fansly (API v1 publique, lots de 20) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : hebdomadaire (rotation 7 j, lot <=200) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync fansly-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='fansly-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-fansly (API v1 publique, lots de 20) | |
| 40 | +- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics. | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
added
docs/connecteurs/instagram-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `instagram-apify` — connecteur enrichissement (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : profil ultra détaillé : abonnés, bio, liens, 12 derniers posts (likes/commentaires/vues/musique/lieu), engagement, cadence, profils reliés | |
| 10 | +- **Signal d'identité** : cross_link (0.90) pour les liens de bio (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-instagram (endpoint web public web_profile_info, proxy résidentiel) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : quotidienne (rotation 5 j, lot <=400) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync instagram-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='instagram-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-instagram (endpoint web public web_profile_info, proxy résidentiel) | |
| 40 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
added
docs/connecteurs/instagram-decouverte.md
+55 −0
@@ -0,0 +1,55 @@ | ||
| 1 | +# `instagram-decouverte` — connecteur decouverte (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +Connecteurs Apify MAISON — acteurs KA (~/Desktop/ka-apify-actors, compte gorgeous_thistle) exécutés sur la plateforme Apify avec proxy RÉSIDENTIEL : enrichissement ULTRA DÉTAILLÉ (abonnés, bio, badges, contenu récent avec métriques, engagement) pour Instagram, TikTok, X, Facebook, Threads et Snapchat + découverte Instagram (topsearch). Un passage = UN run d'acteur par plateforme (lot de handles), au lieu d'une requête Scrapfly par profil — moins cher, plus fiable (IP résidentielles + empreinte TLS Chrome côté acteur). | |
| 10 | + | |
| 11 | +- **Notes (registre)** : découverte de créateurs IG par mots-clés québécois | |
| 12 | +- **Signal d'identité** : profil_source (0.98) (colonne `accounts.signal` / `confidence`) | |
| 13 | +- **Module** : `creaka/connectors/apify_social.py` — classe `InstagramDecouverteApify` (`kind = "discovery"`) | |
| 14 | + | |
| 15 | +## Accès | |
| 16 | + | |
| 17 | +- **Accès (registre)** : acteur Apify maison ka-instagram mode recherche (topsearch web public, proxy résidentiel) | |
| 18 | +- **Type d'accès (code)** : API JSON | |
| 19 | +- **Endpoint de base** : https://api.apify.com/v2 | |
| 20 | +- **URLs du module** : https://api.apify.com/v2 · https://instagram.com/{} · https://x.com/{} · https://youtube.com/{} | |
| 21 | +- **Pagination** : pagination par offset | |
| 22 | +- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli) | |
| 23 | +- **Politesse** : 0.8 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)` | |
| 24 | +- **Authentification** : aucune — contenu public / API anonyme | |
| 25 | + | |
| 26 | +## Champs récupérés → schéma cible | |
| 27 | + | |
| 28 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 29 | + | |
| 30 | +## Fréquence & budget | |
| 31 | + | |
| 32 | +- **Cadence déclarée (registre)** : quotidienne (40 requêtes tournantes sur ~160 combinaisons thème x QC) | |
| 33 | +- **Cadence observée** (médiane sync_log) : — | |
| 34 | +- **Throttling** : 0.8 s entre requêtes (`request_delay`) | |
| 35 | + | |
| 36 | +## Volumétrie & complétude | |
| 37 | + | |
| 38 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 39 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 40 | + | |
| 41 | +## Erreurs connues & dépannage | |
| 42 | + | |
| 43 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 44 | + | |
| 45 | +Rejouer la source seule : `python3 run.py sync instagram-decouverte` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='instagram-decouverte' ORDER BY ts DESC LIMIT 5;`). | |
| 46 | + | |
| 47 | +## Licence, attribution & conditions | |
| 48 | + | |
| 49 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-instagram mode recherche (topsearch web public, proxy résidentiel) | |
| 50 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 51 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 52 | + | |
| 53 | +## Historique | |
| 54 | + | |
| 55 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/instagram-profil.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `instagram-profil` — connecteur enrichissement (palier 4) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : Scrapfly · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
added
docs/connecteurs/kick-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `kick-apify` — connecteur enrichissement (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, live, catégories + liens sociaux déclarés → découverte croisée | |
| 10 | +- **Signal d'identité** : cross_link (0.90) pour les liens sociaux auto-déclarés (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-kick (API v2 publique, proxy résidentiel pour Cloudflare) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : quotidienne (rotation 5 j, lot <=120) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync kick-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='kick-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-kick (API v2 publique, proxy résidentiel pour Cloudflare) | |
| 40 | +- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics. | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/kick-decouverte.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `kick-decouverte` — connecteur decouverte (palier 4) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : Scrapfly · Créateurs découverts : 119 · touchés : 119 |
| 6 | 6 | |
modified
docs/connecteurs/link-in-bio.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `link-in-bio` — connecteur enrichissement (palier 1) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : Firecrawl · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
modified
docs/connecteurs/listes-medias.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `listes-medias` — connecteur decouverte (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 310 · touchés : 310 |
| 6 | 6 | |
added
docs/connecteurs/onlyfans-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `onlyfans-apify` — connecteur enrichissement (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : posts/photos/vidéos/J'aime, prix d'abonnement, bio ; found=false fréquent (mur Cloudflare) | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-onlyfans (page publique, proxy résidentiel) — EXPÉRIMENTAL | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : hebdomadaire (rotation 7 j, lot <=100) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync onlyfans-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='onlyfans-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-onlyfans (page publique, proxy résidentiel) — EXPÉRIMENTAL | |
| 40 | +- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics. | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/onlyqueb.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `onlyqueb` — connecteur decouverte (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 253 · touchés : 253 |
| 6 | 6 | |
modified
docs/connecteurs/palmares-apple.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `palmares-apple` — connecteur decouverte (palier 2) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
modified
docs/connecteurs/podcastindex.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `podcastindex` — connecteur enrichissement (palier 2) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : prêt — clés requises** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
modified
docs/connecteurs/region-bio.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `region-bio` — connecteur enrichissement (palier 1) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
added
docs/connecteurs/snapchat-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `snapchat-apify` — connecteur enrichissement (palier 3) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, bio, badge, catégorie, story/spotlight/lenses, comptes reliés | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-snapchat (__NEXT_DATA__ de snapchat.com/add) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : hebdomadaire (rotation 7 j, lot <=60) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync snapchat-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='snapchat-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-snapchat (__NEXT_DATA__ de snapchat.com/add) | |
| 40 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
added
docs/connecteurs/threads-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `threads-apify` — connecteur enrichissement (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, bio, badge + posts récents si embarqués | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-threads (JSON embarqué de la page publique, proxy résidentiel) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : hebdomadaire (rotation 7 j, lot <=60) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync threads-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='threads-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-threads (JSON embarqué de la page publique, proxy résidentiel) | |
| 40 | +- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics. | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
added
docs/connecteurs/tiktok-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `tiktok-apify` — connecteur enrichissement (palier 4) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, coeurs, vidéos, bio, badge, commerce/live + vidéos récentes si SSR | |
| 10 | +- **Signal d'identité** : cross_link (0.90) pour le lien de bio (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-tiktok (page publique @handle, proxy résidentiel) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : quotidienne (rotation 5 j, lot <=300) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync tiktok-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='tiktok-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-tiktok (page publique @handle, proxy résidentiel) | |
| 40 | +- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics. | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/tiktok-profil.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `tiktok-profil` — connecteur enrichissement (palier 4) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : Scrapfly · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
added
docs/connecteurs/twitch-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `twitch-apify` — connecteur enrichissement (palier 3) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, partenaire/affilié, live en cours, dernière diffusion, jeux + vidéos récentes | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-twitch (GQL public Client-ID web, sans clé) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : quotidienne (rotation 5 j, lot <=300) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync twitch-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='twitch-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-twitch (GQL public Client-ID web, sans clé) | |
| 40 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/twitch-decouverte.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `twitch-decouverte` — connecteur decouverte (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 262 · touchés : 262 |
| 6 | 6 | |
modified
docs/connecteurs/twitch.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `twitch` — connecteur enrichissement (palier 2) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
modified
docs/connecteurs/wikidata-qc.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `wikidata-qc` — connecteur decouverte (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 73 · touchés : 73 |
| 6 | 6 | |
modified
docs/connecteurs/wikipedia-qc.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `wikipedia-qc` — connecteur decouverte (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 84 · touchés : 84 |
| 6 | 6 | |
added
docs/connecteurs/x-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `x-apify` — connecteur enrichissement (palier 3) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : profil complet + 20 tweets récents avec métriques, hashtags, cadence ; remplace la voie locale 429 (TLS) | |
| 10 | +- **Signal d'identité** : cross_link (0.90) pour le site du profil (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-x (syndication officielle des widgets, sans clé) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : quotidienne (rotation 5 j, lot <=300) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync x-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='x-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-x (syndication officielle des widgets, sans clé) | |
| 40 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/x-profil.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `x-profil` — connecteur enrichissement (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
added
docs/connecteurs/youtube-apify.md
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +# `youtube-apify` — connecteur enrichissement (palier 3) | |
| 2 | + | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | + | |
| 5 | +**État : actif** · Famille : enrichissement (enrichissement) · Backend : — · Créateurs découverts : 0 · touchés : 0 | |
| 6 | + | |
| 7 | +## Description de la source | |
| 8 | + | |
| 9 | +- **Notes (registre)** : abonnés, nb vidéos, pays, description + vidéos récentes avec vues ; complète le connecteur youtube (API) quand la clé manque | |
| 10 | +- **Signal d'identité** : — (colonne `accounts.signal` / `confidence`) | |
| 11 | +- **Module** : introuvable (vérifier le registre) | |
| 12 | + | |
| 13 | +## Accès | |
| 14 | + | |
| 15 | +- **Accès (registre)** : acteur Apify maison ka-youtube (ytInitialData de /videos, sans clé) | |
| 16 | + | |
| 17 | +## Champs récupérés → schéma cible | |
| 18 | + | |
| 19 | +Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`. | |
| 20 | + | |
| 21 | +## Fréquence & budget | |
| 22 | + | |
| 23 | +- **Cadence déclarée (registre)** : quotidienne (rotation 5 j, lot <=300) | |
| 24 | +- **Cadence observée** (médiane sync_log) : — | |
| 25 | + | |
| 26 | +## Volumétrie & complétude | |
| 27 | + | |
| 28 | +- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0 | |
| 29 | +- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs | |
| 30 | + | |
| 31 | +## Erreurs connues & dépannage | |
| 32 | + | |
| 33 | +Aucune erreur ni alerte dans les 60 derniers runs journalisés. | |
| 34 | + | |
| 35 | +Rejouer la source seule : `python3 run.py sync youtube-apify` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='youtube-apify' ORDER BY ts DESC LIMIT 5;`). | |
| 36 | + | |
| 37 | +## Licence, attribution & conditions | |
| 38 | + | |
| 39 | +- **Cadre d'accès (registre)** : acteur Apify maison ka-youtube (ytInitialData de /videos, sans clé) | |
| 40 | +- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`). | |
| 41 | +- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion). | |
| 42 | + | |
| 43 | +## Historique | |
| 44 | + | |
| 45 | +- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`). | |
modified
docs/connecteurs/youtube-recherche.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `youtube-recherche` — connecteur decouverte (palier 3) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 7876 · touchés : 7876 |
| 6 | 6 | |
modified
docs/connecteurs/youtube.md
+1 −1
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | # `youtube` — connecteur enrichissement (palier 2) |
| 2 | 2 | |
| 3 | −_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:01 — ne pas éditer à la main, régénérer._ | |
| 3 | +_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 14:47 — ne pas éditer à la main, régénérer._ | |
| 4 | 4 | |
| 5 | 5 | **État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0 |
| 6 | 6 | |
| 7 | 7 | |