Aller au contenu

Pipeline des formats de message

Lors de l’écriture d’un plugin, vous déclarez notify_format pour indiquer à Apprise le format attendu par votre service en amont. Lorsqu’une notification arrive, Apprise résout le format déclaré par l’appelant (body_format), convertit le corps si nécessaire, puis le transmet — déjà dans notify_format — à votre plugin. Au moment où send() s’exécute, le corps est pré-formaté. Votre code n’a pas besoin de le reconvertir.

ValeurSignificationDéfinie par
body_formatFormat du message sourcenotify(), AppriseAsset, CLI ou appelant API
notify_formatFormat préparé pour le service en amontClasse du plugin, éventuellement remplacé par ?format=

Ces valeurs sont indépendantes. Une même source HTML peut devenir du texte brut pour un plugin, du Markdown pour un autre et rester en HTML pour un troisième — le tout dans un seul appel à notify().

Le format source n’est pas toujours None. Il dépend de l’interface qui déclenche la notification :

InterfaceValeur par défaut lorsqu’aucun format n’est déclaré
CLItext — la CLI définit toujours un format source ; remplacez-le avec --input-format
Bibliothèque Pythonnone — sauf si body_format est passé à notify() ou prédéfini dans AppriseAsset
Apprise APInone — sauf si format est inclus dans la charge utile de la requête

Consultez Formatage pour la perspective utilisateur sur ces valeurs par défaut et leur impact sur la livraison vers des destinations mixtes.

Pour chaque plugin sélectionné, Apprise :

  1. Résout body_format depuis l’appel à notify(). Lorsque la bibliothèque Python ou l’API sont utilisées sans format, cette valeur est None. Lorsque la CLI est utilisée sans --input-format, cette valeur est TEXT. Si body_format n’est pas fourni à notify(), il utilise AppriseAsset.body_format, dont la valeur par défaut est None.
  2. Lit notify_format depuis l’instance du plugin (valeur de la classe ou remplacement par ?format=).
  3. Lorsque body_format n’est pas None, appelle convert_between(body_format, notify_format, body) et remplace le corps par le résultat. Les résultats sont mis en cache par format de destination unique afin que la même conversion ne soit pas répétée pour plusieurs plugins partageant un format.
  4. Appelle plugin.notify(body=<converted>, body_format=<original_format>). Le corps qui parvient au plugin est déjà dans notify_format. Le contenu avant conversion n’est pas conservé ni transmis séparément. body_format ne transporte que l’identifiant de format (par exemple NotifyFormat.HTML) — pas une copie du texte original du corps.
Apprise.notify(body=..., body_format=HTML)
|
| convert_between(HTML, plugin.notify_format, body)
| (ignoré lorsque body_format vaut None)
v
plugin.notify(
body=<déjà converti vers notify_format>,
body_format=HTML, # le tag de format d'origine -- pas le corps original
)

La conversion du titre suit le même chemin, mais uniquement lorsque le plugin ne possède pas de champ titre natif (title_maxlen <= 0). Lorsque title_maxlen > 0, le titre est transmis tel quel et le plugin est responsable de tout formatage supplémentaire.

Lorsque body_format vaut None (bibliothèque Python ou appel API sans format), Apprise ignore entièrement la conversion :

apobj.notify(body="<b>Bonjour</b>")

Le plugin reçoit "<b>Bonjour</b>" inchangé ainsi que body_format=None. Déclarer un notify_format sur la classe du plugin ne modifie pas ce comportement — la conversion automatique n’a lieu que lorsque l’appelant déclare un format source.

La CLI ne produit pas cet état. Elle fournit toujours body_format=TEXT sauf si l’appelant utilise --input-format pour le remplacer.

NotifyBase utilise le texte brut par défaut. Remplacez notify_format lorsque le service en amont accepte nativement une autre représentation :

from apprise import NotifyFormat
from apprise.plugins.base import NotifyBase
class NotifyExample(NotifyBase):
notify_format = NotifyFormat.MARKDOWN

La valeur effective est toujours accessible via self.notify_format.

NotifyFormat.TEXT est la valeur par défaut lorsqu’un plugin ne déclare aucun format.

Source body_formatCorps transmis au plugin
TEXTTexte inchangé
MARKDOWNSource Markdown inchangée (aucune conversion destructive)
HTMLBalises supprimées ; structure de bloc réduite en texte brut
NoneCorps inchangé ; aucune conversion effectuée

La conversion Markdown vers texte ne comporte actuellement aucune étape destructive ; la syntaxe Markdown arrive telle quelle. Si le service en amont interprète encore des caractères Markdown sur sa route texte, échappez-les dans send().

Sans remplacement, le notify_format de la classe du plugin est utilisé :

example://host/token

Un paramètre format= dans la requête change la destination pour cette instance uniquement :

example://host/token?format=text
example://host/token?format=markdown
example://host/token?format=html

La destination effective est toujours self.notify_format, qu’elle provienne de la classe ou d’un remplacement URL. La conversion automatique exige toujours un body_format source différent de None.

Lorsque la logique du plugin doit distinguer un remplacement URL de la valeur par défaut de la classe, capturez la présence de format avant d’appeler NotifyBase.__init__ :

def __init__(self, **kwargs):
# Indique si l'appelant a explicitement défini un format.
self.format_overridden = "format" in kwargs
super().__init__(**kwargs)

La plupart des plugins ont seulement besoin de self.notify_format et n’ont pas besoin de suivre le remplacement séparément.

Au moment où send() s’exécute, la conversion et le dimensionnement sont déjà effectués. Ce que reçoit votre plugin :

  • body — déjà converti vers self.notify_format et dimensionné selon overflow_mode. Avec SPLIT, chaque appel à send() reçoit un bloc d’au plus body_maxlen caractères. Avec TRUNCATE, le corps est limité à body_maxlen. Avec UPSTREAM (valeur par défaut), le corps arrive inchangé et peut dépasser la limite déclarée.
  • title — tronqué à title_maxlen caractères. Lorsque title_maxlen <= 0, le titre a été fusionné dans body par la classe de base et arrive sous forme de chaîne vide.
  • body_format — le format de départ du contenu (par exemple NotifyFormat.HTML) ; pas le texte original du corps lui-même

Les plugins lisent body_format pour décider si un travail supplémentaire est nécessaire. Si body_format est HTML et notify_format est MARKDOWN, le corps a été converti de HTML en Markdown standard par Apprise — et le plugin peut avoir besoin de traduire ce Markdown vers la syntaxe propre au service. Si body_format était déjà MARKDOWN lors de l’envoi par l’appelant, le corps est le Markdown de l’appelant et peut généralement être transmis tel quel.

Apprise convertit le corps en Markdown standard. Ce que le plugin fait ensuite de ce Markdown est entièrement de sa responsabilité. Plusieurs services réels nécessitent des transformations supplémentaires au-delà de ce que fournit la conversion intégrée d’Apprise :

  • Slack utilise mrkdwn plutôt que le Markdown standard
  • Telegram utilise MarkdownV2, sa propre syntaxe personnalisée
  • WhatsApp (via Evolution) et Google Chat ont chacun leur propre format
  • Email peut inclure à la fois du HTML et du texte brut dans le même message

Garder l’étape de conversion générique signifie qu’un plugin peut être supprimé et tout le comportement propre au service disparaît avec lui. Les exemples ci-dessous montrent les schémas les plus pratiques.

Gérez la traduction directement dans send(). Le corps est déjà converti au moment où send() s’exécute — vérifiez simplement les deux valeurs de format pour décider si un travail supplémentaire est nécessaire. Si notify_format est MARKDOWN et body_format est HTML, Apprise a converti le corps de HTML en Markdown standard avant d’appeler votre plugin, et vous pouvez maintenant le traduire vers la syntaxe propre au service. Slack utilise cette approche :

def send(self, body, title="", notify_type=..., body_format=None, **kwargs):
if (
self.notify_format == NotifyFormat.MARKDOWN
and body_format == NotifyFormat.HTML
):
# Apprise a converti ce contenu de HTML en Markdown standard.
# Traduire maintenant vers le dialecte attendu par ce service.
body = self._commonmark_to_service_dialect(body)
# ... reste de send() ...

Pour un exemple complet en situation réelle, consultez le code source du plugin Slack.

Définissez overflow_mode sur la classe pour déclarer comment la classe de base gère le contenu qui dépasse body_maxlen ou title_maxlen. Chaque appel à send() reçoit du contenu déjà dimensionné en conséquence — ne redécoupez pas ni ne retronquez dans send() :

ModeAppels à send()Garantie sur le corps
UPSTREAM (défaut)Un seulInchangé ; peut dépasser body_maxlen
TRUNCATEUn seulLimité à body_maxlen
SPLITUn par blocChaque bloc ≤ body_maxlen
from apprise.common import OverflowMode
class NotifyExample(NotifyBase):
body_maxlen = 4096
title_maxlen = 255
overflow_mode = OverflowMode.SPLIT

Le paramètre URL ?overflow= permet aux utilisateurs de remplacer le mode par instance, mais la valeur par défaut de la classe doit correspondre à ce que l’API en amont peut réellement accepter.

  • apprise/conversion.py convertit entre texte, HTML et Markdown standard. Il ne doit contenir aucune règle de syntaxe propre à un service.
  • La syntaxe propre au service, les règles d’échappement, les champs de charge utile et les modes API appartiennent exclusivement au fichier plugin.
  • Supprimer un fichier plugin doit supprimer tout le comportement de ce service sans aucune modification requise dans le code de conversion partagé.
  • Les utilitaires génériques sans connaissance propre à un service peuvent résider dans conversion.py. Exports actuels : build_backtick_run_index, find_unescaped_run, commonmark_escape_link_url, commonmark_scan_angle_dest, commonmark_emphasis_run, commonmark_force_close_spans, commonmark_prepend_title.

Les sections précédentes couvrent l’ensemble du sujet. Cette liste résume les décisions clés pour référence rapide :

  1. Déclarez notify_format sur la classe pour indiquer à Apprise vers quel format convertir avant que votre plugin s’exécute. Omettez-le pour accepter du texte brut (TEXT).
  2. Ne reconvertissez pas le corps dans send(). Il est déjà dans self.notify_format. Utilisez body_format pour vérifier ce qu’était le contenu à l’origine, pas pour déclencher une nouvelle passe de conversion.
  3. Vérifiez body_format pour décider si une mise en forme propre au service est nécessaire. Si votre service utilise sa propre syntaxe Markdown (Slack mrkdwn, Telegram MarkdownV2, WhatsApp), appliquez cette traduction uniquement lorsque body_format est HTML — c’est-à-dire que le corps était initialement du HTML et qu’Apprise l’a converti en Markdown standard.
  4. Pour les charges utiles multi-format (comme l’email avec des parties HTML et texte séparées), appelez convert_between() dans send() pour dériver les représentations supplémentaires à partir du corps déjà converti.
  5. Traitez ?format= comme un remplacement facultatif par instance. La destination effective est toujours self.notify_format quelle que soit son origine. La plupart des plugins n’ont pas besoin de distinguer si la valeur provient de la classe ou d’un paramètre URL.
  6. Définissez overflow_mode, body_maxlen et title_maxlen sur la classe pour déclarer les limites et la stratégie de découpage de votre service en amont. Chaque appel à send() reçoit du contenu déjà dimensionné selon ces limites — aucune modification supplémentaire de la charge utile n’est requise dans send().
  7. Testez la matrice complète : body_format=None, chaque format source pris en charge, les remplacements ?format= par URL et les messages découpés ou tronqués.
Questions ou commentaires ?

Documentation

Vous avez repéré une faute de frappe ou une erreur ?

Problèmes Techniques

Vous rencontrez un problème avec le code ? Ouvrez un ticket sur GitHub :

Conçu avec amour depuis le Canada