No description
  • Rust 99.2%
  • Python 0.6%
  • Dockerfile 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
rltbg de38ae758d LinkedIn : une personne vérifiée sur sa seule page d'expériences (2 pages par ligne au lieu de 3)
La page de profil ne contient que l'en-tête : ses expériences arrivent après
coup. La page d'expériences porte le prénom et le nom liés à l'adresse du
profil ainsi que les postes datés. L'identité est cherchée dans l'objet qui
contient l'identifiant, même après un sous-objet.
2026-10-05 18:31:48 +02:00
docs Revert "LinkedIn lu par le navigateur Wry de la connexion : profil persistant par compte, section « À propos » lue" 2026-10-05 17:01:36 +02:00
scripts first commit 2026-09-21 08:13:22 +02:00
src LinkedIn : une personne vérifiée sur sa seule page d'expériences (2 pages par ligne au lieu de 3) 2026-10-05 18:31:48 +02:00
.dockerignore Docker : export de dist/yellow seul, base Debian 12, cache cargo, cible check 2026-10-05 10:06:20 +02:00
.gitignore Nettoyage des sources et de l'application 2026-10-05 10:22:37 +02:00
Cargo.lock Revert "LinkedIn lu par le navigateur Wry de la connexion : profil persistant par compte, section « À propos » lue" 2026-10-05 17:01:36 +02:00
Cargo.toml Revert "LinkedIn lu par le navigateur Wry de la connexion : profil persistant par compte, section « À propos » lue" 2026-10-05 17:01:36 +02:00
core.604446 Web : site officiel reconnu par la recherche du contact, extraction fiabilisée 2026-10-05 12:37:11 +02:00
Dockerfile Docker : export de dist/yellow seul, base Debian 12, cache cargo, cible check 2026-10-05 10:06:20 +02:00
README.md LinkedIn : une personne vérifiée sur sa seule page d'expériences (2 pages par ligne au lieu de 3) 2026-10-05 18:31:48 +02:00

Yellow — fichier clients

Application de bureau Rust / Iced 0.13, en français, pour vérifier et enrichir les informations client avant de sauvegarder le fichier Excel ouvert ou une nouvelle copie.

Utilisation

  1. L'accueil propose Reprendre le dernier fichier ou Choisir un fichier Excel. Le dernier chemin local est mémorisé entre les lancements. Un fichier OneDrive configuré peut être chargé automatiquement.

  2. Une fois le fichier ouvert, choisir la source puis Analyser et mettre à jour. Mettre en pause conserve la position et le différentiel (propositions et décisions) dans un fichier local du dossier de configuration ; rouvrir le même classeur après redémarrage permet de reprendre l'analyse. Yellow analyse un classeur à la fois : un seul point de reprise existe, et l'analyse d'un autre classeur le remplace. La reprise est refusée si le contenu original du fichier a changé. Ce point est enregistré au départ, toutes les 100 lignes, à la pause et à chaque décision. Ouvrir le tableau sans analyse reste disponible pour consulter les données.

    Une ligne qui échoue (page inaccessible, moteur indisponible, profil ambigu) garde son message et l'analyse continue. Seules une session LinkedIn expirée, une demande de pause de LinkedIn, une clé LinkdAPI refusée ou des crédits épuisés arrêtent l'analyse ; elle peut alors être reprise.

  3. La source LinkedIn + Web demande une connexion depuis le petit engrenage en bas à gauche avant toute analyse. L'application recherche le contact avec son nom et son entreprise. Elle vérifie le titulaire du poste cible, puis cherche seulement l'e-mail ou les éléments de l'adresse de livraison manquants. Une ligne confirmée et complète ne déclenche pas de recherche Web. La recherche Web complète interroge le moteur sélectionné dans les paramètres (DuckDuckGo par défaut, Google retiré) puis recoupe les pages d'entreprise et l'Annuaire des Entreprises. Chaque proposition comporte sa source et reste à accepter dans le tableau. L'autre source est LinkdAPI · payant.

  4. Vérifier les anciennes valeurs rouges et les propositions vertes directement dans les cellules. Une ligne qui porte des propositions est légèrement teintée (orange tant qu'un choix est attendu) et ses cases modifiées le sont plus nettement. ✓ Valider la ligne et ✕ Refuser la ligne traitent toute la ligne ; chaque case garde ses propres ✓, × et Modifier. Tout accepter couvre toutes les feuilles, y compris les lignes masquées par un filtre. Un refus, de cellule comme de ligne, est une décision : une nouvelle analyse ne repropose pas la valeur. Le bouton ↶ (Annuler la dernière action au survol) remonte une à une toutes les acceptations, refus, choix et modifications faits depuis l'ouverture, le dernier enregistrement ou le lancement d'une analyse. La colonne Vérification et sources indique le nombre de sources ; un clic ouvre la fiche, où chaque source, profil LinkedIn compris, est un lien qui s'ouvre dans le navigateur. Un ? en début de ligne signale qu'aucune information n'a été trouvée. Un badge orange « ! » dans la case e-mail signale une adresse probablement périmée (domaine d'un ancien employeur) pour laquelle aucun nouvel e-mail n'a été trouvé ; le survol en donne la raison. Quand plusieurs adresses sont plausibles, un menu déroulant orange s'affiche dans la case de l'adresse : la valeur retenue devient une proposition à accepter, et le menu reste modifiable. Le menu Filtre propose À choisir pour les lignes dont le choix n'est pas encore fait : une ligne en sort dès qu'une valeur est choisie. Glisser la bordure droite d'un en-tête pour redimensionner la colonne. Recherche, filtres et pagination de 40 clients gardent le tableau fluide.

  5. Enregistrer met à jour le fichier ouvert, local ou OneDrive, avec les valeurs validées seulement. Enregistrer sous… crée un nouveau fichier local et le rend actif ; un nom existant est refusé. Les propositions non validées restent en attente après sauvegarde.

La sauvegarde locale directe conserve une copie de récupération .yellow-backup-*.xlsx dans le dossier d'origine, compare le contenu ouvert au contenu présent sur disque, puis publie le nouveau fichier par remplacement atomique. Un conflit laisse les modifications dans l'application. Cette vérification n'est pas un verrou partagé avec Excel : éviter les écritures simultanées. La copie de récupération permet de retrouver la version précédente.

La recherche Web interroge le moteur sélectionné dans les paramètres : DuckDuckGo (sans clé), Brave Search, Firecrawl, Exa, Tavily ou Serper. Google est retiré. Les clés API se configurent dans le coffre-fort système ou via YELLOW_BRAVE_SEARCH_KEY, YELLOW_FIRECRAWL_API_KEY, YELLOW_EXA_API_KEY, YELLOW_TAVILY_API_KEY et YELLOW_SERPER_API_KEY (la variable est prioritaire) ; elles ne sont jamais écrites dans le classeur. Si le moteur choisi échoue (clé refusée, quota, limitation, panne) ou n'a pas de clé, les autres moteurs dont une clé est enregistrée prennent le relais, puis DuckDuckGo. Une page protégée par une vérification JavaScript est rouverte par le navigateur intégré, puis par Firecrawl si une clé Firecrawl existe. Le journal local web-debug.jsonl détaille les requêtes et décisions, sans les clés API ; son chemin apparaît dans les paramètres.

Pour une analyse de plusieurs milliers de lignes, vérifier le quota du moteur actif avant le traitement. Yellow ne recherche pas un e-mail déjà valide et réutilise les contacts nominatifs déjà vus sur les pages publiques. Les mentions légales et exemples de messagerie sont partagés entre les lignes d'une même entreprise ou d'un même domaine. Les recherches sont espacées d'environ une seconde, quel que soit le moteur.

Les pages publiques, les moteurs et LinkdAPI reçoivent l'identifiant HTTP Claude-User. Les requêtes LinkedIn présentent exactement l'identifiant (navigator.userAgent) du navigateur Wry qui a ouvert la session, enregistré avec elle dans le coffre-fort : un autre identifiant sur une session connectée serait incohérent. Une session enregistrée avant cette version garde un identifiant Chrome jusqu'à la reconnexion.

OneDrive entreprise

Les paramètres acceptent l'ID du tenant, l'ID de l'application de service, le Drive ID, le chemin du fichier et un secret client protégé par le coffre-fort natif. Enregistrer et vérifier teste la connexion et l'existence du fichier. Charger ce fichier au démarrage automatise son ouverture. La sauvegarde distante vise exclusivement l'élément chargé et contrôle sa version.

Un administrateur doit accorder à cette identité un accès en lecture/écriture sur le seul fichier, via les permissions Microsoft Selected. Le chemin saisi dans l'application ne remplace pas les autorisations du tenant. Configuration complète pour l'administrateur.

Deux fournisseurs indépendants

LinkedIn utilise votre session conservée dans le coffre-fort système. LinkdAPI · payant est un fournisseur tiers distinct, avec sa propre clé et ses crédits. Il n'y a aucun basculement automatique vers ce service. Dans les paramètres, chaque fournisseur possède sa connexion et sa déconnexion.

L'analyse retrouve d'abord le contact, puis vérifie ses expériences datées dans l'entreprise cible. S'il occupe toujours une fonction équivalente, elle conserve le poste cible du fichier et ne remplit que les coordonnées manquantes. S'il est parti, ou si son profil reste introuvable, elle recherche le titulaire actuel du poste ; lors d'un changement de personne, l'entreprise et la fonction restent les cibles de la ligne. Les indépendants sont suivis par leur activité, sans recherche de successeur dans une entreprise fictive.

Si l'ancien contact déclare un nouveau poste actuel, la révision l'affiche séparément. En sauvegardant un fichier local, Yellow crée ou met à jour un fichier voisin *.anciens-contacts.json avec son nouveau poste, sa nouvelle entreprise et le lien du profil. Si la recherche LinkedIn ne confirme pas le successeur, une recherche Web ciblée peut signaler un candidat à vérifier manuellement. La ville déclarée dans l'expérience LinkedIn peut signaler un écart avec le fichier ; elle ne constitue pas une adresse de livraison.

Le tableau distingue Poste vérifié, Remplacement, Contact parti, Successeur possible, Profil retrouvé, Coordonnées trouvées, Aucune information et Non confirmé. Une expérience sans dates est considérée comme actuelle, et la révision le précise. Les cas ambigus restent inchangés. Les équivalences de fonctions et les abréviations d'entreprise sont deux tables explicites et testées (ROLES et COMPANY_ALIASES dans src/sources/resolution.rs) ; aucun rapprochement approximatif généralisé ne sélectionne un homonyme. Un remplacement regroupe nom, prénom et anciennes coordonnées personnelles : accepter ou refuser une de ces propositions traite le groupe entier. L'ancien e-mail est proposé à l'effacement, jamais attribué au nouveau contact. Une modification manuelle annule le groupe de propositions en attente. Les formules et décisions déjà saisies empêchent un remplacement partiel. Aucun nouveau contact n'est enregistré avant validation.

LinkdAPI suit son contrat documenté : solde vérifié au début de l'analyse (moins d'un crédit refuse le lancement), cadence à 90 % de la limite du palier, palier relu puis une seule nouvelle tentative après un 429. Avec le cache partagé, chaque recherche et chaque profil ne sont achetés qu'une fois par analyse. Une expérience incomplète est ignorée sans rejeter le profil. Les tests HTTP LinkdAPI sont simulés : aucune clé payante réelle n'a été utilisée. Fonctionnement, limites et résultats de vérification.

Votre modèle Excel

La première ligne contient les en-têtes. Le modèle est reconnu par les colonnes D (PRENOM MINUSCULE), F (NOM MINUSCULE) et Q (EMAIL). Les données client sont en A:Q : civilité, noms, fonction, société, adresse et e-mail. Colonne1 correspond à la fonction professionnelle.

Les colonnes R et suivantes sont du suivi : elles ne sont ni proposées à l'enrichissement, ni affichées, ni modifiées. Leur nombre peut augmenter ; même un nouvel en-tête « EMAIL » à droite ne change pas cette limite. Les cellules vides simplement mises en forme ne créent pas de clients fictifs. Les feuilles, formules, styles, polices et métadonnées d'origine restent dans le fichier exporté.

Une formule simple d'une colonne client (par exemple un XLOOKUP vers un autre classeur) peut être remplacée par la valeur acceptée : la formule disparaît de cette cellule et la chaîne de calcul d'Excel (calcChain.xml) est retirée, Excel la reconstruit. Les plages de formules partagées/matricielles (même ancrées hors des colonnes client), cellules fusionnées et feuilles protégées restent en lecture seule. Les résultats de formules sont ceux enregistrés dans le fichier ; Yellow ne les recalcule pas. Excel pourra les recalculer à l'ouverture, selon les paramètres du classeur et la disponibilité des références externes. Aucun suivi ou adresse postale n'est déduit arbitrairement depuis LinkedIn.

Pour les autres fichiers, seuls les en-têtes client reconnus sont importés. Limites : .xlsx uniquement, 100 Mio compressés, 512 Mio décompressés (mesurés pendant la lecture, pas d'après les tailles déclarées dans l'archive), 500 000 cellules client par feuille. Les classeurs chiffrés, signés, .xls et .xlsm ne sont pas pris en charge.

Compiler et distribuer

Rust 1.88 minimum ; Cargo.lock fixe les versions. Sous Linux, installer les dépendances natives de compilation :

# Fedora
sudo dnf install gtk3-devel webkit2gtk4.1-devel
# Ubuntu / Debian : alternative
sudo apt install build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev

cargo build --release --locked
./target/release/yellow
# Ou ouvrir directement un classeur :
./target/release/yellow /chemin/clients.xlsx

Pour compiler sous Docker (Debian 12) et exporter le seul exécutable release Linux x64 dans dist/yellow, compatible glibc 2.35 et plus (Debian 12, Ubuntu 22.04 et suivants) :

docker buildx build --platform linux/amd64 --output type=local,dest=dist .
# Vérifications complètes (fmt, clippy, tests) sans dépendances locales :
docker buildx build --target check .

Un seul exécutable applicatif, sans service, serveur local, script ni second exécutable d'authentification à distribuer. Wry est utilisé uniquement pour la connexion LinkedIn ; les moteurs Web passent par HTTP. Chrome/Chromium n'est pas requis. Les polices et l'interface sont intégrées. Un binaire doit être compilé pour chaque système et architecture ; il n'est pas statique universel. Linux utilise les bibliothèques GTK 3 / WebKitGTK 4.1 du système, un bureau actif, un portail de sélection de fichiers et un coffre-fort Secret Service déverrouillé (GNOME Keyring ou KWallet). Windows utilise WebView2 et Credential Manager ; macOS utilise WebKit et Keychain. Cette livraison a été compilée et vérifiée sur Linux x86_64 ; macOS et Windows n'ont pas été exécutés ici.

Pour compiler sans Wry, utiliser cargo build --release --locked --no-default-features (rendu logiciel). Dans cette variante, la source Web reste utilisable avec DuckDuckGo ou une clé enregistrée/fournie pour Brave Search, Firecrawl, Exa, Tavily ou Serper.

Le rendu GPU est activé par défaut avec repli logiciel Iced. Pour un environnement sans GPU, compiler avec --no-default-features --features wry-auth.

# Repli d'authentification Chromium optionnel ; Chrome/Chromium installé requis.
cargo run --locked --features chromium-auth
# Variante Chromium seule, sans dépendances GTK/WebKit de compilation :
cargo run --locked --no-default-features --features chromium-auth

La source Web n'ouvre aucun moteur dans une fenêtre de navigateur. LinkedIn utilise le client HTTP après connexion ; Wry sert uniquement à établir cette connexion.

Authentification et concurrence

Iced possède le thread principal du processus GUI. Son exécuteur Tokio multithread reçoit les résultats des workers via une Subscription alimentée par un tokio::sync::mpsc borné. Lecture et écriture Excel passent par spawn_blocking.

Wry et Tao tournent dans un processus enfant du même binaire, lancé avec --auth-helper pour LinkedIn avant toute initialisation Iced. Leur boucle d'événements occupe le véritable thread principal de ce processus : cela respecte AppKit sur macOS et la propriété des objets GTK sur Linux, sans bloquer la fenêtre Iced ni créer de fenêtre native sur un worker Tokio.

La fenêtre privée ouvre la connexion LinkedIn. L'API native cookies_for_url récupère le cookie HttpOnly li_at et sa date d'expiration ; aucun JavaScript document.cookie n'est utilisé. Le résultat revient au parent par un pipe hérité, borné et soumis à un délai maximal. L'annulation termine le helper. La lecture des profils utilise ensuite le client HTTP ; aucun secret ne passe dans les arguments de commande.

La session est conservée exclusivement dans le coffre-fort natif du système. Aucun cookie ou mot de passe n'est stocké dans le projet, le classeur ou un fichier en clair. Si le coffre-fort échoue, la connexion le signale. La date d'expiration fournie par LinkedIn est conservée, jamais artificiellement prolongée ; LinkedIn peut révoquer la session plus tôt. La déconnexion supprime la copie locale, elle ne prétend pas révoquer toutes les sessions du compte LinkedIn.

La connexion passe par Wry, avec un repli Chromium facultatif qui ne se déclenche pas après une annulation volontaire. Jusqu'à quatre comptes LinkedIn peuvent être connectés séparément dans les paramètres ; leurs sessions restent dans le coffre-fort natif. L'analyse passe d'abord par LinkedIn, qui décide si la ligne doit changer, puis lance les recherches Web. Huit lignes par compte avancent en même temps : un compte n'est occupé que le temps de télécharger une page, que Yellow analyse ensuite de son côté pendant que le compte sert déjà une autre ligne. Chaque ligne garde le même compte du début à la fin, si bien qu'aucune ligne n'est lue par deux comptes. Côté Web, les sites candidats et leurs pages légales ou de contact sont lus en même temps, et l'adresse et l'e-mail sont cherchés en parallèle ; seules les requêtes aux moteurs de recherche restent espacées d'environ une seconde pour ne pas être bloquées. Quand un compte se libère, la ligne la plus haute du fichier qui l'attend passe en premier, si bien que les premières lignes finissent vite. Chaque ligne s'affiche, et la progression avance, dès qu'elle est terminée ; une nouvelle ligne démarre aussitôt. À la reprise après une pause, seules les lignes pas encore traitées sont relancées. Chaque compte conserve une seule requête simultanée et un délai aléatoire de 3 à 8 secondes, avec cookie jar, limites de taille/durée et redirections restreintes à LinkedIn en HTTPS. L'analyse s'arrête sur authentification expirée, challenge ou limitation de débit. Cette cadence ne garantit pas l'absence de restrictions. Vérifier une personne demande deux pages : la recherche, puis sa page d'expériences, dont les données nomment le titulaire de l'adresse du profil et dont la section liste les postes datés. La page de profil n'est pas lue : ses expériences n'y arrivent qu'après coup. Chaque page n'est décodée qu'une fois, sur le pool de calcul Tokio, puis libérée. Un cache partagé par tous les comptes, borné à 128 recherches et 128 profils par analyse, évite les lectures identiques, y compris celles d'un profil illisible ; l'entrée la plus ancienne sort la première. Les pages HTML ne sont pas conservées.

Export conservateur

La lecture XML sélectionne les colonnes client avant de construire le modèle Rust. Elle évite de décoder avec umya l'intégralité des données de suivi et du formatage.

À l'export, les seules valeurs modifiées sont reportées dans le XML original de chaque feuille, en conservant les attributs et métadonnées des cellules ; une cellule qui contient une formule dans l'original est refusée. Les autres cellules et parties ZIP sont recopiées intactes, y compris les éléments OOXML inconnus d'umya. Une valeur commençant par = reste du texte. La sauvegarde locale prépare et synchronise un fichier temporaire dans le répertoire de destination avant sa publication. Le bouton Enregistrer sous… préserve l'original ; Enregistrer remplace explicitement le fichier ouvert après contrôle de version.

Vérifications

cargo fmt --all -- --check
cargo clippy --locked --all-features --all-targets -- -D warnings
cargo test --locked --all-features
# Vérification supplémentaire sur un fichier local, sans l'altérer :
YELLOW_TEST_WORKBOOK='/chemin/clients.xlsx' cargo test --locked preserves_supplied_workbook -- --ignored

Les tests couvrent les contrats HTTP LinkdAPI et Graph simulés, l'emprunt d'un seul compte LinkedIn par ligne, le cache qui évince l'entrée la plus ancienne, l'annulation de la dernière action et le refus mémorisé d'une ligne, le budget réel de décompression, le refus de Firecrawl pour les pages hors politique, la séparation des authentifications, la résolution automatique des titulaires et la validation groupée des remplacements, le parcours accueil/analyse/tableau, la sauvegarde directe et sa récupération, les conflits locaux/distants, le protocole Graph simulé et le périmètre A:Q malgré l'ajout de colonnes, les cellules vides mises en forme, les formules, le diff, les compteurs de l'interface, l'annulation, l'authentification requise, les URL, le parseur de profils, l'expiration des sessions, le refus d'écrasement et la conservation des parties Excel non modifiées. La session LinkedIn réelle a été vérifiée le 21 septembre 2026 : recherche authentifiée et extraction d'expériences datées. Les essais ne garantissent pas que tout titulaire soit trouvable ni que les déclarations du site soient à jour. --linkedin-url-check https://fr.linkedin.com/in/identifiant vérifie un profil avec la session enregistrée ; --linkedin-check /chemin/clients.xlsx cherche les URL manquantes et vérifie les postes. Aucun de ces diagnostics ne sauvegarde de classeur.

Mesures de performances et protocole Valgrind. Les captures de démonstration dans docs/ utilisent uniquement des données fictives.