Extension Chrome qui trouve qui ne vous suit pas en retour · aucun backend, aucun transfert d'identifiants · s'appuie sur la propre session Instagram de l'utilisateur via un content script injecté
Architecture
Vous ouvrez le popup sur n'importe quel onglet instagram.com. Le Popup UI restaure d'abord le scan précédent depuis chrome.storage.local et vérifie le cooldown de 24 h. Sur Analyze, il envoie un message au Content script, qui demande au Session detector l'identifiant de l'utilisateur connecté (cookies d'abord, requête de profil en repli). Le content script parcourt ensuite /friendships/<id>/followers (25 par page) et /following (200 par page) contre l'Instagram web API avec un rythme de 300 ms entre les requêtes. De retour dans le popup, le diff produit l'ensemble de ceux qui ne suivent pas en retour, le Profile categorizer hydrate chaque compte par lots de 5 pour séparer les influenceurs des utilisateurs classiques, et les résultats plus les caches sont réécrits dans le storage.
Déploiement : Extension → Chrome · Manifest V3, chargée non empaquetée ou via le Web Store
Résultats
- 0 · serveurs · tout tourne dans le navigateur de l'utilisateur
- 40+ · langues d'interface auxquelles la détection de connexion survit
- 1er 429 · déclenche un kill switch, le scan se dégrade au lieu d'échouer
- 24 h · cooldown de scan par compte, résultats restaurés entre-temps
Compétences démontrées : rétro-ingénierie d'API privée · pagination consciente des limites de débit · invalidation de cache (TTL de 7 jours) · message passing Chrome · gestion des tokens CSRF
Problèmes et solutions
Une contrainte gouverne tout : Instagram n'a pas d'API publique pour les listes d'abonnés, donc l'extension doit réutiliser la propre session de l'utilisateur sans jamais détenir d'identifiants ni déclencher les limites de débit.
█ S'appuyer sur la session de l'utilisateur, pas sur un backend
Problème : Il n'existe pas d'API officielle pour lister les abonnés. Un scraper côté serveur aurait besoin du mot de passe de l'utilisateur ou de cookies volés, plus de proxies pour survivre aux blocages. Les deux sont rédhibitoires pour la confiance et le coût.
Un content script injecté dans instagram.com appelle les mêmes endpoints privés que l'application web utilise. Les requêtes partent avec credentials: include, le propre user agent du navigateur, et le header d'app id web, donc aux yeux d'Instagram elles ressemblent à l'utilisateur en train de naviguer.
- Backend de scraping rejeté : zéro gestion d'identifiants vaut mieux que le contrôle total du pipeline
- Scraping DOM de la modale des abonnés rejeté : fragile face aux changements de balisage et bien plus lent que les endpoints JSON
- Compromis accepté : les endpoints non documentés peuvent changer sans préavis, donc le code traite chaque réponse comme optionnelle
▒ Limites de débit : rythmer, détecter, dégrader
Problème : La pagination des abonnés plus une requête de profil par compte peut représenter des centaines de requêtes. Marteler des endpoints privés vaut un 429 et peut signaler le vrai compte de l'utilisateur.
Des pauses de 300 ms entre les pages, l'hydratation des profils par lots de 5, et un kill switch global : le premier 429 stoppe toute requête de profil ultérieure pour la session et le scan continue avec les données de base seulement.
- Retry-with-backoff rejeté : réessayer contre un système anti-abus creuse le trou plus profond
- Dégrader vaut mieux qu'échouer : les utilisateurs obtiennent toujours la liste complète de ceux qui ne suivent pas en retour, juste sans scores d'influenceur
- Le kill switch est un drapeau à sens unique, délibérément conservateur pour protéger le compte de l'utilisateur
▚ Cooldown de scan de 24 heures
Problème : Le scan complet est l'opération la plus lourde. Les utilisateurs cliquant sur Analyze de façon répétée multiplient le risque de blocage pour zéro nouvelle information.
Chaque scan est horodaté par compte dans chrome.storage.local. Dans les 24 h, le bouton est désactivé avec un compte à rebours en direct et les résultats sauvegardés sont affichés à la place.
- Scans illimités rejetés : protéger le compte de l'utilisateur prime sur la fraîcheur
- Compromis accepté : les données peuvent être vieilles d'un jour au maximum
- Les actions follow / unfollow mettent à jour les résultats sauvegardés en place, donc la vue en cache reste correcte entre les scans
▞ Détection de connexion en 40+ langues
Problème : La première version détectait la connexion par l'alt text anglais 'profile picture'. Toute interface Instagram non anglaise faisait croire à l'extension que l'utilisateur était déconnecté.
Cinq replis en couches : une table d'alt text localisée couvrant 40+ langues, une heuristique géométrique (petite image carrée dans la nav), les propres objets de données d'Instagram, le lien de déconnexion, et les cookies de session.
- Un unique sélecteur 'fiable' rejeté : les noms de classe obscurcis d'Instagram changent trop souvent pour s'y fier
- Les heuristiques peuvent faire des faux positifs individuellement, donc cinq signaux indépendants se recoupent
- Le contrôle des cookies est le dernier recours car les cookies peuvent survivre à une vraie session
▓ Mettre en cache agressivement, indexer par compte
Problème : L'hydratation des profils coûte une requête par utilisateur analysé. Les rescans et les changements de compte re-paieraient ce coût et re-risqueraient les limites de débit.
Les résultats d'analyse par nom d'utilisateur vivent dans un cache de 7 jours ; les résultats de scan sont indexés par identifiant de compte, donc les personnes gérant plusieurs comptes conservent des historiques distincts qui survivent au changement.
- Simplicité sans cache rejetée : les requêtes répétées sont la ressource la plus rare ici
- 7 jours équilibrent l'obsolescence et le budget de requêtes, les nombres d'abonnés bougent lentement
- Quand le kill switch 429 se déclenche, les profils en cache remplissent quand même les scores gratuitement
▤ Se désabonner sans perdre l'historique
Problème : Se désabonner de quelqu'un pendant la revue de la liste mute les données en dessous : l'utilisateur disparaît, et un rescan pour le refléter brûlerait le scan quotidien.
Les comptes désabonnés passent dans une liste des récemment désabonnés persistée au lieu de disparaître. Se réabonner les restaure dans la liste normale, et chaque changement édite les résultats sauvegardés en place.
- Rescan-après-action rejeté : un désabonnement ne devrait pas coûter un scan complet
- Suppression silencieuse rejetée : les utilisateurs veulent un chemin d'annulation et une trace de qui ils ont retiré
- Les transitions d'état vivent dans le storage, donc fermer le popup en plein nettoyage ne perd rien