Les nouveaux appels d'offres publics tunisiens chaque jour (source officielle HAICOP), triés par métier et par gouvernorat, avec date limite et caution. Gratuit, sans inscription. طلبات العروض العمومي
翻譯準備中
商業HTML · PythonGitHub @Ah6259上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Les nouveaux appels d'offres publics tunisiens chaque jour, tries par metier et par gouvernorat (source officielle HAICOP)
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Site gratuit, sans inscription : chaque jour, les nouveaux appels d'offres publics tunisiens,
triés par métier et par gouvernorat, avec la date limite (en rouge à moins de 7 jours),
la caution provisoire et le lien vers la fiche officielle. Français + arabe (?lang=ar).
Aussi : recherche par mots-clés (français et arabe, sans souci d'accents), résumé traduit des objets
(arabe -> français et inversement, glossaire maison, « traduction automatique approximative »), rappels « J-7 … J-1 /
Dernier jour » et section « Clôturent bientôt », ventes aux enchères de la Douane tunisienne (encheres/),
publication gratuite d'appels d'offres par les entreprises privées (publier/), alerte Telegram quotidienne.
Partie payante « Alertes Pro » (abonnement/) : alertes personnalisées (métiers + gouvernorats) chaque matin sur Telegram,
25 DT/mois ou 199 DT/an, sans essai gratuit (depuis le 20/10/2026), sans renouvellement automatique. La consultation reste gratuite.
| Étape | Fichier | Rôle |
|---|---|---|
| 1 | robot/lire_haicop.py | lit lentement les nouvelles fiches (1 page / 2,5 s), les classe par métier et gouvernorat → donnees/appels-offres.json |
| 1 b | robot/lire_douane.py | lit 1 page par jour de la Douane tunisienne (ventes aux enchères publiques) → donnees/encheres.json |
| 1 c | robot/prives.py | lit les réponses du formulaire Google des entreprises (CSV publié), les contrôle → donnees/prives.json |
| 2 | robot/construire_site.py | fabrique les 40 pages : accueil, 10 métiers, 26 gouvernorats, à propos, publier, enchères, sitemap.xml ; résumés traduits par robot/glossaire.py |
| 3 | tools/test_site.mjs + tools/test_pannes.py | tests (le robot ne publie rien si un test échoue) |
| 4 | robot/telegram.py | UN message court groupé sur le canal Telegram (nouveaux par métier + « ⏰ Clôturent dans 2 jours ») ; mémoire donnees/telegram-envoyes.json |
| 5 | .github/workflows/maj.yml | fait 1 → 4 chaque jour à 6h05 (heure de Tunis) puis commit ; GitHub Pages republie |
Réglages d'Ahmed : un seul fichier, robot/reglages.py (adresse publique du canal Telegram, du canal WhatsApp,
du formulaire Google et de son CSV). Vide = bouton caché / « Bientôt ». Les jetons secrets ne vont JAMAIS dans ce fichier.
Pages : index.html, metier/<métier>/, gouvernorat/<gouvernorat>/, a-propos/, publier/, encheres/, abonnement/, abonnement/conditions/.
Fichiers à la main : assets/style.css, assets/page.js (langue, en-tête, pied), assets/app.js (filtres, tri,
recherche, étiquettes J-N, dates selon le téléphone du visiteur), assets/logo.svg, assets/og-image-v6.jpg (source : tools/og-image.html).
Photo du bandeau : mosaïque de 4 vraies photos prises en Tunisie (BTP, informatique, santé, port de Radès), car les
marchés publics ne sont pas que la construction : assets/photos/marches-publics-mosaique.jpg (ordinateur, 4 côte à côte) et
-carre.jpg (téléphone, 2 × 2). Wikimedia Commons : Habib M'henni (CC BY 4.0 et CC BY-SA 3.0), Touzrimounir (CC BY-SA 4.0),
M. Rais (CC BY-SA 3.0) ; crédit de chaque photo sous le bandeau, dans « À propos » et sur l'image d'aperçu ; preuves hors dépôt.
Toutes les pages : translate="no" + <meta name="google" content="notranslate"> (Chrome proposait de traduire en anglais).
Icônes de métier et carte de la Tunisie : SVG faits maison.
Votre avis (accueil #avis, lien « Votre avis » dans le pied de page de toutes les pages) : note facultative (😀🙂😐🙁),
message (obligatoire, ≤ 1000 caractères), e-mail facultatif ; envoyé seulement au clic à Formspree (formulaire mwlpakqj,
commun à tous les sites d'Ahmed) avec les champs cachés site = « Alertes appels d'offres Tunisie » et page.
Code : assets/avis.js ; section : constante AVIS dans construire_site.py ; test : node tools/test_avis.mjs (envoi simulé).
python robot/lire_haicop.py --max 40
python robot/lire_douane.py
python robot/prives.py
python robot/telegram.py --essai (affiche le message Telegram sans l'envoyer)
python robot/construire_site.py
npm install --no-save --no-package-lock jsdom (une fois par PC)
node tools/test_site.mjs
node tools/test_sw.mjs
node tools/test_avis.mjs
python tools/test_pannes.py
bash tools/captures.sh (captures téléphone 340/390 px, FR + AR, dans captures/)
alertesaotunisie./newbot → nom « Alertes AO Tunisie », identifiant finissant par bot → il donne un jeton
(ne le donner à personne, ne jamais le coller dans le chat ni dans un fichier).appels-offres-tunisie → Settings → Secrets and variables → Actions → New repository secret :
TELEGRAM_BOT_TOKEN = le jeton ; TELEGRAM_CANAL = @alertesaotunisie (l'adresse du canal avec @).robot/reglages.py : TELEGRAM_CANAL_URL = "https://t.me/alertesaotunisie" → le bouton « Recevoir les alertes
sur Telegram » apparaît sur le site au prochain passage du robot.Sans ces secrets, le robot ne fait rien (et ne sonne pas en échec). Un appel d'offres n'est jamais annoncé deux fois.
Les abonnés ne sont jamais dans ce dépôt public : ils sont dans le dépôt privé Ah6259/appels-offres-abonnes
(voir son README : activer un abonné depuis l'application GitHub du téléphone, bouton activer-abonne).
TELEGRAM_BOT_TOKEN.robot/reglages.py : TELEGRAM_ROBOT_ALERTES = "AlertesAOTunisieBot".pour_activer) : son code reste « en attente » (rien n'est envoyé)
jusqu'au paiement. Plus d'essai gratuit depuis le 20/10/2026 (décision d'Ahmed) : l'action essai n'existe plus.activer-abonne, action paye, code + mois (1 ou 12) → facture ; le résumé
donne le CODE et un lien WhatsApp pour l'envoyer au client → le client envoie /start CODE au robot.
Nouvel abonné qui paie directement : action paye, code vide, avec nom, téléphone, métiers et gouvernorats.WhatsApp ne permet pas à un robot gratuit de publier dans un canal. Il faudra : créer un canal WhatsApp
(Actus → Canaux → Créer un canal), mettre son adresse dans WHATSAPP_CANAL_URL (robot/reglages.py) pour afficher
le bouton, puis recopier chaque jour le message Telegram (ou plus tard l'API WhatsApp Business, payante).
https://forms.google.com → Formulaire vide, titre « Publier un appel d'offres — Alertes appels d'offres Tunisie ». Paramètres → Réponses : ne pas collecter les adresses e-mail. Créer ces questions, dans cet ordre et avec ces mots (le robot reconnaît les colonnes grâce à eux) :
| Question | Type | Obligatoire |
|---|---|---|
| Nom de l'entreprise | Réponse courte | oui |
| Objet de l'appel d'offres | Réponse courte (15 à 300 caractères) | oui |
| Description (facultatif) | Paragraphe | non |
| Métier | Liste déroulante : BTP / génie civil, Électricité, Informatique, Fournitures et mobilier, Nettoyage / gardiennage, Alimentation, Médical / pharmacie, Transport / véhicules, Études / conseil, Autres | oui |
| Gouvernorat | Liste déroulante : Ariana, Béja, Ben Arous, Bizerte, Gabès, Gafsa, Jendouba, Kairouan, Kasserine, Kébili, Le Kef, Mahdia, La Manouba, Médenine, Monastir, Nabeul, Sfax, Sidi Bouzid, Siliana, Sousse, Tataouine, Tozeur, Tunis, Zaghouan, Plusieurs gouvernorats, Toute la Tunisie | oui |
| Date limite de réception des offres | Date | oui |
| Contact pour les candidats (e-mail ou téléphone de l'entreprise) | Réponse courte | oui |
| Je certifie que ces informations sont exactes et j'accepte leur publication | Cases à cocher (une case « Oui ») | oui |
Onglet Réponses → icône Sheets → Créer une feuille de calcul. Dans la feuille : Fichier → Paramètres → Paramètres régionaux France (dates jj/mm/aaaa).
Feuille : Fichier → Partager → Publier sur le Web → feuille « Réponses au formulaire 1 » → format CSV →
Publier → copier l'adresse (https://docs.google.com/spreadsheets/d/e/…/pub?…output=csv) dans CSV_PRIVES_URL.
Formulaire → Envoyer → lien → « Raccourcir l'URL » → copier () dans .
Objectif : le site reste en vie et honnête même si la source tombe en panne ou si personne ne s'en occupe.
Ce qui tourne tout seul
maj.yml chaque jour : lecture HAICOP → construction → tests → commit. Le ?v= des fichiers CSS/JS est
calculé automatiquement (empreinte des fichiers) : les téléphones ne gardent jamais un ancien style.battement-de-coeur.yml le 1er du mois : petit commit pour que GitHub ne mette pas les robots en pause
(pause automatique après 60 jours sans activité).tests.yml à chaque envoi (push) : les tests (site, service worker, pannes).maj-site (jamais deux commits en même temps).Ce qui protège le site
Source en panne, liste vide, < 30 % du volume habituel, format des fiches changé (champs introuvables) : c'est une panne, jamais « aucun appel d'offres ». Les anciennes annonces sont gardées ; seules les annonces dont la date limite est passée disparaissent (calcul fait aussi avec la date du téléphone du visiteur).
Fichier de données illisible, vide ou qui fond de plus de moitié : reprise de donnees/appels-offres.sauvegarde.json.
Sans sauvegarde : le site déjà publié n'est pas touché.
Bandeau daté « La source officielle n'a pas pu être lue depuis le … » dès que la dernière lecture réussie a 2 jours ou plus (vérifié dans la page ET par le JavaScript selon la date du visiteur). La pastille « Mis à jour le … » donne toujours la date de la dernière lecture réussie.
Textes de la source échappés, liens non officiels remplacés par le lien HAICOP de la fiche.
Ventes aux enchères (Douane) : page injoignable, tableau introuvable, < 30 % des lignes d'avant = panne ; anciennes ventes gardées (les expirées disparaissent), bandeau daté sur la page enchères après 2 jours sans lecture, « échec » au journal.
Entreprises privées : CSV injoignable, vide, corrompu ou colonnes changées = les publications déjà acceptées sont gardées. Les réponses refusées ne sont jamais publiées (seule la raison est gardée, sans leur contenu).
Telegram : secrets absents = rien ; Telegram en erreur = « échec » au journal, le site n'est pas touché, et les messages non partis repartent au passage suivant (jamais deux fois le même appel d'offres).
Réglages mal écrits (robot/reglages.py) : ignorés, bouton caché, « échec » au journal.
Sécurité : robots.txt (moteurs de recherche oui, robots d'IA et aspirateurs non), noai, CSP stricte en balise meta,
anti-iframe, protection légère contre la copie (numéros Tender, liens et champs restent copiables), aucun secret dans le
dépôt (vérifié par le test). Limite : un site GitHub Pages en sous-dossier n'a pas son propre robots.txt pour les robots
(seul ah6259.github.io/robots.txt compte) → le copier dans un dépôt Ah6259.github.io, ou plus tard avec un nom de domaine.
Ce qu'un visiteur voit peut toujours être recopié : la vraie protection = © + preuves datées.
Installation sur le téléphone : sw.js (service worker) = réseau d'abord pour les pages et les données (le cache
ne sert que hors connexion : jamais une vieille liste quand Internet marche) ; CSS/JS versionnés (?v=) = cache puis mise à jour.
Si un téléphone garde une vieille version : changer dans .
Ce qui alerte
donnees/etat-source.json.Quoi faire si une alerte arrive
lire_fiche() dans robot/lire_haicop.py, puis relancer les deux tests ;maj-appels-offres → « Run workflow »
(ou gh workflow run maj.yml -R Ah6259/appels-offres-tunisie).LICENSE « tous droits réservés ». Les annonces viennent du portail officiel de la HAICOP ; chaque carte renvoie à la fiche officielle, seule à faire foi.
https://forms.gle/…FORMULAIRE_PRIVES_URLEnsuite tout est automatique : chaque jour le robot contrôle (champs obligatoires, date limite à venir et ≤ 180 jours, pas de lien, pas de HTML, pas de publicité, pas de doublon, ≤ 3 par entreprise et par jour) et publie avec la mention « Publié par l'entreprise — non vérifié par HAICOP ». Pour retirer une annonce : supprimer sa ligne dans la feuille.
CACHE_VERSIONsw.js