Conversion du Format des Messages
Lorsque vous ajoutez un plugin, posez-vous deux questions :
- Quel format l’appelant a-t-il fourni ?
- Quel format le service accepte-t-il ?
L’appelant décrit le premier avec body_format. Votre plugin décrit le second avec notify_format. Apprise effectue la conversion avant d’appeler send().
Dans send(), body_format correspond au format du corps reçu. Ce n’est plus la valeur d’origine de l’appelant. Par exemple, un même message HTML peut parvenir sous forme de texte brut à un plugin, de Markdown à un autre et rester en HTML pour un troisième.
Trouver le Format d’Entrée
Section intitulée « Trouver le Format d’Entrée »Le format d’entrée dépend de la façon dont la notification a été envoyée :
| Interface | Valeur par défaut lorsqu’aucun format n’est déclaré |
|---|---|
| CLI | text : la CLI définit toujours un format d’entrée ; remplacez-le avec --input-format |
| Bibliothèque Python | none : sauf si body_format est passé à notify() ou prédéfini dans AppriseAsset |
| Apprise API | none : le formatage est entièrement optionnel ; définissez format dans la charge utile de la requête pour changer ce comportement |
Consultez Formatage pour le comportement visible par l’utilisateur.
Flux de Conversion
Section intitulée « Flux de Conversion »Pour chaque plugin, Apprise :
- Lit le format d’entrée déclaré par l’appelant.
- Choisit un format accepté par le plugin.
- Convertit le corps lorsque le format d’entrée diffère de celui du plugin.
- Appelle le plugin avec le corps converti et le format choisi.
Si l’appelant n’a déclaré aucun format source, Apprise laisse le corps inchangé. Dans ce cas, body_passthrough vaut True. Sinon, il vaut False.
Apprise.notify(body=..., body_format=HTML) | | cible = plugin.resolve_format(HTML) | convert_between(HTML, cible, body) | (ignoré lorsque le body_format de l'appelant vaut None) vplugin.notify( body=<déjà converti vers le format résolu>, body_format=cible, # format choisi ; jamais None ici body_passthrough=False, # l'appelant a déclaré HTML ci-dessus)Lorsqu’un service ne possède pas de champ distinct pour le titre (title_maxlen <= 0), Apprise ajoute le titre au corps avant l’envoi. Sinon, le titre est transmis séparément.
Lire body_format et body_passthrough dans send()
Section intitulée « Lire body_format et body_passthrough dans send() »Acceptez body_format et body_passthrough dans send(). Apprise a déjà calculé ces deux valeurs :
def send(self, body, title="", notify_type=..., body_format=None, body_passthrough=None, **kwargs): if body_format == NotifyFormat.MARKDOWN: ...Vous n’avez normalement pas besoin d’appeler resolve_format() vous-même. Les appels directs à plugin.notify() ou plugin.send() constituent l’exception : ils ne passent pas par l’étape de conversion d’Apprise. Si vous appelez un plugin directement, préparez le corps dans le format attendu par ce plugin.
Lorsqu’Aucun Format d’Entrée N’est Déclaré
Section intitulée « Lorsqu’Aucun Format d’Entrée N’est Déclaré »Lorsque le body_format de l’appelant de la bibliothèque Python vaut None, Apprise ignore entièrement la conversion :
apobj.notify(body="<b>Bonjour</b>")Le plugin reçoit <b>Bonjour</b> sans modification et body_passthrough=True. Apprise applique tout de même le traitement normal du titre et du dépassement.
La CLI ne produit pas cet état. Elle fournit toujours body_format=TEXT sauf si l’appelant utilise --input-format pour le remplacer.
Choisir le Format du Plugin
Section intitulée « Choisir le Format du Plugin »NotifyBase utilise le texte brut par défaut. Définissez notify_format lorsque le service accepte un autre format :
from apprise import NotifyFormatfrom apprise.plugins.base import NotifyBase
class NotifyExample(NotifyBase): notify_format = NotifyFormat.MARKDOWNLa plupart des plugins prennent en charge un seul format. Dans ce cas, self.notify_format suffit. Si un service accepte plusieurs formats, utilisez body_format dans send() pour connaître celui choisi par Apprise.
NotifyFormat.TEXT est la valeur par défaut lorsqu’un plugin ne déclare aucun format.
Source body_format | Corps transmis au plugin |
|---|---|
TEXT | Texte inchangé |
MARKDOWN | Source Markdown inchangée (aucune conversion destructive) |
HTML | Balises supprimées ; structure de bloc réduite en texte brut |
None | Corps inchangé ; aucune conversion effectuée |
La conversion Markdown vers texte ne modifie pas les caractères Markdown. Si le service les interprète, échappez-les dans send().
Définissez notify_format = NotifyFormat.MARKDOWN lorsque le service accepte Markdown.
Source body_format | Corps transmis au plugin |
|---|---|
TEXT | Les caractères significatifs pour CommonMark (*, _, `, #, etc.) sont échappés par une barre oblique inverse pour se lire littéralement |
MARKDOWN | Markdown inchangé |
HTML | Converti en Markdown standard |
None | Corps inchangé ; aucune conversion effectuée |
Le HTML et le texte brut sont convertis en Markdown standard. Telegram, Slack, WhatsApp et Google Chat utilisent une syntaxe différente. Leurs plugins doivent donc effectuer une conversion supplémentaire propre au service ; consultez Adaptation propre au service.
Définissez notify_format = NotifyFormat.HTML lorsque le service accepte HTML.
Source body_format | Corps transmis au plugin |
|---|---|
TEXT | Texte échappé pour HTML avec sauts de ligne convertis en <br> |
MARKDOWN | HTML rendu |
HTML | HTML inchangé |
None | Corps inchangé ; aucune conversion effectuée |
Le plugin doit effectuer les autres modifications exigées par le service, par exemple supprimer les balises non prises en charge ou placer le HTML dans la requête API.
Plugins Multi-Format
Section intitulée « Plugins Multi-Format »Si un service accepte plusieurs formats, déclarez-les dans l’ordre. Le premier élément est la valeur par défaut. Un tuple est le choix le plus clair pour un paramètre de classe :
from apprise import NotifyFormatfrom apprise.plugins.base import NotifyBase
class NotifyExample(NotifyBase): # Le premier élément est la valeur par défaut -- utilisée si rien # d'autre n'en choisit une autre. notify_format = (NotifyFormat.HTML, NotifyFormat.MARKDOWN)Lisez body_format dans send() pour connaître le format choisi par Apprise :
def send(self, body, title="", notify_type=..., body_format=None, body_passthrough=None, **kwargs): if body_format == NotifyFormat.MARKDOWN: ... else: ...Vous pouvez aussi définir send_text(), send_html() ou send_markdown() au lieu d’utiliser une condition dans une seule méthode send(). Apprise appelle la méthode correspondant au format choisi et se replie sur send() lorsqu’aucune méthode ne correspond :
class NotifyExample(NotifyBase): notify_format = (NotifyFormat.HTML, NotifyFormat.MARKDOWN)
def send_html(self, body, title="", notify_type=..., index=0, total=1, **kwargs): ...
def send_markdown(self, body, title="", notify_type=..., index=0, total=1, **kwargs): ...
# send() peut toujours servir de méthode de repli.La plupart des plugins n’ont besoin que d’un format et peuvent ignorer cette section. Les plugins de relais, comme Custom JSON/XML/Form et Apprise API, peuvent prendre en charge les trois formats. Un relais ne doit transmettre le champ format que si body_passthrough vaut False ; sinon, il choisirait un format à la place de l’appelant.
Un outil qui explore Apprise().details() (un générateur d’URL, par exemple) peut lire les formats déclarés d’un service via args.format.supported — voir L’argument format pour le détail complet du schéma.
Remplacement Facultatif avec ?format=
Section intitulée « Remplacement Facultatif avec ?format= »Sans remplacement, Apprise utilise notify_format, ou son premier élément pour un plugin multi-format :
example://host/tokenLe paramètre format= change le format pour une seule URL de plugin :
example://host/token?format=textexample://host/token?format=markdownexample://host/token?format=htmlPour un plugin à format unique, le remplacement devient le format de cette instance. Pour un plugin multi-format, la valeur demandée doit apparaître dans notify_format. Une valeur non prise en charge produit un avertissement, puis Apprise applique ses règles de sélection habituelles.
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 n’ont pas besoin de suivre cette information séparément.
Ce que Reçoit le Plugin
Section intitulée « Ce que Reçoit le Plugin »Au moment où send() s’exécute, Apprise a préparé les valeurs suivantes :
body: converti et dimensionné selonoverflow_mode. Apprise peut ajouter des caractères Markdown de fermeture lorsqu’il découpe du texte mis en forme.title: tronqué àtitle_maxlencaractères. Lorsquetitle_maxlen <= 0, le titre a été fusionné dansbodypar la classe de base et arrive sous forme de chaîne vide.body_format: le format debody, par exempleNotifyFormat.HTML; il n’est jamaisNoneà ce stade.body_passthrough:Truelorsqu’aucun format d’entrée n’a été déclaré et que la conversion automatique a été ignorée. Le traitement normal du titre et du dépassement s’applique toujours.
Utilisez body_format pour choisir le bon mode d’envoi. N’utilisez body_passthrough que si votre plugin doit distinguer une entrée intacte d’un contenu préparé par Apprise.
Adaptation Propre au Service dans les Plugins
Section intitulée « Adaptation Propre au Service dans les Plugins »Apprise convertit le contenu en Markdown standard. Certains services utilisent une autre syntaxe :
- Slack utilise
mrkdwnplutô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
Conservez ces règles dans le plugin du service. Le code de conversion partagé ne doit rien connaître de Slack, Telegram ou d’un autre service précis.
Surchargez dialect_convert() pour traduire le Markdown standard vers la syntaxe du service avant l’exécution de send() :
class NotifyExample(NotifyBase): def dialect_convert(self, body, body_format=None, *args, **kwargs): """Adapte une partie préparée à la syntaxe du service.""" if body_format != NotifyFormat.MARKDOWN: # Ce service n'a un dialecte propre que pour Markdown ; # tout le reste passe inchangé. return body return self._commonmark_to_example_dialect(body)Apprise ignore cette méthode lorsque body_passthrough vaut True. Retournez les formats non gérés sans les modifier. N’enregistrez aucun état et n’envoyez aucune notification ici. Apprise peut appeler cette méthode plusieurs fois pendant la préparation du message.
L’échappement propre au service peut allonger le texte. Pour SPLIT et TRUNCATE, Apprise vérifie de nouveau la taille après dialect_convert(). Le plugin n’a donc pas besoin de découper lui-même le corps.
Les services de discussion sans syntaxe de titre peuvent réutiliser commonmark_headings_to_bold(). Cet utilitaire transforme # Alerte en **Alerte** avant l’adaptation au dialecte et ne modifie pas le code en ligne ou délimité.
Les analyses de destination des liens nommés partagent un budget de travail pour l’ensemble du message. Une fois ce budget épuisé, chaque destination suivante fait l’objet d’une vérification courte et limitée ; les liens incorrects plus longs restent en texte brut afin d’éviter des analyses excessives.
Certains services envoient plusieurs représentations dans la même requête. L’email et Brevo peuvent envoyer du HTML avec une version en texte brut. Utilisez body_format pour identifier le corps reçu, puis créez l’autre représentation si le service en a besoin :
from ..conversion import convert_between
def send(self, body, title="", notify_type=..., body_format=None, **kwargs): # body est déjà dans le format résolu (HTML ou TEXT). if body_format == NotifyFormat.HTML: # Construire une version texte brut pour les clients ne pouvant # pas afficher HTML. text_body = convert_between(NotifyFormat.HTML, NotifyFormat.TEXT, body) payload = {"html": body, "text": text_body} else: html_body = convert_between(NotifyFormat.TEXT, NotifyFormat.HTML, body) payload = {"html": html_body, "text": body} # ... envoyer la charge utile ...Ce choix dépend du service. Apprise sélectionne le format principal ; le plugin décide si sa charge utile nécessite aussi une autre représentation.
Gestion du Débordement avant l’Adaptation au Service
Section intitulée « Gestion du Débordement avant l’Adaptation au Service »Définissez overflow_mode pour choisir le comportement appliqué lorsque le contenu dépasse body_maxlen ou title_maxlen. Apprise s’en charge avant l’exécution de send().
| Mode | Appels à send() | Garantie sur le corps |
|---|---|---|
UPSTREAM (défaut) | Un seul | Inchangé ; peut dépasser body_maxlen |
TRUNCATE | Un seul | Au mieux près de body_maxlen |
SPLIT | Un par bloc | Au mieux près de body_maxlen |
from apprise.common import OverflowMode
class NotifyExample(NotifyBase): body_maxlen = 4096 title_maxlen = 255 overflow_mode = OverflowMode.SPLITApprise essaie de préserver la lisibilité du Markdown déclaré lorsqu’une découpe traverse sa mise en forme. L’échappement propre au service peut ensuite ajouter des caractères ; SPLIT et TRUNCATE vérifient donc de nouveau la taille après dialect_convert(). UPSTREAM n’impose pas la limite : le service reçoit le corps complet et le gère lui-même.
Le paramètre URL ?overflow= permet de changer le mode pour une seule URL de plugin. La valeur par défaut de la classe doit correspondre aux capacités du service.
Connaître Sa Position Parmi Plusieurs Appels à send()
Section intitulée « Connaître Sa Position Parmi Plusieurs Appels à send() »Un appel à notify() peut produire plusieurs appels à send(). Acceptez index et total si votre plugin doit savoir si un bloc est le premier ou le dernier :
def send(self, body, title="", notify_type=..., index=0, total=1, **kwargs): is_first = index == 0 is_last = index == total - 1 # ... reste de send() ...Un message non découpé utilise index=0 et total=1. La plupart des plugins n’ont besoin d’aucun traitement particulier.
Le placement des pièces jointes n’utilise ni index ni total. Apprise transmet les pièces jointes à un seul appel à send() et choisit le premier par défaut. Pour les placer sur le dernier appel, surchargez _attachment_send_index() :
def _attachment_send_index(self, total: int) -> int: # Place la pièce jointe sur le dernier appel plutôt que le premier. return total - 1Telegram utilise cette surcharge lorsqu’une pièce jointe doit suivre tous les blocs de texte.
Frontières des Plugins
Section intitulée « Frontières des Plugins »apprise.conversiongère la conversion générale entre texte, HTML et Markdown.- La syntaxe, l’échappement et les charges utiles propres à un service appartiennent au plugin.
- Surchargez
dialect_convert()pour un format propre au service. Ne découpez pas le texte converti dans le plugin ; Apprise s’en charge déjà. - La suppression d’un plugin doit supprimer tout le comportement de ce service sans modifier le code de conversion partagé.
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 :