pronote-digest

GitHub Action · Pronote · sans serveur

Le planning de vos enfants pour demain,
chaque soir dans votre boîte.

Pronote a toutes les informations. Il faut juste aller les chercher, tous les soirs, enfant par enfant. pronote-digest le fait pour vous : un mail à 17h30 pour les devoirs, un mail à 19h pour le planning. Rien à installer, rien à héberger.

17:30

les devoirs

19:00

le planning

[Pronote] · 19:00

Planning du lundi 7 septembre

Journée longue pour Alice, pensez au sac de sport pour l'EPS en fin d'après-midi. Bob a un cours de français annulé ce matin.

Alice

Journée 08:00 – 17:00EPS : tenue de sport
08:00–08:55ACC. PERSO. FRANCAISS109
08:55–09:50FRANCAISS109
10:10–11:05ANGLAIS LV1S204
11:05–12:00ESPAGNOL LV2S103
14:00–14:55ARTS PLASTIQUESS001
14:55–17:00ED.PHYSIQUE & SPORT.

Bob

Journée 08:00 – 15:50
08:00–08:55MATHEMATIQUESS110
08:55–09:50FRANCAIS(Annulé)S103
10:10–12:00ED.PHYSIQUE & SPORT.
14:00–14:55EDUCATION MUSICALES002
14:55–15:50FRANCAISS113

Pourquoi

L'information existe. Elle n'est simplement pas là où vous êtes le soir.

01

Un compte par enfant, une appli, des clics

Voir la journée de demain, c'est ouvrir Pronote, changer d'enfant, ouvrir l'emploi du temps, puis le cahier de textes. Tous les soirs.

02

Ce qui compte tient en dix lignes

À quelle heure ça commence, à quelle heure ça finit, s'il y a sport, ce qu'il y a à rendre, si un cours saute. Le reste, c'est du bruit.

03

Le mail arrive quand la question se pose

17h30 pour vérifier les devoirs avant le dîner, 19h pour préparer le cartable. Pas de notification, pas d'application : un mail, que vous lisez ou pas.

Ce que vous recevez

Deux mails. Toujours les mêmes.

[Pronote] · 17:30

Devoirs pour le lundi 7 septembre

Alice

  • ANGLAIS LV1 · THOMAS C. · donné le jeudi 3 septembre

    lesson + vocabulary : être capable de reparler des deux bandes-annonces avec le vocabulaire noté en classe

  • FRANCAIS · MARTIN A. · donné le jeudi 3 septembre

    Apporter le manuel de français couvert, avec l’étiquette complétée.

Bob

  • HISTOIRE-GEOGRAPHIE · DAVID P. · donné le jeudi 3 septembre

    Illustrer la première page du cahier avec des éléments en lien avec le programme de l’année.

  • MATHEMATIQUES · ROUX O. · donné le jeudi 3 septembre

    Apporter les deux cahiers (48 pages et 96 pages) et tout le matériel attendu.

17:30, les devoirs. Le travail à rendre pour le prochain jour de classe, tel que saisi par les enseignants dans le cahier de textes. Matière, enseignant, date où le devoir a été donné, consigne. Rien les jours sans cours.

[Pronote] · 19:00

Planning du lundi 7 septembre

Journée longue pour Alice, pensez au sac de sport pour l'EPS en fin d'après-midi. Bob a un cours de français annulé ce matin.

Alice

Journée 08:00 – 17:00EPS : tenue de sport
08:00–08:55ACC. PERSO. FRANCAISS109
08:55–09:50FRANCAISS109
10:10–11:05ANGLAIS LV1S204
11:05–12:00ESPAGNOL LV2S103
14:00–14:55ARTS PLASTIQUESS001
14:55–17:00ED.PHYSIQUE & SPORT.

19:00, le planning. Les cours du prochain jour de classe, heure de début et de fin de journée, salles, cours annulés ou déplacés barrés, rappel de la tenue de sport. Et, si vous l'activez, une introduction rédigée par le modèle d'IA de votre choix.

Comment ça marche

Trois pièces, aucune à vous.

  1. 1 · Pronote

    Le flux iCal que Pronote publie déjà

    Chaque compte élève expose un calendrier iCal avec les cours, les annulations, et le cahier de textes si l'établissement l'a activé. C'est la seule source.

  2. 2 · GitHub Actions

    Un dépôt privé qui se réveille le soir

    Deux workflows planifiés téléchargent le flux, calculent le jour visé, et rendent le mail. Le quota gratuit de GitHub couvre largement deux runs par jour.

  3. 3 · Votre SMTP

    N'importe quel fournisseur de mail

    Brevo, Mailgun, Postmark, OVH, votre Gmail avec un mot de passe d'application. Le mail part de chez vous, vers qui vous voulez.

Introduction rédigée · optionnel

Quelques phrases en tête du planning, comme si quelqu'un l'avait lu pour vous.

Si vous le souhaitez, un modèle d'IA lit le planning du lendemain et en tire trois à cinq phrases : la journée est longue, il y a sport, un cours saute, un manuel à ne pas oublier. Le tableau reste en dessous, l'introduction ne le remplace pas.

Vous choisissez le fournisseur : Claude, GPT, Gemini, Mistral, ou un modèle qui tourne chez vous avec Ollama. Le modèle ne voit que le contenu du mail, jamais Pronote ni votre boîte. Et si vous ne configurez rien, il n'y a pas d'introduction, et aucun appel n'est fait.

Ce qui est factuel reste écrit par le programme. Si le modèle invente une heure, son texte est écarté et le mail part sans.

Extrait d'un planning · 19:00

« Journée longue pour Alice, qui finit par du sport : prévoyez ses affaires d'EPS et le manuel de français couvert. Bob a un cours de français annulé en fin de matinée. Deux cahiers de mathématiques et le chevalet pour l'anglais sont à glisser dans son cartable. »
Rédigé par le modèle à partir des données du mail, rien d'autre.

Mise en place

Dix minutes, quatre étapes.

Étape 1

Récupérez les URL iCal dans Pronote

Espace Parents ou Élève, emploi du temps, export iCal. Vous obtenez une URL par enfant, du type …/pronote/ical/Edt_prenom.ics?icalsecurise=…. Elle contient un jeton : traitez-la comme un mot de passe.

Étape 2

Créez un dépôt GitHub privé

Il ne contiendra que deux workflows, vos secrets et l'archive des envois. Privé, parce que l'archive porte les prénoms, les enseignants et les consignes.

Étape 3

Renseignez les secrets

Settings, Secrets and variables, Actions : CHILDREN avec le JSON ci-contre, puis SMTP_HOST, SMTP_USER, SMTP_PASS etMAIL_TO.

Étape 4

Ajoutez les deux workflows

Copiez-les dans .github/workflows/. Lancez-en un à la main avecdry_run coché : le rendu est archivé dans le dépôt sans rien envoyer. Puis décochez.

secret CHILDRENjson
[
  { "name": "Alice", "ics": "https://…/pronote/ical/Edt_alice.ics?icalsecurise=…&version=…&param=…" },
  { "name": "Bob",   "ics": "https://…/pronote/ical/Edt_bob.ics?icalsecurise=…&version=…&param=…" }
]
.github/workflows/planning.ymlyaml
name: Planning du lendemain

on:
  schedule:
    # 19h13 Paris, heure d'été et heure d'hiver
    # Minute non ronde : les crons à :00 et :30 sont les plus retardés par GitHub
    - cron: '13 17 * * *'
    - cron: '13 18 * * *'
  workflow_dispatch:
    inputs:
      dry_run:
        type: boolean
        default: false

permissions:
  contents: write

jobs:
  digest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      # On compare le décalage de Paris et l'entrée cron qui a déclenché le run,
      # pas l'heure d'exécution : un cron lancé en retard doit quand même passer.
      - name: Décalage de Paris
        id: paris
        run: echo "offset=$(TZ=Europe/Paris date +%z)" >> "$GITHUB_OUTPUT"

      - name: Digest
        if: >-
          github.event_name != 'schedule'
          || (steps.paris.outputs.offset == '+0200' && github.event.schedule == '13 17 * * *')
          || (steps.paris.outputs.offset == '+0100' && github.event.schedule == '13 18 * * *')
        uses: yoanbernabeu/pronote-digest@v0
        with:
          children: ${{ secrets.CHILDREN }}
          digest: planning
          smtp_host: ${{ secrets.SMTP_HOST }}
          smtp_port: 587
          smtp_user: ${{ secrets.SMTP_USER }}
          smtp_pass: ${{ secrets.SMTP_PASS }}
          mail_from: 'Pronote Digest <digest@example.com>'
          mail_to: ${{ secrets.MAIL_TO }}
          dry_run: ${{ inputs.dry_run }}

      - name: Archive
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add archive
          git diff --cached --quiet || git commit -m "chore: archive $(date -u +%F)"
          git pull --rebase --autostash
          git push
.github/workflows/devoirs.ymlyaml
name: Devoirs du lendemain

on:
  schedule:
    # 17h33 Paris, heure d'été et heure d'hiver
    # Minute non ronde : les crons à :00 et :30 sont les plus retardés par GitHub
    - cron: '33 15 * * *'
    - cron: '33 16 * * *'
  workflow_dispatch:
    inputs:
      dry_run:
        type: boolean
        default: false

permissions:
  contents: write

jobs:
  digest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      # On compare le décalage de Paris et l'entrée cron qui a déclenché le run,
      # pas l'heure d'exécution : un cron lancé en retard doit quand même passer.
      - name: Décalage de Paris
        id: paris
        run: echo "offset=$(TZ=Europe/Paris date +%z)" >> "$GITHUB_OUTPUT"

      - name: Digest
        if: >-
          github.event_name != 'schedule'
          || (steps.paris.outputs.offset == '+0200' && github.event.schedule == '33 15 * * *')
          || (steps.paris.outputs.offset == '+0100' && github.event.schedule == '33 16 * * *')
        uses: yoanbernabeu/pronote-digest@v0
        with:
          children: ${{ secrets.CHILDREN }}
          digest: homework
          on_no_school: skip
          smtp_host: ${{ secrets.SMTP_HOST }}
          smtp_port: 587
          smtp_user: ${{ secrets.SMTP_USER }}
          smtp_pass: ${{ secrets.SMTP_PASS }}
          mail_from: 'Pronote Digest <digest@example.com>'
          mail_to: ${{ secrets.MAIL_TO }}
          dry_run: ${{ inputs.dry_run }}

      - name: Archive
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add archive
          git diff --cached --quiet || git commit -m "chore: archive $(date -u +%F)"
          git pull --rebase --autostash
          git push

Données et sécurité

Tout reste chez vous.

  • Les URL iCal ne quittent pas vos secrets

    L'action les masque dans les logs du workflow. Elles n'apparaissent ni dans le code, ni dans l'archive, ni dans les messages d'erreur.

  • Aucun service intermédiaire

    Pronote, le runner GitHub, votre SMTP. C'est tout. Le fournisseur d'IA n'est appelé que si vous l'activez, et ne reçoit que le contenu du digest.

  • Le HTML des enseignants est assaini

    Les consignes du cahier de textes sont nettoyées avant d'entrer dans le mail : pas de script, pas de style, pas de lien douteux.

  • Code ouvert, tests, licence MIT

    Le parseur est testé sur de vrais flux anonymisés. Chaque version est compilée et taguée par la CI, et publiée sur le GitHub Marketplace.

Questions fréquentes

Référence

Entrées et sorties de l'action.

Ce tableau est généré au build à partir du fichier action.yml du dépôt : il correspond toujours à la version publiée.

EntréeDéfautDescription
childrenrequisTableau JSON des enfants : [{"name":"Alice","ics":"https://…/Edt_x.ics?icalsecurise=…"}]. À passer via un secret.
digestrequisType de digest : planning ou homework.
channelsemailCanaux, séparés par des virgules : email, file.
subject_prefix[Pronote]Préfixe du sujet des messages.
archive_dirarchiveRépertoire où archiver le JSON et les rendus de chaque envoi. none pour désactiver.
on_no_schoolnotifyJour sans cours : notify (message court) ou skip (aucun envoi).
require_homework_datatrueÉchouer si aucun flux ne contient de cahier de textes (digest homework).
dateJour de préparation AAAA-MM-JJ. Par défaut aujourd’hui, heure de Paris.
dry_runfalsetrue pour ne rien envoyer (l’archive est tout de même écrite).
fetch_timeout_ms20000Délai maximal de téléchargement d’un flux, en millisecondes.
smtp_hostServeur SMTP (canal email).
smtp_port587Port SMTP.
smtp_securefalsetrue pour TLS implicite (port 465).
smtp_userIdentifiant SMTP.
smtp_passMot de passe SMTP. À passer via un secret.
mail_fromExpéditeur.
mail_toDestinataires, séparés par des virgules.
file_diroutRépertoire de sortie du canal file.
ai_providerFournisseur pour l’introduction rédigée (optionnel) : anthropic, openai, mistral, google, ollama, openai-compatible. Vide pour désactiver.
ai_modelIdentifiant du modèle chez le fournisseur choisi.
ai_api_keyClé d’API du fournisseur IA. À passer via un secret.
ai_base_urlURL de base pour ollama ou un endpoint compatible OpenAI.
ai_digestsplanningDigests qui reçoivent une introduction rédigée : planning, homework (séparés par des virgules).

Sorties

skipped
true si rien n’a été envoyé (jour sans cours et on_no_school=skip).
target-date
Jour visé par le digest, AAAA-MM-JJ.
school-day
true si le jour visé comporte des cours.
subject
Sujet du message.
archive-json
Chemin du digest JSON archivé (vide si archive désactivée).
archive-html
Chemin du rendu HTML archivé.
archive-markdown
Chemin du rendu Markdown archivé.
delivered
true si tous les canaux ont livré.