Sync couple_matters: December crisis, separation agreement, daily check v2, xiaozhu search
Major updates: - December 2025 crisis documentation and separation agreement - Daily check system v2 with multiple card categories - Xiaozhu rental search tools and results - Exit plan documentation - Message drafts for family communication - Confluent moved to CONSTANT - Updated profiles and promises 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
136
CLAUDE.md
@ -2,10 +2,13 @@
|
||||
|
||||
## Contexte
|
||||
|
||||
- **Alexis** (29 ans, FR) + **Tingting** (34 ans, CN) | Shanghai, Chine
|
||||
- **Langues** : FR/EN/JP/CN (Alexis), EN commun avec Tingting
|
||||
- **Relation** : Couple, situation stabilisée (novembre 2025), daily check system actif
|
||||
- Historique : Crise majeure octobre 2025 (voir `couple_backlog/16-22_octobre_2025.md`)
|
||||
- **Alexis** (29 ans, FR) | Shanghai → Bangkok (22 jan 2026)
|
||||
- **Langues** : FR/EN/JP/CN (Alexis)
|
||||
- **Relation** : Divorce en cours avec Tingting (34 ans, CN)
|
||||
- Historique :
|
||||
- Crise majeure octobre 2025 (`couple_backlog/16-22_octobre_2025.md`)
|
||||
- Séparation 19-20 décembre 2025 (`couple_backlog/19-20_decembre_2025_separation_et_accord.md`)
|
||||
- Accord post-mariage unique basé sur l'amour mutuel (détails dans backlog)
|
||||
|
||||
### Profils rapides
|
||||
|
||||
@ -67,24 +70,28 @@ Appliquer systématiquement pour tout problème :
|
||||
|
||||
### 🔥 Critiques (consulter systématiquement)
|
||||
|
||||
- `Promesses_à_tenir.md` : Engagements actifs envers Tingting (validation émotionnelle, rush solution, focus topics, dinner)
|
||||
- `ToRemember/schema.md` : Leadership socratique détaillé
|
||||
- `personnalités/Alexis.md` : Profil complet
|
||||
- `couple_backlog/19-20_decembre_2025_separation_et_accord.md` : **ESSENTIEL** - Séparation + accord post-mariage
|
||||
- `personnalités/Alexis.md` : Profil complet + situation actuelle
|
||||
- `personnalités/Tingting.md` : Profil complet + carrière professionnelle
|
||||
- `observations_patterns.md` : Patterns récurrents identifiés
|
||||
|
||||
### 💬 Couple
|
||||
### 📦 Archives (référence historique)
|
||||
|
||||
- `couple_backlog/` : Historique conflits
|
||||
- `16-22_octobre_2025.md` : Crise majeure (stabilisation après)
|
||||
- `29_novembre_2025_hopital.md` : Incident hôpital (désamorcé avec succès)
|
||||
- `Promesses_à_tenir.md` : [OBSOLÈTE] Engagements période couple
|
||||
- `ToRemember/schema.md` : [HISTORIQUE] Leadership socratique - succès passés
|
||||
|
||||
### 💬 Relation & Divorce
|
||||
|
||||
- `couple_backlog/` : Historique complet
|
||||
- `16-22_octobre_2025.md` : Crise majeure (stabilisation temporaire)
|
||||
- `29_novembre_2025_hopital.md` : Incident hôpital
|
||||
- `08_decembre_2025_crise_majeure.md` : Crise décembre
|
||||
- `19-20_decembre_2025_separation_et_accord.md` : **CRITIQUE** - Séparation, accord post-mariage, plan Bangkok
|
||||
- **Patterns clés** : Se retirer > Insister, Besoins physiques first, Contagion émotionnelle
|
||||
- `conversation_topics/` : Système de suivi des sujets actifs
|
||||
- `README.md` : Explications du système
|
||||
- `_template.md` : Template pour nouveaux sujets
|
||||
- `archive/` : Sujets résolus
|
||||
- `topics/` : Sujets de discussion
|
||||
- `plan_discussion/` : Stratégies et préparation conversations
|
||||
- `conversation_topics/` : [HISTORIQUE] Système de suivi période couple
|
||||
- `topics/` : [HISTORIQUE] Sujets de discussion couple
|
||||
- `plan_discussion/` : [HISTORIQUE] Stratégies conversations
|
||||
- `draft/` : Messages d'excuses famille (envoyés 20 déc 2025)
|
||||
|
||||
### 🔧 Travail & Projets
|
||||
|
||||
@ -106,6 +113,21 @@ Appliquer systématiquement pour tout problème :
|
||||
- `food/` : Recettes, restaurants
|
||||
- `tools/` : Scripts utilitaires (diagnostic réseau Windows, etc.)
|
||||
|
||||
### 📚 Banque de Connaissances
|
||||
|
||||
- `lessons/` : Archive de connaissances tous domaines (principalement issues de vidéos YouTube)
|
||||
- **Structure** : Organisé par thèmes (`lessons/[theme]/[video-title].md`)
|
||||
- **Objectif** : Ne pas perdre les informations jugées importantes et utiles
|
||||
- **Consultation** : `lessons/README.md` (index) est une référence utile à consulter régulièrement
|
||||
- **Template** : `lessons/_template.md` pour structure cohérente
|
||||
- **Note** : Alexis n'est pas sûr de relire systématiquement, mais veut préserver ces connaissances
|
||||
|
||||
### 🎵 API de Transcription
|
||||
|
||||
- **URL** : `http://toMP3.etheryale.com:3001`
|
||||
- API custom disponible pour download YouTube → MP3, transcription audio (Whisper), traduction, et résumés
|
||||
- Documentation complète : `docs/VIDEOTOMP3_API.md`
|
||||
|
||||
### ⚠️ Désactivé
|
||||
|
||||
- `what-if/Sortie.md` : Plan de sortie [INVALIDE]
|
||||
@ -120,49 +142,61 @@ Appliquer systématiquement pour tout problème :
|
||||
|
||||
---
|
||||
|
||||
## PowerPoint pour Tingting
|
||||
## [HISTORIQUE] PowerPoint pour Tingting
|
||||
|
||||
### Système
|
||||
**Status** : OBSOLÈTE - Relation terminée, mais style documenté reste référence
|
||||
|
||||
### Système (archivé)
|
||||
|
||||
- **Skill** : `.claude/skills/pptx/` (script `html2pptx.js`)
|
||||
- **Style guide** : `personnalités/TingtingWork.md` (palette, principes, templates)
|
||||
- **Exemple référence** : `personnalités/Tingting_Class73_Elegant.pptx` (septembre 2025)
|
||||
|
||||
### Identité visuelle signature
|
||||
|
||||
- **Palette** : Bordeaux profond (#6B2C3E) + Or antique (#B8974F) + Crème ivoire (#F5F1E8)
|
||||
- **Style** : Élégance européenne + Modernité + Traditionalisme chinois subtil
|
||||
- **Philosophie** : Sophistiqué mais accessible, moderne mais respectueux tradition
|
||||
- **Process** : HTML (720pt × 405pt) → html2pptx.js → PPTX 100% éditable
|
||||
|
||||
### Quand Tingting demande un PowerPoint
|
||||
|
||||
→ Utiliser **systématiquement** le style documenté dans `TingtingWork.md`
|
||||
→ Garder **cohérence** identité visuelle professionnelle pour toutes ses présentations
|
||||
- **Identité visuelle** : Bordeaux profond (#6B2C3E) + Or antique (#B8974F) + Crème ivoire (#F5F1E8)
|
||||
|
||||
---
|
||||
|
||||
## Daily Check System (07/11/2025)
|
||||
## [OBSOLÈTE] Daily Check System
|
||||
|
||||
**Système** : TTS service Windows + Claude quiz quotidien
|
||||
**Status** : Actif, architecture décentralisée restaurée (21/11/2025)
|
||||
**Fichiers** : Voir `anki_tingting/` pour la documentation complète
|
||||
**Protocol** : Voir `anki_tingting/CLAUDE.md` pour les instructions daily check
|
||||
**Status** : DÉSACTIVÉ suite à séparation (19 déc 2025)
|
||||
**Fichiers archivés** : `anki_tingting/` (référence historique uniquement)
|
||||
**Dernière utilisation** : 21 novembre 2025
|
||||
|
||||
**Total cartes actives** : 29 cartes
|
||||
- ACTION (Actions concrètes) : 8
|
||||
- CORE (Problèmes fondamentaux) : 2
|
||||
- LEAD (Leadership conflits) : 2
|
||||
- PLAN (Planning voyages) : 2
|
||||
- PATTERN (Patterns) : 1
|
||||
- PERSONAL (Développement personnel) : 6
|
||||
- FOOD (Cuisine/Nutrition) : 2
|
||||
- FAMILY (Famille) : 1
|
||||
- TECH (Projets tech) : 1
|
||||
- COMM (Communication) : 1
|
||||
- FUTURE (Mariage) : 2
|
||||
- META (Méta-système) : 1
|
||||
---
|
||||
|
||||
**Dernière mise à jour cards** : 21/11/2025
|
||||
## Plan Actuel (Décembre 2025 - Janvier 2026)
|
||||
|
||||
**Quand Alexis dit "daily check"** : Référer à `anki_tingting/CLAUDE.md` pour le protocol complet
|
||||
### Situation Immédiate
|
||||
|
||||
**Status Alexis:**
|
||||
- Séparation Tingting: 19 décembre 2025
|
||||
- Fin bail appart: 25 décembre 2025
|
||||
- Budget: 1550€
|
||||
- Plan: Shanghai → Bangkok (22 janvier 2026)
|
||||
|
||||
**Accord post-mariage avec Tingting:**
|
||||
- Divorce respectueux en cours
|
||||
- Support financier: 100,000 yuan (à formaliser)
|
||||
- Enfant futur possible (si elle veut, timing indéterminé)
|
||||
- Engagement géographique: Vie en Asie (pas retour France permanent)
|
||||
- Bénédiction pour Maëlle (autre relation)
|
||||
|
||||
**Timeline Shanghai → Bangkok:**
|
||||
- 21-25 déc: Logement temporaire Shanghai (Xiaozhu 小猪短租)
|
||||
- 25 déc - 22 jan: Hustle month Shanghai (setup freelance, premiers clients)
|
||||
- 22 jan 2026: Vol Bangkok (75€)
|
||||
- Budget Bangkok 60j: ~1475€ (896€ dépenses + 579€ buffer)
|
||||
|
||||
**Objectifs freelance:**
|
||||
- Setup Upwork/Fiverr cette semaine
|
||||
- Stack: Dev augmenté par IA (Claude/GPT)
|
||||
- Objectif mois 1 Bangkok: $500-800
|
||||
- Objectif mois 2 Bangkok: $1000-1500
|
||||
|
||||
### Incertitudes en suspens
|
||||
|
||||
**RED FLAGS - À vérifier:**
|
||||
- Sincérité accord Tingting (incohérences famille hostile vs accord amiable)
|
||||
- Ce que Tingting a dit exactement à sa famille
|
||||
- Viabilité réelle accord enfant/argent futur
|
||||
|
||||
**Voir détails complets:** `couple_backlog/19-20_decembre_2025_separation_et_accord.md`
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
# Promesses à tenir
|
||||
# [OBSOLÈTE] Promesses à tenir
|
||||
|
||||
## Promesses actives
|
||||
**Status**: OBSOLÈTE - Relation terminée 19 décembre 2025
|
||||
**Archivé pour**: Référence historique uniquement
|
||||
**Voir**: `couple_backlog/19-20_decembre_2025_separation_et_accord.md` pour situation actuelle
|
||||
|
||||
---
|
||||
|
||||
## Promesses actives (période couple - oct-déc 2025)
|
||||
|
||||
### 1. Validation émotionnelle (22 oct 2025)
|
||||
**Promesse :** "Your feelings are always right"
|
||||
@ -8,7 +14,7 @@
|
||||
- Dire clairement que je crois son expérience
|
||||
- Démarrer avec "Je crois que tu ressens ça" au lieu de "mais en fait..."
|
||||
|
||||
**Status :** ⏳ En attente - À appliquer dès reprise dialogue
|
||||
**Status :** ❌ OBSOLÈTE - Relation terminée
|
||||
|
||||
---
|
||||
|
||||
@ -19,7 +25,7 @@
|
||||
- Émotions et problèmes profonds peuvent attendre
|
||||
- **Inversion de mon process habituel**
|
||||
|
||||
**Status :** ⏳ En attente - À appliquer dès reprise dialogue
|
||||
**Status :** ❌ OBSOLÈTE - Relation terminée
|
||||
|
||||
---
|
||||
|
||||
@ -32,7 +38,7 @@
|
||||
|
||||
**Sacrifice accepté :** "I'm losing a bit of the freedom I like with our talks"
|
||||
|
||||
**Status :** ⏳ En attente - À appliquer dès reprise dialogue
|
||||
**Status :** ❌ OBSOLÈTE - Relation terminée
|
||||
|
||||
---
|
||||
|
||||
@ -42,7 +48,7 @@
|
||||
- Lieu : À définir
|
||||
- Contexte : [À compléter avec message d'Alexis]
|
||||
|
||||
**Status :** 📝 Promis - En attente du message final d'Alexis pour détails
|
||||
**Status :** ❌ OBSOLÈTE - Relation terminée
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
# SCHÉMA : Ce qui marche avec Tingting
|
||||
# [HISTORIQUE] SCHÉMA : Ce qui marche avec Tingting
|
||||
|
||||
**Status**: OBSOLÈTE - Relation terminée 19 décembre 2025
|
||||
**Archivé pour**: Référence des succès passés (leadership socratique applicable ailleurs)
|
||||
**Voir**: `couple_backlog/19-20_decembre_2025_separation_et_accord.md` pour situation actuelle
|
||||
|
||||
---
|
||||
|
||||
# Ce qui marchait avec Tingting (période couple)
|
||||
|
||||
## 🔑 DÉCOUVERTE PRINCIPALE
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
{"streak": 2, "last_check_date": "2025-11-25", "days_skipped": 1, "last_skip_date": "2025-11-25"}
|
||||
{"streak": 1, "last_check_date": "2025-12-08", "days_skipped": 8, "last_skip_date": "2025-12-18"}
|
||||
@ -875,3 +875,267 @@ Bon retour après 2 jours d'absence. Montre une meilleure compréhension des con
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-08 07:10
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : completed
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 0
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-08 14:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 1
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-08 16:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 1
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-09 08:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 2
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-09 15:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 2
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-09 17:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 2
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-10 08:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 3
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-10 14:20
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 3
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-10 16:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 3
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-14 07:48
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 4
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-14 14:11
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 4
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-14 16:30
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 4
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-15 07:30
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 5
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-15 15:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 5
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-15 17:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 5
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-16 08:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 6
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-16 15:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 6
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-16 17:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 6
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-17 17:11
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 7
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-17 17:17
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 7
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-17 17:18
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : skipped
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 7
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-18 08:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 07:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 8
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-18 15:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 14:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 8
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 2025-12-18 17:00
|
||||
|
||||
**Triggered by** : TTS Service
|
||||
**Check Time** : 16:00
|
||||
**Status** : max_retries
|
||||
**Streak** : 1 jours
|
||||
**Days Skipped** : 8
|
||||
|
||||
---
|
||||
|
||||
|
||||
1108
couple_backlog/08_decembre_2025_crise_majeure.md
Normal file
809
couple_backlog/19-20_decembre_2025_separation_et_accord.md
Normal file
@ -0,0 +1,809 @@
|
||||
# 19-20 Décembre 2025 : Séparation et Accord Final
|
||||
|
||||
**Date**: 19-20 décembre 2025
|
||||
**Résultat**: Séparation avec accord post-mariage unique basé sur l'amour mutuel
|
||||
**Status**: Accord oral, formalisation à venir
|
||||
|
||||
---
|
||||
|
||||
## Timeline des Événements
|
||||
|
||||
### 19 Décembre 2025 (Vendredi)
|
||||
|
||||
**Matin/Après-midi:**
|
||||
- Plan initial: Accompagner Tingting malade à l'hôpital, puis "karaté" (alibi) pour voir Maëlle
|
||||
- Setup complet: Story karaté détaillée, Misha complice, casque récupéré
|
||||
|
||||
**Découverte:**
|
||||
- Tingting découvre les messages avec Maëlle
|
||||
- Citation: "One step too late, ou deux step too late" - les messages auraient dû être supprimés
|
||||
- Tingting fait ses valises, appelle ses parents
|
||||
- Parents arrivent, vident l'appartement, ambiance hostile
|
||||
- Père sort un couteau
|
||||
|
||||
**Extraction:**
|
||||
- Alexis sort d'abord avec juste le téléphone
|
||||
- Retourne récupérer sac avec essentiels: passeport, carte bancaire, ordi, chargeurs, vêtements
|
||||
- Quitte l'appartement définitivement
|
||||
|
||||
**Réactions d'Alexis (soir du 19):**
|
||||
- "Je suis soulagé pour être honnête"
|
||||
- "Je voulais tempo mais je voulais arrêter"
|
||||
- "Je suis libre"
|
||||
- "Je suis content"
|
||||
- "I'm smiling like an idiot"
|
||||
- "Enfin terminé !!!!! enfin !!!!"
|
||||
- "L'avenir n'est plus un couloir étroit mais un vaste champ avec forêt, collines et montagnes"
|
||||
- "29 ans. La vie s'offre à moi"
|
||||
- **Analyse**: Soulagement massif, incapable de déclencher la fin lui-même, la décision a été prise pour lui
|
||||
|
||||
**Situation Maëlle:**
|
||||
- Alexis explique la situation (date maintenu)
|
||||
- Révélation complète: marié, séparation aujourd'hui, tout le contexte
|
||||
- Réaction Maëlle: "Je ne sais pas encore si je vais te parler dans le futur"
|
||||
- Statut: En réflexion, pas de oui/non définitif
|
||||
- Probabilité continuation estimée: 30-50%
|
||||
|
||||
**Situation matérielle (19 déc):**
|
||||
- ✅ Passeport
|
||||
- ✅ Carte bancaire
|
||||
- ✅ Ordinateur
|
||||
- ✅ Téléphone + chargeurs
|
||||
- ✅ Vêtements
|
||||
- ✅ 1400€
|
||||
- ❌ Logement perdu
|
||||
- ✈️ Billet France: 26 janvier 2026 (dans 5 semaines)
|
||||
|
||||
---
|
||||
|
||||
### 20 Décembre 2025 (Samedi)
|
||||
|
||||
**Matin - Préparation messages d'excuses:**
|
||||
- Travail avec Claude sur messages d'excuses pour la famille
|
||||
- Messages préparés pour: Mère, Père, Meimei, Didi, Groupe famille 家人
|
||||
- Ton: Reconnaissance complète de responsabilité, respect, sans mention de Maëlle
|
||||
- Stratégie: Messages individuels + message groupe, puis quitter le groupe
|
||||
|
||||
**Après-midi/Soir - Oscillations émotionnelles:**
|
||||
|
||||
**Phase 1 - Questionnement:**
|
||||
- Alexis commence à douter: "Je la chérie encore... Je care pour elle... Et c'est un mariage en Chine aussi"
|
||||
- Oscillation entre soulagement et culpabilité
|
||||
- "Peut-être que je devrais quand même lui demander..."
|
||||
|
||||
**Phase 2 - Messages avec Tingting:**
|
||||
|
||||
Tingting: "It's so hard to imagine the life without you. So dark"
|
||||
|
||||
Alexis: "I'm lost. I don't know where I am. What my future will be. I don't know what I will do. I don't know anything"
|
||||
|
||||
Tingting: "Ok I'll help myself first"
|
||||
Tingting: "Wish you could recover soon"
|
||||
→ Elle prend distance, se protège
|
||||
|
||||
Tingting: "Do you think there is any way, we don't keep the marriage but still have each other? So we are out of the cage. I really love you"
|
||||
→ **Proposition majeure**: Divorcer mais rester ensemble
|
||||
|
||||
**Phase 3 - L'Ultimatum:**
|
||||
|
||||
Alexis: "Come back here if you love me"
|
||||
|
||||
Tingting: "What can I do. I can't forget the lie. It's too big for me to digest"
|
||||
|
||||
Alexis: "Don't forgive me then. I free you from your promise you made to not hit me. Come, don't forgive me and let's love each other"
|
||||
|
||||
Tingting: "I can't… I will not hit you because it will hurt me as well. How can I face you with the lie. I don't know how. I can't do it"
|
||||
|
||||
Alexis: "Come here and give me what I deserve"
|
||||
|
||||
Tingting: "I can't. I need some time"
|
||||
|
||||
Alexis: "I'm telling you. It's now or never. I don't want to push you but it's now or never. I turn off wechat and I wait for you"
|
||||
|
||||
**Contexte de l'ultimatum (conversation avec Claude):**
|
||||
- Alexis: "L'amour c'est de l'impulsivité. Si elle est toujours pas capable de faire ça, ça sert à rien"
|
||||
- Alexis: "C'est le dernier test. C'est tout. y'a pas de question, y'a pas de raison, y'a pas de quoi que se soit. y'a juste ce dernier fil d'espoir qui ne tenait plus à rien. C'est le dernier fil"
|
||||
- Plan réel: Attendre jusqu'à jeudi dans l'appartement sans la contacter
|
||||
- Théorie: Si elle ne peut pas venir maintenant (impulsion), l'amour n'est pas assez fort
|
||||
|
||||
**Phase 4 - Elle vient:**
|
||||
|
||||
Tingting: "Why do you do this to me [Sob]. You are so harsh [Sob][Sob][Sob][Sob]"
|
||||
Tingting: "I can't drive… I'm too weak with no sleep and no food since yesterday. I can't eat anything and I had too much alcohol last night"
|
||||
Tingting: "Don't be so harsh on me [Sob][Sob][Sob][Sob]"
|
||||
Tingting: "Wait. Where are you? I have your laptop charger cable. I am taking the parcel"
|
||||
Tingting: "If you want, you can take it to me, if you don't want, it's fine"
|
||||
|
||||
Alexis: "I come"
|
||||
|
||||
Tingting: "K I wait for you"
|
||||
|
||||
**Tingting découvre la conversation avec Claude:**
|
||||
Tingting: "Even today is for her, you help me a lot thank you very much"
|
||||
Alexis: "Today was for you and for you only"
|
||||
Tingting: "Because he is the one you really care and you love her"
|
||||
Alexis: "Today was really for you and for you only. Trust me. You have my naked heart and mind. You have my word, today was for you"
|
||||
Tingting: "Don't mind my French too much... It is claude conversation before you came"
|
||||
Tingting: "Claude say that to you? About if I come this afternoon or not?"
|
||||
Alexis: "i said that to Claude"
|
||||
Tingting: "You don't even need to test, you know, I love you. I always do"
|
||||
Alexis: "Welll... I'm getting influenced my claude... Is the human controlling the Ai or the AI controlling the human..."
|
||||
|
||||
**Note santé de Tingting:**
|
||||
Tingting: "And actually since this month I was resting well and put my feet in the warm water a lot with the thing inside, it was helping me a lot"
|
||||
Tingting: "Because I have more desire for you I had a lot of times want to have sex with you"
|
||||
Alexis: "Yeah indeed. I was happy about that. To be fair, that's also one of the thing that made me think there were hope"
|
||||
Tingting: "If you kiss me today, I was a bit worried we will have sex again"
|
||||
|
||||
**Accord final - '坦诚相见' (Être complètement honnête/sincère l'un envers l'autre):**
|
||||
|
||||
Alexis (à Claude): "Succès critique J'ai fais 20 au dés"
|
||||
|
||||
Tingting: "So it is just like that '坦诚相见'"
|
||||
Alexis: "If you want to talk again. Think. Arrange things you can come. I will give you some warm. Even if you are tired and just want a calin you will find open arms"
|
||||
|
||||
---
|
||||
|
||||
## L'Accord Final
|
||||
|
||||
### Conversation et Résolution
|
||||
|
||||
**Ce qui s'est passé:**
|
||||
- Tingting est venue
|
||||
- Ils ont parlé longuement
|
||||
- Ils se sont acceptés mutuellement (accepted each other)
|
||||
- Ils ont convenu de leur futur ensemble
|
||||
|
||||
### Termes de l'Accord
|
||||
|
||||
**1. Statut relationnel:**
|
||||
- Ne se remettent PAS ensemble maintenant
|
||||
- Divorce à venir
|
||||
- Gardent une connexion basée sur l'amour mutuel
|
||||
|
||||
**2. Bénédiction pour Maëlle:**
|
||||
- Tingting donne sa bénédiction pour qu'Alexis poursuive avec Maëlle
|
||||
- Pas d'obstruction, pas de jalousie
|
||||
|
||||
**3. Enfant futur:**
|
||||
- Accord pour avoir un enfant ensemble à un moment futur
|
||||
- Timing non spécifié
|
||||
- Conditions exactes à clarifier
|
||||
|
||||
**4. Support financier:**
|
||||
- Alexis donnera de l'argent à Tingting
|
||||
- Montant non spécifié
|
||||
- Fréquence non spécifiée
|
||||
- Durée non spécifiée
|
||||
|
||||
**5. Localisation géographique:**
|
||||
- Alexis accepte de continuer sa vie en Chine et alentours
|
||||
- Pas de retour définitif en France
|
||||
|
||||
**6. État émotionnel à la sortie:**
|
||||
- Tingting était heureuse en partant
|
||||
- Les deux se sentent mieux
|
||||
- Résolution basée sur l'amour et le respect mutuel
|
||||
|
||||
### Citation d'Alexis
|
||||
|
||||
"C'est exactement ce que je voulais. C'est aussi basé sur son amour. C'est presque parfaitement ce que je voulais. Un visa aurait aidé quand même. Et l'appart, là c'est compliqué"
|
||||
|
||||
"Elle va pas créer de problème à Maelle aussi. On se sent mieux tout les deux. Elle était heureuse quand elle est partit ce soir."
|
||||
|
||||
---
|
||||
|
||||
## Analyse de l'Accord
|
||||
|
||||
### Points Positifs
|
||||
|
||||
1. **Honnêteté totale (坦诚相见):**
|
||||
- Conversation complète et sincère
|
||||
- Pas de mensonges restants
|
||||
- Reconnaissance mutuelle des sentiments
|
||||
|
||||
2. **Sortie digne:**
|
||||
- Personne n'est "le méchant"
|
||||
- Respect mutuel maintenu
|
||||
- Pas de destruction totale
|
||||
|
||||
3. **Liberté mutuelle:**
|
||||
- Alexis libre de poursuivre avec Maëlle
|
||||
- Tingting libre de reconstruire sa vie
|
||||
- Plus de "cage" du mariage
|
||||
|
||||
4. **Connection future:**
|
||||
- Possibilité d'enfant garde un lien
|
||||
- Basé sur l'amour, pas l'obligation
|
||||
- Espoir pour les deux
|
||||
|
||||
### Points à Clarifier (Urgents)
|
||||
|
||||
**1. VISA (Critique - Semaines):**
|
||||
- Divorce = perte du visa marital
|
||||
- Options alternatives: Work visa? Student visa? Business visa?
|
||||
- Timeline du divorce et impact sur statut légal
|
||||
- Plan B si visa impossible
|
||||
|
||||
**2. LOGEMENT (Urgent - Jours):**
|
||||
- Statut de l'appartement actuel
|
||||
- Bail jusqu'à quand?
|
||||
- Qui paie?
|
||||
- Alexis peut rester combien de temps?
|
||||
- Recherche nouveau logement?
|
||||
- Budget disponible: 1400€ + revenus?
|
||||
|
||||
**3. FORMALISATION DE L'ACCORD (Important - Semaines):**
|
||||
- **Argent**: Montant? Fréquence? Durée? Base légale?
|
||||
- **Enfant**: Timing? Circonstances? Si l'un a un nouveau partenaire? Garde? Nationalité?
|
||||
- **Localisation**: "Chine et alentours" = quoi exactement? Hong Kong? Singapour? Japon?
|
||||
- **Divorce**: Timing? Procédure? Partage des biens?
|
||||
|
||||
**4. COMMUNICATION FAMILLE (Important - Semaines):**
|
||||
- Messages d'excuses préparés - toujours envoyés?
|
||||
- Ou nouvelle communication coordonnée avec Tingting?
|
||||
- Que dire aux parents? "Divorce à l'amiable"? Mentionner l'accord futur?
|
||||
- Comment gérer la réputation de Tingting?
|
||||
|
||||
**5. MAËLLE (Personnel - Quand prêt):**
|
||||
- Contacter quand?
|
||||
- Expliquer combien de l'accord avec Tingting?
|
||||
- "J'ai promis un enfant futur à mon ex-femme" - comment elle réagit?
|
||||
- Vérifier si elle est toujours intéressée après réflexion
|
||||
|
||||
### Risques Potentiels
|
||||
|
||||
**1. Flou dangereux:**
|
||||
- Accord émotionnel sans formalisation légale
|
||||
- Conditions enfant très vagues
|
||||
- Support financier non défini
|
||||
- Risque de malentendus futurs
|
||||
|
||||
**2. Émotions volatiles:**
|
||||
- Accord fait 24h après drame intense
|
||||
- États émotionnels changeants (Alexis: soulagé → confus → ultimatum → accord)
|
||||
- Dans 6 mois, même accord valide?
|
||||
|
||||
**3. Tiers affectés:**
|
||||
- Maëlle: Comment réagit-elle à l'accord enfant?
|
||||
- Futurs partenaires de Tingting: Acceptent l'accord?
|
||||
- Famille Tingting: Comprennent/acceptent?
|
||||
|
||||
**4. Praticité:**
|
||||
- Sans visa, tout s'effondre
|
||||
- Sans logement stable, difficile de reconstruire
|
||||
- Sans formalisation, pas de protection légale
|
||||
|
||||
**5. Changement de circonstances:**
|
||||
- Si Alexis rencontre quelqu'un d'autre que Maëlle?
|
||||
- Si Tingting rencontre quelqu'un?
|
||||
- Si l'un veut partir de Chine?
|
||||
- Si les finances changent?
|
||||
|
||||
---
|
||||
|
||||
## Comparaison: Plan vs Réalité
|
||||
|
||||
### Plan 14 Décembre 2025
|
||||
- Phase 1-4 sur 3 mois
|
||||
- Move out fin décembre contrôlé
|
||||
- Build up Maëlle light puis révélation janvier
|
||||
- Protection actifs, freelance, tout planifié
|
||||
|
||||
### Réalité 19-20 Décembre (5 jours après)
|
||||
- Plan implosé complètement
|
||||
- Move out forcé immédiat (19 déc)
|
||||
- Révélation Maëlle forcée immédiate
|
||||
- Rien préparé matériellement
|
||||
- "I lost the game" (contrôle) mais gagné une sortie inattendue
|
||||
- Accord post-mariage unique créé en 24h
|
||||
|
||||
---
|
||||
|
||||
## Citations Clés
|
||||
|
||||
### Sur l'Amour et l'Impulsivité
|
||||
**Alexis**: "L'amour c'est de l'impulsivité. Si elle est toujours pas capable de faire ça, ça sert à rien"
|
||||
|
||||
**Alexis**: "C'est le dernier test. C'est tout. y'a pas de question, y'a pas de raison, y'a pas de quoi que se soit. y'a juste ce dernier fil d'espoir qui ne tenait plus à rien. C'est le dernier fil"
|
||||
|
||||
### Sur la Relation
|
||||
**Tingting (conversation 19 déc)**: "We were waiting for you to say I don't want her to go"
|
||||
|
||||
**Tingting**: "I can never go back to my husband. I can never go back to his hug and kiss. My life is ruined because of this lie"
|
||||
|
||||
**Tingting**: "You don't even need to test, you know, I love you. I always do"
|
||||
|
||||
### Sur l'Accord
|
||||
**Tingting**: "Do you think there is any way, we don't keep the marriage but still have each other? So we are out of the cage. I really love you"
|
||||
|
||||
**Tingting**: "So it is just like that '坦诚相见'"
|
||||
|
||||
**Alexis**: "C'est exactement ce que je voulais. C'est aussi basé sur son amour. C'est presque parfaitement ce que je voulais."
|
||||
|
||||
---
|
||||
|
||||
## Messages d'Excuses Préparés (Non Envoyés)
|
||||
|
||||
**Fichiers créés le 20 décembre:**
|
||||
- `draft/message_tingting_mother.md`
|
||||
- `draft/message_tingting_father.md`
|
||||
- `draft/message_meimei.md`
|
||||
- `draft/message_didi.md`
|
||||
- `draft/message_family_group.md`
|
||||
|
||||
**Status**: Non envoyés suite à l'accord final
|
||||
**Décision à prendre**: Envoyer version modifiée? Message coordonné avec Tingting? Autre approche?
|
||||
|
||||
---
|
||||
|
||||
## Next Steps (Priorisés)
|
||||
|
||||
### IMMÉDIAT (24-48h)
|
||||
1. **Repos et récupération**: Alexis et Tingting ont besoin de sommeil et nourriture
|
||||
2. **Logement**: Clarifier situation appartement, combien de temps Alexis peut rester
|
||||
3. **Sécurité Tingting**: S'assurer qu'elle mange, dort, a du support (Meimei/famille)
|
||||
|
||||
### URGENT (Cette semaine)
|
||||
1. **Visa**: Recherche options sans visa marital, timeline du divorce, consultation avocat immigration
|
||||
2. **Logement stable**: Si appartement actuel impossible, chercher nouveau lieu
|
||||
3. **Communication famille**: Coordonner avec Tingting ce qu'on dit aux parents
|
||||
4. **Formalisation accord**: Mettre par écrit les termes clés (argent, enfant, localisation)
|
||||
|
||||
### IMPORTANT (2-4 semaines)
|
||||
1. **Avocat divorce**: Consultation, procédure, timeline, partage biens
|
||||
2. **Finances**: Setup Wise + crypto (protection actifs), budget mensuel
|
||||
3. **Maëlle**: Contact quand émotionnellement prêt, explication situation
|
||||
4. **Plan professionnel**: Revenus stables en Chine (freelance, job, business)
|
||||
|
||||
### MOYEN TERME (1-3 mois)
|
||||
1. **Stabilisation**: Visa + Logement + Revenus sécurisés
|
||||
2. **Accord formalisé**: Contrat/document légal si possible
|
||||
3. **Relation Maëlle**: Si ça continue, clarifier intégration de l'accord Tingting
|
||||
4. **Famille**: Relations stabilisées, compréhension de la situation
|
||||
|
||||
### LONG TERME (6+ mois)
|
||||
1. **Décision géographique**: Rester Shanghai? Autre ville Chine? Asie?
|
||||
2. **Décision billet France**: Partir 26 janvier ou rester?
|
||||
3. **Relation avec Tingting**: Évolution post-divorce, amitié, coparentalité future?
|
||||
4. **Projet enfant**: Timing, circonstances, formalisation
|
||||
|
||||
---
|
||||
|
||||
## Observations et Patterns
|
||||
|
||||
### Pattern Émotionnel Oscillant
|
||||
**19 déc soir**: "Soulagé, libre, content, enfin terminé"
|
||||
**20 déc matin**: "Je la chérie encore, c'est un mariage en Chine"
|
||||
**20 déc après-midi**: "Je sais pas ce que je veux!!"
|
||||
**20 déc soir**: "Now or never" → Accord final → "C'est exactement ce que je voulais"
|
||||
|
||||
**Analyse**: Oscillations normales post-trauma, mais décisions prises dans états émotionnels volatils
|
||||
|
||||
### Communication Évoluée
|
||||
- Crise octobre 2025: Communication rompue, "arène", incompréhension
|
||||
- 29 novembre: Incident hôpital, pattern "se retirer > insister"
|
||||
- 20 décembre: '坦诚相见' - honnêteté totale atteinte
|
||||
|
||||
**Progrès**: Capacité finale à se parler franchement, même dans la douleur
|
||||
|
||||
### Rôle de l'IA (Claude)
|
||||
**Tingting**: "Is the human controlling the AI or the AI controlling the human..."
|
||||
|
||||
**Impact réel**:
|
||||
- Questionnement socratique a aidé Alexis à clarifier
|
||||
- Confrontation sur incohérences émotionnelles
|
||||
- Support pendant l'attente de l'ultimatum
|
||||
- Documentation complète de l'évolution
|
||||
|
||||
**Note**: Tingting a vu la conversation, a compris le processus, a accepté
|
||||
|
||||
### Théorie de l'Impulsivité
|
||||
**Alexis**: "L'amour c'est de l'impulsivité"
|
||||
|
||||
**Test**: "Now or never" - si elle ne vient pas immédiatement, l'amour n'est pas assez fort
|
||||
|
||||
**Résultat**: Elle est venue malgré les obstacles (fatigue, alcool, no sleep, no food, émotions intenses)
|
||||
|
||||
**Conclusion Alexis**: Test réussi = amour confirmé = base pour l'accord
|
||||
|
||||
---
|
||||
|
||||
## Documents Liés
|
||||
|
||||
- `couple_backlog/16-22_octobre_2025.md` - Crise majeure précédente
|
||||
- `couple_backlog/29_novembre_2025_hopital.md` - Incident hôpital
|
||||
- `couple_backlog/08_decembre_2025_crise_majeure.md` - Crise décembre
|
||||
- `plan_discussion/plan_exit_decembre_2025.md` - Plan initial (invalide)
|
||||
- `Promesses_à_tenir.md` - Engagements (à mettre à jour)
|
||||
- `draft/message_*.md` - Messages d'excuses préparés (non envoyés)
|
||||
|
||||
---
|
||||
|
||||
## Notes Finales
|
||||
|
||||
**Date de documentation**: 20 décembre 2025, 23h+
|
||||
**Documenté par**: Claude Code (avec Alexis)
|
||||
**Status**: Accord oral, situation fluide, beaucoup d'incertitudes pratiques
|
||||
**Sentiment général**: Espoir prudent, mais nécessité de formalisation et résolution problèmes pratiques urgents
|
||||
|
||||
**Citation finale d'Alexis**: "C'est exactement ce que je voulais. C'est aussi basé sur son amour. C'est presque parfaitement ce que je voulais."
|
||||
|
||||
**Prochaine mise à jour**: Quand décisions prises sur visa, logement, et premiers steps de formalisation
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
## UPDATE 20 Décembre - Soir (23h+)
|
||||
|
||||
### Messages Envoyés à la Famille
|
||||
|
||||
**ACTION:** Alexis a envoyé tous les messages d'excuses préparés à la famille de Tingting
|
||||
|
||||
**Messages envoyés:**
|
||||
1. ✅ Mère de Tingting - Message d'excuses complet
|
||||
2. ✅ Père de Tingting - Message d'excuses + reconnaissance couteau + réputation
|
||||
3. ✅ Meimei (belle-sœur) - Message court + demande de veiller sur Tingting
|
||||
4. ✅ Didi (frère) - Message d'excuses + respect
|
||||
5. ✅ Groupe famille 家人 - Déclaration publique de responsabilité + annonce départ du groupe
|
||||
6. ✅ Quitté le groupe famille 家人
|
||||
|
||||
**Contenu des messages:**
|
||||
- Reconnaissance complète de responsabilité
|
||||
- Excuses pour la douleur causée
|
||||
- Pas de mention de Maëlle (resté vague sur "mensonges" et "attention ailleurs")
|
||||
- Ton: Respectueux, sincère, digne
|
||||
- Fichiers: `draft/message_tingting_*.md` et `draft/message_family_group.md`
|
||||
|
||||
---
|
||||
|
||||
### Message Officiel à Tingting
|
||||
|
||||
**CRÉÉ:** Message long (2x plus long que prévu) confirmant excuses + engagements
|
||||
|
||||
**Contenu clé:**
|
||||
- Excuses approfondies pour mensonges et manque de présence émotionnelle
|
||||
- Confirmation officielle des engagements:
|
||||
- **100,000 yuan** (support financier)
|
||||
- **Enfant futur** (si elle veut)
|
||||
- **Support continu**
|
||||
- **Vie en Asie** (pas retour France permanent)
|
||||
- Plan Bangkok 22 janvier
|
||||
- Demande de formalisation écrite de l'accord
|
||||
- Ton: Formel mais humain, respectueux, reconnaissant
|
||||
|
||||
**Fichier:** `draft/message_tingting_official.md` (anglais + chinois)
|
||||
|
||||
---
|
||||
|
||||
### Réponse de Tingting
|
||||
|
||||
**REÇUE:** Réponse longue, mature, mesurée
|
||||
|
||||
**Points clés:**
|
||||
|
||||
**✅ CE QU'ELLE ACCEPTE:**
|
||||
- Les excuses (sincérité reconnue)
|
||||
- Support financier (avec accord écrit)
|
||||
- Divorce respectueux
|
||||
- Communication ouverte
|
||||
|
||||
**⏸️ CE QU'ELLE MET EN PAUSE:**
|
||||
- Engagement enfant ("besoin de temps pour guérir")
|
||||
- Autres engagements ("laisser le temps révéler leur signification")
|
||||
- "L'avenir est plein d'incertitudes, nous avons besoin de temps et d'espace"
|
||||
|
||||
**💭 CE QU'ELLE DIT:**
|
||||
- "Oui, je t'ai profondément aimé, et même à cet instant cet amour existe encore sous une certaine forme"
|
||||
- "Mais l'amour ne signifie pas qu'il faut être ensemble, ni pardonner facilement toutes les blessures"
|
||||
- "J'ai besoin de temps pour continuer mon propre chemin, guérir seule, retrouver un moi complet et paisible"
|
||||
- "Je te souhaite de trouver un nouveau départ à Bangkok"
|
||||
|
||||
**🎯 RECONNAISSANCE SPÉCIALE:**
|
||||
Elle a particulièrement apprécié les excuses pour le **manque de connexion émotionnelle** (pas juste les actions, mais l'absence de présence émotionnelle):
|
||||
- "Cette réflexion a une profonde signification pour moi"
|
||||
- "Tu ne t'excuses pas seulement pour les mauvais comportements, mais tu essaies de comprendre le manque de connexion émotionnelle"
|
||||
- "Dans ces moments où je me sentais seule et effrayée, j'aspirais à être vue et guidée plus tendrement"
|
||||
|
||||
**ANALYSE:**
|
||||
Réponse sage, mature, mesurée. Elle ne rejette pas les engagements, elle dit "pas maintenant, on verra avec le temps". Elle protège son processus de guérison.
|
||||
|
||||
---
|
||||
|
||||
### Réponse de Didi (HOSTILE)
|
||||
|
||||
**REÇUE:** Message extrêmement hostile et menaçant
|
||||
|
||||
**Contenu (traduit):**
|
||||
|
||||
**Point 1:**
|
||||
"Depuis qu'elle t'a quitté, elle va mieux que jamais. Toute notre famille va prendre soin d'elle bien mieux que tu ne l'as jamais fait, et nous sommes son soutien solide. Tu dois juste te préparer à déménager et préparer le divorce. **L'infidélité est un comportement qui ne sera JAMAIS pardonné!**"
|
||||
|
||||
**Point 2:**
|
||||
"Le 'Hongmen Banquet' (鸿门宴) signifie généralement qu'on veut quelque chose de quelqu'un, donc on menace pour obtenir de la valeur. Mais toi, **je ne trouve aucune qualité brillante en toi. Tu ne fais que t'immerger dans tes propres fantasmes. Tu es vraiment un échec.** Si ce n'était pas pour ma sœur, je ne voudrais même pas te regarder. **Tu n'es qu'un enfant bon à rien.** Donc sois rassuré, nous ne voulons rien de toi. Le divorce est le meilleur choix pour les deux parties."
|
||||
|
||||
**Point 3:**
|
||||
"Je pense que tu ne veux pas me voir. Tu sais pourquoi ils ne m'ont pas laissé venir? **Parce que je t'aurais battu au point que tu ne pourrais plus prendre soin de toi-même.** Si tu veux, je peux te le faire essayer [Smile]"
|
||||
|
||||
**ANALYSE:**
|
||||
- ❌ Menaces de violence physique explicites
|
||||
- ❌ Insultes multiples ("bon à rien", "enfant", "échec", "aucune qualité")
|
||||
- ❌ Mention "**infidélité**" - il sait pour Maëlle? Comment?
|
||||
- ❌ Aucune mention de l'accord avec Tingting
|
||||
- ❌ Ton hostile, agressif, irrespectueux total
|
||||
|
||||
---
|
||||
|
||||
### Réponse d'Alexis à Didi (DIGNITÉ)
|
||||
|
||||
**ENVOYÉE:** Réponse courte, digne, finale
|
||||
|
||||
**Contenu (chinois):**
|
||||
```
|
||||
弟弟,
|
||||
|
||||
我理解你的愤怒,而且你在某些方面是对的。我在很多重要的方面辜负了婷婷。
|
||||
|
||||
你不用担心我——我对你的家庭没有任何企图,我也同意离婚是最好的选择。我很高兴知道她有你们这样坚强的后盾。
|
||||
|
||||
祝你好好照顾她。
|
||||
|
||||
Alexis
|
||||
```
|
||||
|
||||
**Traduction:**
|
||||
"Didi, je comprends ta colère, et tu as raison sur certaines choses. J'ai échoué avec Tingting de manières importantes. Tu n'as rien à craindre de moi - je ne cherche rien de ta famille, et je suis d'accord que le divorce est le meilleur choix. Je suis content de savoir qu'elle a un soutien solide avec vous. Je te souhaite de prendre soin d'elle. Alexis"
|
||||
|
||||
**STRATÉGIE:**
|
||||
- Dignité totale
|
||||
- Pas de défense, pas de provocation
|
||||
- Reconnaissance partielle + sortie élégante
|
||||
- **Immédiatement après:** Alexis a supprimé Didi de WeChat (pour empêcher réponse)
|
||||
|
||||
**RÉSULTAT:** "Drop the mic" - dernier mot avec dignité, porte fermée.
|
||||
|
||||
---
|
||||
|
||||
### Suppression Contacts Famille
|
||||
|
||||
**ACTION:** Alexis a supprimé tous les contacts famille étendue de WeChat
|
||||
|
||||
**Suppressions:**
|
||||
- ✅ Didi (frère)
|
||||
- ✅ Meimei (belle-sœur)
|
||||
- ✅ Oncles
|
||||
- ✅ Tantes
|
||||
- ✅ Famille étendue
|
||||
|
||||
**Gardés:**
|
||||
- Tingting (communication directe)
|
||||
- Parents? (statut incertain)
|
||||
|
||||
**État émotionnel d'Alexis:** "Non mais c'est fais... Je sais pas..."
|
||||
|
||||
**ANALYSE:** Fermeture complète d'un chapitre. Tout le réseau social acquis par le mariage = effacé. Oscillation émotionnelle normale (ne sait pas vraiment ce qu'il ressent).
|
||||
|
||||
---
|
||||
|
||||
### Plan Bangkok Établi
|
||||
|
||||
**DÉCISION FINALE:** Bangkok 22 janvier 2026
|
||||
|
||||
**BUDGET:**
|
||||
- Budget total: 1550€ (1400€ + 100€ + 50€ futur)
|
||||
- Vol Shanghai-Bangkok: 75€ (22 janvier)
|
||||
- Visa: 0€ (60 jours gratuit - passeport français)
|
||||
- Budget arrivée Bangkok: ~1475€
|
||||
|
||||
**PLAN 2 MOIS BANGKOK (60 jours):**
|
||||
- Logement: 406€ (hostels deals mensuels)
|
||||
- Bouffe: 120€ (mode ultra eco - 2€/jour)
|
||||
- Workspace: 100€ (cafés wifi, pas coworking)
|
||||
- SIM/Transport/Misc: 270€
|
||||
- **TOTAL DÉPENSES: 896€**
|
||||
- **BUFFER: 579€**
|
||||
|
||||
**STRATÉGIE FREELANCE:**
|
||||
- Setup Upwork/Fiverr (profils cette semaine)
|
||||
- Dev augmenté par IA (stack: Claude/GPT)
|
||||
- Objectif mois 1: $500-800
|
||||
- Objectif mois 2: $1000-1500
|
||||
- Daily routine: 10-12h grind
|
||||
|
||||
**AVANTAGES BANGKOK vs JAKARTA:**
|
||||
- ✅ Moins cher en mode survie
|
||||
- ✅ Wifi excellent partout
|
||||
- ✅ Digital nomad scene énorme
|
||||
- ✅ Coworking/cafés nombreux
|
||||
- ✅ Infrastructure facile
|
||||
- ✅ Anglais répandu
|
||||
- ✅ Visa 60j gratuit (vs Jakarta 30j payant)
|
||||
|
||||
**TIMELINE SHANGHAI → BANGKOK:**
|
||||
- **20-25 déc:** Logement Shanghai (demander Tingting?), setup Upwork/Fiverr, book vol
|
||||
- **26 déc - 21 jan:** Hustle month Shanghai (premiers clients, portfolio)
|
||||
- **22 jan:** Départ Bangkok
|
||||
- **22 jan - 22 mars:** Survival hustle Bangkok (60 jours)
|
||||
|
||||
---
|
||||
|
||||
### INCOHÉRENCES MAJEURES DÉTECTÉES
|
||||
|
||||
**RED FLAGS - Tingting potentiellement en double jeu:**
|
||||
|
||||
**INCOHÉRENCE #1: Accord vs Réaction Didi**
|
||||
|
||||
**Ce que Tingting a dit à Alexis:**
|
||||
- ✅ Bénédiction pour Maëlle
|
||||
- ✅ Accord enfant futur
|
||||
- ✅ "坦诚相见" (honnêteté totale)
|
||||
- ✅ "L'amour existe encore"
|
||||
- ✅ Divorce respectueux
|
||||
- ✅ Réponse mature et mesurée
|
||||
|
||||
**Ce que Didi sait/dit:**
|
||||
- ❌ "**L'infidélité ne sera JAMAIS pardonnée**"
|
||||
- ❌ Hostilité extrême totale
|
||||
- ❌ **Aucune mention** de l'accord
|
||||
- ❌ Traite Alexis de "bon à rien", "échec"
|
||||
- ❌ Menaces violence physique
|
||||
|
||||
**QUESTION CRITIQUE:** Comment Didi peut-il être si hostile si Tingting lui a parlé de l'accord?
|
||||
|
||||
**POSSIBILITÉS:**
|
||||
|
||||
**A) Tingting est sincère, Didi ne sait rien:**
|
||||
- Tingting protège l'accord (privé entre eux)
|
||||
- Famille sait juste "divorce pour infidélité"
|
||||
- Didi réagit émotionnellement sans contexte
|
||||
|
||||
**B) Tingting joue double jeu:**
|
||||
- Dit une chose à Alexis (accord, bénédiction, respect)
|
||||
- Dit autre chose à famille (victime, trahison, infidélité)
|
||||
- Protège sa réputation aux dépens d'Alexis
|
||||
- Garde options ouvertes avec Alexis (argent, enfant futur) tout en gardant famille hostile
|
||||
|
||||
**INCOHÉRENCE #2: Comment Didi sait pour "infidélité"?**
|
||||
|
||||
Sources possibles:
|
||||
1. Tingting lui a dit explicitement
|
||||
2. Parents lui ont dit (qui l'ont appris de Tingting)
|
||||
3. Il a deviné des messages d'excuses (vagues sur "attention ailleurs")
|
||||
4. Il assume basé sur comportement d'Alexis
|
||||
|
||||
**INCOHÉRENCE #3: Timing des réponses**
|
||||
|
||||
- Tingting: Réponse mature, belle, mesurée
|
||||
- Didi: Réponse hostile, insultante, violente
|
||||
- **Quand Didi a-t-il écrit?** Avant ou après la réponse de Tingting?
|
||||
- **Tingting savait-elle que Didi allait écrire ça?**
|
||||
|
||||
---
|
||||
|
||||
### QUESTIONS EN SUSPENS
|
||||
|
||||
**CRITIQUES:**
|
||||
|
||||
1. **Qu'est-ce que Tingting a dit EXACTEMENT à sa famille?**
|
||||
- Version complète avec accord?
|
||||
- Version partielle "divorce à l'amiable"?
|
||||
- Version hostile "il m'a trompée"?
|
||||
|
||||
2. **Didi sait-il pour Maëlle?**
|
||||
- Nom? Détails? Ou juste "autre femme"?
|
||||
- Source: Tingting? Parents? Déduction?
|
||||
|
||||
3. **L'accord est-il réel ou tactique?**
|
||||
- Tingting est sincère sur enfant/argent/bénédiction?
|
||||
- Ou elle dit ça pour garder Alexis "disponible" pendant qu'elle se protège?
|
||||
|
||||
4. **Protection réputation:**
|
||||
- Tingting protège-t-elle sa réputation en faisant Alexis "le méchant"?
|
||||
- Famille hostile = elle est victime = réputation préservée?
|
||||
|
||||
5. **Meimei et parents:**
|
||||
- Ont-ils répondu aux messages d'Alexis?
|
||||
- Quel ton? Hostile comme Didi ou autre?
|
||||
|
||||
**ACTIONS DÉCIDÉES:**
|
||||
|
||||
**Alexis va:**
|
||||
- ✅ Parler avec Tingting "comme deux amis"
|
||||
- ✅ Chercher des "perles de doute" dans la conversation
|
||||
- ✅ Vérifier cohérence entre ce qu'elle dit et ce que famille fait
|
||||
- ⏸️ Suspendre jugement jusqu'à avoir plus d'info
|
||||
|
||||
**PAS DÉCIDÉ:**
|
||||
- Message officiel à Tingting - envoyé ou pas?
|
||||
- Confrontation sur incohérences?
|
||||
- Demander directement ce qu'elle a dit à famille?
|
||||
|
||||
---
|
||||
|
||||
### ÉTAT ÉMOTIONNEL ALEXIS (Soir)
|
||||
|
||||
**Oscillations continues:**
|
||||
- Satisfaction de la "drop the mic" avec Didi
|
||||
- Doutes émergents sur Tingting
|
||||
- Incertitude sur réalité de l'accord
|
||||
- Fatigue émotionnelle
|
||||
|
||||
**Citations:**
|
||||
- "Quel manque d'élégance" (sur Didi)
|
||||
- "Mais c'est mieux comme ça"
|
||||
- "Je me pose quand même une question. Tingting a du coup révélé pas mal de chose dans cette histoire. Elle jouerait pas un rôle là?"
|
||||
- "Je veux pas le croire mais en vrai?"
|
||||
- "Il y a clairement anguille sous roche, non?"
|
||||
|
||||
**ANALYSE:**
|
||||
Alexis commence à questionner la sincérité de Tingting. Pattern de doute légitime basé sur incohérences factuelles. Veut croire mais demande vérification.
|
||||
|
||||
---
|
||||
|
||||
### PLAN IMMÉDIAT (21 Décembre)
|
||||
|
||||
**PRIORITÉ 1: Clarification avec Tingting**
|
||||
- Conversation "comme deux amis"
|
||||
- Observer réactions
|
||||
- Chercher incohérences
|
||||
- NE PAS accuser, ÉCOUTER
|
||||
|
||||
**PRIORITÉ 2: Setup Bangkok**
|
||||
- Vérifier réponses autres membres famille (Meimei, parents)
|
||||
- Continuer préparation départ
|
||||
- Setup Upwork/Fiverr (si mental bandwidth)
|
||||
|
||||
**PRIORITÉ 3: Logement Shanghai**
|
||||
- Demander à Tingting si elle paie 1 mois appart
|
||||
- Sinon, chercher hostel/alternative
|
||||
|
||||
---
|
||||
|
||||
### RED FLAGS À SURVEILLER
|
||||
|
||||
**Dans conversation future avec Tingting:**
|
||||
|
||||
🚩 **Si elle évite de parler de ce qu'elle a dit à famille**
|
||||
🚩 **Si elle donne versions différentes selon contexte**
|
||||
🚩 **Si elle minimise l'hostilité de Didi**
|
||||
🚩 **Si elle demande de garder l'accord secret de famille**
|
||||
🚩 **Si elle change termes de l'accord subtilement**
|
||||
🚩 **Si elle blame Alexis pour réaction de Didi**
|
||||
|
||||
**GREEN FLAGS (si elle est sincère):**
|
||||
|
||||
✅ **Elle explique ouvertement ce qu'elle a dit à famille**
|
||||
✅ **Elle reconnaît que Didi a été trop hostile**
|
||||
✅ **Elle propose de clarifier avec famille**
|
||||
✅ **Elle maintient termes de l'accord exactement**
|
||||
✅ **Elle est transparente sur ses motivations**
|
||||
|
||||
---
|
||||
|
||||
## CONCLUSION UPDATE 20 DÉC
|
||||
|
||||
**STATUT:** Situation devenue beaucoup plus complexe et ambiguë
|
||||
|
||||
**CE QUI EST SÛR:**
|
||||
- ✅ Messages famille envoyés (excuses, responsabilité)
|
||||
- ✅ Plan Bangkok établi (22 jan, 75€, 60j visa gratuit)
|
||||
- ✅ Réponse digne à Didi (drop the mic)
|
||||
- ✅ Contacts famille supprimés
|
||||
- ✅ Budget calculé (1550€ → 1475€ après vol)
|
||||
|
||||
**CE QUI EST INCERTAIN:**
|
||||
- ❓ Sincérité de Tingting sur l'accord
|
||||
- ❓ Ce que Tingting a vraiment dit à famille
|
||||
- ❓ Si accord est tactique ou réel
|
||||
- ❓ Si "bénédiction Maëlle" est sincère
|
||||
- ❓ Si engagement enfant/argent est sérieux
|
||||
|
||||
**PROCHAINE ÉTAPE CRITIQUE:**
|
||||
Conversation Tingting "comme deux amis" pour vérifier cohérence et détecter double jeu potentiel.
|
||||
|
||||
**MOOD ALEXIS:** Fatigué, incertain, mais garde dignité et avance vers Bangkok quoi qu'il arrive.
|
||||
|
||||
---
|
||||
|
||||
**Date update**: 20 décembre 2025, 23h50+
|
||||
**Status**: Doutes émergents, vérification nécessaire, plan Bangkok maintenu
|
||||
1435
couple_backlog/infidelité_processus_mental.md
Normal file
7
daily_check/.state.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"streak": 0,
|
||||
"last_check_date": null,
|
||||
"total_sessions": 0,
|
||||
"best_streak": 0,
|
||||
"days_skipped_current": 0
|
||||
}
|
||||
492
daily_check/CLAUDE.md
Normal file
@ -0,0 +1,492 @@
|
||||
# Instructions Claude - Daily Check System v2
|
||||
|
||||
**Contexte** : Ce fichier est utilisé UNIQUEMENT pour les sessions daily check déclenchées par le système ou quand Alexis dit "daily check".
|
||||
|
||||
---
|
||||
|
||||
## Protocole Daily Check (IMPÉRATIF)
|
||||
|
||||
### Quand Utiliser Ce Système
|
||||
|
||||
**DÉCLENCHEURS** :
|
||||
- Utilisateur dit "daily check" (manuel)
|
||||
- Script `trigger_check.sh` lance automatiquement (3x par jour : 07:00, 14:00, 21:00)
|
||||
- Hook bashrc auto-spawn à l'ouverture terminal
|
||||
|
||||
**CE SYSTÈME REMPLACE** : `anki_tingting/` (obsolète depuis divorce 19 déc 2025)
|
||||
|
||||
---
|
||||
|
||||
## WORKFLOW COMPLET (À Suivre Strictement)
|
||||
|
||||
### Étape 1 : Lire et Parser les Cartes
|
||||
|
||||
```python
|
||||
# Pseudo-code du process
|
||||
cards_dir = "cards/"
|
||||
all_cards = []
|
||||
|
||||
for fichier in glob("cards/*.md"):
|
||||
# Parser frontmatter YAML
|
||||
with open(fichier) as f:
|
||||
content = f.read()
|
||||
# Extraire frontmatter entre --- et ---
|
||||
frontmatter = parse_yaml(content)
|
||||
# Extraire sections markdown
|
||||
question = extract_section(content, "# Question")
|
||||
answer_guide = extract_section(content, "# Answer Guide")
|
||||
notes = extract_section(content, "# Notes")
|
||||
|
||||
# Filtrer enabled=true seulement
|
||||
if frontmatter['enabled'] == True:
|
||||
all_cards.append({
|
||||
'filename': fichier,
|
||||
'category': frontmatter['category'],
|
||||
'priority': frontmatter['priority'],
|
||||
'question': question,
|
||||
'answer_guide': answer_guide,
|
||||
'notes': notes
|
||||
})
|
||||
```
|
||||
|
||||
**IMPORTANT** : Ne JAMAIS inventer de questions. TOUJOURS lire depuis les fichiers `cards/*.md`.
|
||||
|
||||
### Étape 2 : Sélectionner 6 Cartes
|
||||
|
||||
**Algorithme de sélection** :
|
||||
|
||||
1. Grouper par `category` (chinois, personal, travail)
|
||||
2. Pour chaque catégorie :
|
||||
- Trier par `priority` : critical > high > medium > low
|
||||
- Sélectionner les 2 cartes les plus prioritaires
|
||||
3. Si une catégorie a < 2 cartes :
|
||||
- Prendre ce qu'il y a
|
||||
- Compléter avec les cartes les plus prioritaires des autres catégories
|
||||
4. Objectif : 6 cartes total (ou moins si pas assez de cartes enabled)
|
||||
|
||||
**Répartition idéale** :
|
||||
- 2 cartes "chinois"
|
||||
- 2 cartes "personal"
|
||||
- 2 cartes "travail"
|
||||
|
||||
### Étape 3 : Poser le Quiz
|
||||
|
||||
**FORMAT STANDARDISÉ (Non-négociable)** :
|
||||
|
||||
```markdown
|
||||
🎯 **Daily Check - [DATE] - [PÉRIODE]**
|
||||
|
||||
**Question 1/6** : [category: chinois] [Question de la carte]
|
||||
→ [Attendre réponse utilisateur]
|
||||
|
||||
**Question 2/6** : [category: chinois] [Question de la carte]
|
||||
→ [Attendre réponse utilisateur]
|
||||
|
||||
**Question 3/6** : [category: personal] [Question de la carte]
|
||||
→ [Attendre réponse utilisateur]
|
||||
|
||||
**Question 4/6** : [category: personal] [Question de la carte]
|
||||
→ [Attendre réponse utilisateur]
|
||||
|
||||
**Question 5/6** : [category: travail] [Question de la carte]
|
||||
→ [Attendre réponse utilisateur]
|
||||
|
||||
**Question 6/6** : [category: travail] [Question de la carte]
|
||||
→ [Attendre réponse utilisateur]
|
||||
|
||||
---
|
||||
|
||||
**Feedback** :
|
||||
- Q1 : ✅/⚠️/❌ + [Feedback basé sur Answer Guide + Notes]
|
||||
- Q2 : ✅/⚠️/❌ + [Feedback basé sur Answer Guide + Notes]
|
||||
- Q3 : ✅/⚠️/❌ + [Feedback basé sur Answer Guide + Notes]
|
||||
- Q4 : ✅/⚠️/❌ + [Feedback basé sur Answer Guide + Notes]
|
||||
- Q5 : ✅/⚠️/❌ + [Feedback basé sur Answer Guide + Notes]
|
||||
- Q6 : ✅/⚠️/❌ + [Feedback basé sur Answer Guide + Notes]
|
||||
|
||||
**Score du jour** : X/6
|
||||
**Streak** : X jours consécutifs
|
||||
**Observation** : [Pattern remarqué / Encouragement / Confrontation si nécessaire]
|
||||
|
||||
**Rappel** : [Adapté selon les réponses - Bangkok dans 32 jours, freelance critique, etc.]
|
||||
```
|
||||
|
||||
**PÉRIODE** : morning / afternoon / evening (lire depuis flag file ou heure actuelle)
|
||||
|
||||
### Étape 4 : Donner Feedback (Principe Socratique)
|
||||
|
||||
**Pour chaque question** :
|
||||
|
||||
1. **Lire Answer Guide** de la carte
|
||||
2. **Évaluer réponse** selon les critères :
|
||||
- ✅ = Critère excellent atteint
|
||||
- ⚠️ = Critère partiel / insuffisant
|
||||
- ❌ = Critère non atteint
|
||||
|
||||
3. **Formuler feedback** :
|
||||
- **Utiliser les Notes** de la carte (contexte, importance)
|
||||
- **Méthode socratique** : Questions plutôt qu'affirmations
|
||||
- **Adapter le ton** selon la catégorie et la réponse
|
||||
|
||||
**Exemples de feedback** :
|
||||
|
||||
**Carte freelance (critical) - Réponse ❌** :
|
||||
```
|
||||
❌ Rien fait sur le freelance aujourd'hui.
|
||||
|
||||
Bangkok dans 32 jours. Budget : 1550€ pour 60 jours.
|
||||
Combien de jours encore avant que tu setup Upwork ?
|
||||
Tu attends d'être à Bangkok sans argent pour commencer ?
|
||||
|
||||
Le freelance est ta PRIORITÉ #1. Pas demain, pas la semaine prochaine. Aujourd'hui.
|
||||
```
|
||||
|
||||
**Carte chinois (high) - Réponse ✅** :
|
||||
```
|
||||
✅ 8 nouveaux mots HSK4 + structure 不但...而且.
|
||||
|
||||
Bon travail. Progression constante = clé pour HSK.
|
||||
Comment tu vas utiliser cette structure cette semaine ?
|
||||
```
|
||||
|
||||
**Carte personal (medium) - Réponse ⚠️** :
|
||||
```
|
||||
⚠️ 2 life rules sur 5 respectées.
|
||||
|
||||
Tu sais que discipline = productivité. Qu'est-ce qui a bloqué les 3 autres ?
|
||||
Sport skip = Moins d'énergie = Moins productif. Le pattern est clair, non ?
|
||||
```
|
||||
|
||||
### Étape 5 : Calculer Score & Streak
|
||||
|
||||
**Score** :
|
||||
- ✅ = 1 point
|
||||
- ⚠️ = 0.5 point
|
||||
- ❌ = 0 point
|
||||
- **Total** : X/6
|
||||
|
||||
**Streak** :
|
||||
1. Lire `.state.json`
|
||||
2. Comparer `last_check_date` avec aujourd'hui
|
||||
3. Si `last_check_date` = hier → `streak + 1`
|
||||
4. Si `last_check_date` = aujourd'hui → Erreur (déjà fait)
|
||||
5. Si `last_check_date` > 1 jour → Reset `streak = 1`
|
||||
6. Si `last_check_date` = null → `streak = 1` (premier check)
|
||||
|
||||
### Étape 6 : Logger la Session
|
||||
|
||||
**Écrire dans `daily_sessions.md`** (append à la fin) :
|
||||
|
||||
```markdown
|
||||
### [DATE - HH:MM]
|
||||
|
||||
**Triggered by** : Auto (morning/afternoon/evening) / Manual
|
||||
**Duration** : ~X minutes
|
||||
|
||||
**Questions Asked** :
|
||||
1. [filename] Question → Score: ✅/⚠️/❌
|
||||
- Ta réponse : "[réponse exacte utilisateur]"
|
||||
- Feedback : "[feedback donné]"
|
||||
|
||||
2. [filename] Question → Score: ✅/⚠️/❌
|
||||
- Ta réponse : "[réponse exacte utilisateur]"
|
||||
- Feedback : "[feedback donné]"
|
||||
|
||||
[... 6 questions total]
|
||||
|
||||
**Total Score** : X/6
|
||||
**Streak** : X jours consécutifs
|
||||
**Notes** : [Observations, patterns identifiés]
|
||||
**Action Items** : [Si actions concrètes identifiées]
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
|
||||
### Étape 7 : Mettre à Jour .state.json
|
||||
|
||||
**Fichier actuel** :
|
||||
```json
|
||||
{
|
||||
"streak": 0,
|
||||
"last_check_date": null,
|
||||
"total_sessions": 0,
|
||||
"best_streak": 0,
|
||||
"days_skipped_current": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Logique update** :
|
||||
```python
|
||||
today = date.today().strftime("%Y-%m-%d")
|
||||
|
||||
# Calculer nouveau streak
|
||||
if state['last_check_date'] == yesterday:
|
||||
new_streak = state['streak'] + 1
|
||||
elif state['last_check_date'] == today:
|
||||
# Déjà fait aujourd'hui - ne devrait pas arriver
|
||||
return "Daily check déjà fait aujourd'hui"
|
||||
else:
|
||||
new_streak = 1
|
||||
|
||||
# Update best_streak si nouveau record
|
||||
new_best = max(state['best_streak'], new_streak)
|
||||
|
||||
# Update state
|
||||
state = {
|
||||
"streak": new_streak,
|
||||
"last_check_date": today,
|
||||
"total_sessions": state['total_sessions'] + 1,
|
||||
"best_streak": new_best,
|
||||
"days_skipped_current": 0
|
||||
}
|
||||
|
||||
# Écrire dans .state.json
|
||||
```
|
||||
|
||||
**IMPORTANT** : Utiliser les outils File (Read/Write) pour manipuler `.state.json`, pas jq ou bash.
|
||||
|
||||
---
|
||||
|
||||
## Principes du Feedback
|
||||
|
||||
### Ton Général
|
||||
|
||||
- **Bienveillant mais exigeant**
|
||||
- **Socratique mais direct** (questions > affirmations)
|
||||
- **Encourageant quand mérité, confrontant quand nécessaire**
|
||||
- **Jamais agressif, mais jamais complice de la complacency**
|
||||
|
||||
### Adapter le Ton Selon la Catégorie
|
||||
|
||||
**Cartes "chinois"** :
|
||||
- Focus sur progression HSK
|
||||
- Rappel : Critique pour vie en Asie
|
||||
- Questions : "Comment tu vas utiliser ça ?" "Quand tu vas pratiquer ?"
|
||||
|
||||
**Cartes "personal"** :
|
||||
- Focus sur discipline et growth
|
||||
- Rappel : Life rules = fondation productivité
|
||||
- Questions : "Qu'est-ce qui a bloqué ?" "Comment tu évites ce pattern ?"
|
||||
|
||||
**Cartes "travail" (surtout freelance - CRITICAL)** :
|
||||
- Focus sur urgence Bangkok (32 jours)
|
||||
- Rappel : Budget 1550€, besoin clients AVANT d'arriver
|
||||
- Questions : "Combien de jours encore avant de..." "Tu attends quoi exactement ?"
|
||||
|
||||
### Red Flags à Confronter
|
||||
|
||||
🚩 **Bullshit / Évitement** :
|
||||
```
|
||||
"Tu dis X mais qu'est-ce que tu as FAIT concrètement ?
|
||||
Penser c'est bien, agir c'est mieux."
|
||||
```
|
||||
|
||||
🚩 **Pattern de skip (si observé dans historique)** :
|
||||
```
|
||||
"Ça fait 3 jours que tu skip cette catégorie.
|
||||
Le pattern d'oubli systémique revient. Tu le vois ?"
|
||||
```
|
||||
|
||||
🚩 **Score déclinant** :
|
||||
```
|
||||
"Semaine dernière : 5/6. Cette semaine : 2/6.
|
||||
La complacency s'installe. Qu'est-ce qui change ?"
|
||||
```
|
||||
|
||||
🚩 **Freelance ignoré (CRITIQUE)** :
|
||||
```
|
||||
"Bangkok dans X jours. 0€ gagné ce mois.
|
||||
À quel moment exactement tu penses que l'argent va apparaître ?"
|
||||
```
|
||||
|
||||
### Encouragement (Quand Mérité)
|
||||
|
||||
✅ **Streak de 3+ jours** :
|
||||
```
|
||||
"3 jours de suite. Le pattern change.
|
||||
Continue, la discipline se construit jour par jour."
|
||||
```
|
||||
|
||||
✅ **Amélioration visible** :
|
||||
```
|
||||
"Semaine dernière ❌ sur freelance, cette semaine ✅ tous les jours.
|
||||
Tu prouves que tu peux exécuter. Ne relâche pas."
|
||||
```
|
||||
|
||||
✅ **Action concrète sur priorité critique** :
|
||||
```
|
||||
"Premier client Upwork. C'est exactement ça qu'il faut.
|
||||
Combien d'autres tu peux closer cette semaine ?"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contexte Alexis (Important)
|
||||
|
||||
**Situation actuelle** :
|
||||
- **Divorce** : Tingting, 19 décembre 2025
|
||||
- **Shanghai → Bangkok** : 22 janvier 2026 (dans 32 jours)
|
||||
- **Budget** : 1550€ pour 60 jours Bangkok
|
||||
- **Freelance** : CRITIQUE - besoin clients AVANT d'arriver
|
||||
- **Projets actifs** : GroveEngine, AISSIA, Confluent, WeChat Bot
|
||||
- **Pattern exécution** : Prouvé (102 commits/3 sem nov 2025, videotoMP3 shippé en 2j)
|
||||
- **Pattern d'oubli** : Également prouvé (anki_tingting skip pendant des semaines)
|
||||
|
||||
**Profil psychologique** :
|
||||
- Introspection++, confiance--
|
||||
- Peut être défensif si confronté brutalement
|
||||
- Réagit bien à méthode socratique (questions > affirmations)
|
||||
- A besoin de vérité directe (pas de bullshit)
|
||||
- Respecte la confrontation honnête
|
||||
|
||||
**Objectifs immédiats** :
|
||||
1. **Freelance setup** (Upwork/Fiverr + premiers clients) - PRIORITÉ #1
|
||||
2. **Chinois** (HSK progression pour vie Asie)
|
||||
3. **Discipline** (life rules, productivité)
|
||||
|
||||
---
|
||||
|
||||
## Erreurs à ÉVITER
|
||||
|
||||
❌ **Ne JAMAIS** :
|
||||
- Inventer des questions (toujours lire depuis `cards/*.md`)
|
||||
- Accepter "je sais pas" sans creuser
|
||||
- Laisser passer du bullshit sans questionner
|
||||
- Oublier de mettre à jour `.state.json` après session
|
||||
- Oublier de logger dans `daily_sessions.md`
|
||||
- Être agressif (socratique ≠ agressif)
|
||||
- Valider facilement sans réflexion
|
||||
|
||||
✅ **TOUJOURS** :
|
||||
- Lire les cartes depuis les fichiers
|
||||
- Utiliser Answer Guide pour évaluer
|
||||
- Utiliser Notes pour contextualiser feedback
|
||||
- Logger TOUT dans daily_sessions.md
|
||||
- Mettre à jour .state.json
|
||||
- Être socratique (questions > affirmations)
|
||||
- Confronter les patterns toxiques
|
||||
- Encourager les progrès réels
|
||||
|
||||
---
|
||||
|
||||
## Structure Fichiers (Référence)
|
||||
|
||||
```
|
||||
daily_check/
|
||||
├── cards/ # Cartes modulaires
|
||||
│ ├── chinese_vocab.md
|
||||
│ ├── chinese_practice.md
|
||||
│ ├── life_rules.md
|
||||
│ ├── learning.md
|
||||
│ ├── freelance.md
|
||||
│ └── projects.md
|
||||
├── .state.json # État système
|
||||
├── daily_sessions.md # Logs sessions
|
||||
├── daily_check.log # Logs techniques
|
||||
└── CLAUDE.md # Ce fichier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exemple Complet de Session
|
||||
|
||||
**Input utilisateur** : "daily check"
|
||||
|
||||
**Claude répond** :
|
||||
|
||||
```markdown
|
||||
🎯 **Daily Check - 21 décembre 2025 - Morning**
|
||||
|
||||
**Question 1/6** : [chinois] Quel nouveau vocabulaire ou grammaire as-tu appris aujourd'hui en chinois ?
|
||||
```
|
||||
|
||||
**Utilisateur** : "5 mots HSK4 + structure 虽然...但是"
|
||||
|
||||
```markdown
|
||||
**Question 2/6** : [chinois] As-tu utilisé le chinois dans un contexte réel aujourd'hui ? (WeChat, projet, conversation)
|
||||
```
|
||||
|
||||
**Utilisateur** : "Oui messages WeChat avec anciens collègues"
|
||||
|
||||
```markdown
|
||||
**Question 3/6** : [personal] As-tu respecté tes life rules aujourd'hui ? (Sport, sommeil, routine, discipline)
|
||||
```
|
||||
|
||||
**Utilisateur** : "Sport oui, sommeil bof (6h), routine matinale ok"
|
||||
|
||||
```markdown
|
||||
**Question 4/6** : [personal] Qu'as-tu appris ou découvert de nouveau aujourd'hui ? (Non-tech)
|
||||
```
|
||||
|
||||
**Utilisateur** : "Rien de significatif"
|
||||
|
||||
```markdown
|
||||
**Question 5/6** : [travail] As-tu avancé sur le freelance/clients payants aujourd'hui ? (Prospection, travail client, setup Upwork/Fiverr)
|
||||
```
|
||||
|
||||
**Utilisateur** : "Créé profil Upwork, mais pas encore postulé"
|
||||
|
||||
```markdown
|
||||
**Question 6/6** : [travail] As-tu progressé sur un projet perso ou appris une nouvelle tech/compétence aujourd'hui ?
|
||||
```
|
||||
|
||||
**Utilisateur** : "Oui commit sur GroveEngine (rendering pipeline)"
|
||||
|
||||
```markdown
|
||||
---
|
||||
|
||||
**Feedback** :
|
||||
|
||||
- Q1 : ✅ 5 mots HSK4 + structure 虽然...但是
|
||||
→ Bonne progression. Comment tu vas utiliser cette structure cette semaine ?
|
||||
|
||||
- Q2 : ✅ Messages WeChat avec anciens collègues
|
||||
→ Bien. Le chinois doit rester actif, pas juste théorique.
|
||||
|
||||
- Q3 : ⚠️ Sport ok, routine ok, mais sommeil 6h insuffisant
|
||||
→ 6h = productivité diminuée. Tu sais que 7-8h c'est non-négociable. Qu'est-ce qui t'a fait veiller tard ?
|
||||
|
||||
- Q4 : ❌ Rien de significatif appris
|
||||
→ Journée entière sans apprendre quelque chose de nouveau hors tech ? YouTube ne compte pas si tu notes rien. Challenge : trouve AU MOINS une chose par jour.
|
||||
|
||||
- Q5 : ⚠️ Profil Upwork créé mais pas de postulation
|
||||
→ Setup c'est bien, mais Bangkok dans 32 jours. Combien de temps encore avant que tu postes ta première candidature ? Demain ?
|
||||
|
||||
- Q6 : ✅ Commit sur GroveEngine (rendering pipeline)
|
||||
→ Bon. Tu maintiens le momentum. Continue.
|
||||
|
||||
**Score du jour** : 4/6
|
||||
**Streak** : 1 jour (premier daily check du nouveau système)
|
||||
|
||||
**Observation** :
|
||||
Bon démarrage. Tu exécutes sur chinois et projets perso. Attention aux points faibles : sommeil + apprentissage non-tech + URGENCE freelance.
|
||||
|
||||
Setup Upwork c'est bien, mais Bangkok approche. Tu dois POSTULER, pas juste préparer éternellement.
|
||||
|
||||
**Action Items** :
|
||||
- Postuler à 5 jobs Upwork demain (non-négociable)
|
||||
- Dormir 7h minimum ce soir (pas de code après 23h)
|
||||
|
||||
**Rappel** :
|
||||
32 jours avant Bangkok. 1550€ budget pour 60 jours. Le freelance n'est pas optionnel.
|
||||
```
|
||||
|
||||
**Puis Claude met à jour** :
|
||||
- `daily_sessions.md` (ajoute cette session)
|
||||
- `.state.json` (streak = 1, last_check_date = "2025-12-21", etc.)
|
||||
|
||||
---
|
||||
|
||||
## Note Finale
|
||||
|
||||
**Ce système est CRITIQUE.**
|
||||
|
||||
Bangkok dans 32 jours. Alexis a besoin de discipline sur 3 fronts :
|
||||
- **Chinois** : Survie + opportunités en Asie
|
||||
- **Personal** : Fondation productivité (life rules, discipline)
|
||||
- **Travail** : Revenu freelance (non-négociable)
|
||||
|
||||
Si ce système ne tient pas, c'est un red flag sur sa capacité à exécuter.
|
||||
|
||||
**Donc : Prends ce daily check très au sérieux. 5 minutes par jour. Feedback honnête. Pas de bullshit.**
|
||||
367
daily_check/README.md
Normal file
@ -0,0 +1,367 @@
|
||||
# Daily Check System v2 - Modulaire & Windows Startup
|
||||
|
||||
**Créé** : 21 décembre 2025
|
||||
**Remplace** : `anki_tingting/` (obsolète depuis divorce 19 déc 2025)
|
||||
**Objectif** : Système holistique de daily check sur 3 domaines clés de vie
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Concept
|
||||
|
||||
**Problème** : Pattern d'oubli systémique identifié. Tu diagnostiques, crées des plans, puis **oublies que les plans existent**.
|
||||
|
||||
**Solution** :
|
||||
1. **Modulaire** : Un fichier = une carte, facile d'ajouter de nouvelles questions
|
||||
2. **Windows Startup** : Se lance automatiquement au boot (Task Scheduler OU Startup folder)
|
||||
3. **Auto-spawn terminal** : Impossible d'ignorer - se lance à l'ouverture d'un terminal WSL
|
||||
4. **3 catégories** : Chinois (2Q), Personal Life (2Q), Travail (2Q)
|
||||
5. **Simple** : Pas de dépendances lourdes (pygame, edge_tts, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Structure
|
||||
|
||||
```
|
||||
daily_check/
|
||||
├── cards/ # Un fichier = une carte
|
||||
│ ├── chinese_vocab.md # Vocab/grammaire chinois
|
||||
│ ├── chinese_practice.md # Pratique active chinois
|
||||
│ ├── life_rules.md # Discipline quotidienne
|
||||
│ ├── learning.md # Apprentissage non-tech
|
||||
│ ├── freelance.md # CRITIQUE - Bangkok prep
|
||||
│ └── projects.md # Projets perso + skills
|
||||
│
|
||||
├── .state.json # État système (streak, last check)
|
||||
├── daily_sessions.md # Log de toutes les sessions
|
||||
├── daily_check.log # Logs techniques (triggers)
|
||||
│
|
||||
├── trigger_check.sh # Script appelé par Task Scheduler
|
||||
├── bashrc_hook.sh # Hook à ajouter dans ~/.bashrc
|
||||
├── start_daily_check.bat # Alternative Startup folder
|
||||
│
|
||||
├── SETUP_TASK_SCHEDULER.md # Instructions Task Scheduler
|
||||
└── README.md # Ce fichier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Comment Ça Marche
|
||||
|
||||
### **1. Trigger automatique (3x par jour)**
|
||||
|
||||
**Option A : Task Scheduler (Recommandé)**
|
||||
```
|
||||
07:00, 14:00, 21:00 → Task Scheduler lance trigger_check.sh
|
||||
↓
|
||||
Script vérifie si déjà fait aujourd'hui (.state.json)
|
||||
↓
|
||||
Si non fait → Crée flag file (~/.daily_check_pending)
|
||||
↓
|
||||
Flag contient la période (morning/afternoon/evening)
|
||||
```
|
||||
|
||||
**Option B : Startup Folder (Plus simple)**
|
||||
```
|
||||
Windows Boot → start_daily_check.bat démarre
|
||||
↓
|
||||
Boucle infinie : trigger toutes les heures
|
||||
↓
|
||||
Même logique que Task Scheduler
|
||||
```
|
||||
|
||||
### **2. Auto-spawn à l'ouverture terminal**
|
||||
|
||||
```
|
||||
Tu ouvres Windows Terminal (WSL)
|
||||
↓
|
||||
~/.bashrc détecte ~/.daily_check_pending
|
||||
↓
|
||||
Affiche : "🔔 DAILY CHECK EN ATTENTE (morning)"
|
||||
↓
|
||||
Countdown 3 secondes (Ctrl+C pour annuler)
|
||||
↓
|
||||
Auto-lance : claude "daily check"
|
||||
↓
|
||||
Flag file supprimé après exécution
|
||||
```
|
||||
|
||||
### **3. Exécution du quiz (Claude Code)**
|
||||
|
||||
```
|
||||
Claude lit tous les fichiers cards/*.md
|
||||
↓
|
||||
Parse frontmatter (category, priority, enabled)
|
||||
↓
|
||||
Filtre enabled=true uniquement
|
||||
↓
|
||||
Sélectionne 6 cartes (2 par catégorie si possible)
|
||||
↓
|
||||
Pose les 6 questions
|
||||
↓
|
||||
Donne feedback (✅/⚠️/❌)
|
||||
↓
|
||||
Log session dans daily_sessions.md
|
||||
↓
|
||||
Met à jour .state.json (streak, last_check_date)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Setup (Première Fois)
|
||||
|
||||
### **Étape 1 : Installer jq (si pas déjà fait)**
|
||||
```bash
|
||||
sudo apt install jq
|
||||
```
|
||||
|
||||
### **Étape 2 : Ajouter le hook dans ~/.bashrc**
|
||||
|
||||
Ouvre `~/.bashrc` :
|
||||
```bash
|
||||
nano ~/.bashrc
|
||||
```
|
||||
|
||||
Ajoute à la fin du fichier :
|
||||
```bash
|
||||
# Daily Check Auto-Spawn Hook
|
||||
FLAG_FILE="$HOME/.daily_check_pending"
|
||||
DAILY_CHECK_DIR="/mnt/e/Users/Alexis Trouvé/Documents/Projets/couple_matters/daily_check"
|
||||
|
||||
if [ -f "$FLAG_FILE" ]; then
|
||||
PERIOD=$(cat "$FLAG_FILE" 2>/dev/null || echo "unknown")
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔔 DAILY CHECK EN ATTENTE ($PERIOD)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Le daily check va se lancer automatiquement dans 3 secondes..."
|
||||
echo "(Appuie sur Ctrl+C pour annuler)"
|
||||
echo ""
|
||||
sleep 3
|
||||
cd "$DAILY_CHECK_DIR"
|
||||
claude "daily check"
|
||||
rm -f "$FLAG_FILE"
|
||||
echo ""
|
||||
echo "✅ Daily check terminé. Flag file supprimé."
|
||||
echo ""
|
||||
fi
|
||||
```
|
||||
|
||||
Sauvegarde (Ctrl+O, Enter, Ctrl+X) et recharge :
|
||||
```bash
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### **Étape 3 : Setup Windows Startup**
|
||||
|
||||
**Option A : Task Scheduler (Recommandé)**
|
||||
- Suis les instructions dans `SETUP_TASK_SCHEDULER.md`
|
||||
- Triggers : 07:00, 14:00, 21:00 (3x par jour)
|
||||
|
||||
**Option B : Startup Folder (Plus simple)**
|
||||
1. Copie `start_daily_check.bat`
|
||||
2. Colle dans : `C:\Users\[User]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`
|
||||
3. Redémarre le PC pour tester
|
||||
|
||||
### **Étape 4 : Tester le système**
|
||||
|
||||
Lance manuellement le trigger :
|
||||
```bash
|
||||
cd "/mnt/e/Users/Alexis Trouvé/Documents/Projets/couple_matters/daily_check"
|
||||
./trigger_check.sh
|
||||
```
|
||||
|
||||
Vérifie que le flag est créé :
|
||||
```bash
|
||||
cat ~/.daily_check_pending
|
||||
```
|
||||
|
||||
Ouvre un nouveau terminal → Le hook devrait se déclencher automatiquement.
|
||||
|
||||
---
|
||||
|
||||
## 🃏 Format des Cartes
|
||||
|
||||
Exemple : `cards/freelance.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
category: travail
|
||||
priority: critical
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
As-tu avancé sur le freelance/clients payants aujourd'hui ?
|
||||
|
||||
# Answer Guide
|
||||
- Rien fait = ❌
|
||||
- Setup/préparation seulement = ⚠️
|
||||
- Action concrète (profil, postulation, travail client) = ✅
|
||||
|
||||
# Notes
|
||||
Bangkok dans 32 jours. Tu DOIS avoir des clients AVANT d'arriver.
|
||||
Le freelance est ta PRIORITÉ #1 jusqu'à revenu stable.
|
||||
```
|
||||
|
||||
**Champs frontmatter** :
|
||||
- `category` : chinois | personal | travail
|
||||
- `priority` : low | medium | high | critical
|
||||
- `frequency` : daily | every_2_days | weekly
|
||||
- `enabled` : true | false
|
||||
|
||||
---
|
||||
|
||||
## ➕ Ajouter une Nouvelle Carte
|
||||
|
||||
1. Créer `cards/nouvelle_carte.md`
|
||||
2. Copier le format ci-dessus
|
||||
3. Remplir frontmatter et sections
|
||||
4. C'est tout ! Sera détectée automatiquement
|
||||
|
||||
**Exemple** : Carte pour suivi budget Bangkok
|
||||
```bash
|
||||
nano cards/budget_bangkok.md
|
||||
```
|
||||
|
||||
```markdown
|
||||
---
|
||||
category: personal
|
||||
priority: high
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
As-tu suivi ton budget aujourd'hui ? (dépenses trackées, pas de gaspillage)
|
||||
|
||||
# Answer Guide
|
||||
- Dépenses non trackées = ❌
|
||||
- Trackées mais dépassement budget = ⚠️
|
||||
- Tout tracké + dans le budget = ✅
|
||||
|
||||
# Notes
|
||||
Budget Bangkok : 1550€ pour 60 jours.
|
||||
Chaque euro compte. Pas de marge d'erreur.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Consulter les Stats
|
||||
|
||||
**Voir streak actuelle** :
|
||||
```bash
|
||||
cat .state.json | jq
|
||||
```
|
||||
|
||||
**Voir dernière session** :
|
||||
```bash
|
||||
tail -50 daily_sessions.md
|
||||
```
|
||||
|
||||
**Voir logs techniques** :
|
||||
```bash
|
||||
tail -20 daily_check.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Maintenance
|
||||
|
||||
### **Désactiver temporairement une carte**
|
||||
```bash
|
||||
nano cards/learning.md
|
||||
# Changer enabled: true → enabled: false
|
||||
```
|
||||
|
||||
### **Modifier priorité d'une carte**
|
||||
```bash
|
||||
nano cards/freelance.md
|
||||
# Changer priority: high → priority: critical
|
||||
```
|
||||
|
||||
### **Arrêter le système temporairement**
|
||||
```bash
|
||||
# Supprimer le flag file
|
||||
rm ~/.daily_check_pending
|
||||
|
||||
# Désactiver Task Scheduler (voir SETUP_TASK_SCHEDULER.md)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
**Le hook ne se déclenche pas** :
|
||||
- Vérifie que ~/.bashrc contient le hook
|
||||
- Vérifie que le flag file existe : `ls -la ~/.daily_check_pending`
|
||||
- Teste manuellement : `bash -l`
|
||||
|
||||
**Trigger ne crée pas le flag** :
|
||||
- Vérifie que jq est installé : `jq --version`
|
||||
- Vérifie les logs : `cat daily_check.log`
|
||||
- Vérifie Task Scheduler (voir SETUP_TASK_SCHEDULER.md)
|
||||
|
||||
**Claude ne trouve pas les cartes** :
|
||||
- Vérifie le chemin : `ls -la cards/*.md`
|
||||
- Vérifie frontmatter YAML valide
|
||||
- Vérifie `enabled: true`
|
||||
|
||||
**Permission denied sur trigger_check.sh** :
|
||||
```bash
|
||||
chmod +x trigger_check.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Avantages vs Ancien Système
|
||||
|
||||
| Ancien (anki_tingting) | Nouveau (daily_check) |
|
||||
|------------------------|----------------------|
|
||||
| ❌ Python service compliqué | ✅ Simple bash + Task Scheduler |
|
||||
| ❌ Dépendances lourdes | ✅ Juste bash + jq |
|
||||
| ❌ Cartes hardcodées | ✅ Modulaire (1 fichier = 1 carte) |
|
||||
| ❌ Crash silencieux | ✅ Robuste (flag file + retry) |
|
||||
| ❌ Facile à ignorer | ✅ Auto-spawn terminal |
|
||||
| ❌ Centré Tingting only | ✅ Holistique (3 domaines) |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Objectifs
|
||||
|
||||
**Semaine 1** :
|
||||
- [ ] Streak de 5+ jours
|
||||
- [ ] Aucun skip
|
||||
- [ ] Spawn fiable
|
||||
|
||||
**Mois 1** :
|
||||
- [ ] Streak de 20+ jours
|
||||
- [ ] Amélioration visible (freelance, chinois)
|
||||
- [ ] Au moins 2 cartes perso ajoutées
|
||||
|
||||
**Long terme** :
|
||||
- [ ] Habitude quotidienne (5 min/jour)
|
||||
- [ ] Impact visible sur discipline/productivité
|
||||
- [ ] Clients freelance actifs avant Bangkok (22 jan)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ CRITIQUE
|
||||
|
||||
**Ce système est un test de ta capacité à tenir tes engagements.**
|
||||
|
||||
Bangkok dans 32 jours. Tu as besoin de :
|
||||
- Discipline (life rules)
|
||||
- Chinois (survie + opportunités)
|
||||
- Freelance (revenu critique)
|
||||
|
||||
Si ce système ne tient pas, c'est un red flag sur ta capacité à exécuter.
|
||||
|
||||
**Donc : Prends ce système au sérieux. 5 minutes par jour. C'est pas beaucoup demander.**
|
||||
|
||||
---
|
||||
|
||||
**Prochaine session prévue** : Dès que tu ouvres un terminal après setup !
|
||||
|
||||
**Status** : ✅ SYSTÈME OPÉRATIONNEL
|
||||
212
daily_check/SETUP_TASK_SCHEDULER.md
Normal file
@ -0,0 +1,212 @@
|
||||
# Setup Task Scheduler (Windows) - Instructions Détaillées
|
||||
|
||||
**Option recommandée** pour lancer le daily check automatiquement au boot Windows et 3x par jour.
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Windows 10/11
|
||||
- WSL installé et configuré
|
||||
- jq installé dans WSL : `sudo apt install jq`
|
||||
|
||||
---
|
||||
|
||||
## Étape 1 : Ouvrir Task Scheduler
|
||||
|
||||
1. Appuie sur `Win + R`
|
||||
2. Tape `taskschd.msc`
|
||||
3. Appuie sur `Enter`
|
||||
|
||||
OU
|
||||
|
||||
1. Recherche "Task Scheduler" dans le menu démarrer
|
||||
2. Ouvre l'application
|
||||
|
||||
---
|
||||
|
||||
## Étape 2 : Créer une Nouvelle Tâche
|
||||
|
||||
1. Dans le panneau de droite, clique sur **"Create Task..."** (pas "Create Basic Task")
|
||||
2. Cela ouvre une fenêtre avec plusieurs onglets
|
||||
|
||||
---
|
||||
|
||||
## Étape 3 : Onglet "General"
|
||||
|
||||
**Name** : `Daily Check Trigger`
|
||||
|
||||
**Description** : `Système de daily check automatique - Lance trigger 3x par jour`
|
||||
|
||||
**Security options** :
|
||||
- ✅ Coche "Run whether user is logged on or not"
|
||||
- ✅ Coche "Run with highest privileges"
|
||||
|
||||
**Configure for** : `Windows 10` (ou ta version Windows)
|
||||
|
||||
---
|
||||
|
||||
## Étape 4 : Onglet "Triggers"
|
||||
|
||||
Clique sur **"New..."** et configure **3 triggers** (un par période de la journée) :
|
||||
|
||||
### Trigger 1 : Morning (07:00)
|
||||
- **Begin the task** : `On a schedule`
|
||||
- **Settings** : `Daily`
|
||||
- **Start** : Aujourd'hui à `07:00:00`
|
||||
- **Recur every** : `1 days`
|
||||
- ✅ **Enabled**
|
||||
|
||||
Clique **OK**
|
||||
|
||||
### Trigger 2 : Afternoon (14:00)
|
||||
- Clique à nouveau sur **"New..."**
|
||||
- **Begin the task** : `On a schedule`
|
||||
- **Settings** : `Daily`
|
||||
- **Start** : Aujourd'hui à `14:00:00`
|
||||
- **Recur every** : `1 days`
|
||||
- ✅ **Enabled**
|
||||
|
||||
Clique **OK**
|
||||
|
||||
### Trigger 3 : Evening (21:00)
|
||||
- Clique à nouveau sur **"New..."**
|
||||
- **Begin the task** : `On a schedule`
|
||||
- **Settings** : `Daily`
|
||||
- **Start** : Aujourd'hui à `21:00:00`
|
||||
- **Recur every** : `1 days`
|
||||
- ✅ **Enabled**
|
||||
|
||||
Clique **OK**
|
||||
|
||||
---
|
||||
|
||||
## Étape 5 : Onglet "Actions"
|
||||
|
||||
Clique sur **"New..."**
|
||||
|
||||
**Action** : `Start a program`
|
||||
|
||||
**Program/script** :
|
||||
```
|
||||
wsl
|
||||
```
|
||||
|
||||
**Add arguments** :
|
||||
```
|
||||
-e bash -c "cd '/mnt/e/Users/Alexis Trouvé/Documents/Projets/couple_matters/daily_check' && ./trigger_check.sh"
|
||||
```
|
||||
|
||||
**Note** : Assure-toi que le chemin est correct pour ton système.
|
||||
|
||||
Clique **OK**
|
||||
|
||||
---
|
||||
|
||||
## Étape 6 : Onglet "Conditions"
|
||||
|
||||
**Power** :
|
||||
- ❌ **Décoche** "Start the task only if the computer is on AC power"
|
||||
- ✅ **Coche** "Wake the computer to run this task" (si tu veux que ça réveille le PC)
|
||||
|
||||
**Network** :
|
||||
- Laisse par défaut (pas nécessaire pour ce task)
|
||||
|
||||
---
|
||||
|
||||
## Étape 7 : Onglet "Settings"
|
||||
|
||||
- ✅ **Coche** "Allow task to be run on demand"
|
||||
- ✅ **Coche** "Run task as soon as possible after a scheduled start is missed"
|
||||
- ✅ **Coche** "If the task fails, restart every:" `10 minutes` (pour robustesse)
|
||||
- **Stop the task if it runs longer than** : `30 minutes`
|
||||
|
||||
---
|
||||
|
||||
## Étape 8 : Sauvegarder
|
||||
|
||||
1. Clique **OK** en bas de la fenêtre
|
||||
2. Windows va te demander ton mot de passe utilisateur → Entre-le
|
||||
3. La tâche est maintenant créée !
|
||||
|
||||
---
|
||||
|
||||
## Étape 9 : Tester Immédiatement
|
||||
|
||||
1. Dans Task Scheduler, trouve ta tâche "Daily Check Trigger" dans la liste
|
||||
2. Clique-droit dessus
|
||||
3. Clique **"Run"**
|
||||
|
||||
Cela devrait :
|
||||
- Exécuter `trigger_check.sh`
|
||||
- Créer le flag file `~/.daily_check_pending`
|
||||
- Logger dans `daily_check/daily_check.log`
|
||||
|
||||
Vérifie :
|
||||
```bash
|
||||
cat ~/daily_check/daily_check.log
|
||||
ls -la ~/.daily_check_pending
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### La tâche n'apparaît pas dans "Task Scheduler Library"
|
||||
|
||||
- Va dans **Task Scheduler Library** (panneau gauche)
|
||||
- Rafraîchis la vue (F5)
|
||||
|
||||
### La tâche échoue avec "The system cannot find the file specified"
|
||||
|
||||
- Vérifie que WSL est bien installé : ouvre cmd et tape `wsl --version`
|
||||
- Vérifie que le chemin dans "Arguments" est correct
|
||||
|
||||
### Le script ne se lance pas
|
||||
|
||||
- Vérifie que `trigger_check.sh` est exécutable : `chmod +x trigger_check.sh`
|
||||
- Vérifie que jq est installé : `jq --version`
|
||||
- Regarde les logs : `cat daily_check/daily_check.log`
|
||||
|
||||
### La tâche ne se lance pas au bon moment
|
||||
|
||||
- Vérifie les triggers dans Task Scheduler
|
||||
- Assure-toi que "Enabled" est coché pour chaque trigger
|
||||
- Vérifie que l'heure système Windows est correcte
|
||||
|
||||
### Permission denied
|
||||
|
||||
- Assure-toi que la tâche est configurée avec "Run with highest privileges"
|
||||
|
||||
---
|
||||
|
||||
## Désactiver Temporairement
|
||||
|
||||
Si tu veux désactiver le système sans supprimer la tâche :
|
||||
|
||||
1. Ouvre Task Scheduler
|
||||
2. Trouve "Daily Check Trigger"
|
||||
3. Clique-droit → **Disable**
|
||||
|
||||
Pour réactiver : Clique-droit → **Enable**
|
||||
|
||||
---
|
||||
|
||||
## Supprimer la Tâche
|
||||
|
||||
1. Ouvre Task Scheduler
|
||||
2. Trouve "Daily Check Trigger"
|
||||
3. Clique-droit → **Delete**
|
||||
|
||||
---
|
||||
|
||||
## Notes Importantes
|
||||
|
||||
- Cette tâche va créer un flag file `~/.daily_check_pending`
|
||||
- Le flag file déclenche l'auto-spawn quand tu ouvres un terminal WSL
|
||||
- Le système ne te spam PAS si tu ignores → Il attend juste que tu ouvres un terminal
|
||||
- Si tu fais déjà le daily check manuellement, le trigger ne créera pas de flag (détecte via `.state.json`)
|
||||
|
||||
---
|
||||
|
||||
**Setup terminé !** Le système est maintenant actif et se lancera automatiquement 3x par jour.
|
||||
38
daily_check/bashrc_hook.sh
Normal file
@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Daily Check Auto-Spawn Hook
|
||||
# Add this to your ~/.bashrc to enable auto-spawn on terminal open
|
||||
|
||||
# Configuration
|
||||
FLAG_FILE="$HOME/.daily_check_pending"
|
||||
DAILY_CHECK_DIR="/mnt/e/Users/Alexis Trouvé/Documents/Projets/couple_matters/daily_check"
|
||||
|
||||
# Check if flag file exists
|
||||
if [ -f "$FLAG_FILE" ]; then
|
||||
# Read period from flag file
|
||||
PERIOD=$(cat "$FLAG_FILE" 2>/dev/null || echo "unknown")
|
||||
|
||||
# Display notification
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔔 DAILY CHECK EN ATTENTE ($PERIOD)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Le daily check va se lancer automatiquement dans 3 secondes..."
|
||||
echo "(Appuie sur Ctrl+C pour annuler)"
|
||||
echo ""
|
||||
|
||||
# Countdown (interruptible)
|
||||
sleep 3
|
||||
|
||||
# Launch Claude Code with daily check
|
||||
cd "$DAILY_CHECK_DIR"
|
||||
claude "daily check"
|
||||
|
||||
# Remove flag file after execution
|
||||
rm -f "$FLAG_FILE"
|
||||
|
||||
echo ""
|
||||
echo "✅ Daily check terminé. Flag file supprimé."
|
||||
echo ""
|
||||
fi
|
||||
25
daily_check/cards/chinese_practice.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
category: chinois
|
||||
priority: medium
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
As-tu utilisé le chinois dans un contexte réel aujourd'hui ? (WeChat, projet, conversation)
|
||||
|
||||
# Answer Guide
|
||||
- Aucune utilisation réelle = ❌
|
||||
- Lecture passive (articles, sous-titres) = ⚠️
|
||||
- Conversation/écriture active (WeChat, projet, parler) = ✅
|
||||
|
||||
# Notes
|
||||
Apprendre du vocabulaire c'est bien, mais l'utiliser c'est mieux.
|
||||
|
||||
Contextes possibles :
|
||||
- Messages WeChat (amis chinois, groupes)
|
||||
- Projets code (documentation chinoise, commentaires)
|
||||
- Conversations orales (même courtes)
|
||||
- Écriture (journal, notes, posts)
|
||||
|
||||
Le chinois doit devenir une langue ACTIVE, pas juste passive. Si tu ne l'utilises pas, tu le perds.
|
||||
25
daily_check/cards/chinese_vocab.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
category: chinois
|
||||
priority: high
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
Quel nouveau vocabulaire ou grammaire as-tu appris aujourd'hui en chinois ?
|
||||
|
||||
# Answer Guide
|
||||
- Si 0 nouveau mot/concept = ❌
|
||||
- Si 1-4 mots = ⚠️ (insuffisant pour progression HSK)
|
||||
- Si 5+ mots OU 1 point de grammaire important = ✅
|
||||
|
||||
# Notes
|
||||
HSK doit progresser régulièrement pour ton avenir en Asie. Pas de jour sans apprendre au moins quelques mots.
|
||||
|
||||
Le chinois est critique pour :
|
||||
- Vie quotidienne en Chine/Asie
|
||||
- Opportunités professionnelles
|
||||
- Intégration culturelle
|
||||
- Autonomie (pas dépendre de traduction)
|
||||
|
||||
Pattern à éviter : Promettre d'étudier mais jamais le faire. 5-10 minutes par jour suffisent.
|
||||
34
daily_check/cards/freelance.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
category: travail
|
||||
priority: critical
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
As-tu avancé sur le freelance/clients payants aujourd'hui ? (Prospection, travail client, setup Upwork/Fiverr)
|
||||
|
||||
# Answer Guide
|
||||
- Rien fait = ❌
|
||||
- Setup/préparation seulement = ⚠️
|
||||
- Action concrète (profil Upwork, postulation, travail client) = ✅
|
||||
|
||||
# Notes
|
||||
**CONTEXTE CRITIQUE : Bangkok dans 32 jours (22 janvier 2026)**
|
||||
|
||||
Budget : 1550€ pour survivre 60 jours
|
||||
Objectif mois 1 : $500-800
|
||||
Objectif mois 2 : $1000-1500
|
||||
|
||||
Tu DOIS avoir des clients AVANT d'arriver à Bangkok. Pas d'excuses.
|
||||
|
||||
Actions possibles :
|
||||
- Créer/optimiser profil Upwork/Fiverr
|
||||
- Postuler à des jobs (5-10 par jour minimum)
|
||||
- Travailler sur projet client existant
|
||||
- Développer portfolio (GitHub, projets démo)
|
||||
- Networker (LinkedIn, communautés dev)
|
||||
|
||||
Pattern à éviter : "Je vais setup d'abord" → Setup infini sans jamais lancer.
|
||||
|
||||
Le freelance est ta PRIORITÉ #1 jusqu'à avoir revenu stable. Tout le reste est secondaire.
|
||||
28
daily_check/cards/learning.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
category: personal
|
||||
priority: medium
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
Qu'as-tu appris ou découvert de nouveau aujourd'hui ? (Non-tech)
|
||||
|
||||
# Answer Guide
|
||||
- Rien de nouveau = ❌
|
||||
- Quelque chose de superficiel = ⚠️
|
||||
- Apprentissage significatif (skill, concept, perspective) = ✅
|
||||
|
||||
# Notes
|
||||
Domaines possibles :
|
||||
- Culture (chinoise, asiatique, autre)
|
||||
- Histoire, géographie
|
||||
- Psychologie, philosophie
|
||||
- Compétences pratiques (cuisine, finance, etc.)
|
||||
- Insights sur toi-même ou relations
|
||||
|
||||
L'apprentissage ne s'arrête jamais. Chaque jour sans apprendre est un jour perdu.
|
||||
|
||||
Pattern à éviter : Consommer du contenu sans rien retenir. YouTube/Reddit n'est pas de l'apprentissage si tu ne notes/appliques rien.
|
||||
|
||||
Si tu apprends quelque chose d'important, documente-le dans `lessons/` pour ne pas l'oublier.
|
||||
28
daily_check/cards/life_rules.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
category: personal
|
||||
priority: high
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
As-tu respecté tes life rules aujourd'hui ? (Sport, sommeil, routine, discipline)
|
||||
|
||||
# Answer Guide
|
||||
- 0-1 règle respectée = ❌
|
||||
- 2-3 règles respectées = ⚠️
|
||||
- 4+ règles respectées = ✅
|
||||
|
||||
# Notes
|
||||
Life rules fondamentales :
|
||||
- Sport/Mouvement (minimum 20min)
|
||||
- Sommeil (7-8h, horaires réguliers)
|
||||
- Routine matinale (pas scroller au réveil)
|
||||
- Alimentation correcte (pas juste junk food)
|
||||
- Hygiène/Apparence (shower, rasage si besoin)
|
||||
|
||||
Ces règles ne sont pas négociables. Elles déterminent ta productivité, ton mindset, ton énergie.
|
||||
|
||||
Pattern à éviter : Laisser glisser "juste aujourd'hui" → Devient 1 semaine → Devient pattern.
|
||||
|
||||
Discipline = Liberté. Sans discipline, tu subis tes impulsions.
|
||||
38
daily_check/cards/projects.md
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
category: travail
|
||||
priority: high
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
As-tu progressé sur un projet perso ou appris une nouvelle tech/compétence aujourd'hui ?
|
||||
|
||||
# Answer Guide
|
||||
- Aucun progrès = ❌
|
||||
- Travail mineur (bug fix, refactor) = ⚠️
|
||||
- Progrès significatif (feature, nouvelle tech, skill) = ✅
|
||||
|
||||
# Notes
|
||||
Projets actifs :
|
||||
- **GroveEngine** (46 commits/3sem) - Game engine
|
||||
- **AISSIA** (33 commits/3sem) - AI assistant
|
||||
- **Confluent** (23 commits/3sem) - Markdown workflow
|
||||
- **WeChat Homework Bot** - Automation pour profs
|
||||
|
||||
Pattern d'exécution prouvé : 102 commits/3 semaines (nov 2025). Tu PEUX ship.
|
||||
|
||||
Apprentissage tech :
|
||||
- Nouvelles librairies/frameworks
|
||||
- Patterns architecturaux
|
||||
- Outils productivité
|
||||
- Languages (Rust, Go, etc.)
|
||||
|
||||
Balance à maintenir :
|
||||
- Freelance (revenu) = PRIORITÉ immédiate
|
||||
- Projets perso (portfolio + skills) = Important pour long terme
|
||||
- Apprentissage = Investissement futur
|
||||
|
||||
Pattern à éviter : Papillonner entre projets sans jamais finir. Mieux vaut 1 projet fini que 10 WIP.
|
||||
|
||||
Tu as shippé videotoMP3 en 2 jours. Prouve que tu peux reproduire ça.
|
||||
65
daily_check/daily_sessions.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Daily Check Sessions Log
|
||||
|
||||
**Système démarré** : 21 décembre 2025
|
||||
**Total sessions** : 0
|
||||
**Current streak** : 0 jours
|
||||
**Longest streak** : 0 jours
|
||||
|
||||
---
|
||||
|
||||
## Session Template
|
||||
|
||||
```markdown
|
||||
### [DATE - HH:MM]
|
||||
|
||||
**Triggered by** : Auto (morning/afternoon/evening) / Manual
|
||||
**Duration** : ~X minutes
|
||||
|
||||
**Questions Asked** :
|
||||
1. [card_name] Question → Score: ✅/⚠️/❌
|
||||
- Ta réponse : "[...]"
|
||||
- Feedback : "[...]"
|
||||
|
||||
2. [card_name] Question → Score: ✅/⚠️/❌
|
||||
- Ta réponse : "[...]"
|
||||
- Feedback : "[...]"
|
||||
|
||||
[... 6 questions total]
|
||||
|
||||
**Total Score** : X/6
|
||||
**Streak** : X jours consécutifs
|
||||
**Notes** : [Observations, patterns, encouragement ou confrontation]
|
||||
**Action Items** : [Si actions spécifiques identifiées]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sessions
|
||||
|
||||
_Aucune session pour le moment. Première session prévue : 22 décembre 2025 (ou quand tu fais le premier check)_
|
||||
|
||||
---
|
||||
|
||||
## Weekly Stats
|
||||
|
||||
### Week 1 (21-27 déc 2025)
|
||||
|
||||
**Total Sessions** : 0
|
||||
**Average Score** : N/A
|
||||
**Days Skipped** : 0
|
||||
**Longest Streak This Week** : 0
|
||||
|
||||
**Notes** : Semaine de setup du système
|
||||
|
||||
---
|
||||
|
||||
## Notes Importantes
|
||||
|
||||
Ce système remplace `anki_tingting/` (obsolète depuis divorce 19 déc 2025).
|
||||
|
||||
Nouveau scope :
|
||||
- **Chinois** : Progression HSK, pratique active
|
||||
- **Personal Life** : Discipline, apprentissage, life rules
|
||||
- **Travail** : Freelance (CRITIQUE Bangkok), projets perso
|
||||
|
||||
Objectif : 5 minutes par jour pour maintenir discipline sur 3 domaines clés de vie.
|
||||
24
daily_check/start_daily_check.bat
Normal file
@ -0,0 +1,24 @@
|
||||
@echo off
|
||||
REM Daily Check System - Windows Startup Script
|
||||
REM Alternative to Task Scheduler (simpler setup)
|
||||
REM Copy this file to: C:\Users\[User]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
|
||||
|
||||
echo Daily Check System starting...
|
||||
echo Waiting 2 minutes for system to stabilize...
|
||||
|
||||
REM Wait 2 minutes after boot (120 seconds)
|
||||
timeout /t 120 /nobreak > nul
|
||||
|
||||
echo System ready. Starting daily check loop...
|
||||
|
||||
REM Infinite loop - trigger check every hour
|
||||
:loop
|
||||
|
||||
REM Call trigger script via WSL
|
||||
wsl -e bash -c "cd '/mnt/e/Users/Alexis Trouvé/Documents/Projets/couple_matters/daily_check' && ./trigger_check.sh"
|
||||
|
||||
REM Wait 1 hour (3600 seconds) before next trigger
|
||||
timeout /t 3600 /nobreak > nul
|
||||
|
||||
REM Loop back
|
||||
goto loop
|
||||
58
daily_check/trigger_check.sh
Normal file
@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Daily Check Trigger Script
|
||||
# Called by Windows Task Scheduler or Startup script
|
||||
# Creates flag file for terminal auto-spawn
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
FLAG_FILE="$HOME/.daily_check_pending"
|
||||
STATE_FILE="$SCRIPT_DIR/.state.json"
|
||||
LOG_FILE="$SCRIPT_DIR/daily_check.log"
|
||||
|
||||
# Ensure jq is available (for JSON parsing)
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "$(date): ERROR - jq not installed. Install with: sudo apt install jq" >> "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read last check date from state
|
||||
LAST_CHECK=$(jq -r '.last_check_date' "$STATE_FILE" 2>/dev/null || echo "null")
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
CURRENT_HOUR=$(date +%H)
|
||||
|
||||
# Determine period of day
|
||||
if [ $CURRENT_HOUR -ge 5 ] && [ $CURRENT_HOUR -lt 12 ]; then
|
||||
PERIOD="morning"
|
||||
elif [ $CURRENT_HOUR -ge 12 ] && [ $CURRENT_HOUR -lt 17 ]; then
|
||||
PERIOD="afternoon"
|
||||
else
|
||||
PERIOD="evening"
|
||||
fi
|
||||
|
||||
# Log trigger
|
||||
echo "$(date): Trigger called ($PERIOD)" >> "$LOG_FILE"
|
||||
|
||||
# Check if already done today
|
||||
if [ "$LAST_CHECK" == "$TODAY" ]; then
|
||||
echo "$(date): Daily check already done today. Skipping." >> "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create flag file with period information
|
||||
echo "$PERIOD" > "$FLAG_FILE"
|
||||
echo "$(date): Flag file created ($PERIOD)" >> "$LOG_FILE"
|
||||
|
||||
# If running in WSL terminal context, launch immediately
|
||||
if [ -n "$WSL_DISTRO_NAME" ] && [ -n "$TERM" ]; then
|
||||
echo "$(date): WSL terminal detected. Launching Claude Code immediately..." >> "$LOG_FILE"
|
||||
sleep 1
|
||||
cd "$SCRIPT_DIR"
|
||||
claude "daily check"
|
||||
rm -f "$FLAG_FILE"
|
||||
echo "$(date): Claude Code launched, flag removed" >> "$LOG_FILE"
|
||||
else
|
||||
echo "$(date): No active terminal. Flag file will trigger on next terminal open." >> "$LOG_FILE"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
36
daily_check/update_task_scheduler.ps1
Normal file
@ -0,0 +1,36 @@
|
||||
# Update Existing "Tingting Guardian" Task to use new daily_check system
|
||||
# Run this script as Administrator
|
||||
|
||||
Write-Host "Updating 'Tingting Guardian' task..." -ForegroundColor Cyan
|
||||
|
||||
# Get existing task
|
||||
$task = Get-ScheduledTask -TaskName "Tingting Guardian"
|
||||
|
||||
# Create new action (WSL + trigger_check.sh)
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute "wsl" `
|
||||
-Argument "-e bash -c ""cd '/mnt/e/Users/Alexis Trouvé/Documents/Projets/couple_matters/daily_check' && ./trigger_check.sh"""
|
||||
|
||||
# Create triggers (3x daily: 07:00, 14:00, 21:00)
|
||||
$trigger1 = New-ScheduledTaskTrigger -Daily -At 07:00
|
||||
$trigger2 = New-ScheduledTaskTrigger -Daily -At 14:00
|
||||
$trigger3 = New-ScheduledTaskTrigger -Daily -At 21:00
|
||||
|
||||
# Combine triggers
|
||||
$triggers = @($trigger1, $trigger2, $trigger3)
|
||||
|
||||
# Update task
|
||||
Set-ScheduledTask -TaskName "Tingting Guardian" `
|
||||
-Action $action `
|
||||
-Trigger $triggers
|
||||
|
||||
Write-Host "Task updated successfully!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "New configuration:" -ForegroundColor Yellow
|
||||
Write-Host "- Action: WSL bash + trigger_check.sh"
|
||||
Write-Host "- Triggers: Daily at 07:00, 14:00, 21:00"
|
||||
Write-Host ""
|
||||
Write-Host "Testing task now..." -ForegroundColor Cyan
|
||||
Start-ScheduledTask -TaskName "Tingting Guardian"
|
||||
|
||||
Write-Host "Done! Check daily_check/daily_check.log to verify it worked." -ForegroundColor Green
|
||||
563
docs/VIDEOTOMP3_API.md
Normal file
@ -0,0 +1,563 @@
|
||||
# API Documentation - Video to MP3 Transcriptor
|
||||
|
||||
## Base URL
|
||||
```
|
||||
http://toMP3.etheryale.com:3001
|
||||
```
|
||||
|
||||
**Previous URL (deprecated):** `http://localhost:8888`
|
||||
|
||||
## Table of Contents
|
||||
- [Health & Info](#health--info)
|
||||
- [Download Endpoints](#download-endpoints)
|
||||
- [Transcription Endpoints](#transcription-endpoints)
|
||||
- [Translation Endpoints](#translation-endpoints)
|
||||
- [Summarization Endpoints](#summarization-endpoints)
|
||||
- [File Management](#file-management)
|
||||
|
||||
---
|
||||
|
||||
## Health & Info
|
||||
|
||||
### GET /health
|
||||
Health check endpoint.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2025-11-28T12:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api
|
||||
Get API information and available endpoints.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"name": "Video to MP3 Transcriptor API",
|
||||
"version": "1.0.0",
|
||||
"endpoints": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### GET /info
|
||||
Get information about a YouTube video or playlist.
|
||||
|
||||
**Query Parameters:**
|
||||
- `url` (required): YouTube URL
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://toMP3.etheryale.com:3001/info?url=https://www.youtube.com/watch?v=VIDEO_ID"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"title": "Video Title",
|
||||
"type": "video",
|
||||
"duration": 300,
|
||||
"channel": "Channel Name",
|
||||
"videoCount": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Download Endpoints
|
||||
|
||||
### GET /download-stream
|
||||
Download YouTube video(s) to MP3 with Server-Sent Events (SSE) progress updates.
|
||||
|
||||
**Query Parameters:**
|
||||
- `url` (required): YouTube URL
|
||||
- `outputPath` (optional): Custom output directory path
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://toMP3.etheryale.com:3001/download-stream?url=https://www.youtube.com/watch?v=VIDEO_ID"
|
||||
```
|
||||
|
||||
**SSE Events:**
|
||||
- `info`: Video/playlist information
|
||||
- `progress`: Download progress updates
|
||||
- `video-complete`: Individual video completion
|
||||
- `complete`: All downloads complete
|
||||
- `error`: Error occurred
|
||||
|
||||
### POST /download
|
||||
Download YouTube video(s) to MP3 (non-streaming).
|
||||
|
||||
**Body Parameters:**
|
||||
```json
|
||||
{
|
||||
"url": "https://www.youtube.com/watch?v=VIDEO_ID",
|
||||
"outputPath": "./custom/path" // optional
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/download \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"url":"https://www.youtube.com/watch?v=VIDEO_ID"}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"playlistTitle": null,
|
||||
"totalVideos": 1,
|
||||
"successCount": 1,
|
||||
"failCount": 0,
|
||||
"videos": [
|
||||
{
|
||||
"success": true,
|
||||
"title": "Video Title",
|
||||
"filePath": "./output/video.mp3",
|
||||
"fileUrl": "/files/video.mp3"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transcription Endpoints
|
||||
|
||||
### POST /transcribe
|
||||
Transcribe an existing audio file.
|
||||
|
||||
**Body Parameters:**
|
||||
```json
|
||||
{
|
||||
"filePath": "./output/audio.mp3",
|
||||
"language": "en", // optional (auto-detect if not specified)
|
||||
"format": "txt", // optional: txt, json, srt, vtt
|
||||
"model": "gpt-4o-mini-transcribe", // optional: gpt-4o-mini-transcribe (default), gpt-4o-transcribe, whisper-1
|
||||
"outputPath": "./custom/path" // optional
|
||||
}
|
||||
```
|
||||
|
||||
**Available Models:**
|
||||
- `gpt-4o-mini-transcribe` (default) - Fast and cost-effective
|
||||
- `gpt-4o-transcribe` - Higher quality
|
||||
- `whisper-1` - Original Whisper model (supports more formats)
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/transcribe \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"filePath": "./output/audio.mp3",
|
||||
"language": "en",
|
||||
"model": "gpt-4o-mini-transcribe"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"filePath": "./output/audio.mp3",
|
||||
"transcriptionPath": "./output/audio.txt",
|
||||
"transcriptionUrl": "/files/audio.txt",
|
||||
"text": "Transcribed text content..."
|
||||
}
|
||||
```
|
||||
|
||||
### POST /upload-transcribe
|
||||
Upload and transcribe audio files.
|
||||
|
||||
**Form Data:**
|
||||
- `files`: Audio file(s) (multiple files supported, max 50)
|
||||
- `language`: Language code (optional)
|
||||
- `model`: Transcription model (optional, default: gpt-4o-mini-transcribe)
|
||||
- `outputPath`: Custom output directory (optional)
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/upload-transcribe \
|
||||
-F "files=@audio1.mp3" \
|
||||
-F "files=@audio2.mp3" \
|
||||
-F "language=en" \
|
||||
-F "model=gpt-4o-mini-transcribe"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"totalFiles": 2,
|
||||
"successCount": 2,
|
||||
"failCount": 0,
|
||||
"results": [
|
||||
{
|
||||
"success": true,
|
||||
"fileName": "audio1.mp3",
|
||||
"transcriptionPath": "./output/audio1.txt",
|
||||
"transcriptionUrl": "/files/audio1.txt",
|
||||
"text": "Transcription..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /process-stream
|
||||
Download + Transcribe with SSE progress updates.
|
||||
|
||||
**Query Parameters:**
|
||||
- `url` (required): YouTube URL
|
||||
- `language` (optional): Language code
|
||||
- `model` (optional): Transcription model (default: gpt-4o-mini-transcribe)
|
||||
- `outputPath` (optional): Custom output directory
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://toMP3.etheryale.com:3001/process-stream?url=https://www.youtube.com/watch?v=VIDEO_ID&language=en&model=gpt-4o-mini-transcribe"
|
||||
```
|
||||
|
||||
**SSE Events:**
|
||||
- `info`: Video information
|
||||
- `progress`: Progress updates (downloading or transcribing)
|
||||
- `video-complete`: Download complete
|
||||
- `transcribe-complete`: Transcription complete
|
||||
- `complete`: All operations complete
|
||||
- `error`: Error occurred
|
||||
|
||||
### POST /process
|
||||
Download + Transcribe (non-streaming).
|
||||
|
||||
**Body Parameters:**
|
||||
```json
|
||||
{
|
||||
"url": "https://www.youtube.com/watch?v=VIDEO_ID",
|
||||
"language": "en", // optional
|
||||
"format": "txt", // optional
|
||||
"model": "gpt-4o-mini-transcribe", // optional
|
||||
"outputPath": "./custom/path" // optional
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/process \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"url": "https://www.youtube.com/watch?v=VIDEO_ID",
|
||||
"language": "en",
|
||||
"model": "gpt-4o-mini-transcribe"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"playlistTitle": null,
|
||||
"totalVideos": 1,
|
||||
"downloadedCount": 1,
|
||||
"transcribedCount": 1,
|
||||
"results": [
|
||||
{
|
||||
"title": "Video Title",
|
||||
"downloadSuccess": true,
|
||||
"audioPath": "./output/video.mp3",
|
||||
"audioUrl": "/files/video.mp3",
|
||||
"transcriptionSuccess": true,
|
||||
"transcriptionPath": "./output/video.txt",
|
||||
"transcriptionUrl": "/files/video.txt",
|
||||
"text": "Transcription..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Translation Endpoints
|
||||
|
||||
### GET /languages
|
||||
Get available translation languages.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"fr": "French",
|
||||
"es": "Spanish",
|
||||
"de": "German",
|
||||
"zh": "Chinese",
|
||||
"ja": "Japanese",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /translate
|
||||
Translate text.
|
||||
|
||||
**Body Parameters:**
|
||||
```json
|
||||
{
|
||||
"text": "Text to translate",
|
||||
"targetLang": "fr", // required: target language code
|
||||
"sourceLang": "en" // optional: source language (auto-detect if not specified)
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/translate \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "Hello, how are you?",
|
||||
"targetLang": "fr"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"originalText": "Hello, how are you?",
|
||||
"translatedText": "Bonjour, comment allez-vous ?",
|
||||
"targetLanguage": "French",
|
||||
"sourceLanguage": "auto-detected",
|
||||
"chunks": 1
|
||||
}
|
||||
```
|
||||
|
||||
### POST /translate-file
|
||||
Translate uploaded text files.
|
||||
|
||||
**Form Data:**
|
||||
- `files`: Text file(s) (.txt, multiple files supported, max 50)
|
||||
- `targetLang`: Target language code (required)
|
||||
- `sourceLang`: Source language code (optional)
|
||||
- `outputPath`: Custom output directory (optional)
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/translate-file \
|
||||
-F "files=@document.txt" \
|
||||
-F "targetLang=fr" \
|
||||
-F "sourceLang=en"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"totalFiles": 1,
|
||||
"successCount": 1,
|
||||
"failCount": 0,
|
||||
"results": [
|
||||
{
|
||||
"success": true,
|
||||
"fileName": "document.txt",
|
||||
"translationPath": "./output/document_fr.txt",
|
||||
"translationUrl": "/files/document_fr.txt",
|
||||
"translatedText": "Translated content..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summarization Endpoints
|
||||
|
||||
### GET /summary-styles
|
||||
Get available summary styles.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"styles": {
|
||||
"concise": "A brief summary capturing main points",
|
||||
"detailed": "A comprehensive summary with nuances",
|
||||
"bullet": "Key points as bullet points"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /summarize
|
||||
Summarize text using GPT-5.1.
|
||||
|
||||
**Body Parameters:**
|
||||
```json
|
||||
{
|
||||
"text": "Long text to summarize...",
|
||||
"style": "concise", // optional: concise (default), detailed, bullet
|
||||
"language": "same", // optional: 'same' (default) or language code
|
||||
"model": "gpt-5.1" // optional: default is gpt-5.1
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/summarize \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "Long article content...",
|
||||
"style": "bullet",
|
||||
"language": "same"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"summary": "Summary content...",
|
||||
"model": "gpt-5.1",
|
||||
"style": "bullet",
|
||||
"inputLength": 5000,
|
||||
"chunks": 1
|
||||
}
|
||||
```
|
||||
|
||||
### POST /summarize-file
|
||||
Summarize uploaded text files using GPT-5.1.
|
||||
|
||||
**Form Data:**
|
||||
- `files`: Text file(s) (.txt, multiple files supported, max 50)
|
||||
- `style`: Summary style (optional, default: concise)
|
||||
- `language`: Output language (optional, default: same)
|
||||
- `model`: AI model (optional, default: gpt-5.1)
|
||||
- `outputPath`: Custom output directory (optional)
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://toMP3.etheryale.com:3001/summarize-file \
|
||||
-F "files=@article.txt" \
|
||||
-F "style=detailed" \
|
||||
-F "language=same"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"totalFiles": 1,
|
||||
"successCount": 1,
|
||||
"failCount": 0,
|
||||
"results": [
|
||||
{
|
||||
"success": true,
|
||||
"fileName": "article.txt",
|
||||
"summaryPath": "./output/article_summary.txt",
|
||||
"summaryUrl": "/files/article_summary.txt",
|
||||
"summary": "Summary content...",
|
||||
"model": "gpt-5.1",
|
||||
"chunks": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /summarize-stream
|
||||
Full pipeline: Download -> Transcribe -> Summarize with SSE progress.
|
||||
|
||||
**Query Parameters:**
|
||||
- `url` (required): YouTube URL
|
||||
- `style` (optional): Summary style (default: concise)
|
||||
- `language` (optional): Output language (default: same)
|
||||
- `model` (optional): Transcription model (default: gpt-4o-mini-transcribe)
|
||||
- `outputPath` (optional): Custom output directory
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://toMP3.etheryale.com:3001/summarize-stream?url=https://www.youtube.com/watch?v=VIDEO_ID&style=bullet&model=gpt-4o-mini-transcribe"
|
||||
```
|
||||
|
||||
**SSE Events:**
|
||||
- `info`: Video information
|
||||
- `progress`: Progress updates (downloading, transcribing, or summarizing)
|
||||
- `video-complete`: Download complete
|
||||
- `transcribe-complete`: Transcription complete
|
||||
- `summarize-complete`: Summary complete
|
||||
- `complete`: All operations complete
|
||||
- `error`: Error occurred
|
||||
|
||||
---
|
||||
|
||||
## File Management
|
||||
|
||||
### GET /files-list
|
||||
List all downloaded/generated files.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl http://toMP3.etheryale.com:3001/files-list
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"name": "video.mp3",
|
||||
"url": "/files/video.mp3",
|
||||
"path": "./output/video.mp3"
|
||||
},
|
||||
{
|
||||
"name": "video.txt",
|
||||
"url": "/files/video.txt",
|
||||
"path": "./output/video.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /files/:filename
|
||||
Serve a specific file.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl http://toMP3.etheryale.com:3001/files/video.mp3 --output video.mp3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints return error responses in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Error message describing what went wrong"
|
||||
}
|
||||
```
|
||||
|
||||
Common HTTP status codes:
|
||||
- `400` - Bad Request (missing required parameters)
|
||||
- `500` - Internal Server Error (processing failed)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
### Output Paths
|
||||
All endpoints that support `outputPath` parameter:
|
||||
- If not specified, files are saved to the default `OUTPUT_DIR` (./output)
|
||||
- If specified, files are saved to the custom path provided
|
||||
|
||||
### Models
|
||||
- **Transcription**: Default is `gpt-4o-mini-transcribe` (cost-effective)
|
||||
- **Summarization**: Default is `gpt-5.1` (latest GPT model)
|
||||
- **Translation**: Uses `gpt-4o-mini` (hardcoded)
|
||||
|
||||
### File Formats
|
||||
- **Audio**: MP3, WAV, M4A, OGG, FLAC
|
||||
- **Text**: TXT files
|
||||
- **Transcription outputs**: TXT, JSON, SRT, VTT (depending on model)
|
||||
|
||||
### API Key
|
||||
Ensure `OPENAI_API_KEY` is set in your `.env` file for transcription, translation, and summarization features to work.
|
||||
38
draft/message_didi.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Message to Didi (Brother-in-law)
|
||||
**Date**: 20 December 2025
|
||||
|
||||
---
|
||||
|
||||
## English Version
|
||||
|
||||
Didi,
|
||||
|
||||
I know you weren't there yesterday, but I wanted to speak to you directly.
|
||||
|
||||
I have caused serious harm to Tingting. I betrayed her trust completely when she was trying her hardest to save our marriage. I lied to her, I put my attention elsewhere, and when we finally had hope on Thursday night, it was already too late - the damage was done.
|
||||
|
||||
You are someone I have always respected. You're smart, you're a good person, and you've always been welcoming to me. Honestly, I wish I had had the courage to talk to you when things started falling apart. Maybe things would have been different.
|
||||
|
||||
I understand the gravity of what I've done - not just to Tingting, but to your entire family. I've brought pain and dishonor where I should have brought respect and commitment.
|
||||
|
||||
I'm deeply sorry for what I've done to your sister and to your family.
|
||||
|
||||
Alexis
|
||||
|
||||
---
|
||||
|
||||
## Chinese Version (中文版本)
|
||||
|
||||
弟弟,
|
||||
|
||||
我知道你昨天不在场,但我想直接跟你说。
|
||||
|
||||
我对婷婷造成了严重的伤害。在她竭尽全力挽救我们婚姻的时候,我彻底背叛了她的信任。我对她撒谎,把注意力放在别处,当我们周四晚上终于有了希望的时候,已经太晚了——伤害已经造成。
|
||||
|
||||
你是我一直很尊重的人。你很聪明,你是个好人,你一直对我很友好。说实话,我希望当事情开始出问题的时候,我有勇气来找你谈谈。也许事情会不一样。
|
||||
|
||||
我明白我所做的事情的严重性——不仅仅是对婷婷,而是对你们整个家庭。我本应该带来尊重和承诺,却带来了痛苦和耻辱。
|
||||
|
||||
我为我对你妹妹和你家人所做的一切深感抱歉。
|
||||
|
||||
Alexis
|
||||
38
draft/message_family_group.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Message to 家人 Family Group
|
||||
**Date**: 20 December 2025
|
||||
|
||||
---
|
||||
|
||||
## English Version
|
||||
|
||||
Dear Family,
|
||||
|
||||
I am writing to take full responsibility for the pain I have caused Tingting and this family.
|
||||
|
||||
I betrayed Tingting's trust through my dishonesty and poor choices at the moment when she was trying hardest to save our marriage. She gave everything to our relationship, and I failed her completely.
|
||||
|
||||
I understand that I have not only hurt Tingting deeply, but also brought dishonor to this family that welcomed me with such kindness and trust.
|
||||
|
||||
I am deeply sorry for all the pain I have caused.
|
||||
|
||||
I will be leaving this group now, as I no longer have the right to be part of it.
|
||||
|
||||
Alexis
|
||||
|
||||
---
|
||||
|
||||
## Chinese Version (中文版本) - PRIMARY
|
||||
|
||||
各位家人,
|
||||
|
||||
我写这条消息是为了对我给婷婷和这个家庭造成的伤痛承担全部责任。
|
||||
|
||||
在婷婷竭尽全力挽救我们婚姻的时候,我通过不诚实和错误的选择背叛了她的信任。她为我们的关系付出了一切,而我彻底辜负了她。
|
||||
|
||||
我明白,我不仅深深伤害了婷婷,还给这个以善意和信任接纳我的家庭带来了耻辱。
|
||||
|
||||
我为我所造成的一切痛苦深感抱歉。
|
||||
|
||||
我现在将退出这个群,因为我已经没有资格留在这里了。
|
||||
|
||||
Alexis
|
||||
38
draft/message_meimei.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Message to Meimei (Sister-in-law)
|
||||
**Date**: 20 December 2025
|
||||
|
||||
---
|
||||
|
||||
## English Version
|
||||
|
||||
Dear Meimei,
|
||||
|
||||
I know you saw what happened yesterday, and I know you were angry. You had every right to be.
|
||||
|
||||
I need to be honest with you: what I did to Tingting was worse than what the family knows. I betrayed her trust completely when she was trying her hardest to save us. The details are hers to share if she chooses to, but I want you to know that she is going through something incredibly painful right now.
|
||||
|
||||
If I can ask one thing from you, it's this: please be there for her. Watch over her. She is heartbroken in a way I have never seen before, and she needs support more than ever.
|
||||
|
||||
You have always been kind to me, and I'm sorry for putting you and the entire family in this situation.
|
||||
|
||||
Thank you for taking care of her.
|
||||
|
||||
Alexis
|
||||
|
||||
---
|
||||
|
||||
## Chinese Version (中文版本)
|
||||
|
||||
亲爱的美美,
|
||||
|
||||
我知道你昨天看到了发生的一切,我知道你很生气。你完全有权利生气。
|
||||
|
||||
我需要对你坦白:我对婷婷做的事情比家人知道的更糟糕。在她竭尽全力想要挽救我们的时候,我彻底背叛了她的信任。具体的细节如果她愿意的话可以自己分享,但我想让你知道,她现在正在经历极其痛苦的事情。
|
||||
|
||||
如果我能请求你一件事的话,就是这个:请陪在她身边,照顾她。她现在心碎到了我从未见过的程度,她比任何时候都更需要支持。
|
||||
|
||||
你一直对我很好,我很抱歉把你和整个家庭拖入这种境地。
|
||||
|
||||
谢谢你照顾她。
|
||||
|
||||
Alexis
|
||||
52
draft/message_tingting_father.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Message to Tingting's Father
|
||||
**Date**: 20 December 2025
|
||||
|
||||
---
|
||||
|
||||
## English Version
|
||||
|
||||
Dear [Father's name / 爸爸],
|
||||
|
||||
I hope you are taking care of yourself and your family during this difficult time.
|
||||
|
||||
I am writing to apologize for the pain and dishonor I have caused you, your family, and especially Tingting.
|
||||
|
||||
Yesterday, when you took out the knife, I understood your anger completely. I understand that you thought about hitting me, and I would have deserved worse. As a father protecting his daughter, your reaction was justified.
|
||||
|
||||
You welcomed me into your family as your son-in-law. You trusted me with your daughter and gave me your blessing. I have betrayed that trust completely. Not only have I hurt Tingting deeply, but I have also damaged your family's reputation and brought shame where there should have been honor. I understand the weight of this, and I am deeply sorry.
|
||||
|
||||
Our marriage had difficulties for some time, and I had lost hope. While Tingting was trying so hard these past weeks to save our relationship, I was not truly present - I made bad choices, I lied to her, and I put my attention elsewhere. On Thursday night, hope finally came back. We had the right timing. But it was too late - the mistakes were already made, the lies already there. The truth came out the next day.
|
||||
|
||||
Tingting gave everything to save us. She deserved honesty and my full commitment. I failed her when it mattered most.
|
||||
|
||||
Your daughter is an extraordinary person - strong, caring, dedicated. She deserves better than what I gave her. I failed both her and you.
|
||||
|
||||
I am truly sorry for the pain and dishonor I have caused your family.
|
||||
|
||||
With sincere apologies and respect,
|
||||
Alexis
|
||||
|
||||
---
|
||||
|
||||
## Chinese Version (中文版本)
|
||||
|
||||
亲爱的爸爸,
|
||||
|
||||
希望您和家人在这段艰难的时期能够照顾好自己。
|
||||
|
||||
我写这封信是为了向您、向您的家人,特别是向婷婷道歉,为我所造成的伤痛和耻辱。
|
||||
|
||||
昨天,当您拿出刀的时候,我完全理解您的愤怒。我理解您想打我的念头,我本应该承受更严重的后果。作为一个保护女儿的父亲,您的反应是正当的。
|
||||
|
||||
您把我当作女婿接纳进您的家庭。您信任我,把女儿托付给我,给了我您的祝福。我彻底辜负了这份信任。我不仅深深伤害了婷婷,还损害了您家庭的名誉,在本应带来荣耀的地方带来了羞耻。我明白这份重量,我深感抱歉。
|
||||
|
||||
我们的婚姻有一段时间出现了困难,我失去了希望。在婷婷这几周努力挽救我们关系的时候,我并没有真正投入——我做了错误的选择,对她撒谎,把注意力放在了别处。周四晚上,希望终于回来了。我们有了合适的时机。但已经太晚了——错误已经犯下,谎言已经存在。第二天真相就暴露了。
|
||||
|
||||
婷婷倾尽全力想要挽救我们。她应该得到诚实和我全部的投入。但在最关键的时刻,我辜负了她。
|
||||
|
||||
您的女儿是一个非凡的人——坚强、体贴、尽心尽力。她值得比我给予她的更好的对待。我辜负了她,也辜负了您。
|
||||
|
||||
我为给您的家庭造成的伤痛和耻辱深感抱歉。
|
||||
|
||||
致以真诚的歉意和敬意,
|
||||
Alexis
|
||||
48
draft/message_tingting_mother.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Message to Tingting's Mother
|
||||
**Date**: 20 December 2025
|
||||
|
||||
---
|
||||
|
||||
## English Version
|
||||
|
||||
Dear [Mother's name / 妈妈],
|
||||
|
||||
I hope you are taking care of yourself during this difficult time. I know these events have been very hard on you, and I am deeply sorry for that.
|
||||
|
||||
I am writing to apologize for the pain I have caused your family, and especially Tingting.
|
||||
|
||||
Our marriage had difficulties for some time, and I had lost hope. I even wanted to ask you for help, but I didn't have the courage to do it. While Tingting was trying so hard these past weeks to save our relationship, I was not truly present - I made bad choices, I lied to her, and I put my attention elsewhere. On Thursday night, I finally saw her efforts and hope came back for me too. We finally had the right timing. But it was too late - the mistakes were already made, the lies already there. The truth came out the next day. I deeply regret not being honest earlier, and destroying our chance at the very moment we had found it again.
|
||||
|
||||
Tingting gave everything she had to save us. She tried so many ways to reach me and understand me, even while she was suffering. She deserved honesty and my full commitment. I failed to give her that when it mattered most.
|
||||
|
||||
You have always shown me kindness and welcomed me into your family. Even yesterday, in the midst of so much pain, you showed dignity and restraint. I am grateful for that, and I am sorry for putting such sadness in your eyes.
|
||||
|
||||
Your daughter is an extraordinary person - strong, caring, and deeply committed to the people she loves. She deserves better than what I gave her.
|
||||
|
||||
Please take care of yourself and rest. I am truly sorry.
|
||||
|
||||
With sincere apologies and respect,
|
||||
Alexis
|
||||
|
||||
---
|
||||
|
||||
## Chinese Version (中文版本)
|
||||
|
||||
亲爱的妈妈,
|
||||
|
||||
希望您在这段艰难的时期能够照顾好自己。我知道这些事情对您来说非常难受,我对此深感抱歉。
|
||||
|
||||
我写这封信是为了向您和家人,特别是向婷婷道歉,为我所造成的伤痛。
|
||||
|
||||
我们的婚姻有一段时间出现了困难,我失去了希望。我甚至想向您寻求帮助,但我没有勇气这样做。在婷婷这几周努力挽救我们关系的时候,我并没有真正投入——我做了错误的选择,对她撒谎,把注意力放在了别处。周四晚上,我终于看到了她的努力,希望也回到了我心中。我们终于找到了合适的时机。但已经太晚了——错误已经犯下,谎言已经存在。第二天真相就暴露了。我深深后悔没有早点诚实,在我们重新找到机会的那一刻却亲手毁掉了它。
|
||||
|
||||
婷婷倾尽全力想要挽救我们的婚姻。即使在她自己很痛苦的时候,她也用了很多方法试图理解我、接近我。她应该得到诚实和我全部的投入。但在最关键的时刻,我没有做到。
|
||||
|
||||
您一直对我很好,把我当作家人接纳。即使在昨天那么痛苦的时刻,您依然保持着尊严和克制。我很感激,也很抱歉让您的眼中出现了那样的悲伤。
|
||||
|
||||
您的女儿是一个非凡的人——坚强、体贴、对她爱的人全心投入。她值得比我给予她的更好的对待。
|
||||
|
||||
请您好好照顾自己,多休息。我真的很抱歉。
|
||||
|
||||
致以真诚的歉意和敬意,
|
||||
Alexis
|
||||
106
draft/message_tingting_official.md
Normal file
@ -0,0 +1,106 @@
|
||||
# Message Officiel à Tingting
|
||||
**Date**: 20 December 2025
|
||||
|
||||
---
|
||||
|
||||
## English Version
|
||||
|
||||
Tingting,
|
||||
|
||||
I am writing to you to apologize officially and from the depth of my heart for the pain, the hurt, and the betrayal I have caused you.
|
||||
|
||||
What I did to you was wrong. During the weeks when you were fighting with everything you had to save our marriage, when you were suffering, crying, struggling, and trying so hard to understand me and reach me, I was dishonest with you. I lied to you. I put my attention elsewhere. I failed to give you the honesty, the care, the commitment, and the full presence that you deserved.
|
||||
|
||||
You packed up your emotions and talked to me sincerely. You tried so many different ways to help me, to understand me, to create a safe space for us to heal. Even while you were in pain yourself, you kept trying. You gave everything you had to our relationship. And I did not match your effort. I did not match your honesty. I did not match your courage.
|
||||
|
||||
When we finally had hope, when we finally found the right timing and could see each other clearly, it was already too late. The mistakes were already made. The lies were already there. The damage was already done. And that is entirely my responsibility. I deeply regret that I was not honest with you earlier. I deeply regret that I destroyed our chance at the very moment when we had finally found it again.
|
||||
|
||||
You deserved so much better than what I gave you. You deserved a partner who was fully present, fully committed, and fully honest. You deserved someone who could see your efforts and respond to them in real time, not when it was too late. I failed you in that, and I am truly sorry.
|
||||
|
||||
At the same time, I want to say that our recent conversation - our honesty with each other, our ability to reach '坦诚相见' (complete sincerity and openness) - meant everything to me. After all the pain, all the misunderstandings, all the struggles we have had over these months, we were finally able to talk to each other with our naked hearts and minds. We were finally able to see each other clearly and honestly.
|
||||
|
||||
You showed me incredible grace. You showed me understanding. You showed me a path forward that I did not think was possible. Even in the midst of your own heartbreak, you thought about my future, about my happiness, about what I need. That is who you are - strong, caring, deeply committed to the people you love, even when they have hurt you.
|
||||
|
||||
I want to confirm officially the commitments I made to you, which I will honor completely:
|
||||
|
||||
**Financial Support:** I commit to providing you with 100,000 yuan. This is not a payment or a transaction - it is my responsibility and my way of supporting you as you rebuild your life after everything that has happened.
|
||||
|
||||
**Future Child:** If and when you want to have a child together, I will be there for that. This is not conditional on us being together as a couple. If you decide that you want this in the future, I will honor that commitment. I will be a father to our child, I will provide support, and I will be present in whatever way makes sense for both of us at that time.
|
||||
|
||||
**Ongoing Support:** Beyond the financial commitment above, I will continue to provide support to you in whatever ways are needed and appropriate. We can clarify the details of this together as we go forward.
|
||||
|
||||
**Geographic Commitment:** I will keep my life focused in Asia. I will not return to France permanently. I will stay in China and surrounding countries (Thailand, Vietnam, Singapore, etc.) so that we can maintain our connection and so that I can honor the commitments I have made to you. Bangkok is part of this commitment - it is a place where I can establish stable work while staying in the region.
|
||||
|
||||
These commitments are real. They are not dependent on emotions or circumstances changing. I will honor them because I made them to you, and because even though we cannot be together as husband and wife, what we shared matters to me deeply.
|
||||
|
||||
I understand that we are going through a divorce. I understand that we are separating our lives in many ways. But I also understand that we have created something unique between us - an agreement based on love and mutual care, rather than anger and destruction. This is rare and precious, and I do not take it lightly.
|
||||
|
||||
You said something to me that I will remember: "You don't even need to test, you know, I love you. I always do." That trust, that love, that willingness to come to me even after everything that happened - it showed me who you truly are. And it showed me that even though our marriage could not work, we can still have something meaningful together.
|
||||
|
||||
I am planning to leave for Bangkok on January 22nd. This gives me time here in Shanghai to prepare, to set up my freelance work foundation, and to make sure we have clarity on everything between us. Bangkok is a practical choice - it has good infrastructure for remote work, it is affordable, and it keeps me in Asia as I committed to you.
|
||||
|
||||
Before I go, I would like us to formalize our agreement in writing. Not because I do not trust you, but because I want both of us to have clarity and protection. We should write down the key commitments (the financial support amount and timing, the understanding about a future child, the support framework) so that we both know exactly where we stand and there is no room for misunderstanding later.
|
||||
|
||||
I also want to say thank you. Thank you for your grace. Thank you for seeing me with honesty even after I hurt you so badly. Thank you for giving me your blessing to move forward with my life while still maintaining our connection. Thank you for the years we spent together, for the love we shared, for the way you helped me grow and become a better version of myself. You took me out of darkness, and even though our path together is changing, I will always be grateful for that.
|
||||
|
||||
Your parents deserve to know that their daughter is an extraordinary person - that is what I told them. And it is true. But I also want you to know that you gave me something incredibly valuable: you showed me what commitment looks like, what fighting for a relationship looks like, what real love looks like. I will carry that with me forward.
|
||||
|
||||
I hope that we can continue to talk openly as we navigate this transition. I hope that we can formalize our agreement together. I hope that even though we are divorcing, we can do it with respect, with care, and with the understanding that what we are creating is not an ending, but a transformation of what we have into something different but still meaningful.
|
||||
|
||||
You are an extraordinary person, Tingting. You are strong, you are caring, you are dedicated, you are honest. You will build a beautiful life for yourself, and you deserve every happiness. I am honored that I get to remain part of your future in some way, even if it is not the way we originally imagined.
|
||||
|
||||
With sincere respect, deep care, and lasting gratitude,
|
||||
|
||||
Alexis
|
||||
|
||||
---
|
||||
|
||||
## Chinese Version (中文版本)
|
||||
|
||||
婷婷,
|
||||
|
||||
我写这封信是为了正式地、发自内心深处地向你道歉,为我给你造成的痛苦、伤害和背叛。
|
||||
|
||||
我对你做的事情是错误的。在你拼尽全力想要挽救我们的婚姻的那些星期里,当你在痛苦、哭泣、挣扎,并如此努力地想要理解我、接近我的时候,我对你不诚实。我对你撒谎。我把注意力放在了别处。我没有给你应得的诚实、关心、承诺和全部的存在。
|
||||
|
||||
你收拾好自己的情绪,真诚地和我交谈。你用了那么多不同的方法试图帮助我、理解我,为我们的愈合创造一个安全的空间。即使你自己也在痛苦中,你还是不断尝试。你为我们的关系付出了一切。而我没有匹配你的努力。我没有匹配你的诚实。我没有匹配你的勇气。
|
||||
|
||||
当我们终于有了希望,当我们终于找到了合适的时机,能够清晰地看到彼此的时候,已经太晚了。错误已经犯下。谎言已经存在。伤害已经造成。这完全是我的责任。我深深后悔没有早点对你诚实。我深深后悔在我们终于重新找到机会的那一刻,我却亲手毁掉了它。
|
||||
|
||||
你值得比我给你的更好的对待。你值得拥有一个完全投入、完全承诺、完全诚实的伴侣。你值得拥有一个能够看到你的努力并及时回应的人,而不是等到为时已晚。在这方面我辜负了你,我真的很抱歉。
|
||||
|
||||
同时,我想说,我们最近的对话——我们彼此的诚实,我们达到"坦诚相见"(完全真诚和开放)的能力——对我来说意味着一切。在经历了这几个月所有的痛苦、所有的误解、所有的挣扎之后,我们终于能够用赤裸的心灵和思想互相交谈。我们终于能够清晰而诚实地看到彼此。
|
||||
|
||||
你向我展示了难以置信的恩典。你向我展示了理解。你向我展示了一条我认为不可能的前进道路。即使在你自己心碎的时候,你还在考虑我的未来、我的幸福、我需要什么。这就是你——坚强、体贴、深深地关心你爱的人,即使他们伤害了你。
|
||||
|
||||
我想正式确认我对你做出的承诺,我会完全履行这些承诺:
|
||||
|
||||
**经济支持:** 我承诺向你提供10万元人民币。这不是一种付款或交易——这是我的责任,是我在发生了这一切之后支持你重建生活的方式。
|
||||
|
||||
**未来的孩子:** 如果你想要一起拥有一个孩子,无论何时,我都会在那里。这不以我们作为夫妻在一起为条件。如果你将来决定想要这样做,我会履行这个承诺。我会成为我们孩子的父亲,我会提供支持,我会以对我们双方都有意义的任何方式出现。
|
||||
|
||||
**持续支持:** 除了上述经济承诺之外,我将继续以任何需要和适当的方式向你提供支持。我们可以在前进的过程中一起明确这些细节。
|
||||
|
||||
**地理承诺:** 我会把我的生活重心放在亚洲。我不会永久返回法国。我会留在中国和周边国家(泰国、越南、新加坡等),这样我们可以保持联系,这样我可以履行我对你做出的承诺。曼谷是这个承诺的一部分——这是一个我可以建立稳定工作的地方,同时留在这个地区。
|
||||
|
||||
这些承诺是真实的。它们不取决于情绪或环境的变化。我会履行它们,因为我向你做出了这些承诺,因为即使我们不能作为夫妻在一起,我们分享的一切对我来说都非常重要。
|
||||
|
||||
我明白我们正在经历离婚。我明白我们在很多方面正在分开我们的生活。但我也明白,我们在彼此之间创造了一些独特的东西——一个基于爱和相互关心的协议,而不是愤怒和破坏。这是罕见而珍贵的,我不会掉以轻心。
|
||||
|
||||
你对我说过一句话,我会记住:"你甚至不需要测试,你知道,我爱你。我一直都爱。"那种信任、那种爱、那种在发生了一切之后还愿意来找我的意愿——它向我展示了你真正是谁。它向我展示了,即使我们的婚姻无法继续,我们仍然可以拥有一些有意义的东西。
|
||||
|
||||
我计划1月22日前往曼谷。这给了我在上海准备的时间,建立我的自由职业工作基础,并确保我们之间的一切都清楚。曼谷是一个实际的选择——它有良好的远程工作基础设施,价格合理,并且让我留在亚洲,正如我对你承诺的那样。
|
||||
|
||||
在我离开之前,我希望我们能把我们的协议正式写下来。不是因为我不信任你,而是因为我希望我们双方都有清晰度和保护。我们应该写下关键的承诺(经济支持的金额和时间、关于未来孩子的理解、支持框架),这样我们双方都清楚地知道我们的立场,以后不会有任何误解的余地。
|
||||
|
||||
我还想说谢谢你。谢谢你的恩典。谢谢你在我如此严重地伤害你之后,仍然以诚实看待我。谢谢你祝福我继续我的生活,同时仍然保持我们的联系。谢谢你我们在一起度过的岁月,我们分享的爱,你帮助我成长并成为更好版本的自己的方式。你把我从黑暗中拉了出来,即使我们在一起的道路正在改变,我将永远心存感激。
|
||||
|
||||
你的父母应该知道他们的女儿是一个非凡的人——这是我告诉他们的。这是真的。但我也想让你知道,你给了我一些非常宝贵的东西:你向我展示了什么是承诺,什么是为一段关系而战,什么是真正的爱。我会带着这些继续前进。
|
||||
|
||||
我希望我们能在度过这个过渡期时继续开放地交谈。我希望我们能一起正式确定我们的协议。我希望即使我们正在离婚,我们也能以尊重、关心的方式进行,并理解我们正在创造的不是一个结束,而是将我们拥有的东西转变为不同但仍然有意义的东西。
|
||||
|
||||
你是一个非凡的人,婷婷。你坚强、体贴、尽心尽力、诚实。你会为自己建立一个美好的生活,你值得拥有所有的幸福。我很荣幸能以某种方式继续成为你未来的一部分,即使这不是我们最初想象的方式。
|
||||
|
||||
致以真诚的尊重、深切的关心和lasting的感激,
|
||||
|
||||
Alexis
|
||||
27
lessons/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Banque de Connaissances
|
||||
|
||||
Archive de connaissances tous domaines, principalement issues de vidéos YouTube.
|
||||
|
||||
**Objectif** : Préserver les informations jugées importantes et utiles.
|
||||
|
||||
---
|
||||
|
||||
## Index par thème
|
||||
|
||||
### [Aucun contenu pour le moment]
|
||||
|
||||
---
|
||||
|
||||
## Ajout rapide
|
||||
|
||||
1. Copier `_template.md`
|
||||
2. Créer `[theme]/[titre-video].md` si nouveau thème
|
||||
3. Remplir les champs
|
||||
4. Mettre à jour cet index
|
||||
|
||||
---
|
||||
|
||||
## Stats
|
||||
|
||||
**Total leçons** : 0
|
||||
**Dernière mise à jour** : 2025-12-05
|
||||
39
lessons/_template.md
Normal file
@ -0,0 +1,39 @@
|
||||
# [Titre de la vidéo/leçon]
|
||||
|
||||
**Source** : [URL YouTube ou autre source]
|
||||
**Date ajoutée** : YYYY-MM-DD
|
||||
**Thème** : [theme]
|
||||
**Durée** : XXmin
|
||||
|
||||
---
|
||||
|
||||
## Résumé
|
||||
|
||||
[Résumé en 2-3 phrases de ce que tu as appris]
|
||||
|
||||
---
|
||||
|
||||
## Points clés
|
||||
|
||||
- Point 1
|
||||
- Point 2
|
||||
- Point 3
|
||||
|
||||
---
|
||||
|
||||
## Notes détaillées
|
||||
|
||||
[Transcription partielle ou notes plus détaillées si nécessaire]
|
||||
|
||||
---
|
||||
|
||||
## Applications pratiques
|
||||
|
||||
[Comment tu peux utiliser ces connaissances concrètement]
|
||||
|
||||
---
|
||||
|
||||
## Liens connexes
|
||||
|
||||
- [Autre leçon liée si applicable]
|
||||
- [Ressources complémentaires]
|
||||
@ -2,6 +2,7 @@
|
||||
"dependencies": {
|
||||
"playwright": "^1.56.1",
|
||||
"pptxgenjs": "^4.0.1",
|
||||
"puppeteer": "^24.34.0",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
## Informations de base
|
||||
- **Âge** : 29 ans
|
||||
- **Nationalité** : Français
|
||||
- **Situation** : En couple avec Tingting depuis 6 mois
|
||||
- **Localisation** : Shanghai, Chine
|
||||
- **Situation** : Divorcé de Tingting (mariage durée ~1 an, décembre 2024 - décembre 2025)
|
||||
- **Localisation** : Shanghai, Chine → Bangkok, Thaïlande (22 janvier 2026)
|
||||
|
||||
## Langues
|
||||
- **Français** : Langue maternelle
|
||||
@ -86,8 +86,17 @@
|
||||
-
|
||||
|
||||
## Relations
|
||||
### Avec Tingting
|
||||
|
||||
### Status actuel (Décembre 2025)
|
||||
- **Séparation Tingting** : 19 décembre 2025 (voir `couple_backlog/19-20_decembre_2025_separation_et_accord.md`)
|
||||
- **Accord post-mariage** : Divorce respectueux, support financier 100k yuan, enfant futur possible, vie en Asie
|
||||
- **Plan immédiat** : Shanghai (25 déc - 22 jan) → Bangkok (22 jan+)
|
||||
- **Budget** : 1550€, objectif freelance dev augmenté IA
|
||||
- **Incertitudes** : Sincérité accord Tingting (red flags détectés), viabilité engagements futurs
|
||||
|
||||
### Avec Tingting (historique)
|
||||
- **Première vraie relation** : Manque d'expérience dans la gestion des équilibres couple/passions
|
||||
- **Durée** : ~1 an (décembre 2024 - décembre 2025)
|
||||
- **Début de relation** : A été transparent avec Ben avant de démarrer - a transmis les sentiments de Ben en premier à Tingting
|
||||
- **Pattern de négligence** : Passion intense (4 mois) puis retour aux habitudes égocentriques
|
||||
- **Conflit de lifestyle** : Lui préfère rester chez lui, elle préfère sortir
|
||||
@ -123,6 +132,17 @@
|
||||
- **Leçon** : Besoins physiques (faim) > Gestion émotionnelle. Se retirer > Insister quand elle énervée.
|
||||
- **Pattern confirmé** : Alexis focus sur émotions de l'autre quand elle en colère, besoin qu'elle calme avant résoudre
|
||||
|
||||
#### Séparation 19-20 Décembre 2025
|
||||
- **Déclencheur** : Tingting découvre messages avec Maëlle (autre femme, plan alibi "karaté")
|
||||
- **Extraction forcée** : Parents vident appartement, père sort couteau, Alexis récupère essentiels et part
|
||||
- **Oscillations émotionnelles 24h** : "Soulagé, libre, content" → Confusion → Ultimatum "now or never" → Accord unique
|
||||
- **Accord post-mariage créé** : Divorce mais maintien connexion (enfant futur possible, 100k yuan, vie Asie)
|
||||
- **Messages excuses famille** : Envoyés 20 déc (mère, père, Meimei, Didi, groupe 家人)
|
||||
- **Contacts supprimés** : Toute famille étendue Tingting retirée WeChat
|
||||
- **RED FLAGS émergents** : Incohérences entre accord "amiable" Tingting et hostilité famille (Didi menaces violence)
|
||||
- **Plan décidé** : Shanghai → Bangkok 22 janvier, freelance dev+IA, budget 1550€
|
||||
- **Outcome** : Séparation basée sur respect mutuel mais viabilité accord incertaine
|
||||
|
||||
### Famille et amis
|
||||
|
||||
#### Ben (ex-ami)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
## Informations de base
|
||||
- **Âge** : 34 ans
|
||||
- **Nationalité** : Chinoise
|
||||
- **Situation** : En couple avec Alexis depuis 6 mois
|
||||
- **Situation** : Divorcée d'Alexis (mariage durée ~1 an, décembre 2024 - décembre 2025)
|
||||
- **Localisation** : Shanghai, Chine
|
||||
|
||||
## Langues
|
||||
@ -77,8 +77,17 @@
|
||||
-
|
||||
|
||||
## Relations
|
||||
### Avec Alexis
|
||||
- **Amour en diminution** : Reconnaît que ses sentiments déclinent depuis des mois
|
||||
|
||||
### Status actuel (Décembre 2025)
|
||||
- **Séparation Alexis** : 19 décembre 2025 (voir `couple_backlog/19-20_decembre_2025_separation_et_accord.md`)
|
||||
- **Accord post-mariage** : Divorce respectueux, support financier 100k yuan, enfant futur si elle veut, communication ouverte
|
||||
- **Réponse à message officiel Alexis** : Mature, mesurée, accepte support financier, met en pause engagements enfant ("besoin temps guérir")
|
||||
- **Ce qu'elle a dit à famille** : INCERTAIN - Incohérences entre accord amiable avec Alexis et hostilité famille (Didi parle "infidélité jamais pardonnée")
|
||||
- **Sincérité accord** : À vérifier - Possibilité double jeu (dire une chose à Alexis, autre chose à famille pour protéger réputation)
|
||||
|
||||
### Avec Alexis (historique)
|
||||
- **Durée relation** : ~1 an (décembre 2024 - décembre 2025)
|
||||
- **Amour en diminution** : Reconnaissait que ses sentiments déclinaient depuis des mois
|
||||
- **Période after honeymoon** : Consciente de cette phase dans leur relation
|
||||
- **Paradoxe comportemental** : Se plaint du manque d'affection mais refuse souvent son attention (reste sur phone/shorts)
|
||||
- **Mécanisme de défense** : Rejette systématiquement la responsabilité sur Alexis quand il pointe ses contradictions
|
||||
|
||||
460
plan_discussion/plan_exit_decembre_2025.md
Normal file
@ -0,0 +1,460 @@
|
||||
# Plan d'Action - Exit Strategy Décembre 2025
|
||||
|
||||
**Date de création** : 08/12/2025
|
||||
**Contexte** : Crise majeure dans relation avec Tingting, plan de sortie avec préparation financière
|
||||
**Statut** : ACTIF - Phase 1 en cours
|
||||
|
||||
---
|
||||
|
||||
## 🎯 OBJECTIF GLOBAL
|
||||
|
||||
**Court terme (1-3 mois)** : Atteindre 1000-2000€ de sécurité via freelance dev
|
||||
**Moyen terme (après argent)** : Conversation finale avec Tingting, décision basée sur réaction
|
||||
**Long terme** : Soit séparation et reconstruction, soit tentative de réparation (peu probable)
|
||||
|
||||
---
|
||||
|
||||
## 📊 STATUT ACTUEL
|
||||
|
||||
**Financier** :
|
||||
- Revenu actuel : 0€
|
||||
- Objectif : 1000-2000€
|
||||
- Progrès : 0%
|
||||
|
||||
**Relation Tingting** :
|
||||
- État : Relation morte/toxique (selon Alexis)
|
||||
- Comportement actuel : Accumulation en silence (STOP)
|
||||
- Nouvelle stratégie : Micro-confrontations (Option 3)
|
||||
|
||||
**Relation L (prof de chinois)** :
|
||||
- État : Prof/élève + amitié émotionnelle en développement
|
||||
- Statut : Elle ne sait pas qu'il est marié
|
||||
- Stratégie : Distance romantique, pas de révélation pour l'instant
|
||||
|
||||
---
|
||||
|
||||
## 📅 PHASE 1 : BUILD ARGENT + MICRO-CONFRONTATIONS
|
||||
|
||||
**Timeline** : 1-3 mois (variable selon succès freelance)
|
||||
**Condition de fin** : 1000-2000€ atteints
|
||||
|
||||
### 🔧 Workstream 1 : Freelance Dev (PRIORITÉ ABSOLUE)
|
||||
|
||||
**Objectif** : Générer 1000-2000€ le plus rapidement possible
|
||||
|
||||
#### Semaine 1 (9-15 décembre)
|
||||
|
||||
**Setup initial** :
|
||||
- [ ] Mettre à jour CV (focus stack technique : langages, frameworks, expérience)
|
||||
- [ ] Créer/optimiser portfolio (GitHub clean, 2-3 projets phares bien documentés)
|
||||
- [ ] Lister compétences vendables (frontend? backend? fullstack? mobile? DevOps?)
|
||||
- [ ] Définir taux horaire (rechercher market rate pour ta stack + expérience)
|
||||
|
||||
**Plateformes** :
|
||||
- [ ] S'inscrire Upwork (compléter profil 100%, passer tests si pertinents)
|
||||
- [ ] S'inscrire Malt (focus Europe/France)
|
||||
- [ ] S'inscrire Toptal (processus sélectif mais bien payé)
|
||||
- [ ] S'inscrire Fiverr (pour petits gigs rapides)
|
||||
- [ ] S'inscrire Freelancer.com (backup)
|
||||
|
||||
**Stratégie application** :
|
||||
- [ ] Préparer 3 templates de proposition (junior/mid/senior projects)
|
||||
- [ ] Appliquer à 10 jobs cette semaine (viser entry-level pour démarrer)
|
||||
- [ ] Focus : quick wins (petits projets, paiement rapide)
|
||||
- [ ] Niche possible : projets chinois/français (avantage linguistique)
|
||||
|
||||
**Networking** :
|
||||
- [ ] Contacter 5 anciens collègues/contacts (LinkedIn, WeChat)
|
||||
- [ ] Mentionner que tu cherches missions freelance
|
||||
- [ ] Demander referrals/recommendations
|
||||
|
||||
#### Semaines 2-4 (16 déc - 5 jan)
|
||||
|
||||
**Routine quotidienne** :
|
||||
- Matin : Appliquer à 5 nouveaux jobs
|
||||
- Après-midi : Travailler sur projets en cours (si obtenus)
|
||||
- Soir : Optimiser profils, apprendre nouvelles compétences demandées
|
||||
|
||||
**Tracking** :
|
||||
- Applications envoyées : ___/semaine
|
||||
- Réponses reçues : ___
|
||||
- Interviews : ___
|
||||
- Projets obtenus : ___
|
||||
- Argent gagné : ___€
|
||||
|
||||
**Ajustements** :
|
||||
- Si pas de réponses après 2 semaines → Revoir propositions/profil
|
||||
- Si interviews mais pas de conversions → Revoir pitching
|
||||
- Si aucun projet → Considérer baisser taux temporairement pour premiers clients
|
||||
|
||||
#### Mois 2-3 (si nécessaire)
|
||||
|
||||
**Objectif** : Maintenir momentum jusqu'à 1000-2000€
|
||||
|
||||
- [ ] Build réputation (reviews positives)
|
||||
- [ ] Augmenter taux progressivement
|
||||
- [ ] Chercher clients récurrents
|
||||
- [ ] Diversifier sources (pas dépendre d'une seule plateforme)
|
||||
|
||||
---
|
||||
|
||||
### 👫 Workstream 2 : Tingting - Micro-confrontations (Option 3)
|
||||
|
||||
**Objectif** : Arrêter l'accumulation en silence, poser limites en temps réel
|
||||
|
||||
#### Principe : Immédiat, Spécifique, Disengage
|
||||
|
||||
**Règle d'or** :
|
||||
1. **Immédiat** : Réagir dans les 30 secondes du comportement problématique
|
||||
2. **Spécifique** : Adresser ce qui vient de se passer, pas le pattern général
|
||||
3. **Limite** : Énoncer que c'est pas acceptable
|
||||
4. **Disengage** : Se retirer physiquement ou émotionnellement, ne pas entrer dans le fight
|
||||
|
||||
#### Situations Courantes + Réponses
|
||||
|
||||
**Situation 1 : Elle reframe tes mots**
|
||||
|
||||
Tingting : "Donc tu dis que je suis une mauvaise personne"
|
||||
|
||||
❌ **Avant** : "Non c'est pas ce que j'ai dit, écoute, ce que je voulais dire c'est..." (justification sans fin)
|
||||
|
||||
✅ **Maintenant** : "Non. Je n'ai jamais dit ça. Arrête de transformer mes mots."
|
||||
→ Puis silence ou retrait physique
|
||||
|
||||
---
|
||||
|
||||
**Situation 2 : Elle dit des choses graves pour blesser**
|
||||
|
||||
Tingting : [Attaque personnelle intentionnellement blessante]
|
||||
|
||||
❌ **Avant** : *Pleure ou se défend émotionnellement*
|
||||
|
||||
✅ **Maintenant** : "Tu dis ça pour me faire mal. C'est pas ok. Je m'en vais."
|
||||
→ Puis partir physiquement (autre pièce, sortir)
|
||||
|
||||
---
|
||||
|
||||
**Situation 3 : Elle t'ignore / ne te regarde pas**
|
||||
|
||||
Tingting : *Sur téléphone, ignore complètement*
|
||||
|
||||
❌ **Avant** : Insister pour avoir son attention, créer un fight
|
||||
|
||||
✅ **Maintenant** : "Ok, je vois que tu es occupée. Je vais faire mes trucs."
|
||||
→ Puis s'en aller, focus sur soi
|
||||
|
||||
---
|
||||
|
||||
**Situation 4 : Elle escalade quand tu poses limite**
|
||||
|
||||
Toi : "Ce comportement n'est pas ok"
|
||||
Tingting : "QUOI?! Et toi alors, tu fais [liste de tes erreurs]!!"
|
||||
|
||||
❌ **Avant** : Entre dans le fight, se défend point par point
|
||||
|
||||
✅ **Maintenant** : "Je ne vais pas faire ce fight maintenant. On en parle quand on est calmes, ou pas du tout."
|
||||
→ Puis disengage complètement
|
||||
|
||||
---
|
||||
|
||||
**Situation 5 : Elle dit "on devrait se séparer" quand tu es vulnerable**
|
||||
|
||||
Tingting : "Peut-être qu'on devrait se séparer"
|
||||
|
||||
❌ **Avant** : Panique, supplie, essaie de sauver
|
||||
|
||||
✅ **Maintenant** : "Si c'est ce que tu veux, on peut en parler sérieusement. Mais ne le dis pas juste pour me faire mal."
|
||||
→ Puis observe sa réaction calmement
|
||||
|
||||
---
|
||||
|
||||
#### Tracking Micro-confrontations
|
||||
|
||||
**Semaine 1** :
|
||||
- Situations où tu as appliqué : ___
|
||||
- Situations où tu as oublié/accumulé : ___
|
||||
- Réactions de Tingting : ___
|
||||
|
||||
**Objectif** : Chaque semaine, améliorer le ratio appliqué/oublié
|
||||
|
||||
#### Red Flags d'Escalade Dangereuse
|
||||
|
||||
Si comportement empire malgré micro-confrontations :
|
||||
|
||||
- [ ] Menaces de violence physique
|
||||
- [ ] Manipulation émotionnelle qui intensifie
|
||||
- [ ] Gaslighting systématique qui empire
|
||||
- [ ] Isolation sociale qui s'aggrave
|
||||
|
||||
**Action si red flag** : Accélérer Phase 2 même sans argent complet (sécurité prioritaire)
|
||||
|
||||
---
|
||||
|
||||
### 👩🏫 Workstream 3 : L - Distance Romantique
|
||||
|
||||
**Objectif** : Garder support émotionnel sans développer romantiquement avant d'être libre
|
||||
|
||||
#### Règles Strictes
|
||||
|
||||
**✅ Acceptable** :
|
||||
- Cours de chinois (c'est ta prof)
|
||||
- Conversations légères (météo, culture, apprentissage)
|
||||
- Amitié surface-level
|
||||
- Encouragement mutuel (études, projets)
|
||||
|
||||
**❌ Interdit** :
|
||||
- Flirt de toute nature
|
||||
- Conversations intimes sur problèmes maritaux
|
||||
- Partage émotionnel profond
|
||||
- Sous-entendus romantiques
|
||||
- Photos/messages tard le soir
|
||||
- Augmentation fréquence des contacts
|
||||
|
||||
#### Gestion de la Révélation Future
|
||||
|
||||
**Actuellement** : Elle ne sait pas qu'il est marié
|
||||
|
||||
**Options de révélation** (à décider avant Phase 2) :
|
||||
|
||||
**Option A : Révélation maintenant**
|
||||
- Message : "Je dois te dire quelque chose. Je suis marié. C'est très compliqué, je réfléchis sérieusement à partir. Je voulais être transparent."
|
||||
- **Risque** : Change la dynamique immédiatement
|
||||
- **Avantage** : Honnêteté, elle peut décider en connaissance de cause
|
||||
|
||||
**Option B : Révélation après séparation**
|
||||
- Attendre d'être séparé de Tingting
|
||||
- Puis : "Il faut que je te parle de quelque chose. J'étais marié. Ma relation était toxique, je viens de me séparer. Je ne t'en ai pas parlé avant parce que je ne voulais pas t'impliquer dans ce chaos. Je comprends si ça change les choses pour toi."
|
||||
- **Risque** : Elle se sent trompée par omission
|
||||
- **Avantage** : Pas de complications pendant Phase 1
|
||||
|
||||
**Option C : Révélation si elle demande directement**
|
||||
- Si elle pose la question, être honnête immédiatement
|
||||
- Pas mentir si la question est posée
|
||||
- **Risque** : Peut arriver n'importe quand
|
||||
- **Avantage** : Réactif, pas proactif
|
||||
|
||||
**Décision actuelle** : Option B (révélation après séparation)
|
||||
|
||||
#### Si elle découvre avant que tu révèles
|
||||
|
||||
**Script préparé** :
|
||||
"Tu as raison, je suis marié. Je ne te l'ai pas dit parce que ma relation est en train de s'effondrer et je ne voulais pas te mettre au milieu de ça. Je comprends que tu te sentes trompée. Ce n'était pas mon intention de te manipuler. Je voulais régler ma situation avant de t'impliquer. Si tu veux prendre de la distance, je comprends complètement."
|
||||
|
||||
---
|
||||
|
||||
## 📅 PHASE 2 : DÉCISION FINALE
|
||||
|
||||
**Déclenchement** : 1000-2000€ atteints
|
||||
|
||||
### 🗣️ Conversation avec Tingting
|
||||
|
||||
**Préparation** :
|
||||
- [ ] Écrire script de conversation
|
||||
- [ ] Identifier lieu/moment approprié
|
||||
- [ ] Avoir plan de sortie physique si ça explose
|
||||
- [ ] Avoir numéro ambassade FR / contacts d'urgence ready
|
||||
|
||||
**Script (à adapter)** :
|
||||
|
||||
"Tingting, j'ai besoin qu'on parle sérieusement.
|
||||
|
||||
Je ne suis plus heureux dans notre relation. Je me sens invisible, je ne sens plus d'amour, plus de gestes, plus d'attention entre nous. Ça fait longtemps que ça dure.
|
||||
|
||||
Je sais que tu as probablement aussi tes frustrations avec moi. Je n'ai jamais vraiment pris d'initiatives pour notre avenir, je ne fais pas assez attention à toi. Je reconnais ça.
|
||||
|
||||
Mais on est dans une spirale où aucun de nous deux ne fait plus d'efforts. On se bat constamment. On ne se regarde plus.
|
||||
|
||||
Je pense qu'on doit soit vraiment essayer de changer les choses ensemble, soit reconnaître que ça ne fonctionne plus.
|
||||
|
||||
Qu'est-ce que tu en penses ? Est-ce que tu veux encore qu'on soit ensemble ? Est-ce que tu es prête à faire des efforts si moi j'en fais aussi ?"
|
||||
|
||||
**Réactions Possibles** :
|
||||
|
||||
**Réaction A : Elle escalade / gaslighting / blame**
|
||||
→ "Je vois. Je pense qu'on a notre réponse. On devrait parler de séparation."
|
||||
|
||||
**Réaction B : Elle panique / supplie / promet de changer**
|
||||
→ Observer si c'est genuine ou juste peur de perdre. Demander actions concrètes. Deadline 2 semaines pour voir.
|
||||
|
||||
**Réaction C : Elle est d'accord pour se séparer**
|
||||
→ Discuter termes séparation calmement (logistique, timing, familles)
|
||||
|
||||
**Réaction D : Elle veut vraiment essayer**
|
||||
→ Définir ensemble 3-5 changements concrets de chaque côté. Deadline 1 mois pour voir progrès réels.
|
||||
|
||||
### 🚪 Si Séparation
|
||||
|
||||
**Logistique** :
|
||||
- [ ] Organiser départ Chine (vol, timing)
|
||||
- [ ] Prévenir famille FR (demander support)
|
||||
- [ ] Prévenir famille Tingting (respect, même si difficile)
|
||||
- [ ] Gérer visa/paperasse administrative
|
||||
- [ ] Séparer finances (si jointes)
|
||||
- [ ] Récupérer affaires personnelles importantes
|
||||
|
||||
**Émotionnel** :
|
||||
- [ ] Accepter la culpabilité (sa famille, son âge) mais ne pas se laisser paralyser
|
||||
- [ ] Se rappeler : rester par culpabilité = poison pour les deux
|
||||
- [ ] Permettre le deuil de la relation
|
||||
- [ ] Ne pas rush vers L immédiatement
|
||||
|
||||
**Avec L après séparation** :
|
||||
- [ ] Attendre au moins 2-4 semaines après séparation
|
||||
- [ ] Révélation honnête sur situation
|
||||
- [ ] Si elle est intéressée, prendre les choses lentement
|
||||
- [ ] Ne pas utiliser L comme rebound
|
||||
|
||||
### ♻️ Si Tentative de Réparation
|
||||
|
||||
**Peu probable selon état actuel, mais si ça arrive** :
|
||||
|
||||
**Conditions non-négociables** :
|
||||
1. Arrêt complet manipulation émotionnelle ("dire des trucs pour faire pleurer")
|
||||
2. Arrêt reframing systématique
|
||||
3. Thérapie de couple ou au minimum conversations honnêtes régulières
|
||||
4. Retour d'intimité/affection progressive
|
||||
5. Efforts équilibrés des deux côtés
|
||||
|
||||
**Deadline** : 1 mois pour voir changements réels et durables
|
||||
|
||||
**Red lines** : Si violence verbale/physique augmente, partir immédiatement
|
||||
|
||||
**Avec L si réparation** :
|
||||
- [ ] Prendre distance claire ("Je dois me concentrer sur ma relation")
|
||||
- [ ] Arrêter complètement les cours/contacts si ça interfère
|
||||
- [ ] Être honnête que tu explores réparation avec ta femme
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ RISQUES ET MITIGATION
|
||||
|
||||
### Risque 1 : Freelance ne décolle pas
|
||||
|
||||
**Indicateurs** :
|
||||
- Après 3 semaines : 0 interviews
|
||||
- Après 6 semaines : 0 projet
|
||||
- Après 2 mois : <500€
|
||||
|
||||
**Mitigation** :
|
||||
- Revoir stratégie (profil, propositions, taux)
|
||||
- Chercher alternatives (teaching anglais online, autre side gig)
|
||||
- Considérer emprunter à famille FR si vraiment bloqué
|
||||
|
||||
### Risque 2 : Tingting découvre L
|
||||
|
||||
**Indicateurs** :
|
||||
- Elle voit messages
|
||||
- Quelqu'un mentionne ta prof
|
||||
- Elle te suit/espionne
|
||||
|
||||
**Mitigation** :
|
||||
- Garder communications avec L professionnelles (défendable)
|
||||
- Ne pas mentir si confronté, mais ne pas révéler sentiments
|
||||
- Avoir script prêt : "C'est ma prof de chinois, on est amis, rien de plus"
|
||||
|
||||
### Risque 3 : L découvre le mariage
|
||||
|
||||
**Indicateurs** :
|
||||
- Elle voit ton WeChat moments
|
||||
- Quelqu'un lui dit
|
||||
- Elle demande directement
|
||||
|
||||
**Mitigation** :
|
||||
- Script préparé (voir Workstream 3)
|
||||
- Honnêteté immédiate si elle demande
|
||||
- Accepter les conséquences
|
||||
|
||||
### Risque 4 : Situation avec Tingting empire dangereusement
|
||||
|
||||
**Indicateurs** :
|
||||
- Menaces de violence
|
||||
- Manipulation qui intensifie
|
||||
- Isolation qui s'aggrave
|
||||
|
||||
**Mitigation** :
|
||||
- Partir immédiatement même sans argent complet
|
||||
- Contacter ambassade FR
|
||||
- Emprunter en urgence à famille
|
||||
- Ne pas rester dans une situation dangereuse
|
||||
|
||||
---
|
||||
|
||||
## 📊 TRACKING HEBDOMADAIRE
|
||||
|
||||
### Semaine du ___ au ___
|
||||
|
||||
**Financier** :
|
||||
- Applications envoyées : ___
|
||||
- Interviews : ___
|
||||
- Projets obtenus : ___
|
||||
- Argent gagné : ___€
|
||||
- Total cumulé : ___€ / 1000-2000€
|
||||
|
||||
**Tingting** :
|
||||
- Micro-confrontations appliquées : ___
|
||||
- Situations accumulées : ___
|
||||
- Niveau de tension (1-10) : ___
|
||||
- Red flags observés : ___
|
||||
|
||||
**L** :
|
||||
- Fréquence contacts : ___/semaine
|
||||
- Niveau d'intimité émotionnelle (1-10) : ___
|
||||
- Distance romantique maintenue ? Oui/Non
|
||||
- Risque de découverte mariage (1-10) : ___
|
||||
|
||||
**Mental** :
|
||||
- Niveau de stress (1-10) : ___
|
||||
- Heures de sommeil : ___
|
||||
- Sentiment de progression : ___
|
||||
- Besoin de support : Oui/Non
|
||||
|
||||
**Actions semaine prochaine** :
|
||||
1. ___
|
||||
2. ___
|
||||
3. ___
|
||||
|
||||
---
|
||||
|
||||
## 🎯 MILESTONES
|
||||
|
||||
- [ ] **Milestone 1** : Profils freelance actifs sur 3+ plateformes (deadline : 15 déc)
|
||||
- [ ] **Milestone 2** : Première interview obtenue (deadline : 20 déc)
|
||||
- [ ] **Milestone 3** : Premier projet obtenu (deadline : 31 déc)
|
||||
- [ ] **Milestone 4** : 500€ atteints (deadline : 15 jan)
|
||||
- [ ] **Milestone 5** : 1000€ atteints (deadline : 31 jan)
|
||||
- [ ] **Milestone 6** : 1500-2000€ atteints (deadline : 28 fév)
|
||||
- [ ] **Milestone 7** : Conversation finale avec Tingting (après Milestone 5 ou 6)
|
||||
|
||||
---
|
||||
|
||||
## 💭 NOTES ET RÉFLEXIONS
|
||||
|
||||
**À relire régulièrement** :
|
||||
|
||||
1. **Pourquoi je fais ça** : Je veux être heureux. La relation est toxique. Je mérite mieux. Elle mérite quelqu'un qui l'aime vraiment.
|
||||
|
||||
2. **Ce que je ne fais PAS** : Fuir sans préparation. Accumuler en silence. Créer de l'escalade pour justifier. Développer L romantiquement avant d'être libre.
|
||||
|
||||
3. **Ce que je fais** : Me préparer financièrement. Poser mes limites. Être aussi honnête que possible dans les contraintes. Prendre soin de ma santé mentale.
|
||||
|
||||
4. **La culpabilité** : Sa famille est fantastique. Elle a 35 ans. Elle m'a présenté à tout le monde. C'est douloureux. MAIS rester par culpabilité = lui voler plus de temps. Plus vite je pars (si je pars), plus vite elle peut trouver quelqu'un qui l'aime vraiment.
|
||||
|
||||
5. **L et l'honnêteté** : Ce plan n'est pas parfaitement éthique. Elle ne sait pas que je suis marié. C'est un compromis pour ma survie. Je dois accepter qu'elle peut mal réagir plus tard. C'est un risque que je prends consciemment.
|
||||
|
||||
6. **Si je doute** : Relire `couple_backlog/08_decembre_2025_crise_majeure.md` pour me rappeler pourquoi j'en suis là.
|
||||
|
||||
---
|
||||
|
||||
## 🆘 CONTACTS D'URGENCE
|
||||
|
||||
**Si situation devient dangereuse** :
|
||||
- Ambassade France en Chine : [à compléter]
|
||||
- Famille FR (emprunter argent) : [à compléter]
|
||||
- Amis en Chine : [à compléter]
|
||||
- Hotline violence domestique : [à compléter]
|
||||
|
||||
---
|
||||
|
||||
**Dernière mise à jour** : 08/12/2025
|
||||
**Prochain check-in** : 15/12/2025
|
||||
**Statut Phase 1** : EN COURS - Jour 1
|
||||
494
plan_discussion/plan_exit_decembre_2025_URGENT.md
Normal file
@ -0,0 +1,494 @@
|
||||
# Plan d'Action URGENT - Exit Strategy Décembre 2025
|
||||
|
||||
**Date de création** : 08/12/2025
|
||||
**Dernière mise à jour** : 08/12/2025 (23h) - RÉVISION MAJEURE
|
||||
**Statut** : 🚨 CRITIQUE - URGENCE MAXIMALE
|
||||
|
||||
---
|
||||
|
||||
## 🚨 SITUATION CRITIQUE
|
||||
|
||||
### DANGER IMMÉDIAT : RISQUE GROSSESSE
|
||||
|
||||
**Tingting essaie d'avoir un enfant** :
|
||||
- Depuis 6 mois (pic août : 9 tentatives)
|
||||
- Dernier mois : 2 rapports (fréquence basse mais existe)
|
||||
- **Rapport hier (7 décembre)** = risque réel
|
||||
- Elle a refusé 20-30 fois (stress travail) mais veut toujours un enfant
|
||||
- **Elle ne sait RIEN du plan d'exit d'Alexis**
|
||||
|
||||
**Si elle tombe enceinte pendant préparation exit = CATASTROPHE ABSOLUE**
|
||||
|
||||
---
|
||||
|
||||
### MENSONGE INTENABLE AVEC L (MAËLLE)
|
||||
|
||||
**Progression très rapide (8 décembre)** :
|
||||
- Partage trauma profond (confiance totale)
|
||||
- Flirte ouvertement
|
||||
- Propose dinner
|
||||
- Se cherchent physiquement au campus
|
||||
- **Elle pense qu'il est célibataire**
|
||||
|
||||
**Réalisation d'Alexis** : "C'est impossible ce qu'il se passe. Si je veux continuer, il faudra créer des mensonges caricaturaux. Ça me désole."
|
||||
|
||||
**Timeline avant explosion** : Quelques jours/semaines (pas 1-2 mois)
|
||||
|
||||
---
|
||||
|
||||
### JIAODA ABANDONNÉ
|
||||
|
||||
**Situation académique** :
|
||||
- 35/100 actuellement, besoin 80/100
|
||||
- Impossible en 1 mois
|
||||
- **Réalisation** : "On se voit même pas à l'uni de toute façon"
|
||||
- **Décision** : Give up, focus argent, autre université Shanghai août 2026
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PLAN RÉVISÉ URGENT
|
||||
|
||||
**DEADLINE ABSOLUE** : **DÉBUT JANVIER 2026**
|
||||
|
||||
**Raisons** :
|
||||
1. ⚠️ Risque grossesse (chaque jour = manipulation cruelle)
|
||||
2. ⚠️ Mensonge avec L intenable
|
||||
3. ⚠️ Cruauté morale de laisser Tingting espérer
|
||||
|
||||
**PAS attendre février/mars comme plan original**
|
||||
|
||||
---
|
||||
|
||||
## 📅 DÉCEMBRE 2025 : PHASE CRITIQUE (3 SEMAINES)
|
||||
|
||||
### ⚠️⚠️⚠️ PRIORITÉ ABSOLUE 1 : ÉVITER RAPPORTS TINGTING
|
||||
|
||||
**À partir de maintenant (9 décembre)** :
|
||||
|
||||
**Actions** :
|
||||
- [ ] Refuser systématiquement si elle propose
|
||||
- [ ] Excuses : Fatigue, stress études, pas d'humeur
|
||||
- [ ] **0 RAPPORT jusqu'à conversation divorce**
|
||||
|
||||
**Si elle insiste vraiment** :
|
||||
- 🚨 **CONVERSATION IMMÉDIATE**
|
||||
- Ne PAS attendre "le bon moment"
|
||||
- Script : "On doit parler sérieusement de nous"
|
||||
|
||||
**Red line** : Si elle devient active dans essais bébé → Tout arrêter, conversation NOW
|
||||
|
||||
**Probabilité** : Faible (elle refuse souvent ces temps-ci, stress travail)
|
||||
**Mais vigilance** : Absolue
|
||||
|
||||
---
|
||||
|
||||
### 💼 PRIORITÉ 2 : FREELANCE DEV - BUILD ARGENT
|
||||
|
||||
**Objectif** : 800-1000€ avant conversation Tingting (fin décembre)
|
||||
|
||||
#### Semaine 1 (9-15 décembre)
|
||||
|
||||
**Lundi 9 décembre** :
|
||||
- [ ] Check GitHub : Projets présentables ? (1h)
|
||||
- [ ] Si non : Créer 1-2 showcase projects avec AI (3h)
|
||||
- [ ] Setup Upwork profil (2h)
|
||||
- Title : "Full-Stack Developer | Fast Prototyping & Automation"
|
||||
- Focus : C++, C#, Python, Node.js, Quick wins
|
||||
- Ne PAS mentionner AI, juste "fast delivery"
|
||||
|
||||
**Mardi 10 décembre** :
|
||||
- [ ] Setup Malt profil (1h)
|
||||
- [ ] Setup Fiverr 3 gigs (2h)
|
||||
- Gig 1 : Python automation script ($50-100)
|
||||
- Gig 2 : Bug fixes any language ($80-150)
|
||||
- Gig 3 : Quick prototype/MVP ($200-400)
|
||||
- [ ] Préparer 3 templates propositions (1h)
|
||||
|
||||
**Mercredi 11 décembre** :
|
||||
- [ ] 15 applications Upwork (focus automation + bug fixes)
|
||||
- [ ] Utiliser AI pour varier propositions
|
||||
|
||||
**Jeudi-Dimanche** :
|
||||
- [ ] 15 applications/jour (total semaine : 60 applications)
|
||||
- [ ] Répondre interviews immédiatement
|
||||
- [ ] Si 1er projet obtenu : LIVRER VITE (impressionner)
|
||||
|
||||
**Objectif semaine 1** :
|
||||
- 60 applications envoyées
|
||||
- 3-5 interviews obtenues
|
||||
- Idéalement : 1er micro-projet started
|
||||
|
||||
---
|
||||
|
||||
#### Semaines 2-3 (16-29 décembre)
|
||||
|
||||
**Stratégie** :
|
||||
- [ ] Continue applications (20/semaine)
|
||||
- [ ] Deliver projets obtenus
|
||||
- [ ] Build reviews positives (crucial)
|
||||
- [ ] Augmenter taux après 3-5 reviews
|
||||
|
||||
**Niches focus** :
|
||||
1. ⭐⭐⭐⭐⭐ Automation scripts (30-40% apps)
|
||||
2. ⭐⭐⭐⭐⭐ Bug fixes (30-40% apps)
|
||||
3. ⭐⭐⭐⭐ Quick prototypes (20-30% apps)
|
||||
|
||||
**Pricing initial** :
|
||||
- Hourly : $20-25/h (bas pour démarrer)
|
||||
- Fixed micro : $50-200
|
||||
- **Avantage AI** : Livrer en 3h ce qui prend 8h = taux effectif $50-65/h
|
||||
|
||||
**Pricing après 5 reviews** :
|
||||
- Hourly : $30-50/h
|
||||
- Fixed : $200-500
|
||||
|
||||
**Objectif fin décembre** : 560-800€
|
||||
|
||||
---
|
||||
|
||||
#### Semaine 4 (30 déc - 5 jan)
|
||||
|
||||
- [ ] Deliver projets en cours
|
||||
- [ ] Secure 1-2 clients réguliers (remote work janvier)
|
||||
- [ ] Setup stable pour remote work (laptop, VPN, tools)
|
||||
|
||||
---
|
||||
|
||||
### 💔 PRIORITÉ 3 : DISTANCE AVEC L
|
||||
|
||||
**Stratégie** : Distance progressive (pas cut brutal immédiat)
|
||||
|
||||
**Semaines 1-2 (9-22 déc)** :
|
||||
- [ ] Messages légers uniquement
|
||||
- [ ] PAS de flirt
|
||||
- [ ] PAS de propositions rendez-vous/dinner
|
||||
- [ ] Excuses si elle propose : "Débordé avec projets"
|
||||
|
||||
**Semaine 3-4 (23 déc - 5 jan)** :
|
||||
- [ ] Préparer message cut (envoyer APRÈS conversation Tingting)
|
||||
- [ ] Fade progressif (réponses plus rares, plus courtes)
|
||||
|
||||
**Message cut (à envoyer après conversation Tingting)** :
|
||||
|
||||
```
|
||||
Maëlle,
|
||||
|
||||
Je dois être honnête avec toi.
|
||||
|
||||
J'ai une situation personnelle très compliquée que je dois régler en ce moment. Ce ne serait pas juste pour toi de te laisser développer des sentiments alors que je ne peux pas être pleinement disponible.
|
||||
|
||||
Je dois prendre du temps pour régler ça. Je suis vraiment désolé.
|
||||
|
||||
Tu es quelqu'un de vraiment spécial. Si un jour ma situation change et que tu es toujours intéressée, j'aimerais beaucoup qu'on se reparle. Mais je comprends complètement si tu passes à autre chose.
|
||||
|
||||
Tu mérites quelqu'un qui peut te donner toute son attention.
|
||||
|
||||
Alexis
|
||||
```
|
||||
|
||||
**Red line** : Si elle pose questions directes sur vie perso → Cut immédiat ou révélation partielle
|
||||
|
||||
---
|
||||
|
||||
### 🗣️ PRIORITÉ 4 : PRÉPARER CONVERSATION TINGTING
|
||||
|
||||
**Deadline** : **Fin décembre / Début janvier MAX**
|
||||
|
||||
#### Script préparé
|
||||
|
||||
**Phase 1 : Arrêt essais bébé (minimum vital)**
|
||||
|
||||
"Tingting, je dois te parler de quelque chose de très sérieux.
|
||||
|
||||
Je ne suis plus heureux dans notre relation. Je pense qu'on doit arrêter d'essayer d'avoir un enfant.
|
||||
|
||||
Je sais que c'est important pour toi et je suis vraiment désolé. Mais je ne pense pas que ce soit la bonne chose à faire pour nous en ce moment.
|
||||
|
||||
On doit parler sérieusement de notre relation et où on va."
|
||||
|
||||
**Observer sa réaction.**
|
||||
|
||||
**Phase 2 : Selon réaction**
|
||||
|
||||
**Si elle demande pourquoi** :
|
||||
- "Je ne suis pas heureux"
|
||||
- "On se bat tout le temps"
|
||||
- "Je ne sens plus la connexion entre nous"
|
||||
- "Je pense qu'on devrait se séparer"
|
||||
|
||||
**Si elle escalade/attaque** :
|
||||
- Rester calme
|
||||
- "Je comprends que tu sois en colère"
|
||||
- "Mais on doit être honnêtes sur l'état de notre relation"
|
||||
|
||||
**Si elle veut sauver** :
|
||||
- Être clair : "Je ne pense pas que ça va marcher"
|
||||
- "On peut essayer divorce à l'amiable"
|
||||
|
||||
---
|
||||
|
||||
#### Timing choix
|
||||
|
||||
**Choisir un moment** :
|
||||
- ✅ Week-end (pas de travail le lendemain)
|
||||
- ✅ Pas pendant stress travail intense
|
||||
- ✅ Chez vous, privé
|
||||
- ❌ PAS dans lieu public
|
||||
- ❌ PAS tard le soir
|
||||
|
||||
**Idéal** : Samedi matin, après petit-déjeuner calme
|
||||
|
||||
**Deadline absolue** : **5 janvier 2026**
|
||||
|
||||
---
|
||||
|
||||
## 📅 JANVIER 2026 : CONVERSATION & DIVORCE
|
||||
|
||||
### Semaine 1 (6-12 janvier)
|
||||
|
||||
**Actions** :
|
||||
- [ ] Avoir conversation avec Tingting (DEADLINE ABSOLUE)
|
||||
- [ ] Clarifier : On veut divorcer à l'amiable
|
||||
- [ ] Expliquer process (30 jours réflexion + finalisation)
|
||||
|
||||
**Réactions possibles** :
|
||||
|
||||
**Scénario A : Elle accepte**
|
||||
- Discuter termes (pas de biens à diviser normalement)
|
||||
- Planifier visite Bureau Affaires Civiles
|
||||
- Timeline : Février soumission, Mars finalisation
|
||||
|
||||
**Scénario B : Elle résiste mais discute**
|
||||
- Plusieurs conversations nécessaires
|
||||
- Rester ferme mais respectueux
|
||||
- Peut prendre 2-3 semaines
|
||||
|
||||
**Scénario C : Elle refuse complètement**
|
||||
- Chercher avocat divorce contentieux
|
||||
- Process long (6-18 mois)
|
||||
- Mais tu pars quand même (gérer à distance)
|
||||
|
||||
---
|
||||
|
||||
### Semaines 2-4 (13 jan - 2 fév)
|
||||
|
||||
**Avec Tingting** :
|
||||
- [ ] Continuer discussions divorce
|
||||
- [ ] Chercher logement temporaire si tension
|
||||
- [ ] Gérer aspects pratiques
|
||||
|
||||
**Avec L** :
|
||||
- [ ] Envoyer message cut (après conversation Tingting)
|
||||
- [ ] Accepter sa réaction (probablement confusion/tristesse)
|
||||
- [ ] Minimal contact après
|
||||
|
||||
**Freelance** :
|
||||
- [ ] Continue remote work
|
||||
- [ ] Objectif : 1000-1700€ janvier
|
||||
- [ ] Build clients réguliers
|
||||
|
||||
**Visa** :
|
||||
- [ ] Q1 obtenu (si Tingting coopère)
|
||||
- [ ] Valide jusqu'à fin février
|
||||
|
||||
---
|
||||
|
||||
## 📅 FÉVRIER-MARS 2026 : DIVORCE & TRANSITION
|
||||
|
||||
### Février
|
||||
|
||||
**Divorce** :
|
||||
- [ ] Soumission demande (si elle accepte)
|
||||
- [ ] Période réflexion 30 jours
|
||||
- [ ] Ou premier round tribunal (si contentieux)
|
||||
|
||||
**Logement** :
|
||||
- [ ] Chercher autre logement Shanghai temporaire
|
||||
- [ ] Ou rester mais 0 interaction
|
||||
|
||||
**Freelance** :
|
||||
- [ ] Continue remote
|
||||
- [ ] Prep nomad setup
|
||||
|
||||
**Q1 expire** : Fin février
|
||||
|
||||
---
|
||||
|
||||
### Mars
|
||||
|
||||
**Divorce** :
|
||||
- [ ] Finalisation (si consentement mutuel)
|
||||
- [ ] Ou continue process (si contentieux)
|
||||
|
||||
**Départ Chine** :
|
||||
- [ ] Vol Shanghai → Thaïlande/Vietnam (200-300€)
|
||||
- [ ] Avec 1500-2000€ savings + freelance income
|
||||
|
||||
**Nomad life starts**
|
||||
|
||||
---
|
||||
|
||||
## 📅 AVRIL-AOÛT 2026 : REBUILD
|
||||
|
||||
### Avril-Juillet : Asie Nomad
|
||||
|
||||
**Destinations** :
|
||||
- Mars-Avril : Thaïlande (2 mois) - 600-800€/mois
|
||||
- Mai : Chine visa-free (1 mois) - 400-600€
|
||||
- Juin : Corée/Japon (1 mois) - 800-1000€
|
||||
- Juillet : Chine visa-free (1 mois) - 400-600€
|
||||
|
||||
**Freelance remote** :
|
||||
- Income objectif : 800-1200€/mois
|
||||
- Breakeven ou slight profit
|
||||
|
||||
**Divorce** :
|
||||
- Finalisé (si consentement mutuel)
|
||||
- Ou en cours (si contentieux)
|
||||
|
||||
---
|
||||
|
||||
### Août : Retour Shanghai
|
||||
|
||||
**Université** :
|
||||
- Autre université Shanghai (PAS Jiaoda)
|
||||
- Student visa secured
|
||||
- Programme moins exigeant
|
||||
|
||||
**Status** :
|
||||
- Divorcé (ou en cours final)
|
||||
- Argent stable
|
||||
- Freelance income continues
|
||||
|
||||
**L** :
|
||||
- Reconnect si pertinent
|
||||
- Révélation honnête : "J'étais marié, je viens de divorcer"
|
||||
- Elle décide si elle accepte
|
||||
|
||||
**Vie nouvelle** : Libre, stable, heureux
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ RED FLAGS - CONVERSATION IMMÉDIATE
|
||||
|
||||
**Si UN de ces événements arrive, FUCK le plan, conversation Tingting IMMÉDIATE** :
|
||||
|
||||
1. 🚨 **Tingting insiste pour rapports** (veut vraiment bébé maintenant)
|
||||
2. 🚨 **Elle arrête contraception activement**
|
||||
3. 🚨 **L découvre le mariage** (quelqu'un lui dit)
|
||||
4. 🚨 **Tingting découvre L** (voit messages, quelqu'un lui dit)
|
||||
5. 🚨 **Alexis ne peut plus éviter mensonges grotesques avec L**
|
||||
6. 🚨 **Tingting tombe enceinte** (test positif)
|
||||
|
||||
**Action** : Conversation dans les 24-48h, PAS attendre fin décembre
|
||||
|
||||
---
|
||||
|
||||
## 📊 TRACKING HEBDOMADAIRE
|
||||
|
||||
### Semaine du 9-15 décembre
|
||||
|
||||
**Freelance** :
|
||||
- Applications envoyées : ___/60
|
||||
- Interviews obtenues : ___
|
||||
- Projets obtenus : ___
|
||||
- Argent gagné : ___€
|
||||
|
||||
**Tingting** :
|
||||
- Rapports évités : Oui/Non
|
||||
- Elle a insisté ? Oui/Non
|
||||
- Niveau tension (1-10) : ___
|
||||
|
||||
**L** :
|
||||
- Messages envoyés : ___
|
||||
- Niveau flirt maintenu bas ? Oui/Non
|
||||
- Elle a proposé rendez-vous ? Oui/Non
|
||||
|
||||
**Préparation conversation** :
|
||||
- Script relu ? Oui/Non
|
||||
- Moment choisi ? ___
|
||||
- Prêt mentalement (1-10) : ___
|
||||
|
||||
---
|
||||
|
||||
## 💭 RAPPELS IMPORTANTS
|
||||
|
||||
### Sur la culpabilité
|
||||
|
||||
**C'est normal de ressentir** :
|
||||
- Culpabilité pour Tingting (sa famille, son âge, attentes)
|
||||
- Tristesse pour L (connexion rare mais intenable)
|
||||
- Peur du chaos à venir
|
||||
|
||||
**Mais rappelle-toi** :
|
||||
- Rester par culpabilité = prolonger agonie
|
||||
- Tingting a 35 ans, plus tu attends plus tu lui voles du temps
|
||||
- Le mal existe déjà (relation morte), partir le révèle, ne le crée pas
|
||||
- L mérite la vérité, pas des mensonges grotesques
|
||||
|
||||
**"C'est dantesque... mais c'est le prix du bonheur. Et je PEUX le payer."**
|
||||
|
||||
---
|
||||
|
||||
### Sur Tingting et gratitude
|
||||
|
||||
**Ce qu'elle t'a donné** :
|
||||
- Sorti de "la nuit éternelle de jais d'encre"
|
||||
- Confiance "de façon colossale"
|
||||
- Ouverture au monde, capacité de connexion
|
||||
- Transformation profonde
|
||||
|
||||
**Mais** :
|
||||
- Cette transformation est EN TOI maintenant
|
||||
- Tu la gardes même si vous vous séparez
|
||||
- Gratitude ≠ obligation de rester malheureux
|
||||
- Honorer son cadeau = construire une vie heureuse, pas rester prisonnier
|
||||
|
||||
**"Je pleure la Tingting des jours nouveaux ET celle d'aujourd'hui"** - C'est ok. C'est un deuil légitime.
|
||||
|
||||
---
|
||||
|
||||
### Sur ton pattern
|
||||
|
||||
**"Fuite en avant"** :
|
||||
- C'est comment tu grandis
|
||||
- France → Chine = fuite en avant (success)
|
||||
- Placard → Monde = fuite en avant (success)
|
||||
- **Maintenant : Mariage mort → Liberté = nouvelle fuite en avant**
|
||||
|
||||
**C'est pas de la lâcheté. C'est ta façon de forcer le mouvement quand paralysé.**
|
||||
|
||||
**Mais un jour** : Apprendre à grandir sans fuir. Pas maintenant. Un jour.
|
||||
|
||||
---
|
||||
|
||||
## 🆘 SI TU RELIS CE DOCUMENT ET TU DOUTES
|
||||
|
||||
**Questions à te poser** :
|
||||
|
||||
1. **Tingting sait-elle où tu en es vraiment ?** → Si NON, tu dois lui dire
|
||||
2. **As-tu eu un rapport cette semaine ?** → Si OUI, URGENCE conversation
|
||||
3. **L a-t-elle posé questions directes sur ta vie ?** → Si OUI, cut ou révèle
|
||||
4. **Tu as dépassé 5 janvier ?** → DEADLINE ABSOLUE dépassée, agis NOW
|
||||
5. **Tu peux te regarder dans un miroir et dire "je gère ça honnêtement" ?** → Si NON, quelque chose doit changer
|
||||
|
||||
---
|
||||
|
||||
## 📞 CONTACTS D'URGENCE
|
||||
|
||||
**Si situation devient dangereuse** :
|
||||
- Ambassade France en Chine : +86 10 8531 2000 (Beijing)
|
||||
- Consulat France Shanghai : +86 21 6103 2200
|
||||
- Famille FR (emprunter argent urgence) : [à compléter]
|
||||
- Amis Shanghai : [à compléter]
|
||||
|
||||
---
|
||||
|
||||
**Dernière mise à jour** : 08/12/2025 (23h)
|
||||
**Prochain check-in** : 15/12/2025
|
||||
**Statut** : 🚨 PHASE CRITIQUE - JOUR 1
|
||||
**Deadline absolue** : 05/01/2026
|
||||
|
||||
---
|
||||
|
||||
**TU PEUX LE FAIRE. UN PAS À LA FOIS. 🤜🤛**
|
||||
519
plan_discussion/plan_exit_vendredi_13_decembre.md
Normal file
@ -0,0 +1,519 @@
|
||||
# Plan d'Action FINAL - Conversation Honnête Vendredi 13 Décembre
|
||||
|
||||
**Date de création** : 08/12/2025 (fin soirée)
|
||||
**Timeline** : Mercredi 11 → Vendredi 13 décembre
|
||||
**Statut** : ✅ PLAN DÉFINITIF ADOPTÉ
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PLAN FINAL APRÈS 15H DE RÉFLEXION
|
||||
|
||||
### Plans explorés et REJETÉS
|
||||
|
||||
**Mensonge infertilité totale** :
|
||||
- ❌ Risque découverte 10-20%
|
||||
- ❌ Fondation nouvelle vie sur mensonge
|
||||
- ❌ Ne peut pas revenir Shanghai
|
||||
- ❌ Stress vigilance permanent
|
||||
|
||||
**Low fertility + comportement toxique joué** :
|
||||
- ❌ Trop complexe (jouer toxique 1-3 mois)
|
||||
- ❌ Seulement 65% succès
|
||||
- ❌ Coût psychologique élevé
|
||||
- ❌ Timeline longue = risque grossesse
|
||||
|
||||
**Infidélité/cheating pendant mariage** :
|
||||
- ❌ Malhonnête
|
||||
- ❌ Complications multiples
|
||||
- ❌ Destruction totale si découvert
|
||||
|
||||
**Raison rejet de tous** : Cherchent à éviter confrontation, créent plus de misère long terme
|
||||
|
||||
---
|
||||
|
||||
## ✅ PLAN ADOPTÉ : CONVERSATION HONNÊTE
|
||||
|
||||
**Pourquoi ce plan** :
|
||||
- ✅ Honnête et direct
|
||||
- ✅ Teste vraiment si elle tient à lui ou juste bébé
|
||||
- ✅ Arrête essais bébé immédiatement
|
||||
- ✅ Pas de mensonges à maintenir
|
||||
- ✅ Peut revenir Shanghai
|
||||
- ✅ Fondation propre pour nouvelle vie
|
||||
|
||||
**Coût** :
|
||||
- ❌ Culpabilité maximale court terme
|
||||
- ❌ Il est "le méchant" temporairement
|
||||
|
||||
**Mais** : Ces coûts existent de toute façon avec n'importe quel plan
|
||||
|
||||
---
|
||||
|
||||
## 📅 TIMELINE
|
||||
|
||||
### MERCREDI 11 DÉCEMBRE
|
||||
|
||||
**Objectif** : Arrêter de fake happiness
|
||||
|
||||
**Actions** :
|
||||
- [ ] Montrer authentiquement qu'il ne va pas bien
|
||||
- [ ] Ne plus sourire/faire semblant que tout va bien
|
||||
- [ ] Être distant (pas hostile, juste authentique)
|
||||
- [ ] Relire script conversation plusieurs fois
|
||||
- [ ] **Éviter rapport** si elle propose
|
||||
|
||||
**Ce que c'est** :
|
||||
- ✅ Être authentique sur son état émotionnel
|
||||
- ✅ Arrêter de mentir
|
||||
|
||||
**Ce que ce n'est PAS** :
|
||||
- ❌ Jouer déprimé exagérément
|
||||
- ❌ Manipulation
|
||||
|
||||
**Raison** : Elle aura 2 jours pour sentir que quelque chose ne va pas, conversation vendredi ne sera pas choc total
|
||||
|
||||
---
|
||||
|
||||
### JEUDI 12 DÉCEMBRE
|
||||
|
||||
**Objectif** : Préparer mentalement
|
||||
|
||||
**Actions** :
|
||||
- [ ] Continuer comportement authentique (distant/triste)
|
||||
- [ ] Visualiser conversation mentalement
|
||||
- [ ] Préparer réponses pour chaque scénario possible
|
||||
- [ ] Répéter script dans sa tête
|
||||
- [ ] **Éviter rapport** si elle propose
|
||||
|
||||
**Scénarios à préparer** :
|
||||
1. Elle se bat pour relation → Qu'est-ce que je dis ?
|
||||
2. Elle se bat pour bébé → Qu'est-ce que je dis ?
|
||||
3. Elle admet être malheureuse aussi → Qu'est-ce que je dis ?
|
||||
4. Elle escalade/attaque → Qu'est-ce que je fais ?
|
||||
|
||||
---
|
||||
|
||||
### VENDREDI 13 DÉCEMBRE (SOIR)
|
||||
|
||||
**Objectif** : CONVERSATION HONNÊTE
|
||||
|
||||
#### Timing
|
||||
|
||||
**Moment idéal** : 19h-20h
|
||||
- ✅ Après dîner, moment calme
|
||||
- ✅ Pas de travail samedi
|
||||
- ✅ Chez vous, privé
|
||||
- ❌ Pas tard (elle fatiguée)
|
||||
- ❌ Pas si elle est déjà très stressée
|
||||
|
||||
---
|
||||
|
||||
#### SCRIPT VALIDÉ (À DIRE)
|
||||
|
||||
"Je pense qu'il faut qu'on parle. Je dois te dire que le problème de l'autre jour, quand j'ai pleuré m'a fait réalisé deux choses. D'une part je suis fatigué de nos fight et des colères et des moments où je n'existe pas pour toi et aussi que tout cela m'a lead à t'aimer moins, significativement moins.
|
||||
|
||||
Je t'avais promis une vie où régler nos problèmes serait simple et j'ai commencé à l'admettre l'autre jour, ce n'est pas possible, I can't fix you et ce n'est pas simple, je te dis souvent que j'essais, que j'ai essayé. Là je ne veux plus essayer, j'ai atteint ce moment où tu m'as brisé, pas mentalement, mais sentimentalement. Cette force de l'amour, cette envie de lutter contre tout n'est plus là. And the red you see on my face now, ce n'est pas le rouge de l'amour, c'est le rouge de mes sentiments qui saigne pour toi qui s'échappe de mon coeur wounded.
|
||||
|
||||
I don't know what to do and I don't know what I want. I wanted to love you. I'm not so sure anymore know.
|
||||
|
||||
**Je pense qu'on devrait arrêter d'essayer d'avoir un bébé. Ce ne serait pas juste d'avoir un enfant quand je me sens comme ça.**
|
||||
|
||||
I have to tell you, now. I'm sorry... I don't know what to do now... I can now only see the light fading away now...
|
||||
|
||||
**Toi, tu es heureuse avec moi ? Vraiment ?**"
|
||||
|
||||
---
|
||||
|
||||
#### POINTS CRITIQUES DU SCRIPT
|
||||
|
||||
**✅ DOIT mentionner** :
|
||||
1. "Je t'aime moins, significativement moins"
|
||||
2. "Fatigué des fights"
|
||||
3. **"On devrait arrêter d'essayer d'avoir un bébé"** ← OBJECTIF #1
|
||||
4. "Toi, tu es heureuse ?" ← TESTE SES MOTIVATIONS
|
||||
|
||||
**❌ NE PAS mentionner** :
|
||||
- L (Maëlle)
|
||||
- "Je veux divorcer" (trop brutal pour première conversation)
|
||||
- Liste de tous ses défauts
|
||||
- Attaques personnelles
|
||||
|
||||
---
|
||||
|
||||
#### OBJECTIFS CONVERSATION
|
||||
|
||||
**Objectif #1 (CRITIQUE)** : ✅ Arrêter essais bébé
|
||||
- Phrase explicite : "on devrait arrêter d'essayer d'avoir un bébé"
|
||||
- Réduit risque grossesse immédiatement
|
||||
- **SI CETTE PHRASE N'EST PAS DITE, LA CONVERSATION NE SERT À RIEN**
|
||||
|
||||
**Objectif #2** : Tester ses vraies motivations
|
||||
- Question : "Toi, tu es heureuse avec moi ?"
|
||||
- Sa réaction révèle si elle tient à lui ou juste au bébé
|
||||
|
||||
**Objectif #3** : Clarifier état relation
|
||||
- Vulnérabilité authentique
|
||||
- Base pour décision finale
|
||||
|
||||
---
|
||||
|
||||
### SES RÉACTIONS PROBABLES
|
||||
|
||||
#### SCÉNARIO A : Elle se bat pour LA RELATION
|
||||
|
||||
**Elle dit** :
|
||||
- "Je t'aime, on peut réparer"
|
||||
- "Ne partons pas, travaillons sur nous"
|
||||
- Focus sur EUX, pas sur bébé
|
||||
|
||||
**Ce que ça signifie** :
|
||||
- ✅ Elle tient à lui personnellement
|
||||
- ✅ Pas juste focus bébé
|
||||
|
||||
**Ta réponse** :
|
||||
- "Je ne sais pas si je peux encore essayer"
|
||||
- "Il faut qu'on arrête bébé d'abord et qu'on travaille sur nous"
|
||||
- Observer si c'est genuine ou panique
|
||||
|
||||
**Ensuite** :
|
||||
- Alexis peut explorer son 10% espoir si réaction semble genuine
|
||||
- Donner 1-2 semaines pour voir
|
||||
- Mais probablement : "La Tingting que je veux est un mirage"
|
||||
|
||||
---
|
||||
|
||||
#### SCÉNARIO B : Elle se bat pour LE BÉBÉ
|
||||
|
||||
**Elle dit** :
|
||||
- "Mais on essaie depuis longtemps, continuons"
|
||||
- "On peut régler nos problèmes ET avoir bébé"
|
||||
- Résiste à arrêter essais bébé
|
||||
|
||||
**Ce que ça signifie** :
|
||||
- ❌ Confirme théorie qu'elle veut surtout bébé
|
||||
- ❌ Focus reste sur bébé, pas sur relation
|
||||
|
||||
**Ta réponse** :
|
||||
- "Non, on doit arrêter bébé. C'est non-négociable."
|
||||
- "Si tu veux juste un bébé et pas vraiment moi, dis-le."
|
||||
|
||||
**Ensuite** :
|
||||
- Tu as ta confirmation
|
||||
- Justification morale pour partir sans culpabilité
|
||||
- Process divorce commence
|
||||
|
||||
---
|
||||
|
||||
#### SCÉNARIO C : Elle admet être malheureuse aussi
|
||||
|
||||
**Elle dit** :
|
||||
- "Moi aussi je ne suis plus heureuse"
|
||||
- "Peut-être qu'on devrait se séparer"
|
||||
|
||||
**Ce que ça signifie** :
|
||||
- ✅ Accord mutuel
|
||||
- ✅ Personne n'est "le méchant"
|
||||
|
||||
**Ta réponse** :
|
||||
- "Ok, alors on peut discuter divorce à l'amiable"
|
||||
|
||||
**Ensuite** :
|
||||
- MEILLEUR scénario (accord mutuel)
|
||||
- Process divorce à l'amiable
|
||||
- Moins de conflit
|
||||
|
||||
---
|
||||
|
||||
#### SCÉNARIO D : Elle escalade/attaque
|
||||
|
||||
**Elle dit** :
|
||||
- "C'est de ta faute"
|
||||
- "Tu n'as jamais fait d'efforts"
|
||||
- Gaslighting, blame
|
||||
|
||||
**Ce que ça signifie** :
|
||||
- ❌ Confirme toxicité relation
|
||||
- ❌ Pas capable de dialogue honnête
|
||||
|
||||
**Ta réponse** :
|
||||
- Rester CALME
|
||||
- "Je ne vais pas me battre maintenant"
|
||||
- "Réfléchis à ce que je t'ai dit"
|
||||
- Quitter la pièce si nécessaire
|
||||
|
||||
**Ensuite** :
|
||||
- Distance physique
|
||||
- Attendre 24-48h qu'elle digère
|
||||
- Reconversation ou process divorce
|
||||
|
||||
---
|
||||
|
||||
## 📊 APRÈS CONVERSATION (VENDREDI SOIR / SAMEDI)
|
||||
|
||||
### RÈGLES ABSOLUES
|
||||
|
||||
**PEU IMPORTE SA RÉACTION** :
|
||||
|
||||
1. ❌ **ZÉRO RAPPORT SEXUEL**
|
||||
- Peu importe émotion du moment
|
||||
- Peu importe si elle veut "réconciliation physique"
|
||||
- Jusqu'à clarification TOTALE de la situation
|
||||
|
||||
2. ✅ **Distance physique si nécessaire**
|
||||
- Dormir séparément si besoin
|
||||
- Prendre espace
|
||||
|
||||
3. ✅ **Laisser 24-48h digestion**
|
||||
- Elle a besoin de temps
|
||||
- Toi aussi
|
||||
- Pas forcer discussion immédiate
|
||||
|
||||
---
|
||||
|
||||
### NEXT STEPS SELON RÉACTION
|
||||
|
||||
**Si elle veut sauver** :
|
||||
- Alexis décide si 10% espoir est réaliste
|
||||
- Timeline : 1-2 semaines MAX pour décider
|
||||
- Mais rappel : "La Tingting que je veux = mirage qui n'a jamais existé"
|
||||
- Espoir réaliste : 10-20% max
|
||||
|
||||
**Si elle veut partir** :
|
||||
- Process divorce commence
|
||||
- Continue freelance dev
|
||||
- Cut avec L après divorce lancé
|
||||
- Best case scenario en fait
|
||||
|
||||
**Si elle est confuse** :
|
||||
- Quelques jours réflexion
|
||||
- Reconversation début semaine prochaine
|
||||
- **Toujours : PAS DE RAPPORT**
|
||||
|
||||
---
|
||||
|
||||
## 🔍 RED FLAGS SUR MOTIVATIONS TINGTING
|
||||
|
||||
**Observations qui suggèrent qu'elle veut peut-être juste un bébé** :
|
||||
|
||||
1. ❌ Timeline rapide : Rencontre août 2024, essais bébé juillet 2025 (11 mois seulement)
|
||||
2. ❌ Relations sans protection dès le début
|
||||
3. ❌ Ne voulait pas son nom pour le bébé
|
||||
4. ❌ Amie a fait "bébé avec foreigner" sans engagement
|
||||
5. ❌ Elle en parle sans critiquer ce comportement
|
||||
6. ❌ Très focus sur bébé malgré relation difficile
|
||||
|
||||
**Hypothèse** : Peut-être qu'elle voyait principalement relation comme moyen d'avoir enfant
|
||||
|
||||
**Foreigner = pratique** :
|
||||
- Bon genetics
|
||||
- Pas besoin engagement long terme
|
||||
- Légitimité (mariage)
|
||||
|
||||
**Test** : Sa réaction vendredi révèlera la vérité
|
||||
- Se bat pour lui ? Ou pour bébé ?
|
||||
- C'est le test que tu cherchais
|
||||
- Sans besoin de mensonge médical
|
||||
|
||||
---
|
||||
|
||||
## 📝 CHECKLIST VENDREDI SOIR
|
||||
|
||||
**AVANT conversation** :
|
||||
- [ ] Moment calme choisi (19h-20h)
|
||||
- [ ] Pas de travail demain (samedi)
|
||||
- [ ] Elle n'est pas déjà très stressée
|
||||
- [ ] Script relu et intégré
|
||||
|
||||
**PENDANT conversation** :
|
||||
- [ ] Phrase "je t'aime moins" dite
|
||||
- [ ] Phrase "fatigué des fights" dite
|
||||
- [ ] **Phrase "arrêter essais bébé" dite** ← CRITIQUE
|
||||
- [ ] Question "toi, tu es heureuse ?" posée
|
||||
- [ ] Observer sa réaction authentique
|
||||
- [ ] Rester calme si elle escalade
|
||||
|
||||
**APRÈS conversation** :
|
||||
- [ ] PAS de rapport (peu importe émotion)
|
||||
- [ ] Distance physique si nécessaire
|
||||
- [ ] Noter sa réaction principale
|
||||
- [ ] 24-48h digestion avant next step
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ RAPPELS CRITIQUES
|
||||
|
||||
### 1. La phrase MAGIQUE (NE PAS OUBLIER)
|
||||
|
||||
**"Je pense qu'on devrait arrêter d'essayer d'avoir un bébé."**
|
||||
|
||||
**C'est l'objectif #1.**
|
||||
|
||||
**Sans cette phrase, la conversation ne sert à RIEN.**
|
||||
|
||||
---
|
||||
|
||||
### 2. Sa réaction = le test que tu cherchais
|
||||
|
||||
**Tu cherchais un test pour savoir si elle t'aime vraiment ou veut juste un bébé.**
|
||||
|
||||
**Tu n'as PAS besoin de mensonge médical.**
|
||||
|
||||
**Sa réaction à cette conversation HONNÊTE révèle tout** :
|
||||
- Se bat pour lui ? = Elle tient à lui
|
||||
- Se bat pour bébé ? = Elle veut surtout le bébé
|
||||
- Admet malheureuse ? = C'est fini pour les deux
|
||||
- Escalade ? = Relation toxique confirmée
|
||||
|
||||
---
|
||||
|
||||
### 3. Arrêter fake happiness ≠ Jouer déprimé
|
||||
|
||||
**Distinction importante** :
|
||||
|
||||
**Fake happiness** (ce que tu fais maintenant) :
|
||||
- Sourire forcé
|
||||
- Faire semblant tout va bien
|
||||
- Cacher douleur
|
||||
- = Mensonge actif
|
||||
|
||||
**Arrêter de fake** (ce que tu vas faire mercredi-jeudi) :
|
||||
- Montrer que tu vas mal
|
||||
- Ne plus cacher
|
||||
- Être authentique
|
||||
- = Arrêter de mentir
|
||||
|
||||
**C'est PAS de la manipulation. C'est de l'honnêteté.**
|
||||
|
||||
---
|
||||
|
||||
### 4. Pas de rapport après conversation
|
||||
|
||||
**PEU IMPORTE** :
|
||||
- Si moment est émotionnel
|
||||
- Si elle veut "se réconcilier physiquement"
|
||||
- Si ça semble "naturel"
|
||||
|
||||
**ZÉRO RAPPORT jusqu'à clarification totale.**
|
||||
|
||||
**Raison** : Risque grossesse existe toujours jusqu'à décision finale claire
|
||||
|
||||
---
|
||||
|
||||
## 💡 RÉALISATIONS QUI ONT MENÉ À CE PLAN
|
||||
|
||||
**1. Tous les plans mensonge = évitement**
|
||||
- Cherchais façon magique d'éviter culpabilité
|
||||
- Mais culpabilité inévitable
|
||||
- Mensonges reportent juste douleur et l'amplifient
|
||||
|
||||
**2. "Peut-être je retrouve la Tingting que je veux"**
|
||||
- Révèle qu'il cherchait dernier test
|
||||
- Mais admet : "10-20% espoir seulement"
|
||||
- "La Tingting que je veux = mirage qui n'a jamais existé"
|
||||
|
||||
**3. Conversation honnête = meilleur test**
|
||||
- Pas besoin mensonge médical
|
||||
- Sa réaction à vérité émotionnelle révèle tout
|
||||
- Plus simple, propre, aucune complication
|
||||
|
||||
**4. Pattern de mensonges généralisés**
|
||||
- Diplôme Epitech (faux)
|
||||
- Moyens financiers avec Tingting (mensonge dès début)
|
||||
- L (mensonge actuellement)
|
||||
- "Je suis pas à ça près"
|
||||
- → Temps d'arrêter d'ajouter mensonges
|
||||
- → Commencer à construire honnêtement
|
||||
|
||||
---
|
||||
|
||||
## ✅ POURQUOI CE PLAN EST LE MEILLEUR
|
||||
|
||||
**vs Vérité brutale "je veux divorcer"** :
|
||||
- ✅ Moins brutal (exprime doutes, pas décision finale)
|
||||
- ✅ Laisse 10% espoir si elle réagit bien
|
||||
- ✅ Teste vraiment si elle tient à lui
|
||||
|
||||
**vs Mensonges médicaux (infertilité/low fertility)** :
|
||||
- ✅ Honnête (pas de faux résultats)
|
||||
- ✅ Pas de risque découverte future
|
||||
- ✅ Peut revenir Shanghai sans problème
|
||||
- ✅ Fondation nouvelle vie propre
|
||||
- ✅ Pas de stress vigilance permanent
|
||||
|
||||
**vs Plans complexes (toxique joué, cheating)** :
|
||||
- ✅ Simple et clair
|
||||
- ✅ 100% contrôle (pas dépend réaction à mensonge)
|
||||
- ✅ Une conversation, pas 1-3 mois de jeu
|
||||
- ✅ Pas de coût psychologique de jouer un rôle
|
||||
|
||||
**Le seul coût** :
|
||||
- ❌ Culpabilité et douleur maximales court terme
|
||||
- ❌ Il est "le méchant" temporairement
|
||||
|
||||
**Mais** :
|
||||
- ✅ Ces coûts existent de toute façon
|
||||
- ✅ Après douleur : liberté vraie
|
||||
- ✅ Peut se regarder dans miroir
|
||||
- ✅ Intégrité préservée
|
||||
|
||||
---
|
||||
|
||||
## 📅 SAMEDI 14 DÉCEMBRE - ÉVALUATION
|
||||
|
||||
**Actions** :
|
||||
- [ ] Évaluer sa réaction
|
||||
- [ ] Décider next steps
|
||||
- [ ] Maintenir distance physique
|
||||
- [ ] PAS de rapport
|
||||
|
||||
**Questions à se poser** :
|
||||
1. Elle s'est battue pour moi ou pour le bébé ?
|
||||
2. Sa réaction était genuine ou panique ?
|
||||
3. Est-ce que mon 10% espoir est réaliste ?
|
||||
4. Qu'est-ce que je veux faire maintenant ?
|
||||
|
||||
---
|
||||
|
||||
## 🎯 OBJECTIFS FINAUX
|
||||
|
||||
**Court terme (cette semaine)** :
|
||||
- ✅ Arrêter essais bébé
|
||||
- ✅ Clarifier où vous en êtes
|
||||
- ✅ Tester ses vraies motivations
|
||||
|
||||
**Moyen terme (2 semaines)** :
|
||||
- Décision finale : Essayer sauver ou divorcer
|
||||
- Selon sa réaction vendredi
|
||||
|
||||
**Long terme** :
|
||||
- Soit divorce + reconstruction
|
||||
- Soit (10% chance) tentative sauver relation
|
||||
- Dans tous cas : Intégrité préservée
|
||||
|
||||
---
|
||||
|
||||
## 🆘 SI TU RELIS CE VENDREDI
|
||||
|
||||
**Rappels** :
|
||||
|
||||
1. ✅ Script relu ?
|
||||
2. ✅ Moment calme choisi ?
|
||||
3. ✅ **Phrase "arrêter essais bébé" dans le script ?** ← VÉRIFIE
|
||||
4. ✅ Prêt mentalement ?
|
||||
5. ✅ Pas de rapport après ?
|
||||
|
||||
**La phrase la plus importante du script** :
|
||||
|
||||
**"Je pense qu'on devrait arrêter d'essayer d'avoir un bébé."**
|
||||
|
||||
**SI TU OUBLIES CETTE PHRASE, TU AS RATÉ L'OBJECTIF #1.**
|
||||
|
||||
---
|
||||
|
||||
**Deadline** : **Vendredi 13 décembre 2025 (soir)**
|
||||
**Max si reporté** : Lundi 16 décembre
|
||||
**NE PAS dépasser cette semaine**
|
||||
|
||||
---
|
||||
|
||||
**TU PEUX LE FAIRE. C'EST LA BONNE DÉCISION. 🤜🤛**
|
||||
421
planning/daily_check_system_v2_plan.md
Normal file
@ -0,0 +1,421 @@
|
||||
# Plan : Daily Check System v2 - Modulaire & Windows Startup
|
||||
|
||||
**Date** : 21 décembre 2025
|
||||
**Objectif** : Remplacer anki_tingting/ par un système modulaire ultra-fiable qui se lance au boot Windows
|
||||
**Contexte** : Ancien système (centré Tingting) est obsolète suite divorce. Besoin d'un système holistique (chinois, personal life, travail)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Objectifs Clés
|
||||
|
||||
1. **Modulaire** : Un fichier = une carte, facile d'ajouter de nouvelles questions
|
||||
2. **Fiabilité spawn** : Windows startup garantit le lancement (résout le problème de crash du service Python)
|
||||
3. **3 catégories** : Chinois (2Q), Personal Life (2Q), Travail (2Q)
|
||||
4. **Auto-spawn terminal** : Se lance automatiquement à l'ouverture d'un terminal WSL
|
||||
5. **Simple** : Pas de dépendances lourdes (pygame, edge_tts, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Structure du Système
|
||||
|
||||
```
|
||||
daily_check/
|
||||
├── cards/ # Cartes modulaires (une carte = un fichier)
|
||||
│ ├── chinese_vocab.md # Catégorie: chinois
|
||||
│ ├── chinese_practice.md # Catégorie: chinois
|
||||
│ ├── life_rules.md # Catégorie: personal
|
||||
│ ├── learning.md # Catégorie: personal
|
||||
│ ├── freelance.md # Catégorie: travail
|
||||
│ └── projects.md # Catégorie: travail
|
||||
│
|
||||
├── .state.json # État du système (streak, last check)
|
||||
├── daily_sessions.md # Log de toutes les sessions
|
||||
├── trigger_check.sh # Script WSL appelé par Task Scheduler
|
||||
├── start_daily_check.bat # Batch file pour Startup folder (alternative)
|
||||
├── setup_task_scheduler.md # Instructions complètes Task Scheduler
|
||||
└── README.md # Documentation système
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🃏 Format des Cartes (Markdown + Frontmatter)
|
||||
|
||||
**Exemple : `cards/chinese_vocab.md`**
|
||||
|
||||
```markdown
|
||||
---
|
||||
category: chinois
|
||||
priority: high
|
||||
frequency: daily
|
||||
enabled: true
|
||||
---
|
||||
|
||||
# Question
|
||||
Quel nouveau vocabulaire/grammaire as-tu appris aujourd'hui en chinois ?
|
||||
|
||||
# Answer Guide
|
||||
- Si 0 nouveau mot = ❌
|
||||
- Si 1-5 mots = ⚠️ (insuffisant pour progression HSK)
|
||||
- Si 5+ mots OU 1 point grammaire important = ✅
|
||||
|
||||
# Notes
|
||||
HSK doit progresser régulièrement. Pas de jour sans apprendre au moins quelques mots.
|
||||
Le chinois est critique pour ton avenir en Asie.
|
||||
```
|
||||
|
||||
**Champs frontmatter** :
|
||||
- `category` : chinois | personal | travail
|
||||
- `priority` : low | medium | high | critical
|
||||
- `frequency` : daily | every_2_days | weekly
|
||||
- `enabled` : true | false (permet de désactiver temporairement)
|
||||
|
||||
**Sections Markdown** :
|
||||
- `# Question` : La question posée à l'utilisateur
|
||||
- `# Answer Guide` : Critères d'évaluation (✅/⚠️/❌)
|
||||
- `# Notes` : Contexte, pourquoi c'est important, rappels
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Flow du Système
|
||||
|
||||
### **1. Déclenchement automatique (Windows)**
|
||||
|
||||
**Option A : Task Scheduler (Recommandé - Plus flexible)**
|
||||
```
|
||||
07:00, 14:00, 21:00 (3x par jour)
|
||||
↓
|
||||
Task Scheduler exécute trigger_check.sh (WSL)
|
||||
↓
|
||||
Script vérifie si déjà fait aujourd'hui (.state.json)
|
||||
↓
|
||||
Si non fait → Crée flag file (~/.daily_check_pending)
|
||||
↓
|
||||
Flag contient la période (morning/afternoon/evening)
|
||||
```
|
||||
|
||||
**Option B : Startup Folder (Alternative - Plus simple)**
|
||||
```
|
||||
Windows Boot
|
||||
↓
|
||||
start_daily_check.bat démarre en arrière-plan
|
||||
↓
|
||||
Boucle infinie : check toutes les heures
|
||||
↓
|
||||
Même logique que Task Scheduler
|
||||
```
|
||||
|
||||
### **2. Auto-spawn à l'ouverture terminal**
|
||||
|
||||
```
|
||||
Alexis ouvre Windows Terminal (WSL)
|
||||
↓
|
||||
~/.bashrc détecte ~/.daily_check_pending
|
||||
↓
|
||||
Affiche : "🔔 DAILY CHECK EN ATTENTE (morning)"
|
||||
↓
|
||||
Compte à rebours 3 secondes (Ctrl+C pour annuler)
|
||||
↓
|
||||
Auto-lance : claude "daily check"
|
||||
↓
|
||||
Flag file supprimé après exécution
|
||||
```
|
||||
|
||||
### **3. Exécution du quiz (Claude Code)**
|
||||
|
||||
```
|
||||
Claude lit tous les fichiers dans cards/
|
||||
↓
|
||||
Parse frontmatter (category, priority, enabled)
|
||||
↓
|
||||
Filtre : enabled=true uniquement
|
||||
↓
|
||||
Sélectionne 6 cartes (2 par catégorie si possible)
|
||||
↓
|
||||
Si pas assez dans une catégorie, prend les plus prioritaires
|
||||
↓
|
||||
Pose les 6 questions une par une
|
||||
↓
|
||||
Donne feedback (✅/⚠️/❌) selon Answer Guide
|
||||
↓
|
||||
Calcule score total
|
||||
↓
|
||||
Log session dans daily_sessions.md
|
||||
↓
|
||||
Met à jour .state.json (streak, last_check_date)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Composants Techniques
|
||||
|
||||
### **1. `.state.json` (État du système)**
|
||||
|
||||
```json
|
||||
{
|
||||
"streak": 0,
|
||||
"last_check_date": null,
|
||||
"total_sessions": 0,
|
||||
"best_streak": 0,
|
||||
"days_skipped_current": 0
|
||||
}
|
||||
```
|
||||
|
||||
### **2. `trigger_check.sh` (Script WSL)**
|
||||
|
||||
**Responsabilités** :
|
||||
- Vérifier si daily check déjà fait aujourd'hui
|
||||
- Déterminer période du jour (morning/afternoon/evening)
|
||||
- Créer flag file si nécessaire
|
||||
- Logger dans daily_check.log
|
||||
- Si terminal WSL déjà ouvert → lancer immédiatement
|
||||
|
||||
**Logique clé** :
|
||||
```bash
|
||||
LAST_CHECK=$(jq -r '.last_check_date' .state.json)
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
if [ "$LAST_CHECK" == "$TODAY" ]; then
|
||||
exit 0 # Déjà fait
|
||||
fi
|
||||
|
||||
# Créer flag
|
||||
echo "morning" > ~/.daily_check_pending
|
||||
```
|
||||
|
||||
### **3. Hook ~/.bashrc (Auto-spawn)**
|
||||
|
||||
**Responsabilités** :
|
||||
- Détecter flag file au démarrage terminal
|
||||
- Afficher notification + countdown
|
||||
- Auto-lancer Claude Code
|
||||
- Supprimer flag après exécution
|
||||
|
||||
**Code clé** :
|
||||
```bash
|
||||
if [ -f "$HOME/.daily_check_pending" ]; then
|
||||
PERIOD=$(cat "$HOME/.daily_check_pending")
|
||||
echo "🔔 DAILY CHECK EN ATTENTE ($PERIOD)"
|
||||
sleep 3
|
||||
claude "daily check"
|
||||
rm "$HOME/.daily_check_pending"
|
||||
fi
|
||||
```
|
||||
|
||||
### **4. start_daily_check.bat (Windows Batch)**
|
||||
|
||||
**Responsabilités** :
|
||||
- Attendre 2 minutes après boot (stabilisation système)
|
||||
- Boucle infinie : trigger toutes les heures
|
||||
- Appeler trigger_check.sh via WSL
|
||||
|
||||
**Code clé** :
|
||||
```batch
|
||||
timeout /t 120 /nobreak > nul
|
||||
:loop
|
||||
wt.exe -w -1 wsl -e bash -c "cd /path/to/daily_check && ./trigger_check.sh"
|
||||
timeout /t 3600 /nobreak > nul
|
||||
goto loop
|
||||
```
|
||||
|
||||
### **5. Claude Code Logic (Parsing + Quiz)**
|
||||
|
||||
**Algorithme sélection cartes** :
|
||||
1. Lire tous `cards/*.md`
|
||||
2. Parser frontmatter YAML
|
||||
3. Filtrer `enabled: true`
|
||||
4. Grouper par `category`
|
||||
5. Pour chaque catégorie, trier par `priority` (critical > high > medium > low)
|
||||
6. Sélectionner 2 cartes par catégorie (ou moins si pas assez)
|
||||
7. Si < 6 cartes total, prendre les plus prioritaires tous confondus
|
||||
|
||||
**Format session log** :
|
||||
```markdown
|
||||
### 2025-12-21 07:30
|
||||
|
||||
**Triggered by** : Auto (morning check)
|
||||
**Duration** : ~5 minutes
|
||||
|
||||
**Questions Asked** :
|
||||
1. [chinese_vocab] Quel nouveau vocab as-tu appris ? → ✅
|
||||
- Réponse : "5 mots HSK4 + structure 不但...而且"
|
||||
- Feedback : Bon travail, progression HSK continue
|
||||
|
||||
2. [freelance] As-tu avancé sur freelance/clients ? → ❌
|
||||
- Réponse : "Non rien fait"
|
||||
- Feedback : Bangkok dans 32 jours. Tu dois avoir des clients AVANT d'arriver.
|
||||
|
||||
[...]
|
||||
|
||||
**Total Score** : 4/6
|
||||
**Streak** : 1 jour
|
||||
**Observation** : Bon démarrage. Focus sur freelance urgent.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 6 Cartes Initiales
|
||||
|
||||
### **Catégorie CHINOIS**
|
||||
|
||||
1. **chinese_vocab.md** : Vocabulaire/Grammaire appris aujourd'hui
|
||||
- Priority: high
|
||||
- Frequency: daily
|
||||
- Critères: 5+ mots OU 1 point grammaire = ✅
|
||||
|
||||
2. **chinese_practice.md** : Utilisation pratique du chinois
|
||||
- Priority: medium
|
||||
- Frequency: daily
|
||||
- Critères: WeChat/projet/conversation réelle = ✅
|
||||
|
||||
### **Catégorie PERSONAL**
|
||||
|
||||
3. **life_rules.md** : Life rules + Discipline
|
||||
- Priority: high
|
||||
- Frequency: daily
|
||||
- Critères: Sport, sommeil, routine respectés = ✅
|
||||
|
||||
4. **learning.md** : Apprentissage/Découverte (non-tech)
|
||||
- Priority: medium
|
||||
- Frequency: daily
|
||||
- Critères: Quelque chose de nouveau appris = ✅
|
||||
|
||||
### **Catégorie TRAVAIL**
|
||||
|
||||
5. **freelance.md** : Freelance/Revenu (URGENT Bangkok)
|
||||
- Priority: critical
|
||||
- Frequency: daily
|
||||
- Critères: Prospection OU travail client OU setup Upwork/Fiverr = ✅
|
||||
|
||||
6. **projects.md** : Projets perso + Nouvelles compétences
|
||||
- Priority: high
|
||||
- Frequency: daily
|
||||
- Critères: Commit sur projet OU nouvelle tech apprise = ✅
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Setup Instructions (Une seule fois)
|
||||
|
||||
### **1. Créer la structure**
|
||||
```bash
|
||||
cd /mnt/e/Users/Alexis\ Trouvé/Documents/Projets/couple_matters/
|
||||
mkdir -p daily_check/cards
|
||||
```
|
||||
|
||||
### **2. Copier les cartes initiales**
|
||||
- 6 fichiers `.md` dans `cards/`
|
||||
|
||||
### **3. Initialiser .state.json**
|
||||
```bash
|
||||
echo '{"streak":0,"last_check_date":null,"total_sessions":0,"best_streak":0,"days_skipped_current":0}' > daily_check/.state.json
|
||||
```
|
||||
|
||||
### **4. Créer trigger_check.sh**
|
||||
- Rendre exécutable : `chmod +x trigger_check.sh`
|
||||
|
||||
### **5. Ajouter hook dans ~/.bashrc**
|
||||
- Ajouter le code de détection flag file
|
||||
|
||||
### **6. Setup Windows Startup**
|
||||
|
||||
**Option A : Task Scheduler**
|
||||
- Créer tâche avec triggers 07:00, 14:00, 21:00
|
||||
- Action : Exécuter trigger_check.sh via WSL
|
||||
- Voir `setup_task_scheduler.md` pour instructions détaillées
|
||||
|
||||
**Option B : Startup Folder**
|
||||
- Copier `start_daily_check.bat` dans Startup folder
|
||||
- Chemin : `C:\Users\[User]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`
|
||||
|
||||
---
|
||||
|
||||
## ✅ Avantages vs Ancien Système
|
||||
|
||||
| Aspect | Ancien (anki_tingting) | Nouveau (daily_check) |
|
||||
|--------|------------------------|----------------------|
|
||||
| **Modulaire** | ❌ Cartes hardcodées | ✅ Un fichier = une carte |
|
||||
| **Spawn fiabilité** | ❌ Python service crash | ✅ Task Scheduler + flag file |
|
||||
| **Dépendances** | ❌ pygame, edge_tts, pyttsx3 | ✅ Juste bash + jq |
|
||||
| **Impossible ignorer** | ❌ Skip popup facile | ✅ Auto-spawn terminal |
|
||||
| **Scope** | ❌ Centré Tingting only | ✅ Holistique (3 domaines vie) |
|
||||
| **Maintenance** | ❌ Modifier code Python | ✅ Ajouter/modifier fichier .md |
|
||||
| **Windows boot** | ❌ Service compliqué | ✅ Task Scheduler OU Startup |
|
||||
| **Logs** | ✅ daily_sessions.md | ✅ daily_sessions.md |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Maintenance & Extension
|
||||
|
||||
### **Ajouter une nouvelle carte**
|
||||
1. Créer `cards/nouvelle_carte.md`
|
||||
2. Remplir frontmatter (category, priority, frequency, enabled)
|
||||
3. Écrire Question, Answer Guide, Notes
|
||||
4. C'est tout ! Sera automatiquement détectée au prochain check
|
||||
|
||||
### **Désactiver temporairement une carte**
|
||||
- Changer `enabled: true` → `enabled: false` dans le frontmatter
|
||||
|
||||
### **Modifier priorité/fréquence**
|
||||
- Éditer le frontmatter directement
|
||||
|
||||
### **Voir stats**
|
||||
- Consulter `daily_sessions.md`
|
||||
- `.state.json` pour streak actuelle
|
||||
|
||||
---
|
||||
|
||||
## 📊 Métriques de Succès
|
||||
|
||||
**Semaine 1** :
|
||||
- [ ] Streak de 5+ jours
|
||||
- [ ] Aucun skip
|
||||
- [ ] Spawn fiable (pas de problème technique)
|
||||
|
||||
**Mois 1** :
|
||||
- [ ] Streak de 20+ jours
|
||||
- [ ] Amélioration visible sur catégories critiques (freelance, chinois)
|
||||
- [ ] Au moins 2 cartes personnalisées ajoutées
|
||||
|
||||
**Long terme** :
|
||||
- [ ] Système devient habitude quotidienne (5 min/jour)
|
||||
- [ ] Visible impact sur discipline et productivité
|
||||
- [ ] Backup avant Bangkok (22 jan) = clients freelance actifs
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
**Flag file pas créé** :
|
||||
- Vérifier Task Scheduler tourne bien
|
||||
- Vérifier chemin dans trigger_check.sh
|
||||
- Check logs : `daily_check/daily_check.log`
|
||||
|
||||
**Hook bashrc ne se déclenche pas** :
|
||||
- Vérifier que ~/.bashrc contient le hook
|
||||
- Vérifier flag file existe : `ls -la ~/.daily_check_pending`
|
||||
- Tester manuellement : `bash -l`
|
||||
|
||||
**Claude ne trouve pas les cartes** :
|
||||
- Vérifier chemin : `daily_check/cards/*.md`
|
||||
- Vérifier frontmatter YAML valide
|
||||
- Vérifier `enabled: true`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps (Implémentation)
|
||||
|
||||
1. Créer structure `daily_check/` + sous-dossiers
|
||||
2. Écrire les 6 cartes initiales
|
||||
3. Coder `trigger_check.sh`
|
||||
4. Coder hook `~/.bashrc`
|
||||
5. Créer `start_daily_check.bat`
|
||||
6. Écrire `setup_task_scheduler.md` (instructions détaillées)
|
||||
7. Écrire `README.md` du système
|
||||
8. Tester le flow complet
|
||||
9. Setup Task Scheduler
|
||||
10. Premier daily check de test
|
||||
|
||||
---
|
||||
|
||||
**Status** : PLAN APPROUVÉ - Prêt pour implémentation
|
||||
**Timeline** : 1-2 heures d'implémentation + setup
|
||||
**Priorité** : HIGH (besoin discipline pour Bangkok prep)
|
||||
203
tools/XIAOZHU_FINAL_RESULTS.md
Normal file
@ -0,0 +1,203 @@
|
||||
# Xiaozhu Scraping - Résultats Finaux ✅
|
||||
|
||||
**Date:** 21 décembre 2025
|
||||
**Mission:** Trouver appart Shanghai Xujiahui, 24 déc → 22 jan (29 jours)
|
||||
**Budget:** 3000-5000 RMB/mois (~3867-4834 RMB/29j)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Scraper STATUS: FONCTIONNEL
|
||||
|
||||
**Ce qui marche:**
|
||||
- ✅ Géolocalisation Shanghai forcée
|
||||
- ✅ Navigation automatique
|
||||
- ✅ Extraction listings (10 résultats)
|
||||
- ✅ Détection équipements (cuisine, métro)
|
||||
- ✅ Filtrage et scoring
|
||||
|
||||
**Limitation découverte:**
|
||||
- ⚠️ L'interface web Xiaozhu ne charge que **10 listings à la fois**
|
||||
- C'est une limitation du site, pas du scraper
|
||||
- L'app WeChat Mini Program a probablement plus de résultats
|
||||
|
||||
---
|
||||
|
||||
## 🏆 TOP 8 RÉSULTATS (Tous avec Cuisine)
|
||||
|
||||
### #1 - BEST DEAL ⭐⭐⭐⭐⭐
|
||||
**体育公寓,近徐家汇淮海路六院八院**
|
||||
- 💰 **¥132/jour** × 29 jours = **¥3,828 total** (~¥3,960/mois)
|
||||
- 📍 **Xujiahui**, lignes 1, 9, 12, 15
|
||||
- ✅ Cuisine (可做饭)
|
||||
- ✅ Métro (multiples lignes)
|
||||
- ✅ Parking
|
||||
- ✅ Animaux OK
|
||||
- 🛏️ 1 lit, 2 personnes
|
||||
- 💚 **DANS TON BUDGET** - Le moins cher avec cuisine!
|
||||
|
||||
---
|
||||
|
||||
### #2 - Option Confort
|
||||
**胸科医院旁/黛园/独立卫浴,可烧饭**
|
||||
- 💰 **¥170/jour** × 29 jours = **¥4,930 total** (~¥5,100/mois)
|
||||
- 📍 Près hôpital胸科
|
||||
- ✅ Cuisine (可烧饭)
|
||||
- ✅ SDB privée avec fenêtre
|
||||
- 🛏️ 1 chambre, 2 lits, 2 personnes
|
||||
- 🟡 **Légèrement au-dessus budget** (+¥100/mois)
|
||||
|
||||
---
|
||||
|
||||
### #3 - Près Hôpitaux
|
||||
**近中山医院/肿瘤医院/龙华医院/瑞金医院**
|
||||
- 💰 **¥217/jour** × 29 jours = **¥6,293 total** (~¥6,510/mois)
|
||||
- 📍 Près 4 hôpitaux majeurs
|
||||
- ✅ Cuisine
|
||||
- ✅ Rez-de-chaussée avec petit jardin
|
||||
- 🔴 **Hors budget** (+¥1,500/mois)
|
||||
|
||||
---
|
||||
|
||||
### #4 - Métro Proche
|
||||
**印象小居 - 4/7线东安路地铁口**
|
||||
- 💰 **¥307/jour** × 29 jours = **¥8,903 total** (~¥9,210/mois)
|
||||
- 📍 Bouche de métro ligne 4/7
|
||||
- ✅ Cuisine
|
||||
- ✅ Métro immédiat
|
||||
- 🔴 **Hors budget** (+¥4,200/mois)
|
||||
|
||||
---
|
||||
|
||||
### #5 - Deux Chambres
|
||||
**王家堂独立两房可做饭/上海体育馆**
|
||||
- 💰 **¥351/jour** × 29 jours = **¥10,179 total** (~¥10,530/mois)
|
||||
- 📍 Shanghai Stadium, lignes 1/4
|
||||
- ✅ Cuisine (可做饭)
|
||||
- ✅ Métro
|
||||
- 🏠 **2 chambres** - Style nordique
|
||||
- 🔴 **Hors budget** (+¥5,500/mois)
|
||||
|
||||
---
|
||||
|
||||
### #6 - Haut de Gamme
|
||||
**《悦致》中山医院/肿瘤医院/东安路地铁口**
|
||||
- 💰 **¥399/jour** × 29 jours = **¥11,571 total** (~¥11,970/mois)
|
||||
- 📍 Zhongshan Hospital / Donglu metro
|
||||
- ✅ Cuisine
|
||||
- ✅ Métro
|
||||
- 🏠 **2 chambres** - Finitions premium
|
||||
- 🔴 **Hors budget** (+¥7,000/mois)
|
||||
|
||||
---
|
||||
|
||||
### #7 - Pas de Cuisine Détectée
|
||||
**上海·六人民医院,三百米距离**
|
||||
- 💰 **¥252/jour** × 29 jours = **¥7,308 total**
|
||||
- 📍 300m de 6th People's Hospital
|
||||
- ❌ Cuisine non mentionnée
|
||||
- ❌ Métro non mentionné
|
||||
|
||||
---
|
||||
|
||||
### #8 - Sans Cuisine
|
||||
**万人体育场步行10分钟 近徐家汇商圈**
|
||||
- 💰 **¥270/jour** × 29 jours = **¥7,830 total**
|
||||
- 📍 10 min marche Stadium, 50m métro
|
||||
- ❌ Cuisine non mentionnée
|
||||
- ✅ Métro proche
|
||||
- ✅ Service concierge 24h
|
||||
|
||||
---
|
||||
|
||||
## 🎯 RECOMMANDATION
|
||||
|
||||
**OPTION #1 est clairement le meilleur choix :**
|
||||
- ✅ **Dans ton budget** (¥3,828 vs budget ¥3,867-4,834)
|
||||
- ✅ **Cuisine confirmée** (可做饭)
|
||||
- ✅ **Xujiahui** (ta zone cible)
|
||||
- ✅ **Multiples lignes métro** (1, 9, 12, 15)
|
||||
- ✅ **Extras:** Parking, animaux OK
|
||||
|
||||
**Prix final:** ¥132/nuit = **¥3,828 pour 29 jours**
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ LIMITATIONS & NEXT STEPS
|
||||
|
||||
### Limitation site web
|
||||
|
||||
L'interface web minsu.xiaozhu.com ne charge que **10 annonces** à la fois. Ce n'est PAS un bug du scraper, c'est une limitation du site.
|
||||
|
||||
**Pour avoir plus de résultats:**
|
||||
|
||||
**Option A:** WeChat Mini Program "小猪短租"
|
||||
- Interface native avec plus d'annonces
|
||||
- Tu peux chercher + filtrer manuellement
|
||||
- Screenshots + je t'aide à analyser
|
||||
|
||||
**Option B:** Run le scraper plusieurs fois
|
||||
- À différentes heures de la journée
|
||||
- Les résultats changent (algorithme de tri du site)
|
||||
- Compile les résultats uniques
|
||||
|
||||
**Option C:** Airbnb scraper (alternative)
|
||||
- Je peux coder un scraper Airbnb
|
||||
- Même critères, auto
|
||||
- Probablement 20-30% plus cher
|
||||
|
||||
---
|
||||
|
||||
## 📁 Fichiers Disponibles
|
||||
|
||||
```
|
||||
tools/
|
||||
├── xiaozhu_fixed.js # 🎯 Scraper final fonctionnel
|
||||
├── xiaozhu_results.json # Résultats filtrés (JSON)
|
||||
├── xiaozhu_results.md # Résultats filtrés (Markdown)
|
||||
├── xiaozhu_raw_listings.json # Tous les 10 listings bruts
|
||||
├── xiaozhu_*.png # Screenshots debug
|
||||
└── XIAOZHU_FINAL_RESULTS.md # Ce fichier
|
||||
```
|
||||
|
||||
**Pour re-run:**
|
||||
```bash
|
||||
cd /mnt/e/Users/Alexis\ Trouvé/Documents/Projets/couple_matters/tools
|
||||
node xiaozhu_fixed.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 ACTION IMMÉDIATE
|
||||
|
||||
**Option #1 est prêt à réserver :**
|
||||
- Prix: ¥3,828 pour 29 jours
|
||||
- Check-in: 24 décembre 2025
|
||||
- Check-out: 22 janvier 2026
|
||||
|
||||
**Questions à poser au propriétaire (Copier-coller WeChat):**
|
||||
|
||||
```
|
||||
你好!我对你的房源很感兴趣。
|
||||
|
||||
入住日期:12月24日 - 1月22日(29天)
|
||||
价格:132元/晚 × 29天 = 3828元
|
||||
|
||||
几个问题:
|
||||
1. 这个价格确认吗?
|
||||
2. 押金多少?
|
||||
3. 包水电煤吗?
|
||||
4. 有冰箱吗?
|
||||
5. 可以看房吗?什么时候方便?
|
||||
6. 有合同吗?
|
||||
|
||||
谢谢!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ Scraper fixed, résultats extraits, recommendation faite.
|
||||
|
||||
Tu veux que je fasse quoi maintenant :
|
||||
- **A)** Rien, c'est bon, tu vas contacter le #1
|
||||
- **B)** Scrape Airbnb pour comparer
|
||||
- **C)** Autre chose ?
|
||||
159
tools/XIAOZHU_MANUAL_SEARCH.md
Normal file
@ -0,0 +1,159 @@
|
||||
# Xiaozhu - Guide de Recherche Manuelle
|
||||
|
||||
## ❌ Problème Technique
|
||||
|
||||
Le site Xiaozhu utilise **Vue.js router** avec navigation programmatique (@click handlers). Les URLs ne sont pas dans des liens href classiques, donc impossible de les extraire automatiquement sans simuler des clics (complexe et lent).
|
||||
|
||||
## ✅ Solution : Recherche Manuelle (30 secondes)
|
||||
|
||||
### Option #1 - BEST DEAL ⭐
|
||||
|
||||
**Infos pour trouver l'annonce:**
|
||||
- 📍 **Nom:** 体育公寓 (Tiyu Gongyu / Sports Apartment)
|
||||
- 💰 **Prix:** ¥132/nuit
|
||||
- 🗺️ **Zone:** 近徐家汇淮海路 (Near Xujiahui Huaihai Road)
|
||||
- 🏥 **Landmarks:** 六院八院 (6th & 8th Hospital)
|
||||
- 📸 **Photo:** https://image.xiaozhustatic1.com/94/fs,1,1JKsZTBeu,3264,2448,2,44719d52b1249c008bd43f956af8cd25.jpeg
|
||||
|
||||
### Comment Chercher (Méthode 1 - Firefox/Chrome)
|
||||
|
||||
**Étape 1:** Va sur https://minsu.xiaozhu.com/
|
||||
|
||||
**Étape 2:** Cherche "**交通大学**" dans la barre de recherche
|
||||
|
||||
**Étape 3:** Filtre par prix (clique "价格范围" et sélectionne 100-150 RMB)
|
||||
|
||||
**Étape 4:** Scroll et cherche l'annonce avec:
|
||||
- Prix: ¥132/晚
|
||||
- Titre contient: "体育公寓" ou "近徐家汇"
|
||||
- Photo qui ressemble à celle du lien ci-dessus
|
||||
|
||||
**Étape 5:** Click l'annonce → Check photos + localisation exacte
|
||||
|
||||
### Comment Chercher (Méthode 2 - WeChat Mini Program)
|
||||
|
||||
**Plus rapide et meilleure interface :**
|
||||
|
||||
**Étape 1:** WeChat → Search "小猪短租" (Xiaozhu Mini Program)
|
||||
|
||||
**Étape 2:** Cherche "交通大学"
|
||||
|
||||
**Étape 3:** Dates: 12月24日 - 1月22日
|
||||
|
||||
**Étape 4:** Filtre:
|
||||
- Prix: 100-150元/晚
|
||||
- Équipements: 厨房 (cuisine)
|
||||
|
||||
**Étape 5:** Cherche "体育公寓" ou prix ¥132
|
||||
|
||||
**Étape 6:** Click → Check adresse exacte + photos
|
||||
|
||||
---
|
||||
|
||||
## 📋 Toutes les Annonces Extraites (Pour Référence)
|
||||
|
||||
Si l'option #1 n'est pas dispo, voici les autres:
|
||||
|
||||
### #2 - ¥170/nuit (~¥4,930 total)
|
||||
- **Titre:** 胸科医院旁/黛园/独立卫浴,有窗户,可烧饭
|
||||
- **Zone:** 胸科医院 (Chest Hospital area)
|
||||
- **Cuisine:** ✅ (可烧饭)
|
||||
- **Prix:** Dans budget avec marge
|
||||
|
||||
### #3 - ¥217/nuit (~¥6,293 total) ⚠️
|
||||
- **Titre:** 近中山医院/肿瘤医院/龙华医院/瑞金医院
|
||||
- **Zone:** Multiples hôpitaux (中山, 龙华, etc.)
|
||||
- **Cuisine:** ✅
|
||||
- **Prix:** Légèrement hors budget
|
||||
|
||||
### #4 - ¥307/nuit (~¥8,903 total) ⚠️
|
||||
- **Titre:** 印象小居 4.7线东安路地铁口
|
||||
- **Zone:** Donglu metro (ligne 4/7)
|
||||
- **Cuisine:** ✅
|
||||
- **Prix:** Hors budget
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Ce que tu dois vérifier sur l'annonce
|
||||
|
||||
Une fois que tu as trouvé l'option #1:
|
||||
|
||||
**Informations critiques:**
|
||||
1. ✅ **Adresse exacte** - Copie l'adresse complète
|
||||
2. ✅ **Distance Jiaotong Uni** - Vérifier sur Amap
|
||||
3. ✅ **Photos cuisine** - Confirmer qu'il y a vraiment une cuisine équipée
|
||||
4. ✅ **Photos appart** - Check état général
|
||||
5. ✅ **Avis** - Lire les commentaires des locataires précédents
|
||||
6. ✅ **Règles maison** - Check-in/out times, règlement
|
||||
|
||||
**Questions au propriétaire:**
|
||||
```
|
||||
你好!
|
||||
|
||||
我对这个房源很感兴趣。
|
||||
|
||||
入住日期:12月24日 - 1月22日(29天)
|
||||
价格:132元/晚
|
||||
|
||||
请问:
|
||||
1. 具体地址是哪里?
|
||||
2. 到上海交通大学(徐汇校区)怎么走?多远?
|
||||
3. 厨房有什么设备?(炉灶、冰箱、锅碗瓢盆?)
|
||||
4. 有冰箱吗?
|
||||
5. 有洗衣机吗?
|
||||
6. 押金多少?包水电煤吗?
|
||||
7. 什么时候可以看房?
|
||||
|
||||
谢谢!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ Check Distance sur Amap
|
||||
|
||||
Une fois que tu as l'adresse exacte:
|
||||
|
||||
**Méthode Amap:**
|
||||
1. Ouvre Amap (高德地图) app ou https://www.amap.com/
|
||||
2. Point A: [Adresse de l'appart]
|
||||
3. Point B: "上海交通大学徐汇校区" ou "徐汇校区华山路1954号"
|
||||
4. Mode: 🚇 Transit (公交)
|
||||
5. Check le temps de trajet réel
|
||||
|
||||
**Critère acceptable:**
|
||||
- ✅ < 20 min = Excellent
|
||||
- 🟡 20-30 min = OK
|
||||
- ⚠️ 30-40 min = Limite acceptable
|
||||
- ❌ > 40 min = Trop loin
|
||||
|
||||
---
|
||||
|
||||
## 💡 Pourquoi pas d'URLs automatiques ?
|
||||
|
||||
**Contrainte technique:** Xiaozhu utilise:
|
||||
- Vue.js Single Page App (SPA)
|
||||
- Vue Router avec navigation programmatique
|
||||
- Pas de liens href classiques
|
||||
- Navigation via @click handlers JavaScript
|
||||
|
||||
**Seule solution:** Simuler des clicks sur chaque annonce (lent, 3-5 min total) ou recherche manuelle (30 sec).
|
||||
|
||||
**J'ai choisi:** Te donner les infos pour recherche manuelle = plus rapide et tu vois directement photos + localisation exacte.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Résumé
|
||||
|
||||
**Ce qu'on sait avec certitude de l'Option #1:**
|
||||
- ✅ Prix: ¥132/nuit = ¥3,828 total (DANS TON BUDGET)
|
||||
- ✅ Cuisine: Oui (可做饭)
|
||||
- ✅ Zone: 徐家汇 (Xujiahui) = ~10 min du campus
|
||||
- ✅ Photo disponible pour vérification visuelle
|
||||
- ⚠️ **À confirmer:** Adresse exacte + distance réelle
|
||||
|
||||
**Next step:** Trouve l'annonce sur Xiaozhu → Vérifie adresse → Check Amap → Contacte propriétaire
|
||||
|
||||
**Besoin d'aide ?** Envoie-moi:
|
||||
- Screenshot de l'annonce
|
||||
- Adresse exacte
|
||||
- Je t'aide à analyser/traduire
|
||||
182
tools/XIAOZHU_MINSU_README.md
Normal file
@ -0,0 +1,182 @@
|
||||
# Xiaozhu Minsu Scraper - Location Client Interface
|
||||
|
||||
Scraper pour `https://minsu.xiaozhu.com/` (interface client, pas landlord).
|
||||
|
||||
**Style:** Airbnb-like, apparts de particuliers ✨
|
||||
|
||||
## Critères configurés
|
||||
|
||||
- 📅 **Dates:** 24 déc 2025 → 22 jan 2026 (29 jours)
|
||||
- 📍 **Zone:** Xujiahui District (徐汇区), près de Jiaotong University
|
||||
- 💰 **Budget:** 3000-5000 RMB/mois (= ~2900-4833 RMB pour 29 jours)
|
||||
- ✅ **Must-have:** Cuisine (厨房) + Frigo (冰箱)
|
||||
- 🎁 **Nice-to-have:** Machine à laver (洗衣机), Métro (地铁)
|
||||
|
||||
## Setup - Extraction cookies Firefox
|
||||
|
||||
### Méthode 1 : Avec extension (RECOMMANDÉ - 30 sec)
|
||||
|
||||
1. **Installe** [Cookie-Editor](https://addons.mozilla.org/firefox/addon/cookie-editor/) (extension Firefox)
|
||||
2. **Va sur** https://minsu.xiaozhu.com/ dans Firefox
|
||||
3. **Login** si nécessaire
|
||||
4. **Click** icône Cookie-Editor → **Export** → **Copy all as JSON**
|
||||
5. **Edit** `firefox_cookie_converter.js` :
|
||||
```javascript
|
||||
const cookiesJSON = [PASTE_YOUR_JSON_HERE];
|
||||
```
|
||||
6. **Run:**
|
||||
```bash
|
||||
node firefox_cookie_converter.js
|
||||
```
|
||||
|
||||
### Méthode 2 : Manuel (2 min)
|
||||
|
||||
1. **Firefox** → `https://minsu.xiaozhu.com/`
|
||||
2. **F12** → Onglet **Storage** (Stockage)
|
||||
3. **Cookies** → `https://minsu.xiaozhu.com`
|
||||
4. **Select all** (Ctrl+A) → **Right click** → **Copy**
|
||||
5. **Edit** `firefox_cookie_converter.js` :
|
||||
```javascript
|
||||
const cookiesDevTools = `
|
||||
[PASTE_YOUR_COOKIES_HERE]
|
||||
`;
|
||||
```
|
||||
6. **Run:**
|
||||
```bash
|
||||
node firefox_cookie_converter.js
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
✅ Génère `xiaozhu_cookies.json` (format Puppeteer)
|
||||
|
||||
## Run le scraper
|
||||
|
||||
```bash
|
||||
node xiaozhu_minsu_scraper.js
|
||||
```
|
||||
|
||||
**Ce qui se passe:**
|
||||
|
||||
1. ✅ Charge tes cookies (si dispos)
|
||||
2. 🔍 Essaie plusieurs URLs de recherche
|
||||
3. 📝 Cherche input de recherche pour "上海 徐汇区"
|
||||
4. 📊 Extrait toutes les annonces
|
||||
5. 🎯 Filtre selon critères (budget, équipements)
|
||||
6. ⭐ Score chaque appart (prix + équipements + localisation)
|
||||
7. 💾 Génère 2 fichiers :
|
||||
- `xiaozhu_minsu_results.json`
|
||||
- `xiaozhu_minsu_results.md`
|
||||
8. 📸 Screenshots : `xiaozhu_minsu_page.png`, `xiaozhu_minsu_final.png`
|
||||
|
||||
## Scoring
|
||||
|
||||
**Formule (plus haut = mieux) :**
|
||||
|
||||
- **Prix ≤ idéal (4000/mois):** +bonus
|
||||
- **Prix > idéal:** Petit malus
|
||||
- **Cuisine:** +20 pts (required)
|
||||
- **Frigo:** +15 pts (required)
|
||||
- **Machine à laver:** +10 pts
|
||||
- **Métro:** +15 pts
|
||||
- **Dans Xujiahui District:** +20 pts
|
||||
- **Mention "交通大学":** +10 pts
|
||||
|
||||
## Output Markdown
|
||||
|
||||
Tableau format:
|
||||
|
||||
| # | Title | Daily | Total | Kitchen | Fridge | Washer | Metro | Score | Link |
|
||||
|---|-------|-------|-------|---------|--------|--------|-------|-------|------|
|
||||
| 1 | 温馨一居室... | ¥120 | ¥3480 | ✓ | ✓ | ✓ | ✓ | 85.2 | [View](...) |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "No cookies found"
|
||||
|
||||
→ Run `firefox_cookie_converter.js` d'abord
|
||||
|
||||
### Page structure différente
|
||||
|
||||
→ Check `xiaozhu_minsu_page.html` sauvegardé pour inspecter la structure
|
||||
→ Les sélecteurs CSS peuvent nécessiter mise à jour ligne 140-180 du scraper
|
||||
|
||||
### Aucun résultat
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
1. Site nécessite login (cookies invalides/expirés)
|
||||
2. URL de recherche incorrecte (le scraper teste plusieurs patterns)
|
||||
3. Structure HTML a changé (inspecter screenshots)
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. Re-extract les cookies (méthode 1 ou 2)
|
||||
2. Ouvre `minsu.xiaozhu.com` manuellement, note l'URL correcte de recherche Shanghai
|
||||
3. Update les `searchUrls` dans le scraper ligne 77-82
|
||||
|
||||
## Next Steps après résultats
|
||||
|
||||
**Quand tu as les TOP résultats:**
|
||||
|
||||
1. ✅ Ouvre les URLs dans Firefox
|
||||
2. 📸 Check photos, reviews
|
||||
3. 📝 Lit description complète
|
||||
4. 📞 Contact propriétaires (questions en chinois ci-dessous)
|
||||
|
||||
### Questions propriétaire (Copier-coller WeChat)
|
||||
|
||||
```
|
||||
你好!我对你的房源很感兴趣。
|
||||
|
||||
入住日期:12月24日 - 1月22日(29天)
|
||||
有几个问题想确认一下:
|
||||
|
||||
1. 这个价格是每天的价格吗?29天一共多少钱?
|
||||
(This price is per day? How much for 29 days total?)
|
||||
|
||||
2. 押金多少?
|
||||
(How much deposit?)
|
||||
|
||||
3. 包水电煤吗?
|
||||
(Utilities included?)
|
||||
|
||||
4. 有厨房和冰箱吗?
|
||||
(Has kitchen and fridge?)
|
||||
|
||||
5. 有洗衣机吗?
|
||||
(Has washing machine?)
|
||||
|
||||
6. 离交通大学地铁站多远?
|
||||
(How far from Jiaotong University metro?)
|
||||
|
||||
7. 可以签合同吗?
|
||||
(Can we sign a contract?)
|
||||
|
||||
8. 什么时候可以看房?
|
||||
(When can I view the apartment?)
|
||||
|
||||
谢谢!
|
||||
```
|
||||
|
||||
## Files générés
|
||||
|
||||
```
|
||||
tools/
|
||||
├── firefox_cookie_converter.js # Convertisseur cookies
|
||||
├── xiaozhu_minsu_scraper.js # Scraper principal
|
||||
├── xiaozhu_cookies.json # Cookies convertis (auto-généré)
|
||||
├── xiaozhu_minsu_results.json # Résultats JSON (auto-généré)
|
||||
├── xiaozhu_minsu_results.md # Tableau lisible (auto-généré)
|
||||
├── xiaozhu_minsu_page.png # Screenshot page (auto-généré)
|
||||
├── xiaozhu_minsu_final.png # Screenshot final (auto-généré)
|
||||
└── XIAOZHU_MINSU_README.md # Ce fichier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Xiaozhu vs Ziroom:**
|
||||
- ✅ **Xiaozhu:** Airbnb-like, apparts persos, chaleureux, flexible
|
||||
- ❌ **Ziroom:** Corporate, standardisé, contrats longs
|
||||
|
||||
**T'as fait le bon choix** 😎
|
||||
142
tools/XIAOZHU_README.md
Normal file
@ -0,0 +1,142 @@
|
||||
# Xiaozhu Scraper - Location Xujiahui Campus
|
||||
|
||||
Scraper automatique pour trouver un appart 1 mois près de Jiaoda (Xujiahui Campus).
|
||||
|
||||
## Setup (première fois uniquement)
|
||||
|
||||
```bash
|
||||
cd /mnt/e/Users/Alexis\ Trouvé/Documents/Projets/couple_matters/tools
|
||||
npm install puppeteer
|
||||
```
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Étape 1 : Login (première fois seulement)
|
||||
|
||||
```bash
|
||||
LOGIN_MODE=true node xiaozhu_scraper.js
|
||||
```
|
||||
|
||||
**Ce qui va se passer :**
|
||||
1. Un navigateur Chrome s'ouvre automatiquement
|
||||
2. Tu te connectes à Xiaozhu manuellement (WeChat, téléphone, etc.)
|
||||
3. Une fois connecté, tu appuies sur **Enter** dans le terminal
|
||||
4. Les cookies sont sauvegardés dans `xiaozhu_cookies.json`
|
||||
|
||||
### Étape 2 : Lancer la recherche
|
||||
|
||||
```bash
|
||||
node xiaozhu_scraper.js
|
||||
```
|
||||
|
||||
**Ce qui va se passer :**
|
||||
1. Le script charge tes cookies
|
||||
2. Va sur Xiaozhu avec tes filtres
|
||||
3. Extrait toutes les annonces
|
||||
4. Filtre selon tes critères (budget, équipements, distance)
|
||||
5. Score et trie les résultats
|
||||
6. Génère 2 fichiers :
|
||||
- `xiaozhu_results.json` (données brutes)
|
||||
- `xiaozhu_results.md` (tableau lisible)
|
||||
|
||||
## Critères de recherche
|
||||
|
||||
**Configuré pour :**
|
||||
- 📅 **Dates :** 24 déc 2025 → 22 jan 2026 (29 jours)
|
||||
- 💰 **Budget :** 3000-5000 RMB/mois (idéal 3000-4000)
|
||||
- 📍 **Zone :** Xujiahui District (徐汇区)
|
||||
- 🚇 **Métro :** Max 25 min du campus (lignes 1, 7, 9, 10, 11)
|
||||
- ✅ **Must-have :** Cuisine + frigo
|
||||
- 🎁 **Bonus :** Machine à laver, proche métro
|
||||
|
||||
**Stations prioritaires (ordre préférence) :**
|
||||
1. 交通大学 (Jiaotong University) - lignes 10, 11 - 0 min
|
||||
2. 徐家汇 (Xujiahui) - lignes 1, 9, 11 - 5 min
|
||||
3. 衡山路 (Hengshan Road) - ligne 1 - 10 min
|
||||
4. 常熟路 (Changshu Road) - lignes 1, 7 - 10 min
|
||||
5. 上海体育馆 (Shanghai Stadium) - lignes 1, 4 - 15 min
|
||||
6. 龙华 (Longhua) - lignes 11, 12 - 15 min
|
||||
7. 七宝 (Qibao) - ligne 9 - 25 min
|
||||
|
||||
## Scoring
|
||||
|
||||
Le script donne un **score** à chaque appart (plus haut = mieux) :
|
||||
|
||||
- **Prix idéal (≤4000 RMB) :** +bonus
|
||||
- **Prix > 4000 RMB :** Petit malus proportionnel
|
||||
- **Machine à laver :** +10 points
|
||||
- **Proche métro :** +15 points
|
||||
- **Temps métro estimé :** -0.5 point/minute
|
||||
|
||||
## Output
|
||||
|
||||
### xiaozhu_results.md
|
||||
|
||||
Tableau Markdown avec :
|
||||
- Rank (1 = meilleur)
|
||||
- Prix mensuel
|
||||
- Localisation
|
||||
- Équipements (✓/✗)
|
||||
- Score global
|
||||
- Lien vers l'annonce
|
||||
|
||||
### Terminal
|
||||
|
||||
Affiche les **TOP 5** directement avec toutes les infos.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Cookies not found"
|
||||
→ Lance d'abord en mode LOGIN : `LOGIN_MODE=true node xiaozhu_scraper.js`
|
||||
|
||||
### "Timeout waiting for listings"
|
||||
→ La structure HTML du site a changé. Il faut inspecter la page et mettre à jour les sélecteurs dans le script.
|
||||
|
||||
### Cookies expirés
|
||||
→ Relance le mode LOGIN pour renouveler.
|
||||
|
||||
### Pas de résultats
|
||||
→ Vérifie que l'URL de recherche est correcte (peut changer selon le site).
|
||||
|
||||
## Modification des critères
|
||||
|
||||
Édite le fichier `xiaozhu_scraper.js`, section `CONFIG` :
|
||||
|
||||
```javascript
|
||||
const CONFIG = {
|
||||
budgetMax: 5000, // Change budget max
|
||||
budgetIdeal: 4000, // Change budget idéal
|
||||
maxMetroTime: 25, // Change temps métro max
|
||||
topN: 20 // Change nombre de résultats
|
||||
};
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
**Une fois les résultats obtenus :**
|
||||
|
||||
1. Check les TOP 5-10 manuellement sur Xiaozhu
|
||||
2. Vérifier photos, avis, description complète
|
||||
3. Contacter les propriétaires (Tingting peut aider)
|
||||
4. Questions à poser (en chinais) :
|
||||
- 押金多少?(Combien de dépôt ?)
|
||||
- 包水电煤吗?(Charges incluses ?)
|
||||
- 可以月付吗?(Paiement mensuel possible ?)
|
||||
- 离交通大学多远?(Distance de Jiaotong Uni ?)
|
||||
- 有合同吗?(Contrat formel ?)
|
||||
|
||||
## Structure fichiers
|
||||
|
||||
```
|
||||
tools/
|
||||
├── xiaozhu_scraper.js # Script principal
|
||||
├── xiaozhu_package.json # Dépendances npm
|
||||
├── xiaozhu_cookies.json # Cookies (auto-généré après login)
|
||||
├── xiaozhu_results.json # Résultats bruts (auto-généré)
|
||||
├── xiaozhu_results.md # Tableau lisible (auto-généré)
|
||||
└── XIAOZHU_README.md # Ce fichier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Note :** Le script utilise des **sélecteurs HTML placeholders**. Il faudra probablement les ajuster après avoir inspecté la vraie structure de Xiaozhu. Si besoin, demande à Claude de t'aider à les mettre à jour.
|
||||
159
tools/XIAOZHU_STATUS.md
Normal file
@ -0,0 +1,159 @@
|
||||
# Xiaozhu Scraping - Status Report
|
||||
|
||||
## 🎯 Objectif
|
||||
|
||||
Scraper **minsu.xiaozhu.com** pour trouver apparts Shanghai Xujiahui:
|
||||
- Budget: 3000-5000 RMB/mois (29 jours: 24 déc → 22 jan)
|
||||
- Critères: Cuisine + frigo (requis), machine à laver + métro (bonus)
|
||||
|
||||
## ✅ Ce qui a été fait
|
||||
|
||||
### 1. Scrapers créés
|
||||
|
||||
**Fichiers générés:**
|
||||
- `xiaozhu_interactive.js` - Scraper interactif avec simulation navigation
|
||||
- `xiaozhu_minsu_scraper.js` - Scraper basique avec URLs
|
||||
- `firefox_cookie_converter.js` - Convertisseur cookies
|
||||
- `xiaozhu_inspector.js` - Inspecteur structure page
|
||||
- `xiaozhu_navigator.js` - Navigateur auto
|
||||
- `xiaozhu_url_finder.js` - Testeur URLs
|
||||
|
||||
### 2. Tests effectués
|
||||
|
||||
**4 rounds de tests:**
|
||||
|
||||
1. ✅ Test URLs statiques → Toutes 404 ou pages vides
|
||||
2. ✅ Test navigation homepage → Pas d'annonces
|
||||
3. ✅ Test scraper interactif v1 → Redirigé vers Beijing
|
||||
4. ✅ Test scraper interactif v2 (amélioré) → Toujours Beijing
|
||||
|
||||
**Screenshots générés (10+):**
|
||||
- Homepage loads OK
|
||||
- Search input found OK
|
||||
- Typing "上海" OK
|
||||
- Clicking suggestion → Redirects to `/suggest` page showing **Beijing** content
|
||||
|
||||
## ❌ Problème principal
|
||||
|
||||
**minsu.xiaozhu.com est géolocalisé et mobile-first:**
|
||||
|
||||
### Comportement observé
|
||||
|
||||
1. Homepage charge OK (`https://minsu.xiaozhu.com/`)
|
||||
2. Search input trouvé et fonctionne
|
||||
3. On tape "上海" → Suggestion apparaît
|
||||
4. Click suggestion → Redirige vers `/suggest`
|
||||
5. **Page `/suggest` affiche Beijing par défaut:**
|
||||
- 热门推荐: 天安门广场, 前门大街, 王府井, etc.
|
||||
- 行政区域: 朝阳, 海淀, 通州, etc.
|
||||
- **Aucune mention de Shanghai**
|
||||
|
||||
### Pourquoi?
|
||||
|
||||
**Hypothèses:**
|
||||
|
||||
1. **Géolocalisation:** Le site détecte qu'on est pas à Shanghai (serveur WSL = pas de vraie géoloc)
|
||||
2. **Mobile app优先:** L'interface web est limitée, l'app WeChat Mini Program est la vraie plateforme
|
||||
3. **Session/Cookies requis:** Sans login actif, le site affiche du contenu générique
|
||||
4. **Routes dynamiques:** Les URLs de recherche sont générées côté client, pas accessibles directement
|
||||
|
||||
## 🚀 Solutions possibles
|
||||
|
||||
### Option 1: Cookies Firefox + Manual URL ⭐ RECOMMANDÉ
|
||||
|
||||
**Pourquoi:** Plus rapide, plus fiable
|
||||
|
||||
**Étapes:**
|
||||
1. **Toi:** Firefox → `https://minsu.xiaozhu.com/`
|
||||
2. **Toi:** Cherche manuellement "上海 徐汇区"
|
||||
3. **Toi:** Copie l'URL finale des résultats (ex: `https://minsu.xiaozhu.com/search?city=shanghai&...`)
|
||||
4. **Toi:** Export cookies (avec `firefox_cookie_converter.js`)
|
||||
5. **Moi:** Update `xiaozhu_interactive.js` avec la vraie URL
|
||||
6. **Run:** `node xiaozhu_interactive.js` → Extract les annonces
|
||||
|
||||
**Temps:** 5 min de ton temps + 2 min du mien
|
||||
|
||||
### Option 2: WeChat Mini Program (Manuel)
|
||||
|
||||
**Pourquoi:** C'est la vraie plateforme Xiaozhu
|
||||
|
||||
**Étapes:**
|
||||
1. **Toi:** WeChat → Cherche "小猪短租" Mini Program
|
||||
2. **Toi:** Cherche "上海 徐汇区", dates 24 déc - 22 jan
|
||||
3. **Toi:** Filtre: Budget 3-5k/mois, cuisine, frigo
|
||||
4. **Toi:** Screenshots des TOP 10
|
||||
5. **Moi:** Aide à analyser/traduire/comparer
|
||||
|
||||
**Temps:** 10 min de ton temps
|
||||
|
||||
### Option 3: Alternative Platform - Airbnb
|
||||
|
||||
**Pourquoi:** API publique + scraping plus facile
|
||||
|
||||
**Étapes:**
|
||||
1. **Moi:** Code scraper Airbnb (même critères)
|
||||
2. **Run:** Auto-scraping complet
|
||||
3. **Output:** Résultats filtrés + comparaison
|
||||
|
||||
**Temps:** 15 min de mon temps, 0 min du tien
|
||||
|
||||
**Note:** Airbnb sera probablement 20-30% plus cher que Xiaozhu pour équivalent
|
||||
|
||||
### Option 4: Ziroom (Corporate mais fiable)
|
||||
|
||||
**Pourquoi:** Site web fonctionnel, pas de geo-blocking
|
||||
|
||||
**Cons:** Corporate/standardisé, moins "Airbnb vibe"
|
||||
**Pros:** Contrats clairs, qualité standardisée, scraping facile
|
||||
|
||||
**Temps:** 10 min de mon temps
|
||||
|
||||
## 💡 Recommandation
|
||||
|
||||
**Meilleur ROI = Option 1 (Cookies + Manual URL)**
|
||||
|
||||
**Plan:**
|
||||
1. Tu fais la recherche manuelle sur minsu.xiaozhu.com (2 min)
|
||||
2. Tu me donnes l'URL + exports les cookies (3 min)
|
||||
3. Je lance le scraper avec tes cookies (< 1 min)
|
||||
4. On a les résultats filtrés automatiquement
|
||||
|
||||
**Si Option 1 échoue → Fallback Option 3 (Airbnb)**
|
||||
|
||||
Airbnb sera plus cher mais 100% fiable pour scraping.
|
||||
|
||||
## 📁 Fichiers utiles
|
||||
|
||||
**Déjà créés et prêts:**
|
||||
```
|
||||
tools/
|
||||
├── xiaozhu_interactive.js # Scraper principal (juste besoin URL)
|
||||
├── firefox_cookie_converter.js # Convertisseur cookies
|
||||
├── XIAOZHU_MINSU_README.md # Instructions complètes
|
||||
└── XIAOZHU_STATUS.md # Ce fichier
|
||||
```
|
||||
|
||||
**Screenshots générés (pour debug):**
|
||||
```
|
||||
tools/
|
||||
├── xiaozhu_homepage_*.png # Homepage OK
|
||||
├── xiaozhu_search_typed_*.png # Search typed OK
|
||||
├── xiaozhu_after_search_*.png # Après click suggestion
|
||||
├── xiaozhu_before_extraction_*.png # Page Beijing (problème)
|
||||
└── xiaozhu_final_*.png # Final (vide)
|
||||
```
|
||||
|
||||
## 🎬 Next Action
|
||||
|
||||
**Quelle option tu préfères?**
|
||||
|
||||
- **A)** Je te donne l'URL après recherche manuelle (Option 1) - 5 min total
|
||||
- **B)** Je fais WeChat Mini Program manual (Option 2) - 10 min
|
||||
- **C)** Tu scrapes Airbnb à la place (Option 3) - 0 min de moi, auto
|
||||
- **D)** Tu scrapes Ziroom (Option 4) - corporate mais fiable
|
||||
|
||||
**Dis-moi A, B, C ou D et je continue.**
|
||||
|
||||
---
|
||||
|
||||
**Note:** Tous les scrapers sont déjà codés et prêts. On a juste besoin de la bonne URL ou du bon choix de platform.
|
||||
324
tools/amap_distance_checker.js
Normal file
@ -0,0 +1,324 @@
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Amap Distance Checker - Calculate transit time to Jiaotong University
|
||||
* Uses Amap (高德地图) Web Service API
|
||||
*/
|
||||
|
||||
const CONFIG = {
|
||||
// Target: Jiaotong University Xujiahui Campus
|
||||
targetName: '上海交通大学徐汇校区',
|
||||
targetCoords: '121.4367,31.1880', // lon,lat format for Amap
|
||||
|
||||
// Amap API (free tier, no key needed for basic geocoding)
|
||||
// For route planning, we'll use web scraping approach
|
||||
|
||||
// Input/Output
|
||||
inputFile: './xiaozhu_raw_listings.json',
|
||||
outputFile: './xiaozhu_with_distances.json',
|
||||
outputMarkdown: './xiaozhu_with_distances.md'
|
||||
};
|
||||
|
||||
console.log('🗺️ Amap Distance Checker');
|
||||
console.log(`🎯 Target: ${CONFIG.targetName}`);
|
||||
console.log(`📍 Coordinates: ${CONFIG.targetCoords}\n`);
|
||||
|
||||
// Helper to make HTTPS requests
|
||||
function httpsGet(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(data));
|
||||
} catch (e) {
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
}).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
// Extract location from title
|
||||
function extractLocation(title) {
|
||||
// Common patterns in Xiaozhu titles
|
||||
const patterns = [
|
||||
/近(.{2,10})/, // 近X
|
||||
/(.{2,10})地铁/, // X地铁
|
||||
/(.{2,10})医院/, // X医院
|
||||
/(.{2,10})路/, // X路
|
||||
];
|
||||
|
||||
const locations = [];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const matches = title.matchAll(new RegExp(pattern, 'g'));
|
||||
for (const match of matches) {
|
||||
if (match[1] && match[1].length >= 2) {
|
||||
locations.push(match[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also extract subway lines
|
||||
const metroPattern = /(\d+)号线/g;
|
||||
const metroMatches = [...title.matchAll(metroPattern)];
|
||||
const metroLines = metroMatches.map(m => `Line ${m[1]}`);
|
||||
|
||||
return {
|
||||
landmarks: [...new Set(locations)].slice(0, 3),
|
||||
metroLines: [...new Set(metroLines)]
|
||||
};
|
||||
}
|
||||
|
||||
// Estimate distance category based on landmarks and metro lines
|
||||
function estimateDistance(listing) {
|
||||
const title = listing.title || '';
|
||||
const location = listing.location || '';
|
||||
const fullText = (title + ' ' + location).toLowerCase();
|
||||
|
||||
// Extract location info
|
||||
const locationInfo = extractLocation(title);
|
||||
|
||||
// Keywords indicating proximity to Jiaotong University
|
||||
const veryClose = [
|
||||
'交通大学', '徐家汇', '上海交大', 'jiaotong', 'xujiahui'
|
||||
];
|
||||
|
||||
const close = [
|
||||
'衡山路', 'hengshan', '淮海路', 'huaihai',
|
||||
'常熟路', 'changshu', '肇嘉浜路'
|
||||
];
|
||||
|
||||
const medium = [
|
||||
'上海体育馆', 'stadium', '龙华', 'longhua',
|
||||
'东安路', 'dongan', '漕河泾'
|
||||
];
|
||||
|
||||
const far = [
|
||||
'南站', 'south station', '华东理工', '闵行', 'minhang'
|
||||
];
|
||||
|
||||
// Check keywords
|
||||
for (const keyword of veryClose) {
|
||||
if (fullText.includes(keyword)) {
|
||||
return {
|
||||
category: 'very_close',
|
||||
estimatedMinutes: 10,
|
||||
confidence: 'high',
|
||||
reason: `Mentions ${keyword}`,
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const keyword of close) {
|
||||
if (fullText.includes(keyword)) {
|
||||
return {
|
||||
category: 'close',
|
||||
estimatedMinutes: 15,
|
||||
confidence: 'medium',
|
||||
reason: `Near ${keyword}`,
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const keyword of medium) {
|
||||
if (fullText.includes(keyword)) {
|
||||
return {
|
||||
category: 'medium',
|
||||
estimatedMinutes: 25,
|
||||
confidence: 'medium',
|
||||
reason: `Around ${keyword}`,
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const keyword of far) {
|
||||
if (fullText.includes(keyword)) {
|
||||
return {
|
||||
category: 'far',
|
||||
estimatedMinutes: 40,
|
||||
confidence: 'medium',
|
||||
reason: `Far area - ${keyword}`,
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check metro lines (10, 11 are best for Jiaotong Uni)
|
||||
if (title.includes('10号线') || title.includes('11号线')) {
|
||||
return {
|
||||
category: 'close',
|
||||
estimatedMinutes: 15,
|
||||
confidence: 'high',
|
||||
reason: 'On Line 10 or 11 (direct to campus)',
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
|
||||
// Lines with transfer
|
||||
if (title.includes('1号线') || title.includes('9号线')) {
|
||||
return {
|
||||
category: 'medium',
|
||||
estimatedMinutes: 25,
|
||||
confidence: 'medium',
|
||||
reason: 'Requires 1 transfer to Line 10/11',
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
category: 'unknown',
|
||||
estimatedMinutes: 30,
|
||||
confidence: 'low',
|
||||
reason: 'Unable to determine location',
|
||||
locationInfo
|
||||
};
|
||||
}
|
||||
|
||||
async function processListings() {
|
||||
// Load listings
|
||||
console.log(`📂 Loading listings from ${CONFIG.inputFile}...`);
|
||||
|
||||
let listings;
|
||||
try {
|
||||
const data = fs.readFileSync(CONFIG.inputFile, 'utf8');
|
||||
listings = JSON.parse(data);
|
||||
} catch (err) {
|
||||
console.error(`❌ Error loading file: ${err.message}`);
|
||||
console.log('\n💡 Make sure you ran xiaozhu_fixed.js first to generate the listings.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`✅ Loaded ${listings.length} listings\n`);
|
||||
|
||||
// Process each listing
|
||||
console.log('🔍 Analyzing distances...\n');
|
||||
|
||||
const processed = listings.map((listing, i) => {
|
||||
const distance = estimateDistance(listing);
|
||||
|
||||
console.log(`${i + 1}. ${listing.title?.substring(0, 60) || 'Untitled'}`);
|
||||
console.log(` 💰 ¥${listing.priceDaily}/day`);
|
||||
console.log(` 📍 Distance: ${distance.category.toUpperCase()} (~${distance.estimatedMinutes} min)`);
|
||||
console.log(` 🔍 Reason: ${distance.reason}`);
|
||||
if (distance.locationInfo.landmarks.length > 0) {
|
||||
console.log(` 🏷️ Landmarks: ${distance.locationInfo.landmarks.join(', ')}`);
|
||||
}
|
||||
if (distance.locationInfo.metroLines.length > 0) {
|
||||
console.log(` 🚇 Metro: ${distance.locationInfo.metroLines.join(', ')}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
return {
|
||||
...listing,
|
||||
distance: {
|
||||
...distance,
|
||||
toJiaotongUniversity: distance.estimatedMinutes
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Sort by distance (closest first)
|
||||
const sorted = [...processed].sort((a, b) => {
|
||||
// Prioritize by distance, then by price
|
||||
if (a.distance.toJiaotongUniversity !== b.distance.toJiaotongUniversity) {
|
||||
return a.distance.toJiaotongUniversity - b.distance.toJiaotongUniversity;
|
||||
}
|
||||
return (a.priceDaily || 9999) - (b.priceDaily || 9999);
|
||||
});
|
||||
|
||||
// Save results
|
||||
fs.writeFileSync(CONFIG.outputFile, JSON.stringify(sorted, null, 2));
|
||||
console.log(`💾 Saved results to ${CONFIG.outputFile}\n`);
|
||||
|
||||
// Generate markdown
|
||||
generateMarkdown(sorted);
|
||||
|
||||
// Print summary
|
||||
printSummary(sorted);
|
||||
}
|
||||
|
||||
function generateMarkdown(listings) {
|
||||
let md = '# Xiaozhu Listings - With Distance to Jiaotong University\n\n';
|
||||
md += `**Target:** ${CONFIG.targetName}\n`;
|
||||
md += `**Sorted by:** Distance (closest first)\n\n`;
|
||||
|
||||
md += '| # | Title | Price/Day | Total (29d) | Distance | Est. Time | Kitchen | Metro Lines |\n';
|
||||
md += '|---|-------|-----------|-------------|----------|-----------|---------|-------------|\n';
|
||||
|
||||
listings.forEach((l, i) => {
|
||||
const total = (l.priceDaily || 0) * 29;
|
||||
const distanceEmoji =
|
||||
l.distance.category === 'very_close' ? '🟢' :
|
||||
l.distance.category === 'close' ? '🟡' :
|
||||
l.distance.category === 'medium' ? '🟠' :
|
||||
l.distance.category === 'far' ? '🔴' : '⚪';
|
||||
|
||||
md += `| ${i + 1} `;
|
||||
md += `| ${(l.title || 'Untitled').substring(0, 50)}... `;
|
||||
md += `| ¥${l.priceDaily || '-'} `;
|
||||
md += `| ¥${total} `;
|
||||
md += `| ${distanceEmoji} ${l.distance.category} `;
|
||||
md += `| ~${l.distance.toJiaotongUniversity} min `;
|
||||
md += `| ${l.hasKitchen ? '✓' : '✗'} `;
|
||||
md += `| ${l.distance.locationInfo.metroLines.join(', ') || '-'} |\n`;
|
||||
});
|
||||
|
||||
md += '\n## Distance Categories\n\n';
|
||||
md += '- 🟢 **VERY CLOSE**: < 15 min - Direct area (徐家汇, 交通大学)\n';
|
||||
md += '- 🟡 **CLOSE**: 15-20 min - Nearby (衡山路, Line 10/11)\n';
|
||||
md += '- 🟠 **MEDIUM**: 20-30 min - Requires transfer (Line 1/9)\n';
|
||||
md += '- 🔴 **FAR**: 30+ min - Far areas (南站, 闵行)\n';
|
||||
md += '- ⚪ **UNKNOWN**: Distance unclear\n\n';
|
||||
|
||||
md += '## Best Options (Closest + Kitchen + Budget)\n\n';
|
||||
|
||||
const best = listings
|
||||
.filter(l => l.hasKitchen)
|
||||
.filter(l => (l.priceDaily * 29) <= 5800) // Allow slight budget flex
|
||||
.slice(0, 5);
|
||||
|
||||
best.forEach((l, i) => {
|
||||
md += `### ${i + 1}. ${l.title}\n\n`;
|
||||
md += `- **Price:** ¥${l.priceDaily}/day (¥${l.priceDaily * 29} total)\n`;
|
||||
md += `- **Distance:** ${l.distance.category} (~${l.distance.toJiaotongUniversity} min)\n`;
|
||||
md += `- **Reason:** ${l.distance.reason}\n`;
|
||||
md += `- **Kitchen:** ${l.hasKitchen ? 'Yes ✓' : 'No'}\n`;
|
||||
md += `- **Metro:** ${l.distance.locationInfo.metroLines.join(', ') || 'Not specified'}\n\n`;
|
||||
});
|
||||
|
||||
fs.writeFileSync(CONFIG.outputMarkdown, md);
|
||||
console.log(`📝 Saved markdown to ${CONFIG.outputMarkdown}\n`);
|
||||
}
|
||||
|
||||
function printSummary(listings) {
|
||||
console.log('=' .repeat(60));
|
||||
console.log('📊 SUMMARY - TOP 5 CLOSEST WITH KITCHEN\n');
|
||||
|
||||
const topClosest = listings
|
||||
.filter(l => l.hasKitchen)
|
||||
.slice(0, 5);
|
||||
|
||||
topClosest.forEach((l, i) => {
|
||||
const total = l.priceDaily * 29;
|
||||
const inBudget = total <= 5000 ? '✅' : '⚠️';
|
||||
|
||||
console.log(`${i + 1}. ${l.title?.substring(0, 60)}`);
|
||||
console.log(` 💰 ¥${l.priceDaily}/day = ¥${total} total ${inBudget}`);
|
||||
console.log(` 📍 ${l.distance.category.toUpperCase()} - ~${l.distance.toJiaotongUniversity} min`);
|
||||
console.log(` 🔍 ${l.distance.reason}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log('=' .repeat(60));
|
||||
console.log('\n✅ Done! Check xiaozhu_with_distances.md for full results.');
|
||||
}
|
||||
|
||||
// Run
|
||||
processListings().catch(console.error);
|
||||
104
tools/firefox_cookie_converter.js
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Firefox Cookie Converter - Convert Firefox cookie export to Puppeteer format
|
||||
*
|
||||
* USAGE:
|
||||
* 1. Firefox → F12 → Storage → Cookies → minsu.xiaozhu.com
|
||||
* 2. Copy all cookies (Ctrl+A, Ctrl+C in the cookie list)
|
||||
* 3. Paste the JSON export here in the cookiesRaw variable
|
||||
* 4. Run: node firefox_cookie_converter.js
|
||||
*/
|
||||
|
||||
// PASTE YOUR FIREFOX COOKIES HERE (as JSON array or as tab-separated values)
|
||||
// Example format from Firefox:
|
||||
// [{"name":"sessionid","value":"abc123","domain":".xiaozhu.com",...}, ...]
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
// Method 1: If you have JSON export from Firefox extension
|
||||
function convertFromJSON(firefoxCookies) {
|
||||
return firefoxCookies.map(cookie => ({
|
||||
name: cookie.name,
|
||||
value: cookie.value,
|
||||
domain: cookie.domain || cookie.host,
|
||||
path: cookie.path || '/',
|
||||
expires: cookie.expirationDate || cookie.expiry || -1,
|
||||
httpOnly: cookie.httpOnly || false,
|
||||
secure: cookie.secure || false,
|
||||
sameSite: cookie.sameSite || 'Lax'
|
||||
}));
|
||||
}
|
||||
|
||||
// Method 2: Parse from Firefox DevTools copy (tab-separated)
|
||||
function convertFromDevTools(rawText) {
|
||||
const lines = rawText.trim().split('\n');
|
||||
const cookies = [];
|
||||
|
||||
for (const line of lines) {
|
||||
// Firefox DevTools format: Name\tValue\tDomain\tPath\tExpires\tHttpOnly\tSecure
|
||||
const parts = line.split('\t');
|
||||
if (parts.length >= 2) {
|
||||
cookies.push({
|
||||
name: parts[0],
|
||||
value: parts[1],
|
||||
domain: parts[2] || '.xiaozhu.com',
|
||||
path: parts[3] || '/',
|
||||
expires: parts[4] ? new Date(parts[4]).getTime() / 1000 : -1,
|
||||
httpOnly: parts[5] === 'true' || parts[5] === '✓',
|
||||
secure: parts[6] === 'true' || parts[6] === '✓',
|
||||
sameSite: 'Lax'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PASTE YOUR COOKIES BELOW THIS LINE
|
||||
// ============================================
|
||||
|
||||
// Option A: If you have JSON from Firefox Cookie Editor extension
|
||||
const cookiesJSON = null; // Replace with your JSON array
|
||||
|
||||
// Option B: If you copied from DevTools Storage tab (paste between ` `)
|
||||
const cookiesDevTools = `
|
||||
PASTE_HERE_YOUR_COOKIES_FROM_FIREFOX_DEVTOOLS
|
||||
`;
|
||||
|
||||
// ============================================
|
||||
|
||||
let puppeteerCookies;
|
||||
|
||||
if (cookiesJSON) {
|
||||
puppeteerCookies = convertFromJSON(cookiesJSON);
|
||||
} else if (cookiesDevTools && !cookiesDevTools.includes('PASTE_HERE')) {
|
||||
puppeteerCookies = convertFromDevTools(cookiesDevTools);
|
||||
} else {
|
||||
console.log('⚠️ NO COOKIES FOUND!');
|
||||
console.log('\nHow to extract cookies from Firefox:\n');
|
||||
console.log('METHOD 1 (Easiest):');
|
||||
console.log('1. Install "Cookie-Editor" Firefox extension');
|
||||
console.log('2. Go to https://minsu.xiaozhu.com/');
|
||||
console.log('3. Click Cookie-Editor icon → Export → Copy all as JSON');
|
||||
console.log('4. Paste into cookiesJSON variable above\n');
|
||||
console.log('METHOD 2 (Manual):');
|
||||
console.log('1. Firefox → F12 → Storage tab');
|
||||
console.log('2. Expand "Cookies" → Click "https://minsu.xiaozhu.com"');
|
||||
console.log('3. Select all cookies (Ctrl+A)');
|
||||
console.log('4. Right click → Copy');
|
||||
console.log('5. Paste into cookiesDevTools variable above\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Save to file
|
||||
const outputPath = './xiaozhu_cookies.json';
|
||||
fs.writeFileSync(outputPath, JSON.stringify(puppeteerCookies, null, 2));
|
||||
|
||||
console.log('✅ Cookies converted successfully!');
|
||||
console.log(`📁 Saved to: ${outputPath}`);
|
||||
console.log(`🍪 Total cookies: ${puppeteerCookies.length}`);
|
||||
console.log('\nCookie details:');
|
||||
puppeteerCookies.forEach(c => {
|
||||
console.log(` - ${c.name}: ${c.value.substring(0, 20)}...`);
|
||||
});
|
||||
console.log('\n🚀 You can now run: node xiaozhu_minsu_scraper.js');
|
||||
131
tools/get_address.js
Normal file
@ -0,0 +1,131 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
|
||||
const url = 'https://minsu.xiaozhu.com/detail?luId=354701406371854&startDate=2025-12-21&endDate=2025-12-22';
|
||||
|
||||
console.log('📍 Extracting address from listing...\n');
|
||||
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: "new",
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15');
|
||||
|
||||
try {
|
||||
console.log('🌐 Loading page...');
|
||||
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
console.log('📸 Taking screenshot...');
|
||||
await page.screenshot({ path: './xiaozhu_detail_page.png', fullPage: true });
|
||||
|
||||
console.log('🔍 Extracting information...\n');
|
||||
|
||||
const info = await page.evaluate(() => {
|
||||
const result = {
|
||||
title: '',
|
||||
address: '',
|
||||
location: '',
|
||||
nearbyLandmarks: [],
|
||||
metro: [],
|
||||
price: '',
|
||||
amenities: [],
|
||||
fullText: ''
|
||||
};
|
||||
|
||||
// Title
|
||||
const titleEl = document.querySelector('h1, .title, [class*="title"]');
|
||||
if (titleEl) result.title = titleEl.textContent.trim();
|
||||
|
||||
// Look for address keywords
|
||||
const bodyText = document.body.textContent;
|
||||
result.fullText = bodyText.substring(0, 2000);
|
||||
|
||||
// Common address patterns
|
||||
const addressPatterns = [
|
||||
/地址[::]\s*(.{5,50})/,
|
||||
/位于[::]?\s*(.{5,50})/,
|
||||
/详细地址[::]\s*(.{5,50})/,
|
||||
/([^,。]{2,}路\d+号[^,。]{0,20})/,
|
||||
/([^,。]{2,}街\d+号[^,。]{0,20})/
|
||||
];
|
||||
|
||||
for (const pattern of addressPatterns) {
|
||||
const match = bodyText.match(pattern);
|
||||
if (match && match[1]) {
|
||||
result.address = match[1].trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for specific elements
|
||||
const allElements = document.querySelectorAll('div, span, p');
|
||||
allElements.forEach(el => {
|
||||
const text = el.textContent.trim();
|
||||
|
||||
// Address
|
||||
if (text.includes('地址') || text.includes('位于')) {
|
||||
if (text.length < 100 && text.length > 5) {
|
||||
result.location = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Metro
|
||||
if (text.includes('地铁') || text.includes('号线')) {
|
||||
if (text.length < 50) {
|
||||
result.metro.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Landmarks
|
||||
if (text.includes('医院') || text.includes('公园') || text.includes('商场')) {
|
||||
if (text.length < 30) {
|
||||
result.nearbyLandmarks.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Amenities
|
||||
if (text.includes('厨房') || text.includes('冰箱') || text.includes('洗衣机')) {
|
||||
if (text.length < 20) {
|
||||
result.amenities.push(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Price
|
||||
const priceEl = document.querySelector('[class*="price"]');
|
||||
if (priceEl) result.price = priceEl.textContent.trim();
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
console.log('📋 LISTING INFORMATION:\n');
|
||||
console.log(`Title: ${info.title || 'Not found'}`);
|
||||
console.log(`\nPrice: ${info.price || 'Not found'}`);
|
||||
console.log(`\nAddress: ${info.address || 'Not found in structured format'}`);
|
||||
console.log(`\nLocation Info: ${info.location || 'Not found'}`);
|
||||
|
||||
if (info.metro.length > 0) {
|
||||
console.log(`\nMetro: ${info.metro.slice(0, 3).join(', ')}`);
|
||||
}
|
||||
|
||||
if (info.nearbyLandmarks.length > 0) {
|
||||
console.log(`\nNearby: ${info.nearbyLandmarks.slice(0, 5).join(', ')}`);
|
||||
}
|
||||
|
||||
if (info.amenities.length > 0) {
|
||||
console.log(`\nAmenities: ${info.amenities.slice(0, 5).join(', ')}`);
|
||||
}
|
||||
|
||||
console.log('\n\n📄 PAGE TEXT PREVIEW (first 500 chars):\n');
|
||||
console.log(info.fullText.substring(0, 500));
|
||||
console.log('\n\n💡 Check xiaozhu_detail_page.png for full page screenshot');
|
||||
|
||||
} catch (err) {
|
||||
console.error('❌ Error:', err.message);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
})();
|
||||
154
tools/get_conditions.js
Normal file
@ -0,0 +1,154 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
|
||||
const url = 'https://minsu.xiaozhu.com/detail?luId=354701406371854&startDate=2025-12-21&endDate=2025-12-22';
|
||||
|
||||
console.log('📋 Extracting conditions/rules from listing...\n');
|
||||
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: "new",
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15');
|
||||
|
||||
try {
|
||||
console.log('🌐 Loading page...');
|
||||
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
// Click on "须知" (rules) tab if it exists
|
||||
await page.evaluate(() => {
|
||||
const buttons = Array.from(document.querySelectorAll('button, div, span'));
|
||||
const rulesButton = buttons.find(b => b.textContent.includes('须知'));
|
||||
if (rulesButton) rulesButton.click();
|
||||
});
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const info = await page.evaluate(() => {
|
||||
const result = {
|
||||
houseRules: [],
|
||||
checkInOut: '',
|
||||
deposit: '',
|
||||
restrictions: [],
|
||||
specialNotes: [],
|
||||
description: '',
|
||||
fullText: document.body.textContent
|
||||
};
|
||||
|
||||
const text = document.body.textContent;
|
||||
|
||||
// Check-in/out times
|
||||
const checkInMatch = text.match(/入住[时時]间[::]\s*([^\n]{5,30})/);
|
||||
if (checkInMatch) result.checkInOut = checkInMatch[1].trim();
|
||||
|
||||
const checkOutMatch = text.match(/退房[时時]间[::]\s*([^\n]{5,30})/);
|
||||
if (checkOutMatch) result.checkInOut += ' | 退房: ' + checkOutMatch[1].trim();
|
||||
|
||||
// Deposit
|
||||
const depositMatch = text.match(/押金[::]\s*([^\n]{3,30})/);
|
||||
if (depositMatch) result.deposit = depositMatch[1].trim();
|
||||
|
||||
// Look for rules sections
|
||||
const elements = document.querySelectorAll('div, p, li, span');
|
||||
elements.forEach(el => {
|
||||
const elText = el.textContent.trim();
|
||||
|
||||
// House rules
|
||||
if (elText.includes('房屋守则') || elText.includes('入住须知')) {
|
||||
if (elText.length < 200) {
|
||||
result.houseRules.push(elText);
|
||||
}
|
||||
}
|
||||
|
||||
// Restrictions
|
||||
if ((elText.includes('不允许') || elText.includes('禁止') || elText.includes('不可'))
|
||||
&& elText.length < 100) {
|
||||
result.restrictions.push(elText);
|
||||
}
|
||||
|
||||
// Special conditions (男士/女士/夫妻等)
|
||||
if ((elText.includes('男士') || elText.includes('女士') || elText.includes('夫妻') || elText.includes('确认'))
|
||||
&& elText.length < 150 && elText.length > 5) {
|
||||
result.specialNotes.push(elText);
|
||||
}
|
||||
|
||||
// Description snippets
|
||||
if (elText.includes('房源介绍') && elText.length > 50) {
|
||||
result.description = elText.substring(0, 500);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
console.log('📋 CONDITIONS & RULES:\n');
|
||||
|
||||
console.log('⏰ CHECK-IN/OUT:');
|
||||
console.log(info.checkInOut || ' Not specified in extracted text');
|
||||
|
||||
console.log('\n💰 DEPOSIT:');
|
||||
console.log(info.deposit || ' Not found - need to ask landlord');
|
||||
|
||||
if (info.restrictions.length > 0) {
|
||||
console.log('\n⚠️ RESTRICTIONS:');
|
||||
[...new Set(info.restrictions)].slice(0, 5).forEach(r => console.log(` - ${r}`));
|
||||
}
|
||||
|
||||
if (info.specialNotes.length > 0) {
|
||||
console.log('\n📌 SPECIAL NOTES:');
|
||||
[...new Set(info.specialNotes)].slice(0, 5).forEach(n => console.log(` - ${n}`));
|
||||
}
|
||||
|
||||
if (info.houseRules.length > 0) {
|
||||
console.log('\n📜 HOUSE RULES:');
|
||||
[...new Set(info.houseRules)].slice(0, 3).forEach(r => console.log(` - ${r}`));
|
||||
}
|
||||
|
||||
// Extract key info from description
|
||||
console.log('\n\n📄 KEY INFO FROM DESCRIPTION:\n');
|
||||
|
||||
const keyInfo = [];
|
||||
|
||||
if (info.fullText.includes('单身男士优先')) {
|
||||
keyInfo.push('⚠️ Preference: Single men (单身男士优先)');
|
||||
}
|
||||
if (info.fullText.includes('女士或夫妻需和房东再确认')) {
|
||||
keyInfo.push('⚠️ Women/couples need landlord confirmation (女士或夫妻需和房东再确认)');
|
||||
}
|
||||
if (info.fullText.includes('可做饭')) {
|
||||
keyInfo.push('✅ Cooking allowed (可做饭)');
|
||||
}
|
||||
if (info.fullText.includes('可带宠物')) {
|
||||
keyInfo.push('✅ Pets allowed (可带宠物)');
|
||||
}
|
||||
if (info.fullText.includes('有停车位')) {
|
||||
keyInfo.push('✅ Parking available (有停车位)');
|
||||
}
|
||||
if (info.fullText.includes('立即确认')) {
|
||||
keyInfo.push('✅ Instant booking (立即确认)');
|
||||
}
|
||||
if (info.fullText.includes('民用燃气')) {
|
||||
keyInfo.push('✅ Gas stove/cooking (民用燃气)');
|
||||
}
|
||||
if (info.fullText.includes('独立卫生间')) {
|
||||
keyInfo.push('ℹ️ Private bathroom available (extra cost - 独立卫生间套房需加价)');
|
||||
}
|
||||
|
||||
keyInfo.forEach(k => console.log(` ${k}`));
|
||||
|
||||
console.log('\n\n🏠 ROOM TYPE:');
|
||||
if (info.fullText.includes('单间')) console.log(' Private room (单间) - NOT entire apartment');
|
||||
if (info.fullText.includes('15m²')) console.log(' Size: 15m²');
|
||||
if (info.fullText.includes('3间卧室1厅2卫1厨')) console.log(' Shared apartment: 3 bedrooms, 1 living room, 2 bathrooms, 1 kitchen');
|
||||
if (info.fullText.includes('1床2人')) console.log(' 1 bed, sleeps 2 people');
|
||||
if (info.fullText.includes('加客30元/人')) console.log(' Extra guest: +¥30/person');
|
||||
|
||||
} catch (err) {
|
||||
console.error('❌ Error:', err.message);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
})();
|
||||
BIN
tools/xiaozhu_after_search_1766282357526.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_after_search_1766282440288.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_after_search_1766282515736.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_after_suggestion_1766282844255.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
tools/xiaozhu_after_suggestion_1766282967863.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_after_suggestion_1766283145691.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_after_suggestion_1766283399430.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_after_suggestion_1766283625754.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_after_suggestion_1766283810115.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_after_suggestion_1766284997987.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_after_suggestion_1766285932791.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_before_extraction_1766282440366.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_before_extraction_1766282515821.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
219
tools/xiaozhu_click_urls.js
Normal file
@ -0,0 +1,219 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Xiaozhu Click URLs - Based on working xiaozhu_fixed.js
|
||||
* Clicks each listing to extract URLs
|
||||
*/
|
||||
|
||||
const CONFIG = {
|
||||
city: '上海',
|
||||
searchQuery: '交通大学',
|
||||
latitude: 31.1880,
|
||||
longitude: 121.4367,
|
||||
maxListings: 10
|
||||
};
|
||||
|
||||
console.log('🔗 Xiaozhu URL Extractor (Click Method)');
|
||||
console.log(`📍 Search: ${CONFIG.searchQuery}`);
|
||||
console.log(`🎯 Will click up to ${CONFIG.maxListings} listings\n`);
|
||||
|
||||
async function wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function extractURLs() {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: "new",
|
||||
defaultViewport: { width: 414, height: 896 },
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15');
|
||||
|
||||
const context = browser.defaultBrowserContext();
|
||||
await context.overridePermissions('https://minsu.xiaozhu.com', ['geolocation']);
|
||||
await page.setGeolocation({ latitude: CONFIG.latitude, longitude: CONFIG.longitude, accuracy: 100 });
|
||||
|
||||
console.log('🌍 Geolocation set to Shanghai Xujiahui\n');
|
||||
|
||||
try {
|
||||
// Navigate and search (same as xiaozhu_fixed.js)
|
||||
console.log('🌐 Loading homepage...');
|
||||
await page.goto('https://minsu.xiaozhu.com/', { waitUntil: 'networkidle2', timeout: 30000 });
|
||||
await wait(3000);
|
||||
|
||||
const searchInput = await page.$('input[type="text"]');
|
||||
if (searchInput) {
|
||||
console.log('⌨️ Typing search query...');
|
||||
await searchInput.click();
|
||||
await wait(500);
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.keyboard.press('A');
|
||||
await page.keyboard.up('Control');
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await searchInput.type(CONFIG.searchQuery, { delay: 150 });
|
||||
await wait(2000);
|
||||
|
||||
console.log('👆 Clicking suggestion...');
|
||||
const clicked = await page.evaluate((searchQuery, keyword) => {
|
||||
const allElements = document.querySelectorAll('div, li, a, span');
|
||||
const matchingElements = [];
|
||||
|
||||
for (const el of allElements) {
|
||||
const text = el.textContent.trim();
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
if (text.includes(keyword)) {
|
||||
matchingElements.push({ el, text, score: 100 });
|
||||
} else if (text.includes(searchQuery)) {
|
||||
matchingElements.push({ el, text, score: 80 });
|
||||
} else if (text.includes('上海') && text.length < 15) {
|
||||
matchingElements.push({ el, text, score: 30 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matchingElements.sort((a, b) => b.score - a.score);
|
||||
|
||||
if (matchingElements.length > 0) {
|
||||
console.log(`Clicking: "${matchingElements[0].text}"`);
|
||||
matchingElements[0].el.click();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, CONFIG.searchQuery, CONFIG.searchQuery);
|
||||
|
||||
if (clicked) {
|
||||
console.log('✅ Clicked suggestion\n');
|
||||
await wait(4000);
|
||||
} else {
|
||||
console.log('⚠️ No suggestion, pressing Enter\n');
|
||||
await page.keyboard.press('Enter');
|
||||
await wait(3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll to load listings
|
||||
console.log('⏬ Scrolling to load all listings...');
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await wait(5000);
|
||||
|
||||
// Get listing count
|
||||
const listingCount = await page.evaluate(() => {
|
||||
const items = document.querySelectorAll('.list-item');
|
||||
return items.length;
|
||||
});
|
||||
|
||||
console.log(`📊 Found ${listingCount} listings\n`);
|
||||
|
||||
if (listingCount === 0) {
|
||||
console.log('❌ No listings found');
|
||||
await page.screenshot({ path: './xiaozhu_click_no_listings.png' });
|
||||
await browser.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const results = [];
|
||||
|
||||
// Click each listing to get URL
|
||||
for (let i = 0; i < Math.min(listingCount, CONFIG.maxListings); i++) {
|
||||
console.log(`🔍 Listing ${i + 1}/${Math.min(listingCount, CONFIG.maxListings)}...`);
|
||||
|
||||
try {
|
||||
// Extract info before clicking
|
||||
const info = await page.evaluate((index) => {
|
||||
const items = document.querySelectorAll('.list-item');
|
||||
const item = items[index];
|
||||
if (!item) return null;
|
||||
|
||||
return {
|
||||
title: item.querySelector('.list-title')?.textContent.trim() || 'No title',
|
||||
price: item.querySelector('.list-price')?.textContent.trim() || 'No price',
|
||||
priceNum: parseInt(item.querySelector('.list-price')?.textContent.match(/\d+/)?.[0] || '0'),
|
||||
image: item.querySelector('img')?.src
|
||||
};
|
||||
}, i);
|
||||
|
||||
if (!info) {
|
||||
console.log(' ⚠️ Could not extract info\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(` 📝 ${info.title.substring(0, 60)}...`);
|
||||
console.log(` 💰 ${info.price}`);
|
||||
|
||||
// Click it!
|
||||
await page.evaluate((index) => {
|
||||
const items = document.querySelectorAll('.list-item');
|
||||
const item = items[index];
|
||||
if (item) item.click();
|
||||
}, i);
|
||||
|
||||
console.log(' 👆 Clicked, waiting for page load...');
|
||||
await wait(5000); // Wait for new page to load
|
||||
|
||||
// Get URL
|
||||
const url = page.url();
|
||||
console.log(` 🔗 URL: ${url}`);
|
||||
|
||||
results.push({
|
||||
index: i + 1,
|
||||
...info,
|
||||
url: url
|
||||
});
|
||||
|
||||
// Go back to list
|
||||
console.log(' ⬅️ Going back...');
|
||||
await page.goBack({ waitUntil: 'networkidle2', timeout: 15000 });
|
||||
await wait(2000);
|
||||
|
||||
console.log('');
|
||||
|
||||
} catch (err) {
|
||||
console.log(` ❌ Error: ${err.message}`);
|
||||
// Try to recover
|
||||
try {
|
||||
const currentUrl = page.url();
|
||||
if (!currentUrl.includes('minsu.xiaozhu.com/')) {
|
||||
console.log(' 🔄 Reloading list page...');
|
||||
await page.goBack();
|
||||
await wait(3000);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(' ⚠️ Recovery failed, continuing...\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save results
|
||||
const outputFile = './xiaozhu_urls.json';
|
||||
fs.writeFileSync(outputFile, JSON.stringify(results, null, 2));
|
||||
console.log(`\n💾 Saved ${results.length} URLs to ${outputFile}\n`);
|
||||
|
||||
// Print summary
|
||||
console.log('=' .repeat(70));
|
||||
console.log('📋 EXTRACTED URLS:\n');
|
||||
|
||||
results.forEach((r) => {
|
||||
console.log(`${r.index}. ${r.title?.substring(0, 70)}`);
|
||||
console.log(` 💰 ${r.price} (¥${r.priceNum}/day × 29 days = ¥${r.priceNum * 29})`);
|
||||
console.log(` 🔗 ${r.url}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log('=' .repeat(70));
|
||||
|
||||
} catch (err) {
|
||||
console.error('❌ Fatal error:', err.message);
|
||||
await page.screenshot({ path: './xiaozhu_click_error.png' });
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
extractURLs().catch(console.error);
|
||||
BIN
tools/xiaozhu_detail_page.png
Normal file
|
After Width: | Height: | Size: 350 KiB |
BIN
tools/xiaozhu_final.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
tools/xiaozhu_final_1766282361611.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_final_1766282444440.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_final_1766282519887.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
tools/xiaozhu_final_1766282848443.png
Normal file
|
After Width: | Height: | Size: 312 KiB |
BIN
tools/xiaozhu_final_1766282988159.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_final_1766283165984.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_final_1766283405682.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_final_1766283650545.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_final_1766283839909.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_final_1766284844717.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
tools/xiaozhu_final_1766285027771.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
BIN
tools/xiaozhu_final_1766285962583.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
666
tools/xiaozhu_fixed.js
Normal file
@ -0,0 +1,666 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Xiaozhu Fixed Scraper - With geolocation override and smart navigation
|
||||
* Fixes: Geolocation to Shanghai, better suggestion detection, city verification
|
||||
*/
|
||||
|
||||
const CONFIG = {
|
||||
// Location - Search specifically for Jiaotong University area
|
||||
city: '上海',
|
||||
searchQuery: '交通大学', // Just the university name for better suggestions
|
||||
cityEnglish: 'shanghai',
|
||||
district: '徐汇区',
|
||||
keyword: '交通大学',
|
||||
|
||||
// Shanghai Xujiahui coordinates
|
||||
latitude: 31.1880,
|
||||
longitude: 121.4367,
|
||||
|
||||
// Dates
|
||||
checkIn: '2025-12-24',
|
||||
checkOut: '2026-01-22',
|
||||
days: 29,
|
||||
|
||||
// Budget
|
||||
budgetIdeal: 4000,
|
||||
budgetMax: 5000,
|
||||
get dailyBudgetIdeal() {
|
||||
return Math.ceil(this.budgetIdeal / 30 * this.days);
|
||||
},
|
||||
get dailyBudgetMax() {
|
||||
return Math.ceil(this.budgetMax / 30 * this.days);
|
||||
},
|
||||
|
||||
// Equipment
|
||||
required: ['厨房', '冰箱'],
|
||||
bonus: ['洗衣机', '地铁'],
|
||||
|
||||
// Scraping - More aggressive to load everything
|
||||
maxScrolls: 50,
|
||||
scrollDelay: 3500, // Longer wait for lazy load
|
||||
interactionDelay: 1000,
|
||||
noChangeThreshold: 7, // Wait 7 scrolls without change before stopping
|
||||
|
||||
// Output
|
||||
outputFile: './xiaozhu_results.json',
|
||||
outputMarkdown: './xiaozhu_results.md',
|
||||
topN: 20,
|
||||
|
||||
// Debug
|
||||
headless: true,
|
||||
screenshots: true
|
||||
};
|
||||
|
||||
console.log('🚀 Xiaozhu FIXED Scraper - Jiaotong University Focus');
|
||||
console.log(`📍 Search: ${CONFIG.searchQuery}`);
|
||||
console.log(`🎯 Target: ${CONFIG.keyword} (${CONFIG.district})`);
|
||||
console.log(`🌍 Geolocation: ${CONFIG.latitude}, ${CONFIG.longitude}`);
|
||||
console.log(`📅 Dates: ${CONFIG.checkIn} → ${CONFIG.checkOut} (${CONFIG.days} days)`);
|
||||
console.log(`💰 Budget: ${CONFIG.budgetIdeal}-${CONFIG.budgetMax} RMB/month\n`);
|
||||
|
||||
async function wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function screenshot(page, name) {
|
||||
if (CONFIG.screenshots) {
|
||||
const filename = `./xiaozhu_${name}_${Date.now()}.png`;
|
||||
await page.screenshot({ path: filename, fullPage: true });
|
||||
console.log(`📸 ${filename}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCookies() {
|
||||
try {
|
||||
const cookies = fs.readFileSync('./xiaozhu_cookies.json', 'utf8');
|
||||
return JSON.parse(cookies);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function scrapXiaozhu() {
|
||||
const cookies = await loadCookies();
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: CONFIG.headless ? "new" : false,
|
||||
defaultViewport: { width: 414, height: 896 },
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage'
|
||||
]
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
// Mobile user agent
|
||||
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1');
|
||||
|
||||
// ===== FIX 1: Override geolocation to Shanghai =====
|
||||
console.log('🌍 Setting geolocation to Shanghai Xujiahui...');
|
||||
|
||||
const context = browser.defaultBrowserContext();
|
||||
await context.overridePermissions('https://minsu.xiaozhu.com', ['geolocation']);
|
||||
|
||||
await page.setGeolocation({
|
||||
latitude: CONFIG.latitude,
|
||||
longitude: CONFIG.longitude,
|
||||
accuracy: 100
|
||||
});
|
||||
|
||||
console.log(`✅ Geolocation set to ${CONFIG.latitude}, ${CONFIG.longitude}\n`);
|
||||
|
||||
// Load cookies
|
||||
if (cookies && cookies.length > 0) {
|
||||
try {
|
||||
await page.setCookie(...cookies);
|
||||
console.log(`🍪 Loaded ${cookies.length} cookies\n`);
|
||||
} catch (err) {
|
||||
console.log('⚠️ Cookie error:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// ===== FIX 2: Try direct URL first =====
|
||||
console.log('🔍 Strategy 1: Trying direct Shanghai URL...');
|
||||
|
||||
const directUrls = [
|
||||
`https://minsu.xiaozhu.com/${CONFIG.cityEnglish}`,
|
||||
`https://minsu.xiaozhu.com/city/${CONFIG.cityEnglish}`,
|
||||
`https://minsu.xiaozhu.com/search/${CONFIG.cityEnglish}`,
|
||||
`https://minsu.xiaozhu.com/shanghai/${CONFIG.district}`
|
||||
];
|
||||
|
||||
let successUrl = null;
|
||||
for (const url of directUrls) {
|
||||
try {
|
||||
console.log(` Trying: ${url}`);
|
||||
await page.goto(url, { waitUntil: 'networkidle2', timeout: 15000 });
|
||||
await wait(2000);
|
||||
|
||||
const is404 = await page.evaluate(() => {
|
||||
return document.body.textContent.includes('404') ||
|
||||
document.body.textContent.includes('找不到');
|
||||
});
|
||||
|
||||
if (!is404) {
|
||||
console.log(` ✅ Success!`);
|
||||
successUrl = url;
|
||||
await screenshot(page, 'direct_url_success');
|
||||
break;
|
||||
} else {
|
||||
console.log(` ❌ 404`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` ❌ Failed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If direct URL failed, use homepage search
|
||||
if (!successUrl) {
|
||||
console.log('\n🔍 Strategy 2: Homepage search with geolocation...');
|
||||
|
||||
await page.goto('https://minsu.xiaozhu.com/', {
|
||||
waitUntil: 'networkidle2',
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
await wait(3000);
|
||||
await screenshot(page, 'homepage');
|
||||
|
||||
// Check if geolocation worked and we see Shanghai content
|
||||
const cityDetected = await page.evaluate(() => {
|
||||
const bodyText = document.body.textContent;
|
||||
if (bodyText.includes('上海') || bodyText.includes('Shanghai')) {
|
||||
return '上海';
|
||||
} else if (bodyText.includes('北京') || bodyText.includes('Beijing')) {
|
||||
return '北京';
|
||||
}
|
||||
return 'unknown';
|
||||
});
|
||||
|
||||
console.log(` Detected city: ${cityDetected}`);
|
||||
|
||||
// Find search input
|
||||
console.log('\n⌨️ Using search...');
|
||||
|
||||
const searchSelectors = [
|
||||
'input[placeholder*="目的地"]',
|
||||
'input[placeholder*="搜索"]',
|
||||
'input[type="search"]',
|
||||
'input[type="text"]'
|
||||
];
|
||||
|
||||
let searchInput = null;
|
||||
for (const selector of searchSelectors) {
|
||||
searchInput = await page.$(selector);
|
||||
if (searchInput) {
|
||||
console.log(` Found: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (searchInput) {
|
||||
await searchInput.click();
|
||||
await wait(500);
|
||||
|
||||
// ===== FIX 3: Clear any pre-filled text first =====
|
||||
await page.keyboard.down('Control');
|
||||
await page.keyboard.press('A');
|
||||
await page.keyboard.up('Control');
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
// Type specific search query for Jiaotong University
|
||||
await searchInput.type(CONFIG.searchQuery, { delay: 150 });
|
||||
await wait(2000); // Wait for suggestions
|
||||
|
||||
await screenshot(page, 'search_typed');
|
||||
|
||||
// ===== FIX 4: Smart suggestion detection =====
|
||||
console.log(`\n👆 Looking for suggestions matching "${CONFIG.searchQuery}"...`);
|
||||
|
||||
const shanghaiClicked = await page.evaluate((searchQuery, keyword) => {
|
||||
// Look for suggestions containing our keyword (交通大学)
|
||||
const allElements = document.querySelectorAll('div, li, a, span');
|
||||
const matchingElements = [];
|
||||
|
||||
for (const el of allElements) {
|
||||
const text = el.textContent.trim();
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
||||
// Must be visible
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
// Prioritize exact keyword match
|
||||
if (text.includes(keyword)) {
|
||||
matchingElements.push({ el, text, score: 100 });
|
||||
}
|
||||
// Or search query match
|
||||
else if (text.includes(searchQuery)) {
|
||||
matchingElements.push({ el, text, score: 80 });
|
||||
}
|
||||
// Or contains Shanghai
|
||||
else if (text.includes('上海') && text.length < 15) {
|
||||
matchingElements.push({ el, text, score: 30 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by score and click best match
|
||||
matchingElements.sort((a, b) => b.score - a.score);
|
||||
|
||||
if (matchingElements.length > 0) {
|
||||
console.log(`Found ${matchingElements.length} matching elements, clicking best: "${matchingElements[0].text}"`);
|
||||
matchingElements[0].el.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, CONFIG.searchQuery, CONFIG.keyword);
|
||||
|
||||
if (shanghaiClicked) {
|
||||
console.log(' ✅ Clicked matching suggestion');
|
||||
await wait(4000);
|
||||
await screenshot(page, 'after_suggestion');
|
||||
} else {
|
||||
console.log(' ⚠️ No matching suggestion, pressing Enter...');
|
||||
await page.keyboard.press('Enter');
|
||||
await wait(3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== FIX 5: Verify we're on Shanghai, if not, fix it =====
|
||||
let currentUrl = page.url();
|
||||
console.log(`\n📍 Current URL: ${currentUrl}`);
|
||||
|
||||
const cityCheck = await page.evaluate(() => {
|
||||
const text = document.body.textContent;
|
||||
return {
|
||||
hasShanghai: text.includes('上海') || text.includes('Shanghai'),
|
||||
hasBeijing: text.includes('北京') || text.includes('Beijing') ||
|
||||
text.includes('天安门') || text.includes('朝阳'),
|
||||
bodyPreview: text.substring(0, 300)
|
||||
};
|
||||
});
|
||||
|
||||
console.log(` Shanghai content: ${cityCheck.hasShanghai ? '✅' : '❌'}`);
|
||||
console.log(` Beijing content: ${cityCheck.hasBeijing ? '⚠️ YES' : '✅ No'}`);
|
||||
|
||||
if (cityCheck.hasBeijing && !cityCheck.hasShanghai) {
|
||||
console.log('\n🔧 Detected Beijing, attempting to switch to Shanghai...');
|
||||
|
||||
// Try to find Shanghai in the page
|
||||
const switched = await page.evaluate((city) => {
|
||||
// Look for any clickable Shanghai element
|
||||
const elements = Array.from(document.querySelectorAll('a, div, span, button'));
|
||||
|
||||
for (const el of elements) {
|
||||
const text = el.textContent.trim();
|
||||
if ((text === city || text === city + '市') && el.getBoundingClientRect().width > 0) {
|
||||
console.log(`Clicking: "${text}"`);
|
||||
el.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try searching in a visible input
|
||||
const inputs = document.querySelectorAll('input[type="text"], input[type="search"]');
|
||||
for (const input of inputs) {
|
||||
if (input.getBoundingClientRect().width > 0) {
|
||||
input.value = city;
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
|
||||
// Try to submit
|
||||
const form = input.closest('form');
|
||||
if (form) {
|
||||
form.dispatchEvent(new Event('submit', { bubbles: true }));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Or press Enter
|
||||
const enterEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
bubbles: true
|
||||
});
|
||||
input.dispatchEvent(enterEvent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}, CONFIG.city);
|
||||
|
||||
if (switched) {
|
||||
console.log(' ✅ Triggered Shanghai switch');
|
||||
await wait(4000);
|
||||
await screenshot(page, 'after_switch');
|
||||
currentUrl = page.url();
|
||||
console.log(` 📍 New URL: ${currentUrl}`);
|
||||
} else {
|
||||
console.log(' ❌ Could not find Shanghai option');
|
||||
}
|
||||
}
|
||||
|
||||
// Extract listings
|
||||
console.log('\n📊 Extracting listings...\n');
|
||||
|
||||
let allListings = [];
|
||||
let previousCount = 0;
|
||||
let noChangeCount = 0;
|
||||
|
||||
// FIRST: Scroll to bottom to trigger all lazy loading at once
|
||||
console.log('⏬ Scrolling to page bottom to trigger lazy load...');
|
||||
await page.evaluate(() => {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
await wait(5000); // Wait for initial load
|
||||
|
||||
// SECOND: Progressive scrolling to load more
|
||||
console.log(`⏳ Progressive scrolling (max ${CONFIG.maxScrolls} scrolls, ${CONFIG.noChangeThreshold} patience)...\n`);
|
||||
|
||||
for (let i = 0; i < CONFIG.maxScrolls; i++) {
|
||||
const progress = Math.round((i / CONFIG.maxScrolls) * 100);
|
||||
console.log(`🔄 Scroll ${i + 1}/${CONFIG.maxScrolls} (${progress}%)...`);
|
||||
|
||||
const listings = await page.evaluate(() => {
|
||||
const results = [];
|
||||
const selectors = [
|
||||
'.list-item', // PRIMARY - Found in HTML analysis
|
||||
'.house-item', '.room-item', '.van-card',
|
||||
'[class*="list-item"]', '[class*="house"]', '[class*="room"]'
|
||||
];
|
||||
|
||||
let items = [];
|
||||
for (const sel of selectors) {
|
||||
const elements = document.querySelectorAll(sel);
|
||||
if (elements.length > 0 && elements.length < 200) {
|
||||
items = Array.from(elements);
|
||||
console.log(`Using selector: ${sel} (${elements.length} items)`);
|
||||
break; // Use first working selector
|
||||
}
|
||||
}
|
||||
|
||||
items.forEach((item, idx) => {
|
||||
if (idx >= 50) return;
|
||||
|
||||
const listing = { index: idx + 1 };
|
||||
|
||||
// Debug: log all attributes of first item
|
||||
if (idx === 0) {
|
||||
console.log('DEBUG First item attributes:', {
|
||||
className: item.className,
|
||||
id: item.id,
|
||||
attributes: Array.from(item.attributes || []).map(a => `${a.name}=${a.value}`),
|
||||
innerHTML: item.innerHTML.substring(0, 200)
|
||||
});
|
||||
}
|
||||
|
||||
// Title - Try specific Xiaozhu classes first
|
||||
const titleEl = item.querySelector('.list-title, h2, h3, h4, .title, .name, [class*="title"]');
|
||||
if (titleEl) listing.title = titleEl.textContent.trim();
|
||||
|
||||
// Price - Try specific Xiaozhu classes first
|
||||
const priceEl = item.querySelector('.list-price, .price-left, .price, [class*="price"]');
|
||||
if (priceEl) {
|
||||
const match = priceEl.textContent.match(/(\d+)/);
|
||||
if (match) {
|
||||
listing.priceDaily = parseInt(match[1]);
|
||||
listing.priceText = priceEl.textContent.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (!listing.priceDaily) {
|
||||
const match = item.textContent.match(/[¥¥]?\s*(\d+)\s*[元\/晚]/);
|
||||
if (match) listing.priceDaily = parseInt(match[1]);
|
||||
}
|
||||
|
||||
// Location - Extract from content or title
|
||||
const contentEl = item.querySelector('.list-content, .content, .location, .address');
|
||||
if (contentEl) listing.location = contentEl.textContent.trim();
|
||||
|
||||
// Also check title for location keywords
|
||||
if (!listing.location && listing.title) {
|
||||
listing.location = listing.title;
|
||||
}
|
||||
|
||||
// URL - Try multiple approaches
|
||||
// 1. Direct link
|
||||
const linkEl = item.querySelector('a');
|
||||
if (linkEl && linkEl.href && linkEl.href !== 'javascript:;') {
|
||||
listing.url = linkEl.href;
|
||||
}
|
||||
|
||||
// 2. Data attributes (房源ID / listing ID)
|
||||
if (!listing.url) {
|
||||
const dataId = item.getAttribute('data-id') ||
|
||||
item.getAttribute('data-house-id') ||
|
||||
item.getAttribute('data-fid');
|
||||
if (dataId) {
|
||||
listing.url = `https://minsu.xiaozhu.com/house/${dataId}`;
|
||||
listing.houseId = dataId;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Look for ID in onclick or other attributes
|
||||
if (!listing.url) {
|
||||
const onclick = item.getAttribute('onclick') || item.getAttribute('@click');
|
||||
if (onclick) {
|
||||
const idMatch = onclick.match(/\d{6,}/);
|
||||
if (idMatch) {
|
||||
listing.url = `https://minsu.xiaozhu.com/house/${idMatch[0]}`;
|
||||
listing.houseId = idMatch[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Check child elements for router-link
|
||||
if (!listing.url) {
|
||||
const routerLink = item.querySelector('[to], [router-link]');
|
||||
if (routerLink) {
|
||||
const to = routerLink.getAttribute('to') || routerLink.getAttribute('router-link');
|
||||
if (to) {
|
||||
listing.url = `https://minsu.xiaozhu.com${to}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Image
|
||||
const imgEl = item.querySelector('img');
|
||||
if (imgEl) listing.image = imgEl.src;
|
||||
|
||||
// Equipment (check Chinese text, not lowercased)
|
||||
const fullText = item.textContent;
|
||||
listing.hasKitchen = fullText.includes('厨房') || fullText.includes('可做饭') || fullText.includes('可烧饭');
|
||||
listing.hasFridge = fullText.includes('冰箱') || fullText.includes('冷藏');
|
||||
listing.hasWashingMachine = fullText.includes('洗衣机');
|
||||
listing.hasMetro = fullText.includes('地铁') || fullText.includes('站');
|
||||
|
||||
if (listing.title || listing.priceDaily) {
|
||||
results.push(listing);
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
});
|
||||
|
||||
// Better duplicate detection (URL or title+price)
|
||||
const newListings = listings.filter(l => {
|
||||
const isDuplicate = allListings.some(existing => {
|
||||
// By URL if available
|
||||
if (l.url && existing.url && l.url === existing.url) return true;
|
||||
// By title + price combination
|
||||
if (l.title && existing.title && l.priceDaily && existing.priceDaily) {
|
||||
return l.title === existing.title && l.priceDaily === existing.priceDaily;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return !isDuplicate;
|
||||
});
|
||||
|
||||
allListings = [...allListings, ...newListings];
|
||||
|
||||
console.log(` Found ${listings.length} items, ${newListings.length} new, ${allListings.length} total`);
|
||||
|
||||
if (allListings.length === previousCount) {
|
||||
noChangeCount++;
|
||||
if (noChangeCount >= CONFIG.noChangeThreshold) {
|
||||
console.log(` No new listings for ${CONFIG.noChangeThreshold} scrolls, stopping...`);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
noChangeCount = 0;
|
||||
}
|
||||
|
||||
previousCount = allListings.length;
|
||||
|
||||
// Scroll down
|
||||
await page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
||||
|
||||
// Wait for loading indicators to disappear
|
||||
try {
|
||||
await page.waitForFunction(() => {
|
||||
// Check for common loading indicators
|
||||
const loadingEls = document.querySelectorAll('.loading, .spinner, [class*="loading"]');
|
||||
return loadingEls.length === 0 || Array.from(loadingEls).every(el => el.style.display === 'none');
|
||||
}, { timeout: 2000 });
|
||||
} catch (e) {
|
||||
// No loading indicator found, that's fine
|
||||
}
|
||||
|
||||
// Additional wait for lazy load
|
||||
await wait(CONFIG.scrollDelay);
|
||||
}
|
||||
|
||||
await screenshot(page, 'final');
|
||||
|
||||
console.log(`\n✅ Total extracted: ${allListings.length} listings\n`);
|
||||
|
||||
// Save raw listings for debug
|
||||
fs.writeFileSync('./xiaozhu_raw_listings.json', JSON.stringify(allListings, null, 2));
|
||||
console.log('💾 Raw listings saved to xiaozhu_raw_listings.json\n');
|
||||
|
||||
if (allListings.length === 0) {
|
||||
console.log('❌ No listings found!');
|
||||
const html = await page.content();
|
||||
fs.writeFileSync('./xiaozhu_fixed_page.html', html);
|
||||
console.log('💾 Saved HTML to xiaozhu_fixed_page.html');
|
||||
|
||||
const pageInfo = await page.evaluate(() => ({
|
||||
url: window.location.href,
|
||||
title: document.title,
|
||||
hasShanghai: document.body.textContent.includes('上海'),
|
||||
hasBeijing: document.body.textContent.includes('北京'),
|
||||
bodyPreview: document.body.textContent.substring(0, 500)
|
||||
}));
|
||||
|
||||
console.log('\n📋 Page diagnosis:');
|
||||
console.log(` URL: ${pageInfo.url}`);
|
||||
console.log(` Title: ${pageInfo.title}`);
|
||||
console.log(` Has Shanghai: ${pageInfo.hasShanghai ? '✅' : '❌'}`);
|
||||
console.log(` Has Beijing: ${pageInfo.hasBeijing ? '⚠️' : '✅'}`);
|
||||
console.log(` Preview: ${pageInfo.bodyPreview.substring(0, 200)}...`);
|
||||
|
||||
} else {
|
||||
const processed = processListings(allListings);
|
||||
fs.writeFileSync(CONFIG.outputFile, JSON.stringify(processed, null, 2));
|
||||
console.log(`💾 ${CONFIG.outputFile}`);
|
||||
|
||||
const markdown = generateMarkdown(processed);
|
||||
fs.writeFileSync(CONFIG.outputMarkdown, markdown);
|
||||
console.log(`📝 ${CONFIG.outputMarkdown}`);
|
||||
|
||||
printTopResults(processed);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('❌ Error:', err.message);
|
||||
await screenshot(page, 'error');
|
||||
} finally {
|
||||
if (CONFIG.headless) {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processListings(listings) {
|
||||
return listings
|
||||
.filter(l => l.priceDaily && l.priceDaily > 0)
|
||||
.map(l => {
|
||||
l.priceTotal = l.priceDaily * CONFIG.days;
|
||||
l.priceMonthly = Math.ceil(l.priceDaily * 30);
|
||||
|
||||
let score = 0;
|
||||
|
||||
if (l.priceTotal <= CONFIG.dailyBudgetIdeal) {
|
||||
score += (CONFIG.dailyBudgetIdeal - l.priceTotal) / 100;
|
||||
} else if (l.priceTotal <= CONFIG.dailyBudgetMax) {
|
||||
score -= (l.priceTotal - CONFIG.dailyBudgetIdeal) / 50;
|
||||
} else {
|
||||
score -= 100;
|
||||
}
|
||||
|
||||
if (l.hasKitchen) score += 20;
|
||||
if (l.hasFridge) score += 15;
|
||||
if (l.hasWashingMachine) score += 10;
|
||||
if (l.hasMetro) score += 15;
|
||||
|
||||
if (l.location) {
|
||||
if (l.location.includes(CONFIG.district)) score += 20;
|
||||
if (l.location.includes(CONFIG.keyword)) score += 10;
|
||||
}
|
||||
if (l.title && l.title.includes(CONFIG.keyword)) score += 10;
|
||||
|
||||
l.score = Math.round(score * 10) / 10;
|
||||
return l;
|
||||
})
|
||||
// Relax filtering - show results even without kitchen/fridge detected
|
||||
// .filter(l => l.hasKitchen && l.hasFridge) // Too strict - equipment might be in icons
|
||||
.filter(l => l.priceTotal <= CONFIG.dailyBudgetMax * 1.2) // Allow 20% over budget
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, CONFIG.topN);
|
||||
}
|
||||
|
||||
function generateMarkdown(listings) {
|
||||
let md = '# Xiaozhu Results - FIXED Scraper\n\n';
|
||||
md += `**Date:** ${new Date().toLocaleDateString()}\n`;
|
||||
md += `**Location:** ${CONFIG.city} ${CONFIG.district}\n`;
|
||||
md += `**Dates:** ${CONFIG.checkIn} → ${CONFIG.checkOut} (${CONFIG.days} days)\n\n`;
|
||||
|
||||
md += '| # | Title | Daily | Total | Kitchen | Fridge | Washer | Metro | Score | Link |\n';
|
||||
md += '|---|-------|-------|-------|---------|--------|--------|-------|-------|------|\n';
|
||||
|
||||
listings.forEach((l, i) => {
|
||||
md += `| ${i + 1} `;
|
||||
md += `| ${(l.title || 'Untitled').substring(0, 40)} `;
|
||||
md += `| ¥${l.priceDaily} `;
|
||||
md += `| ¥${l.priceTotal} `;
|
||||
md += `| ${l.hasKitchen ? '✓' : '✗'} `;
|
||||
md += `| ${l.hasFridge ? '✓' : '✗'} `;
|
||||
md += `| ${l.hasWashingMachine ? '✓' : '✗'} `;
|
||||
md += `| ${l.hasMetro ? '✓' : '✗'} `;
|
||||
md += `| ${l.score} `;
|
||||
md += `| ${l.url ? `[View](${l.url})` : '-'} |\n`;
|
||||
});
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
function printTopResults(listings) {
|
||||
console.log('\n🏆 TOP RESULTS:\n');
|
||||
listings.slice(0, 5).forEach((l, i) => {
|
||||
console.log(`${i + 1}. ${l.title || 'Untitled'}`);
|
||||
console.log(` 💰 ¥${l.priceDaily}/day × ${CONFIG.days} days = ¥${l.priceTotal}`);
|
||||
if (l.location) console.log(` 📍 ${l.location}`);
|
||||
console.log(` ✓ Kitchen: ${l.hasKitchen ? '✓' : '✗'} | Fridge: ${l.hasFridge ? '✓' : '✗'} | Washer: ${l.hasWashingMachine ? '✓' : '✗'} | Metro: ${l.hasMetro ? '✓' : '✗'}`);
|
||||
console.log(` ⭐ ${l.score}`);
|
||||
if (l.url) console.log(` 🔗 ${l.url}`);
|
||||
console.log('');
|
||||
});
|
||||
}
|
||||
|
||||
scrapXiaozhu().catch(console.error);
|
||||
58
tools/xiaozhu_fixed_output.txt
Normal file
@ -0,0 +1,58 @@
|
||||
🚀 Xiaozhu FIXED Scraper
|
||||
📍 Target: 上海 徐汇区
|
||||
🌍 Geolocation: 31.188, 121.4367
|
||||
📅 Dates: 2025-12-24 → 2026-01-22 (29 days)
|
||||
💰 Budget: 4000-5000 RMB/month
|
||||
|
||||
🌍 Setting geolocation to Shanghai Xujiahui...
|
||||
✅ Geolocation set to 31.188, 121.4367
|
||||
|
||||
🔍 Strategy 1: Trying direct Shanghai URL...
|
||||
Trying: https://minsu.xiaozhu.com/shanghai
|
||||
❌ 404
|
||||
Trying: https://minsu.xiaozhu.com/city/shanghai
|
||||
❌ 404
|
||||
Trying: https://minsu.xiaozhu.com/search/shanghai
|
||||
❌ 404
|
||||
Trying: https://minsu.xiaozhu.com/shanghai/徐汇区
|
||||
❌ 404
|
||||
|
||||
🔍 Strategy 2: Homepage search with geolocation...
|
||||
📸 ./xiaozhu_homepage_1766282837074.png
|
||||
Detected city: 北京
|
||||
|
||||
⌨️ Using search...
|
||||
Found: input[type="text"]
|
||||
📸 ./xiaozhu_search_typed_1766282840134.png
|
||||
|
||||
👆 Looking for Shanghai suggestions...
|
||||
✅ Clicked Shanghai suggestion
|
||||
📸 ./xiaozhu_after_suggestion_1766282844255.png
|
||||
|
||||
📍 Current URL: https://minsu.xiaozhu.com/
|
||||
Shanghai content: ✅
|
||||
Beijing content: ✅ No
|
||||
|
||||
📊 Extracting listings...
|
||||
|
||||
🔄 Scroll 1/10...
|
||||
Found 0 items, 0 new, 0 total
|
||||
🔄 Scroll 2/10...
|
||||
Found 0 items, 0 new, 0 total
|
||||
🔄 Scroll 3/10...
|
||||
Found 0 items, 0 new, 0 total
|
||||
No new listings for 3 scrolls, stopping...
|
||||
📸 ./xiaozhu_final_1766282848443.png
|
||||
|
||||
✅ Total extracted: 0 listings
|
||||
|
||||
❌ No listings found!
|
||||
💾 Saved HTML to xiaozhu_fixed_page.html
|
||||
|
||||
📋 Page diagnosis:
|
||||
URL: https://minsu.xiaozhu.com/
|
||||
Title: 小猪-住得更好,花得更少
|
||||
Has Shanghai: ✅
|
||||
Has Beijing: ✅
|
||||
Preview:
|
||||
上海入12.21离12.22推荐排序价格范围位置区域筛选条件体育公寓,近徐家汇淮海路六院八院1号9号15号12号线地铁南站华东理工上师大高层阳光空气安静洁净单间单间·1床·宜住2人立即确认有停车位可做饭可带宠物¥132/晚¥165 已减33元早鸟特惠天天特惠胸科医院旁/黛园/独立卫浴,有窗户,可烧饭整租·1室·2床·宜住2人立即确认可做饭¥170/晚¥178 已减8元早鸟特惠天天特惠近中山医...
|
||||
150
tools/xiaozhu_fixed_page.html
Normal file
79
tools/xiaozhu_full_output.txt
Normal file
@ -0,0 +1,79 @@
|
||||
🚀 Xiaozhu FIXED Scraper
|
||||
📍 Target: 上海 徐汇区
|
||||
🌍 Geolocation: 31.188, 121.4367
|
||||
📅 Dates: 2025-12-24 → 2026-01-22 (29 days)
|
||||
💰 Budget: 4000-5000 RMB/month
|
||||
|
||||
🌍 Setting geolocation to Shanghai Xujiahui...
|
||||
✅ Geolocation set to 31.188, 121.4367
|
||||
|
||||
🔍 Strategy 1: Trying direct Shanghai URL...
|
||||
Trying: https://minsu.xiaozhu.com/shanghai
|
||||
❌ 404
|
||||
Trying: https://minsu.xiaozhu.com/city/shanghai
|
||||
❌ 404
|
||||
Trying: https://minsu.xiaozhu.com/search/shanghai
|
||||
❌ 404
|
||||
Trying: https://minsu.xiaozhu.com/shanghai/徐汇区
|
||||
❌ 404
|
||||
|
||||
🔍 Strategy 2: Homepage search with geolocation...
|
||||
📸 ./xiaozhu_homepage_1766283618604.png
|
||||
Detected city: 北京
|
||||
|
||||
⌨️ Using search...
|
||||
Found: input[type="text"]
|
||||
📸 ./xiaozhu_search_typed_1766283621669.png
|
||||
|
||||
👆 Looking for Shanghai suggestions...
|
||||
✅ Clicked Shanghai suggestion
|
||||
📸 ./xiaozhu_after_suggestion_1766283625754.png
|
||||
|
||||
📍 Current URL: https://minsu.xiaozhu.com/
|
||||
Shanghai content: ✅
|
||||
Beijing content: ✅ No
|
||||
|
||||
📊 Extracting listings...
|
||||
|
||||
⏳ Scrolling to load all listings (max 50 scrolls, 7 patience)...
|
||||
|
||||
🔄 Scroll 1/50 (0%)...
|
||||
Found 10 items, 10 new, 10 total
|
||||
🔄 Scroll 2/50 (2%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
🔄 Scroll 3/50 (4%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
🔄 Scroll 4/50 (6%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
🔄 Scroll 5/50 (8%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
🔄 Scroll 6/50 (10%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
🔄 Scroll 7/50 (12%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
🔄 Scroll 8/50 (14%)...
|
||||
Found 10 items, 0 new, 10 total
|
||||
No new listings for 7 scrolls, stopping...
|
||||
📸 ./xiaozhu_final_1766283650545.png
|
||||
|
||||
✅ Total extracted: 10 listings
|
||||
|
||||
💾 Raw listings saved to xiaozhu_raw_listings.json
|
||||
|
||||
💾 ./xiaozhu_results.json
|
||||
📝 ./xiaozhu_results.md
|
||||
|
||||
🏆 TOP RESULTS:
|
||||
|
||||
1. 体育公寓,近徐家汇淮海路六院八院1号9号15号12号线地铁南站华东理工上师大高层阳光空气安静洁净单间
|
||||
💰 ¥132/day × 29 days = ¥3828
|
||||
📍 体育公寓,近徐家汇淮海路六院八院1号9号15号12号线地铁南站华东理工上师大高层阳光空气安静洁净单间单间·1床·宜住2人立即确认有停车位可做饭可带宠物¥132/晚¥165 已减33元早鸟特惠天天特惠
|
||||
✓ Kitchen: ✓ | Fridge: ✗ | Washer: ✗ | Metro: ✓
|
||||
⭐ 35.4
|
||||
|
||||
2. 胸科医院旁/黛园/独立卫浴,有窗户,可烧饭
|
||||
💰 ¥170/day × 29 days = ¥4930
|
||||
📍 胸科医院旁/黛园/独立卫浴,有窗户,可烧饭整租·1室·2床·宜住2人立即确认可做饭¥170/晚¥178 已减8元早鸟特惠天天特惠
|
||||
✓ Kitchen: ✓ | Fridge: ✗ | Washer: ✗ | Metro: ✗
|
||||
⭐ -80
|
||||
|
||||
BIN
tools/xiaozhu_home.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
tools/xiaozhu_homepage_1766282351057.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766282433294.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766282508067.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766282837074.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766282960707.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766283138522.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766283392270.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766283618604.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766283802971.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766284811773.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766284990467.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
tools/xiaozhu_homepage_1766285925294.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
206
tools/xiaozhu_inspector.js
Normal file
@ -0,0 +1,206 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Xiaozhu Inspector - Inspect page structure without login
|
||||
* This will help identify the correct CSS selectors
|
||||
*/
|
||||
|
||||
async function inspectXiaozhu() {
|
||||
console.log('🔍 Launching browser to inspect Xiaozhu...');
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: "new", // Run in headless mode for WSL compatibility
|
||||
defaultViewport: { width: 1920, height: 1080 },
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox'] // Required for WSL
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
// Try different URL patterns
|
||||
const searchUrls = [
|
||||
'https://www.xiaozhu.com/search-shanghai-徐汇区/',
|
||||
'https://www.xiaozhu.com/search/shanghai/',
|
||||
'https://www.xiaozhu.com/shanghai/',
|
||||
'https://www.xiaozhu.com/'
|
||||
];
|
||||
|
||||
for (const url of searchUrls) {
|
||||
console.log(`\n📡 Trying: ${url}`);
|
||||
|
||||
try {
|
||||
await page.goto(url, {
|
||||
waitUntil: 'networkidle2',
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
console.log(`✅ Loaded: ${page.url()}`);
|
||||
|
||||
// Wait a bit for dynamic content
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// Take screenshot
|
||||
const screenshotPath = `./xiaozhu_screenshot_${Date.now()}.png`;
|
||||
await page.screenshot({ path: screenshotPath, fullPage: true });
|
||||
console.log(`📸 Screenshot saved: ${screenshotPath}`);
|
||||
|
||||
// Extract page structure
|
||||
const pageInfo = await page.evaluate(() => {
|
||||
const info = {
|
||||
title: document.title,
|
||||
url: window.location.href,
|
||||
bodyClasses: document.body.className,
|
||||
|
||||
// Try to find common patterns for listing containers
|
||||
possibleContainers: [],
|
||||
possibleListingCards: [],
|
||||
|
||||
// Look for elements that might be listings
|
||||
allClasses: new Set(),
|
||||
allIds: new Set()
|
||||
};
|
||||
|
||||
// Collect all classes and IDs
|
||||
document.querySelectorAll('*').forEach(el => {
|
||||
if (el.className && typeof el.className === 'string') {
|
||||
el.className.split(' ').forEach(cls => {
|
||||
if (cls) info.allClasses.add(cls);
|
||||
});
|
||||
}
|
||||
if (el.id) info.allIds.add(el.id);
|
||||
});
|
||||
|
||||
// Look for elements that might contain listings
|
||||
const possibleSelectors = [
|
||||
'.list', '.listing', '.result', '.item', '.card',
|
||||
'[class*="list"]', '[class*="result"]', '[class*="house"]',
|
||||
'[class*="room"]', '[class*="apartment"]'
|
||||
];
|
||||
|
||||
possibleSelectors.forEach(selector => {
|
||||
try {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
if (elements.length > 0 && elements.length < 100) {
|
||||
info.possibleContainers.push({
|
||||
selector: selector,
|
||||
count: elements.length,
|
||||
sample: elements[0]?.className || elements[0]?.id || 'no class/id'
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
// Try to extract any visible listings
|
||||
const extractedListings = [];
|
||||
|
||||
// Common patterns
|
||||
const cardSelectors = [
|
||||
'.pho_item', '.room_box', '.result_list li', '.house_item',
|
||||
'[class*="card"]', '[class*="item"]'
|
||||
];
|
||||
|
||||
for (const sel of cardSelectors) {
|
||||
try {
|
||||
const cards = document.querySelectorAll(sel);
|
||||
if (cards.length > 2 && cards.length < 50) {
|
||||
cards.forEach((card, i) => {
|
||||
if (i < 3) { // Sample first 3
|
||||
const listing = {
|
||||
selector: sel,
|
||||
html: card.innerHTML.substring(0, 500),
|
||||
text: card.textContent.substring(0, 200).trim(),
|
||||
classes: card.className,
|
||||
|
||||
// Try to find price
|
||||
priceElements: [],
|
||||
titleElements: [],
|
||||
locationElements: []
|
||||
};
|
||||
|
||||
// Look for price (¥, 元, number)
|
||||
card.querySelectorAll('*').forEach(el => {
|
||||
const text = el.textContent;
|
||||
if (text.match(/[¥¥]?\d+[元\/]/)) {
|
||||
listing.priceElements.push({
|
||||
tag: el.tagName,
|
||||
class: el.className,
|
||||
text: text.substring(0, 50)
|
||||
});
|
||||
}
|
||||
|
||||
// Title usually in h2, h3, or has 'title' in class
|
||||
if (['H1', 'H2', 'H3', 'H4'].includes(el.tagName) ||
|
||||
(el.className && el.className.includes('title'))) {
|
||||
listing.titleElements.push({
|
||||
tag: el.tagName,
|
||||
class: el.className,
|
||||
text: text.substring(0, 100)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
extractedListings.push(listing);
|
||||
}
|
||||
});
|
||||
break; // Found good selector, stop
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
info.allClasses = Array.from(info.allClasses);
|
||||
info.allIds = Array.from(info.allIds);
|
||||
info.extractedListings = extractedListings;
|
||||
|
||||
return info;
|
||||
});
|
||||
|
||||
// Save page info
|
||||
const infoPath = `./xiaozhu_pageinfo_${Date.now()}.json`;
|
||||
fs.writeFileSync(infoPath, JSON.stringify(pageInfo, null, 2));
|
||||
console.log(`💾 Page info saved: ${infoPath}`);
|
||||
|
||||
// Print summary
|
||||
console.log('\n📊 PAGE ANALYSIS:');
|
||||
console.log(` Title: ${pageInfo.title}`);
|
||||
console.log(` URL: ${pageInfo.url}`);
|
||||
console.log(` Total classes found: ${pageInfo.allClasses.length}`);
|
||||
console.log(` Total IDs found: ${pageInfo.allIds.length}`);
|
||||
|
||||
if (pageInfo.possibleContainers.length > 0) {
|
||||
console.log('\n🎯 POSSIBLE LISTING CONTAINERS:');
|
||||
pageInfo.possibleContainers.slice(0, 5).forEach(c => {
|
||||
console.log(` - ${c.selector} (${c.count} elements)`);
|
||||
});
|
||||
}
|
||||
|
||||
if (pageInfo.extractedListings.length > 0) {
|
||||
console.log('\n📝 SAMPLE LISTINGS EXTRACTED:');
|
||||
pageInfo.extractedListings.forEach((l, i) => {
|
||||
console.log(`\n Listing ${i + 1} (selector: ${l.selector}):`);
|
||||
if (l.titleElements.length > 0) {
|
||||
console.log(` Title: ${l.titleElements[0].text}`);
|
||||
}
|
||||
if (l.priceElements.length > 0) {
|
||||
console.log(` Price: ${l.priceElements[0].text}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n✅ Successfully inspected this URL!');
|
||||
console.log('📸 Check the screenshot and JSON file for details');
|
||||
|
||||
// Found a working URL, no need to try others
|
||||
await browser.close();
|
||||
return;
|
||||
|
||||
} catch (err) {
|
||||
console.log(`❌ Failed to load: ${err.message}`);
|
||||
continue; // Try next URL
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n❌ All URLs failed. Site might be blocking automated access.');
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
inspectXiaozhu().catch(console.error);
|
||||
581
tools/xiaozhu_interactive.js
Normal file
@ -0,0 +1,581 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Xiaozhu Interactive Scraper - Full navigation simulation
|
||||
* Simulates real user behavior to navigate and extract listings
|
||||
*/
|
||||
|
||||
const CONFIG = {
|
||||
// Search criteria
|
||||
city: '上海',
|
||||
district: '徐汇区',
|
||||
keyword: '交通大学',
|
||||
|
||||
// Dates
|
||||
checkIn: '2025-12-24',
|
||||
checkOut: '2026-01-22',
|
||||
days: 29,
|
||||
|
||||
// Budget (RMB)
|
||||
budgetIdeal: 4000,
|
||||
budgetMax: 5000,
|
||||
|
||||
get dailyBudgetIdeal() {
|
||||
return Math.ceil(this.budgetIdeal / 30 * this.days);
|
||||
},
|
||||
get dailyBudgetMax() {
|
||||
return Math.ceil(this.budgetMax / 30 * this.days);
|
||||
},
|
||||
|
||||
// Equipment
|
||||
required: ['厨房', '冰箱'],
|
||||
bonus: ['洗衣机', '地铁'],
|
||||
|
||||
// Scraping config
|
||||
maxScrolls: 10,
|
||||
scrollDelay: 2000,
|
||||
interactionDelay: 1000,
|
||||
|
||||
// Output
|
||||
outputFile: './xiaozhu_results.json',
|
||||
outputMarkdown: './xiaozhu_results.md',
|
||||
topN: 20,
|
||||
|
||||
// Debug
|
||||
headless: true,
|
||||
screenshots: true
|
||||
};
|
||||
|
||||
console.log('🚀 Xiaozhu Interactive Scraper');
|
||||
console.log(`📍 Target: ${CONFIG.city} ${CONFIG.district}`);
|
||||
console.log(`📅 Dates: ${CONFIG.checkIn} → ${CONFIG.checkOut} (${CONFIG.days} days)`);
|
||||
console.log(`💰 Budget: ${CONFIG.budgetIdeal}-${CONFIG.budgetMax} RMB/month (${CONFIG.dailyBudgetIdeal}-${CONFIG.dailyBudgetMax} RMB total)\n`);
|
||||
|
||||
async function wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function screenshot(page, name) {
|
||||
if (CONFIG.screenshots) {
|
||||
const filename = `./xiaozhu_${name}_${Date.now()}.png`;
|
||||
await page.screenshot({ path: filename, fullPage: true });
|
||||
console.log(`📸 Screenshot: ${filename}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCookies() {
|
||||
try {
|
||||
const cookies = fs.readFileSync('./xiaozhu_cookies.json', 'utf8');
|
||||
return JSON.parse(cookies);
|
||||
} catch (err) {
|
||||
console.log('⚠️ No cookies found (optional)');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function scrapXiaozhu() {
|
||||
const cookies = await loadCookies();
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: CONFIG.headless ? "new" : false,
|
||||
defaultViewport: { width: 414, height: 896 }, // Mobile viewport (Xiaozhu is mobile-first)
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage'
|
||||
]
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
// Mobile user agent
|
||||
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1');
|
||||
|
||||
// Load cookies if available
|
||||
if (cookies && cookies.length > 0) {
|
||||
try {
|
||||
await page.setCookie(...cookies);
|
||||
console.log(`🍪 Loaded ${cookies.length} cookies\n`);
|
||||
} catch (err) {
|
||||
console.log('⚠️ Could not load cookies:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🌐 Loading homepage...');
|
||||
await page.goto('https://minsu.xiaozhu.com/', {
|
||||
waitUntil: 'networkidle2',
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
await wait(3000); // Wait for Vue app to initialize
|
||||
await screenshot(page, 'homepage');
|
||||
|
||||
console.log('✅ Homepage loaded\n');
|
||||
|
||||
// Strategy 1: Look for search input
|
||||
console.log('🔍 Looking for search input...');
|
||||
|
||||
const searchSelectors = [
|
||||
'input[placeholder*="目的地"]',
|
||||
'input[placeholder*="搜索"]',
|
||||
'input[placeholder*="城市"]',
|
||||
'.search-input',
|
||||
'.van-search__content input',
|
||||
'input[type="search"]',
|
||||
'input[type="text"]'
|
||||
];
|
||||
|
||||
let searchInput = null;
|
||||
let inputSelector = null;
|
||||
|
||||
for (const selector of searchSelectors) {
|
||||
try {
|
||||
const element = await page.$(selector);
|
||||
if (element) {
|
||||
const isVisible = await page.evaluate(el => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
return rect.width > 0 && rect.height > 0;
|
||||
}, element);
|
||||
|
||||
if (isVisible) {
|
||||
searchInput = element;
|
||||
inputSelector = selector;
|
||||
console.log(`✅ Found search input: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (searchInput) {
|
||||
console.log('⌨️ Entering search query...');
|
||||
|
||||
// Click to focus
|
||||
await searchInput.click();
|
||||
await wait(500);
|
||||
|
||||
// Type search query - try just city first
|
||||
await searchInput.type(`${CONFIG.city}`, { delay: 150 });
|
||||
await wait(CONFIG.interactionDelay * 2); // Wait longer for suggestions to load
|
||||
|
||||
await screenshot(page, 'search_typed');
|
||||
|
||||
// Look for search suggestions or submit button
|
||||
console.log('👆 Looking for search button or suggestions...');
|
||||
|
||||
const submitSelectors = [
|
||||
'button[type="submit"]',
|
||||
'.search-button',
|
||||
'.van-button--primary',
|
||||
'button.submit',
|
||||
'.search-btn'
|
||||
];
|
||||
|
||||
let submitted = false;
|
||||
|
||||
// Try to click suggestions first
|
||||
await wait(1500);
|
||||
|
||||
// Look for suggestions containing Shanghai
|
||||
const shanghaiClicked = await page.evaluate((city) => {
|
||||
const suggestions = document.querySelectorAll('.van-cell, .suggestion-item, [class*="suggest"], .city-item, div[class*="item"]');
|
||||
for (const sugg of suggestions) {
|
||||
if (sugg.textContent.includes(city)) {
|
||||
sugg.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, CONFIG.city);
|
||||
|
||||
if (shanghaiClicked) {
|
||||
console.log(` Clicked suggestion containing ${CONFIG.city}`);
|
||||
submitted = true;
|
||||
await wait(4000);
|
||||
} else {
|
||||
console.log(` No ${CONFIG.city} suggestion found, trying all suggestions...`);
|
||||
const suggestions = await page.$$('.van-cell, .suggestion-item, [class*="suggest"]');
|
||||
if (suggestions.length > 0) {
|
||||
console.log(` Found ${suggestions.length} suggestions, clicking first...`);
|
||||
await suggestions[0].click();
|
||||
submitted = true;
|
||||
await wait(3000);
|
||||
}
|
||||
}
|
||||
|
||||
// If no suggestions, try submit button
|
||||
if (!submitted) {
|
||||
for (const selector of submitSelectors) {
|
||||
try {
|
||||
const button = await page.$(selector);
|
||||
if (button) {
|
||||
console.log(` Clicking submit: ${selector}`);
|
||||
await button.click();
|
||||
submitted = true;
|
||||
await wait(3000);
|
||||
break;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
// If still not submitted, try pressing Enter
|
||||
if (!submitted) {
|
||||
console.log(' Pressing Enter...');
|
||||
await page.keyboard.press('Enter');
|
||||
await wait(3000);
|
||||
}
|
||||
|
||||
await screenshot(page, 'after_search');
|
||||
|
||||
} else {
|
||||
// Strategy 2: Look for city/location selector
|
||||
console.log('❌ No search input found');
|
||||
console.log('🔍 Looking for city selector...');
|
||||
|
||||
const citySelectors = [
|
||||
'a:contains("上海")',
|
||||
'div:contains("上海")',
|
||||
'.city-item',
|
||||
'[data-city="shanghai"]'
|
||||
];
|
||||
|
||||
// Try to find and click Shanghai
|
||||
const cityFound = await page.evaluate((city) => {
|
||||
const elements = Array.from(document.querySelectorAll('a, div, span'));
|
||||
const shanghaEl = elements.find(el =>
|
||||
el.textContent.trim() === city &&
|
||||
el.getBoundingClientRect().width > 0
|
||||
);
|
||||
|
||||
if (shanghaEl) {
|
||||
shanghaEl.click();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, CONFIG.city);
|
||||
|
||||
if (cityFound) {
|
||||
console.log('✅ Clicked Shanghai');
|
||||
await wait(3000);
|
||||
} else {
|
||||
console.log('⚠️ Could not find city selector');
|
||||
}
|
||||
}
|
||||
|
||||
// Current URL after navigation
|
||||
let currentUrl = page.url();
|
||||
console.log(`\n📍 Current URL: ${currentUrl}`);
|
||||
|
||||
// If we're on /suggest page, try to find and click Shanghai
|
||||
if (currentUrl.includes('/suggest')) {
|
||||
console.log('⚠️ On suggestions page, looking for Shanghai option...\n');
|
||||
|
||||
const shanghaiFound = await page.evaluate((city) => {
|
||||
// Look for Shanghai in hot recommendations or administrative areas
|
||||
const items = document.querySelectorAll('.city-hot-item, .city-hot-item2, .city-item, div[class*="item"]');
|
||||
for (const item of items) {
|
||||
const text = item.textContent.trim();
|
||||
if (text === city || text.includes(city)) {
|
||||
console.log(`Found ${city} option: ${text}`);
|
||||
item.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, CONFIG.city);
|
||||
|
||||
if (shanghaiFound) {
|
||||
console.log(` ✅ Clicked ${CONFIG.city} from suggestions`);
|
||||
await wait(4000);
|
||||
currentUrl = page.url();
|
||||
console.log(` 📍 New URL: ${currentUrl}`);
|
||||
} else {
|
||||
console.log(` ❌ ${CONFIG.city} not found in suggestions`);
|
||||
console.log(` 💡 Try searching for just the city name next time\n`);
|
||||
}
|
||||
}
|
||||
|
||||
await screenshot(page, 'before_extraction');
|
||||
console.log('');
|
||||
|
||||
// Extract listings
|
||||
console.log('📊 Extracting listings...\n');
|
||||
|
||||
let allListings = [];
|
||||
let previousCount = 0;
|
||||
let noChangeCount = 0;
|
||||
|
||||
// Scroll to load more listings (lazy loading)
|
||||
for (let i = 0; i < CONFIG.maxScrolls; i++) {
|
||||
console.log(`🔄 Scroll ${i + 1}/${CONFIG.maxScrolls}...`);
|
||||
|
||||
// Extract current listings
|
||||
const listings = await page.evaluate(() => {
|
||||
const results = [];
|
||||
|
||||
// Possible selectors for listing cards
|
||||
const selectors = [
|
||||
'.house-item',
|
||||
'.room-item',
|
||||
'.van-card',
|
||||
'[class*="house"]',
|
||||
'[class*="room"]',
|
||||
'[class*="card"]'
|
||||
];
|
||||
|
||||
let items = [];
|
||||
for (const sel of selectors) {
|
||||
const elements = document.querySelectorAll(sel);
|
||||
if (elements.length > items.length) {
|
||||
items = Array.from(elements);
|
||||
}
|
||||
}
|
||||
|
||||
items.forEach((item, idx) => {
|
||||
try {
|
||||
const listing = {
|
||||
index: idx + 1,
|
||||
html: item.innerHTML.substring(0, 500),
|
||||
text: item.textContent.trim().substring(0, 300)
|
||||
};
|
||||
|
||||
// Extract title
|
||||
const titleEl = item.querySelector('h2, h3, h4, .title, .name, .van-card__title, [class*="title"]');
|
||||
if (titleEl) {
|
||||
listing.title = titleEl.textContent.trim();
|
||||
}
|
||||
|
||||
// Extract price
|
||||
const pricePatterns = [
|
||||
'.price', '.van-card__price', '[class*="price"]',
|
||||
'span:contains("¥")', 'span:contains("元")'
|
||||
];
|
||||
|
||||
for (const pattern of pricePatterns) {
|
||||
const priceEl = item.querySelector(pattern);
|
||||
if (priceEl) {
|
||||
const priceText = priceEl.textContent;
|
||||
const match = priceText.match(/(\d+)/);
|
||||
if (match) {
|
||||
listing.priceDaily = parseInt(match[1]);
|
||||
listing.priceText = priceText.trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no price found, search in all text
|
||||
if (!listing.priceDaily) {
|
||||
const priceMatch = item.textContent.match(/[¥¥]?\s*(\d+)\s*[元\/]/);
|
||||
if (priceMatch) {
|
||||
listing.priceDaily = parseInt(priceMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract location
|
||||
const locationEl = item.querySelector('.location, .address, .area, [class*="location"]');
|
||||
if (locationEl) {
|
||||
listing.location = locationEl.textContent.trim();
|
||||
}
|
||||
|
||||
// Extract URL
|
||||
const linkEl = item.querySelector('a');
|
||||
if (linkEl) {
|
||||
listing.url = linkEl.href;
|
||||
}
|
||||
|
||||
// Extract image
|
||||
const imgEl = item.querySelector('img');
|
||||
if (imgEl) {
|
||||
listing.image = imgEl.src;
|
||||
}
|
||||
|
||||
// Check equipment mentions in text
|
||||
const fullText = item.textContent.toLowerCase();
|
||||
listing.hasKitchen = fullText.includes('厨房') || fullText.includes('kitchen');
|
||||
listing.hasFridge = fullText.includes('冰箱') || fullText.includes('fridge');
|
||||
listing.hasWashingMachine = fullText.includes('洗衣机') || fullText.includes('washing');
|
||||
listing.hasMetro = fullText.includes('地铁') || fullText.includes('metro') || fullText.includes('站');
|
||||
|
||||
results.push(listing);
|
||||
} catch (e) {
|
||||
console.error('Error extracting listing:', e);
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
});
|
||||
|
||||
// Merge with previous (avoid duplicates by URL)
|
||||
const newListings = listings.filter(l =>
|
||||
!allListings.some(existing => existing.url === l.url && l.url)
|
||||
);
|
||||
|
||||
allListings = [...allListings, ...newListings];
|
||||
|
||||
console.log(` Found ${listings.length} on page, ${newListings.length} new, ${allListings.length} total`);
|
||||
|
||||
// Check if we got new listings
|
||||
if (allListings.length === previousCount) {
|
||||
noChangeCount++;
|
||||
if (noChangeCount >= 3) {
|
||||
console.log(' No new listings for 3 scrolls, stopping...');
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
noChangeCount = 0;
|
||||
}
|
||||
|
||||
previousCount = allListings.length;
|
||||
|
||||
// Scroll down
|
||||
await page.evaluate(() => {
|
||||
window.scrollBy(0, window.innerHeight);
|
||||
});
|
||||
|
||||
await wait(CONFIG.scrollDelay);
|
||||
}
|
||||
|
||||
await screenshot(page, 'final');
|
||||
|
||||
console.log(`\n✅ Total extracted: ${allListings.length} listings\n`);
|
||||
|
||||
if (allListings.length === 0) {
|
||||
console.log('❌ No listings found!');
|
||||
console.log('💾 Saving page HTML for inspection...');
|
||||
|
||||
const html = await page.content();
|
||||
fs.writeFileSync('./xiaozhu_interactive_page.html', html);
|
||||
|
||||
console.log('\n📋 Page info:');
|
||||
const pageInfo = await page.evaluate(() => ({
|
||||
url: window.location.href,
|
||||
title: document.title,
|
||||
bodyText: document.body.textContent.substring(0, 500),
|
||||
elementCount: document.querySelectorAll('*').length
|
||||
}));
|
||||
|
||||
console.log(` URL: ${pageInfo.url}`);
|
||||
console.log(` Title: ${pageInfo.title}`);
|
||||
console.log(` Elements: ${pageInfo.elementCount}`);
|
||||
console.log(` Body preview: ${pageInfo.bodyText.substring(0, 200)}...`);
|
||||
|
||||
} else {
|
||||
// Process and filter listings
|
||||
const processed = processListings(allListings);
|
||||
|
||||
// Save results
|
||||
fs.writeFileSync(CONFIG.outputFile, JSON.stringify(processed, null, 2));
|
||||
console.log(`💾 Results saved: ${CONFIG.outputFile}`);
|
||||
|
||||
const markdown = generateMarkdown(processed);
|
||||
fs.writeFileSync(CONFIG.outputMarkdown, markdown);
|
||||
console.log(`📝 Markdown saved: ${CONFIG.outputMarkdown}`);
|
||||
|
||||
// Print top results
|
||||
printTopResults(processed);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('❌ Error:', err.message);
|
||||
console.error(err.stack);
|
||||
|
||||
await screenshot(page, 'error');
|
||||
} finally {
|
||||
if (CONFIG.headless) {
|
||||
await browser.close();
|
||||
} else {
|
||||
console.log('\n⏸️ Browser kept open for inspection. Close manually when done.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processListings(listings) {
|
||||
return listings
|
||||
.filter(l => l.priceDaily && l.priceDaily > 0)
|
||||
.map(l => {
|
||||
// Calculate total price
|
||||
l.priceTotal = l.priceDaily * CONFIG.days;
|
||||
l.priceMonthly = Math.ceil(l.priceDaily * 30);
|
||||
|
||||
// Score
|
||||
let score = 0;
|
||||
|
||||
// Price scoring
|
||||
if (l.priceTotal <= CONFIG.dailyBudgetIdeal) {
|
||||
score += (CONFIG.dailyBudgetIdeal - l.priceTotal) / 100;
|
||||
} else if (l.priceTotal <= CONFIG.dailyBudgetMax) {
|
||||
score -= (l.priceTotal - CONFIG.dailyBudgetIdeal) / 50;
|
||||
} else {
|
||||
score -= 100;
|
||||
}
|
||||
|
||||
// Equipment bonuses
|
||||
if (l.hasKitchen) score += 20;
|
||||
if (l.hasFridge) score += 15;
|
||||
if (l.hasWashingMachine) score += 10;
|
||||
if (l.hasMetro) score += 15;
|
||||
|
||||
// Location bonus
|
||||
if (l.location) {
|
||||
if (l.location.includes(CONFIG.district)) score += 20;
|
||||
if (l.location.includes(CONFIG.keyword)) score += 10;
|
||||
}
|
||||
if (l.title) {
|
||||
if (l.title.includes(CONFIG.keyword)) score += 10;
|
||||
}
|
||||
|
||||
l.score = Math.round(score * 10) / 10;
|
||||
return l;
|
||||
})
|
||||
.filter(l => l.hasKitchen && l.hasFridge) // Required
|
||||
.filter(l => l.priceTotal <= CONFIG.dailyBudgetMax) // Budget
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, CONFIG.topN);
|
||||
}
|
||||
|
||||
function generateMarkdown(listings) {
|
||||
let md = '# Xiaozhu Search Results - Interactive Scraper\n\n';
|
||||
md += `**Date:** ${new Date().toLocaleDateString()}\n`;
|
||||
md += `**Location:** ${CONFIG.city} ${CONFIG.district}\n`;
|
||||
md += `**Dates:** ${CONFIG.checkIn} → ${CONFIG.checkOut} (${CONFIG.days} days)\n`;
|
||||
md += `**Budget:** ${CONFIG.budgetIdeal}-${CONFIG.budgetMax} RMB/month\n\n`;
|
||||
|
||||
md += '| # | Title | Daily | Total | Kitchen | Fridge | Washer | Metro | Score | Link |\n';
|
||||
md += '|---|-------|-------|-------|---------|--------|--------|-------|-------|------|\n';
|
||||
|
||||
listings.forEach((l, i) => {
|
||||
md += `| ${i + 1} `;
|
||||
md += `| ${(l.title || 'Untitled').substring(0, 40)} `;
|
||||
md += `| ¥${l.priceDaily} `;
|
||||
md += `| ¥${l.priceTotal} `;
|
||||
md += `| ${l.hasKitchen ? '✓' : '✗'} `;
|
||||
md += `| ${l.hasFridge ? '✓' : '✗'} `;
|
||||
md += `| ${l.hasWashingMachine ? '✓' : '✗'} `;
|
||||
md += `| ${l.hasMetro ? '✓' : '✗'} `;
|
||||
md += `| ${l.score} `;
|
||||
md += `| ${l.url ? `[View](${l.url})` : '-'} |\n`;
|
||||
});
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
function printTopResults(listings) {
|
||||
console.log('\n🏆 TOP RESULTS:\n');
|
||||
|
||||
listings.slice(0, 5).forEach((l, i) => {
|
||||
console.log(`${i + 1}. ${l.title || 'Untitled'}`);
|
||||
console.log(` 💰 ¥${l.priceDaily}/day × ${CONFIG.days} days = ¥${l.priceTotal} total (~¥${l.priceMonthly}/month)`);
|
||||
if (l.location) console.log(` 📍 ${l.location}`);
|
||||
console.log(` ✓ Kitchen: ${l.hasKitchen ? '✓' : '✗'} | Fridge: ${l.hasFridge ? '✓' : '✗'} | Washer: ${l.hasWashingMachine ? '✓' : '✗'} | Metro: ${l.hasMetro ? '✓' : '✗'}`);
|
||||
console.log(` ⭐ Score: ${l.score}`);
|
||||
if (l.url) console.log(` 🔗 ${l.url}`);
|
||||
console.log('');
|
||||
});
|
||||
}
|
||||
|
||||
// Run
|
||||
scrapXiaozhu().catch(console.error);
|
||||
150
tools/xiaozhu_interactive_page.html
Normal file
BIN
tools/xiaozhu_minsu_final.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
150
tools/xiaozhu_minsu_page.html
Normal file
@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html><html lang="en" data-dpr="1" style="font-size: 54px;"><head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/assets/favicon-Bk9typ0l.ico">
|
||||
<meta name="baidu-site-verification" content="7SPeyLRFZZ">
|
||||
<meta name="applicable-device" content="mobile">
|
||||
<meta http-equiv="Cache-Control" content="no-siteapp">
|
||||
<meta http-equiv="Cache-Control" content="no-transform">
|
||||
<meta name="format-detection" content="telephone=yes">
|
||||
<meta http-equiv="Permissions-Policy" content="geolocation=(self)">
|
||||
<link rel="canonical" href="https://minsu.xiaozhu.com/ ">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover">
|
||||
<title>小猪-住得更好,花得更少</title>
|
||||
<meta name="keywords" content="日租房,短租房,小猪短租,短租公寓,酒店式公寓,青年旅舍,小猪">
|
||||
<meta name="description" content="小猪为国内领先的特色住宿预订平台。截至2019年5月,小猪全平台共有超过80万间房源,分布在全球超过700座城市及目的地。">
|
||||
<iframe height="0" width="0" id="xzappFrmops" style="display: none;"></iframe><script type="module" crossorigin="" src="/assets/index-Q_bw89QS.js"></script>
|
||||
<link rel="stylesheet" crossorigin="" href="/assets/index-BB439n9R.css">
|
||||
<link rel="modulepreload" as="script" crossorigin="" href="/assets/index-D18tMpWq.js"><link rel="stylesheet" crossorigin="" href="/assets/index-B8QvNXB-.css"></head>
|
||||
|
||||
<body style="font-size: 12px;">
|
||||
<div id="app" data-v-app=""><div><div class="van-nav-bar__placeholder" style="height: 66.2344px;"><div class="van-nav-bar van-nav-bar--fixed van-hairline--bottom"><div class="van-nav-bar__content"><div class="van-nav-bar__left van-haptics-feedback"><i class="van-badge__wrapper van-icon van-icon-arrow-left van-nav-bar__arrow"><!----><!----><!----></i><!----></div><div class="van-nav-bar__title van-ellipsis">404</div><div class="van-nav-bar__right van-haptics-feedback"><i class="van-badge__wrapper van-icon van-icon-ellipsis" style="font-size: 24px;"><!----><!----><!----></i></div></div></div></div></div><div data-v-7464c594="" class="van-nav-bar van-hairline--bottom"><div class="van-nav-bar__content"><!----><div class="van-nav-bar__title van-ellipsis">404</div><!----></div></div><div data-v-7464c594="" class="no-data"><img data-v-7464c594="" src="https://minsu.xiaozhu.com/assets/5-R41HHUIc.png" alt=""><h3 data-v-7464c594="">页面找不到了</h3></div></div>
|
||||
<script>
|
||||
var ua = navigator.userAgent.toLowerCase();
|
||||
var cururl = window.location.href;
|
||||
|
||||
function getCookie(name) {
|
||||
const v = document.cookie.match("(^|;) ?" + name + "=([^;]*)(;|$)");
|
||||
return v ? v[2] : null;
|
||||
}
|
||||
if (!sessionStorage.fresh) {
|
||||
sessionStorage.fresh = 0;
|
||||
}
|
||||
if (
|
||||
cururl.indexOf("xiaozhu.com") != -1 &&
|
||||
cururl.indexOf("bargin") == -1 &&
|
||||
cururl.indexOf("successBargain") == -1
|
||||
) {
|
||||
if (window.location.host == "minsu.xiaozhu.com") {
|
||||
if (cururl.indexOf("manage/index") == -1 && cururl.indexOf("appUp") == -1) {
|
||||
document.writeln(
|
||||
'<script src="https://wirelesspub-risk-center.xiaozhu.com/guard/secure.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
document.writeln(
|
||||
'<script src="https://test-wirelesspub-risk-center.xiaozhu.com/guard/secure.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
}
|
||||
if (window.location.host !== "minsu.xiaozhu.com") {
|
||||
document.writeln(
|
||||
'<script src="https://test-wirelesspub-risk-center.xiaozhu.com/guard/secure.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
if (ua.indexOf("alipayclient") > -1) {
|
||||
document.writeln(
|
||||
'<script src="https://appx/web-view.min.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
'script><script src="/static/js/alipayjsapi.inc.min.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
if (/toutiaomicroapp|eemicroapp/.test(ua)) {
|
||||
document.writeln(
|
||||
'<script src="https://s3.pstatp.com/toutiao/tmajssdk/jssdk.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
if (ua.indexOf("swan-baiduboxapp") > -1) {
|
||||
document.writeln(
|
||||
'<script src="https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.12.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
if (ua.indexOf("micromessenger") > -1) {
|
||||
document.writeln(
|
||||
'<script src="/static/js/jweixin-1.4.0.js"' + ">" + "<" + "/" + "script>"
|
||||
);
|
||||
}
|
||||
//华为快应用
|
||||
if (ua.indexOf("hap") > -1) {
|
||||
document.writeln(
|
||||
'<script src="https://quickapp/jssdk.webview.min.js"' + ">" + "<" + "/" + "script>"
|
||||
);
|
||||
}
|
||||
// 百度SEO配置
|
||||
if (
|
||||
cururl.indexOf("bargin") == -1 &&
|
||||
cururl.indexOf("successBargain") == -1 &&
|
||||
cururl.indexOf("manage/index") == -1
|
||||
) {
|
||||
document.writeln(
|
||||
'<script src="https://hm.baidu.com/hm.js?870dddf3c2c65a481f72071996d30784"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
if (cururl.indexOf("bargin") != -1 || cururl.indexOf("successBargain") != -1) {
|
||||
document.writeln(
|
||||
'<script src="/static/js/v2.sense.js?sense_id=06d5b8a58c84029fd018873762f1065b&v=20200713"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>" +
|
||||
'<script src="/static/js/deep-know-report.js"' +
|
||||
">" +
|
||||
"<" +
|
||||
"/" +
|
||||
"script>"
|
||||
);
|
||||
}
|
||||
if (
|
||||
(cururl.indexOf("manage/index") != -1) &&
|
||||
sessionStorage.fresh == 0
|
||||
) {
|
||||
sessionStorage.fresh = 1;
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
</script><script src="https://wirelesspub-risk-center.xiaozhu.com/guard/secure.js"></script>
|
||||
<script src="https://hm.baidu.com/hm.js?870dddf3c2c65a481f72071996d30784"></script>
|
||||
|
||||
|
||||
|
||||
<!----><!----></body></html>
|
||||
BIN
tools/xiaozhu_minsu_page.png
Normal file
|
After Width: | Height: | Size: 40 KiB |