# ETM Telegram Bot — Réécriture Python ## Contexte métier Entreprise ETM-Schurig SARL — installateur RGE (PV, PAC, IRVE, HEMS) en Alsace. Bot Telegram pour les techniciens terrain — remplace les appels téléphoniques. ## Stack cible - **python-telegram-bot** v20+ (async) - **python-nextcloud** ou WebDAV direct (httpx) pour Nextcloud - **Ollama** (LLM local) pour FAQ/SAV intelligent — phase 2 - **SQLite** ou fichier JSON pour la session/état utilisateur - Déployé sur le VPS Proxmox existant (même infra que n8n) --- ## Credentials | Paramètre | Valeur | |---|---| | Bot Token | `8608752199:AAGZ9Vyop0MVm4msxUyDsUuoNvN1hKM12vc` | | Groupe Chat Chantier | `-5208574803` | | Groupe ETM-Magasinier | `-5056192608` | | Patrick ID | `8022751692` | | Nextcloud WebDAV | `https://cloud.etm-schurig.eu/remote.php/webdav` | | Nextcloud User | `Patrick.Schurig` | | Nextcloud Deck API | `https://cloud.etm-schurig.eu/index.php/apps/deck/api/v1/` | --- ## Fonctionnalités MVP ### 3 actions via boutons inline | Bouton | callback_data | Règles | |---|---|---| | 📸 Fin de Chantier | `fin` | Photo **obligatoire** + légende `NomClient / Notes` | | ⚠️ Alerte SAV | `sav` | Photo optionnelle + `NomChantier / Description` | | 📦 Matériel Manquant | `materiel` | Texte seul accepté + `NomChantier / - item1 - item2` | ### Format de saisie universel ``` NomChantier / description ou liste avec - comme séparateur ``` Exemples : - `Müller / onduleur + batterie posés` - `Müller / - cuivre 28mm - disconnecteur - soupape anti-gel` ### Chemins Nextcloud ``` Fin chantier : Chantiers/YYYYMMDD_Client/30_Photos_Chantier/photo_timestamp.jpg SAV : SAV_Urgent/YYYY-MM-DD_Client/photo.jpg Matériel : Logistique/Manquants/Client/photo_timestamp.jpg ``` ### Notifications | Action | Destinataire | Contenu | |---|---|---| | Fin chantier | Groupe Chat Chantier `-5208574803` | ✅ + ouvrier + chantier + chemin Nextcloud + "prépare la facture" | | SAV | Groupe ETM-SAV (à créer) | 🚨 + ouvrier + chantier + description + photo | | Matériel | Groupe ETM-Magasinier `-5056192608` | 📦 + ouvrier + chantier + liste formatée | ### Cartes Nextcloud Deck (phase 1b) - Tableau : **ETM Chantiers** - 3 colonnes : `Fin de Chantier` / `SAV` / `Matériel` - Créer une carte automatiquement à chaque signalement --- ## Architecture Python recommandée ``` etm_bot/ ├── main.py # Entry point, ConversationHandler ├── config.py # Tokens, IDs, URLs (chargés depuis .env) ├── handlers/ │ ├── menu.py # /start, boutons inline │ ├── fin_chantier.py # Workflow fin de chantier │ ├── sav.py # Workflow SAV │ └── materiel.py # Workflow matériel manquant ├── services/ │ ├── nextcloud.py # Upload WebDAV + Deck API │ ├── telegram.py # Helpers send_message, notify_group │ └── llm.py # Ollama FAQ/SAV — phase 2 ├── models/ │ └── session.py # État conversation par user_id (dict en mémoire ou SQLite) └── requirements.txt ``` ### États ConversationHandler ```python MENU = 0 ATTENTE_PHOTO_FIN = 1 ATTENTE_CONTENU_SAV = 2 ATTENTE_CONTENU_MATERIEL = 3 ``` --- ## Phase 2 — Wiki/FAQ SAV avec Ollama Quand un technicien signale un SAV, proposer automatiquement des solutions : ```python # services/llm.py async def chercher_solution_sav(description: str) -> str: # Appel Ollama local (mistral ou llama3) response = await ollama.chat( model="mistral", messages=[{ "role": "system", "content": "Tu es un expert en installation photovoltaïque, PAC et IRVE. " "Donne des pistes de diagnostic pour ce problème terrain." }, { "role": "user", "content": description }] ) return response["message"]["content"] ``` Exemples de questions SAV que le LLM doit pouvoir traiter : - "onduleur Fronius affiche erreur 567" - "PAC ne démarre plus après coupure EDF" - "borne IRVE ne charge plus depuis hier" --- ## Prochaine session — ordre de travail 1. `pip install python-telegram-bot httpx python-dotenv` + scaffold du projet 2. Implémenter `ConversationHandler` avec les 3 états 3. Upload WebDAV sur Nextcloud (tester avec une vraie photo) 4. Notifications groupes Telegram 5. Cartes Nextcloud Deck 6. (optionnel) Intégration Ollama FAQ SAV --- ## ⚠️ Priorité n°1 — Gestion albums multi-photos (media_group_id) ### Problème Un technicien envoie 5-8 photos d'un coup (onduleur, batteries, HEMS, tableau, câblage...). Telegram envoie chaque photo comme un webhook séparé avec le même `media_group_id`. Il faut les regrouper avant d'uploader et n'envoyer qu'une seule notification. ### Solution Python ```python # Collecter les photos d'un même album pendant 2 secondes media_groups = {} # media_group_id → liste de fichiers async def handle_photo(update, context): msg = update.message group_id = msg.media_group_id if group_id: # Ajouter à l'album en cours if group_id not in media_groups: media_groups[group_id] = { 'photos': [], 'caption': msg.caption or '', 'chat_id': msg.chat_id, 'user': msg.from_user } # Déclencher l'upload après 3 secondes (le temps de tout recevoir) context.job_queue.run_once( upload_album, 3, data={'group_id': group_id}, name=group_id ) media_groups[group_id]['photos'].append( msg.photo[-1].file_id # Meilleure qualité ) else: # Photo seule — uploader directement await upload_single_photo(msg) async def upload_album(context): group_id = context.job.data['group_id'] album = media_groups.pop(group_id, None) if not album: return # Uploader toutes les photos for i, file_id in enumerate(album['photos']): file = await context.bot.get_file(file_id) # Upload WebDAV → Nextcloud/Chantiers/YYYYMMDD_Client/30_Photos_Chantier/photo_01.jpg await upload_to_nextcloud(file, album['caption'], i + 1) # Une seule notification await notify_fin_chantier(album, len(album['photos'])) ``` ### Notification finale (exemple 6 photos) ``` ✅ Fin de chantier 👷 Patrick Schurig 🏠 Chantier : Müller 📸 6 photos archivées → Nextcloud/Chantiers/20260325_Müller/30_Photos_Chantier/ 💶 Tu peux préparer la facture finale. ``` ### Nommage des photos sur Nextcloud ``` 30_Photos_Chantier/ ├── photo_01_20260325_143201.jpg ├── photo_02_20260325_143202.jpg ├── photo_03_20260325_143203.jpg ... └── photo_06_20260325_143208.jpg ```