Reçu
J'ai conçu Reçu pour transformer les SMS de confirmation mobile money en transactions structurées, en restant robuste aux variations de format et sans qu'aucune donnée ne quitte la machine.
Projet 5 du défi mensuel (bibliothèque Python, finances personnelles). État : MVP validé sur un corpus synthétique ET sur 1 294 vrais SMS de l'auteur (mesure locale, aucun message publié). Bibliothèque standard seulement, aucune dépendance, aucun accès réseau. Distribution PyPI : recu-momo (le nom recu est déjà pris sur PyPI) ; on l'importe toujours par import recu.
Le principe : au lieu d'écrire une expression régulière par format de SMS, Reçu apprend les gabarits à partir de messages bruts (induction), les écrit dans un fichier JSON qu'un humain relit, puis s'en sert pour extraire le type, le montant, les frais, le solde, la contrepartie, la référence et la date. Un message qu'il ne comprend pas reste None : il ne devine jamais.
from recu import Parser
p = Parser.learn_from(messages_bruts) # induction de gabarits ; p.save("gabarits.json") pour les relire
tx = p.parse("Vous avez reçu 5.000 FCFA de JEAN DUPONT (22997123456). Solde: 12 500 F. Frais: 0 F. Ref: MP240101.1234.A56789")
tx.kind, tx.amount, tx.currency, tx.fee, tx.balance # ('reception', Decimal('5000'), 'XOF', Decimal('0'), Decimal('12500'))
p.parse("Votre code est 4821") # None : inconnu, jamais une valeur devinée
python -m recu learn sms.xml --sender momo -o gabarits.json # apprend et affiche chaque gabarit à relire
python -m recu parse sms.xml --sender momo --templates gabarits.json --format csv > transactions.csv
Ce qui est mesuré sur de VRAIS SMS (les miens, en local : docs/mesures.md, section 5)
1 294 SMS reçus d'opérateurs mobile money (MTN MoMo, Moov Money, Celtiis, y compris leurs publicités, codes de connexion et alertes de forfait). Apprentissage sur les 70 % les plus anciens, test sur les 30 % les plus récents. Pas d'étiquettes : on ne peut pas mesurer directement « le montant est exact ». On mesure donc un invariant indépendant de l'analyseur : la continuité des soldes (nouveau solde = ancien solde ± montant ± frais).
| Mesure | Résultat |
|---|---|
| Continuité des soldes, paires sans message de transaction inconnu entre elles | 94,2 % (180 sur 191) ; paiement 103/103, envoi 23/23, réception 27/28, retrait 18/24, dépôt 9/12 |
| Part de la boîte reconnue comme transaction | 35,6 % de l'ensemble ; 25,2 % des messages récents (le reste : publicités, codes de connexion, alertes de consommation, volontairement inconnus) |
| Formats MTN MoMo reconnus (d'après les gabarits appris) | paiement, transfert envoyé, transfert reçu, retrait via agent, dépôt reçu ; l'activation de forfait est reconnue aussi, mais sa justesse n'est pas vérifiée (aucun solde pour la chaîner) |
Ce que ces chiffres ne disent pas : la vraie couverture des transactions (sans étiquettes, je ne sais pas combien de vraies transactions sont restées inconnues) ; ni pourquoi 6 retraits sur 24 et 3 dépôts sur 12 ne font pas la chaîne (règle de frais différente, message manquant d'un autre opérateur, ou lecture fausse : à examiner à la main, un par un). Une seule personne, un seul téléphone, un seul pays.
Ce que la vraie mesure a trouvé, que le corpus synthétique ne pouvait pas montrer (tous corrigés, avec un test chacun) :
- « TRANSFERT 5 000 F DE nom » classé envoi alors que c'est une réception : le mot « transfert » ne dit pas le sens, la préposition le dit. La continuité par type l'a révélé (envoi 36/90, réception 0/12 avant correction).
- Des publicités reconnues comme transactions : un « retrait » dont le « montant » était un code USSD (
*855*5#), des « recharges » issues de mots trop larges (« forfait », « internet »). Désormais : un montant doit être collé à une devise, un code USSD est un mot, et une transaction doit porter au moins une information en plus du montant (solde, référence, contrepartie ou date) ; les frais seuls ne comptent pas. - Le prix d'un forfait pris pour son volume (« forfait 500 Mo activé à 1 000 FCFA ») : le montant est le nombre collé à la devise.
Ce qui est mesuré sur le corpus SYNTHÉTIQUE (docs/mesures.md, sections 1 à 4)
| Mesure (1 500 messages d'apprentissage, 1 500 de test jamais vus) | Résultat |
|---|---|
| Couverture des transactions des formats appris | 100 % (38 gabarits appris pour 14 formats cachés) |
| Montants faux / types faux | 0 / 0 sur 1 337 reconnus |
| Non-transactions (codes, publicités) reconnues à tort | 0 sur 163 |
| Deux formats retenus à l'apprentissage, présentés au test | 0 reconnu à tort sur 600 |
| Exemples nécessaires pour apprendre | 14 → 21 % · 56 → 77 % · 112 → 90 % · 300 → 98,7 % · 1 500 → 100 % de couverture |
| Débit d'analyse | environ 4 600 messages/s (médiane de 5 passes, la machine varie du simple au triple) ; apprentissage de 1 500 messages : environ 8 s |
Comparaison avec des expressions régulières écrites à la main (14 regex, une par format) : une version « soignée » atteint la même couverture, 100 %, et la même exactitude, avec un débit 4,3 fois plus élevé (environ 20 000 messages/s). Reçu ne bat donc pas des regex bien écrites en précision ni en vitesse. Son avantage mesuré est l'effort humain : il a trouvé seul les gabarits (il en faut une centaine d'exemples pour dépasser 90 %), alors que les regex demandent à un développeur d'écrire du code pour chaque nouveau format. Une version « naïve » (chiffres seuls, casse exacte) ne couvre que 0,3 % des messages bruités : c'est un homme de paille, pas une référence.
Limites (à lire avant de faire confiance)
- Le corpus synthétique est écrit par moi et circulaire : il prouve la tenue à des variations connues, pas à de vrais SMS. Ses formats sont inspirés de structures publiées (Ghana, M-Pesa), pas copiés. Aucun jeu public de vrais SMS de confirmation n'existe, à ma connaissance (cherché).
- Sur les vrais SMS, la couverture réelle des transactions est inconnue (pas d'étiquettes) ; seule la cohérence des soldes est mesurée.
- Pas de gabarit appris pour : les confirmations d'achat de forfait au format « forfait N Mo activé à… », les transactions d'opérateurs absents de ma boîte (Orange Money, Wave, Celtiis en détail). Un format absent des exemples reste inconnu, jamais deviné.
- Non publié sur PyPI (voir docs/PUBLIER-SUR-PYPI.md) ; langues : français et anglais ; fuseaux horaires non gérés ; SMS longs découpés en plusieurs messages non recollés ; un gabarit à un seul exemple est écarté par défaut (
min_support = 2). - L'index de reconnaissance est un index inversé sur le premier mot fixe, pas le trie de préfixes du catalogue. La correspondance finale utilise le moteur d'expressions régulières de Python, sans garantie de temps linéaire dans le pire cas.
Comment ça marche (docs/conception.md)
- Montants (
amounts.py) : « 1.000 FCFA », « 1 000F », « 1,000.00 XOF ». Un point ou une virgule seuls sont ambigus ; la règle (décimale, milliers, ou refus) est écrite et testée,Decimalpartout. - Tokenisation typée : nombre, date, heure, identifiant, mot (les codes USSD en sont un), ponctuation.
- Regroupement par distance d'édition pondérée : changer un nom propre coûte peu, changer un verbe coûte cher, un texte ajouté en fin de message est presque gratuit.
- Alignement sur le message représentatif de chaque groupe : identique devient fixe, variable devient case typée ou texte libre.
- Rôles et type déduits du vocabulaire ; sans mot reconnu, le gabarit reste « type indéterminé » et ses messages restent inconnus. Règles de phrase : « X vous a envoyé » et « TRANSFERT … DE » sont des réceptions.
Sécurité et confidentialité
Aucun accès réseau. Lecture XML en flux, entités externes non résolues (testé). --sender filtre avant tout traitement. Le CSV neutralise l'injection de formules (un nom commençant par =, +, - ou @ reçoit une apostrophe), car le nom vient du SMS, donc d'un tiers. Aucun SMS réel dans ce dépôt : le corpus est généré, les exports réels vont dans un dossier hors dépôt, l'outil d'export refuse d'écrire dans un dépôt Git et n'affiche jamais un message, et seuls des chiffres agrégés sont publiés.
Tests et outils
pip install -e .[dev]
python -m pytest # 102 tests, dont des tests de propriété (Hypothesis)
python tools/evaluate.py --n 1500 --out docs/mesures-synthetique.json # corpus synthétique, regex de référence, courbe d'apprentissage
python tools/export_sms_adb.py --sender MoMo --out <hors-dépôt>/real.xml # téléphone branché ; aucun message affiché
python tools/eval_real.py <hors-dépôt>/real.xml --sender momo # mesure sur vrais SMS, en local (agrégats seulement)
Licence
MIT, Copyright (c) 2026 Gilles MEDENOU : voir LICENSE.
Metadata
Release files for recu-momo 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| recu_momo-0.1.0.tar.gz | 37.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| recu_momo-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.6 kB
Release files / recu_momo-0.1.0.tar.gz
| Download URL | recu_momo-0.1.0.tar.gz |
|---|---|
| Size | 37.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3a76e63e253acdb1810e83c7a68c8a4fee351cb34f8273713c25495261f64e8f
|
|
BLAKE2b-256 checksum How to use checksums |
a317d841afcab9836a919814525e5acd4bf2f8d70a0d631dbed8ff42efeb1f24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / recu_momo-0.1.0-py3-none-any.whl
| Download URL | recu_momo-0.1.0-py3-none-any.whl |
|---|---|
| Size | 27.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
722e899c9edb8c2dee4bb5a81d4cfaafe391e6a48f7f709a9357ab3cc67e7e6f
|
|
BLAKE2b-256 checksum How to use checksums |
37a25a6ea611a5b0c6e4a54500442d2b8cf0d8ef44d7027d5d50264ddc32edc2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for recu-momo 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.