Pièces Jointes
Lorsque vous passez attach= à Apprise.notify(), Apprise normalise chaque entrée
en un objet AppriseAttachment (reposant en interne sur des implémentations
de AttachBase). Les plugins qui activent cette prise en charge
(en définissant attachment_support = True) reçoivent une liste d’objets
pièces jointes via l’argument attach de send().
Du point de vue de l’auteur d’un plugin, une pièce jointe est une petite API uniforme qui vous permet de :
- valider sa disponibilité (
if not attachment: ...) ; - lire son contenu (
attachment.path,attachment.open(),attachment.chunk()) ; - récupérer ses métadonnées (
attachment.name,attachment.mimetype,len(attachment)) ; - la convertir en base64 (
attachment.base64()), pour les API qui exigent une charge utile inline ; - préserver la confidentialité dans les logs (
attachment.url(privacy=True)).
Ce que Reçoivent les Plugins
Section intitulée « Ce que Reçoivent les Plugins »Lorsqu’un plugin déclare la prise en charge des pièces jointes :
class NotifyFooBar(NotifyBase): # Declare awareness to the Apprise library that this service supports # attachments attachment_support = True
def send(self, body, title="", notify_type=NotifyType.INFO, attach=None, **kwargs):
# Add 'attach' into your send() call as it will be populated when one # or more attachments exist. if attach: for a in attach: # ... passattach vaut soit :
Noneou une liste vide lorsqu’aucune pièce jointe n’a été fournie ;- une liste d’objets
AttachBase(par exemple :AttachFile,AttachHTTP,AttachMemory).
Raccourci pratique :
if not a:signifie que la pièce jointe n’est pas exploitable pour le moment ;a.pathdéclenche un téléchargement ou une validation si nécessaire ;len(a)renvoie la taille de la pièce jointe en octets, lorsqu’elle est connue.
Sources des Pièces Jointes
Section intitulée « Sources des Pièces Jointes »Apprise prend en charge plusieurs sources de pièces jointes. Elles sont toutes normalisées vers la même surface d’API.
AttachFile — les fichiers locaux référencent des chemins côté serveur. L’usage
le plus simple consiste à passer directement une chaîne de chemin : Apprise
la convertit en URL file:// en interne :
# Single path — Apprise wraps it in AttachFile automaticallyapobj.notify(body="See log", attach="/var/log/syslog")
# Multiple pathsapobj.notify( body="Build artifacts", attach=["/var/log/syslog", "/tmp/report.pdf"],)Constructeur direct — utilisez AttachFile lorsque vous devez surcharger
le nom de fichier ou le type MIME présenté au service distant, sans renommer
le fichier sur le disque :
from apprise.attachment import AttachFile
# Override the filename and MIME type the service seesa = AttachFile( "/var/log/app.log", name="2026-03-20-app.log", # presented name, not a rename mimetype="text/plain",)
apobj.notify(body="Nightly log", attach=a)Comportements Clés :
- Le contenu est validé sur place : il n’est ni copié ni déplacé.
name=etmimetype=surchargent ce que le plugin envoie en amont ; le fichier sur disque reste inchangé.- Les limites de taille sont appliquées via
max_file_size(1 Go par défaut). attachment.pathrenvoie le chemin absolu du fichier.attachment.open()renvoie un handle lisible et constitue la bonne façon de diffuser le contenu dans les plugins.
AttachHTTP — les URL hébergées sont téléchargées dans un fichier temporaire
avant d’être fournies aux plugins. Passez directement une chaîne URL :
Apprise crée alors un AttachHTTP en interne :
# Single URLapobj.notify( body="Latest report", attach="https://example.com/report.pdf",)
# Multiple URLsapobj.notify( body="Spec documents", attach=( "https://example.com/widget-ICD.pdf", "https://example.com/draft-manual.pdf", ),)Téléchargement authentifié — intégrez les identifiants directement dans l’URL :
# Apprise authenticates before downloadingapobj.notify( body="Security camera snapshot", attach="https://admin:secret@camera.local/snapshot.jpg",)Constructeur direct — utilisez AttachHTTP lorsque vous avez besoin
d’un contrôle fin sur le cache, les timeouts ou les en-têtes personnalisés :
from apprise.attachment import AttachHTTP
# Disable caching so the URL is always re-fetched on each notify() calla = AttachHTTP( "https://reports.internal/latest.pdf", cache=False, name="latest-report.pdf", # override filename presented to the service)
apobj.notify(body="Fresh report", attach=a)En-têtes HTTP personnalisés — utilisez la syntaxe d’URL +Clé=Valeur
pour transmettre des en-têtes avec la requête de téléchargement :
# Bearer token forwarded when downloading the assetapobj.notify( body="Private asset", attach="https://cdn.example.com/asset.zip?+Authorization=Bearer%20TOKEN",)Comportements Clés :
- Le téléchargement est stocké dans un fichier temporaire et nettoyé automatiquement lorsque la pièce jointe est invalidée ou sort de portée.
- Un cache par URL s’applique (600 secondes par défaut). Utilisez
cache=nopour retélécharger à chaque fois, oucache=<seconds>pour définir un TTL personnalisé. - La vérification SSL est activée par défaut ; définissez
verify=nouniquement pour des endpoints internes de confiance. cto=etrto=contrôlent respectivement les timeouts de connexion et de lecture.
Paramètres Réservés
Section intitulée « Paramètres Réservés »Les paramètres suivants sont consommés par Apprise et ne sont pas transmis en amont :
cacheverifymimenamectorto
Tous les autres paramètres de requête sont transmis tels quels.
En-têtes Personnalisés
Section intitulée « En-têtes Personnalisés »Des en-têtes HTTP personnalisés peuvent être définis avec la syntaxe
+Header=Value.
Exemple :
https://example.com/file.png?+Authorization=Bearer%20TOKENCes entrées deviennent des en-têtes HTTP, et non des paramètres de requête.
Vérification des Certificats
Section intitulée « Vérification des Certificats »Le paramètre verify= contrôle la vérification des certificats TLS pour
les pièces jointes basées sur HTTPS.
verify=yes(par défaut) : les certificats TLS sont validés à l’aide du magasin de confiance du système.verify=no: la vérification des certificats TLS est désactivée. Cela ne doit être utilisé que pour les tests ou lors d’interactions avec des endpoints internes de confiance.
Timeouts
Section intitulée « Timeouts »Les pièces jointes HTTP prennent en charge une configuration explicite des timeouts :
cto=timeout de connexion (secondes)rto=timeout de lecture (secondes)
Les valeurs de timeout persistent avec la pièce jointe.
Définit pendant combien de temps une pièce jointe téléchargée est considérée
comme valide avant d’être retéléchargée. C’est utile pour des cas comme
les caméras de sécurité : si votre application conserve un objet
AppriseAttachment longtemps, vous pouvez toujours garantir une version fraîche.
cache=yesmet en cache indéfiniment ;cache=nodésactive le cache (les URL hébergées sont récupérées à chaque fois) ;cache=<seconds>met en cache pendant la durée indiquée (TTL en secondes) ; la valeur par défaut est 600.
Ordre de Détection du Nom de Fichier et du MIME
Section intitulée « Ordre de Détection du Nom de Fichier et du MIME »name=oumime=explicite- en-tête
Content-Disposition - nom de fichier extrait du chemin URL
- valeurs de repli générées
Streaming et Limites
Section intitulée « Streaming et Limites »Les pièces jointes HTTP sont diffusées vers le disque et validées
progressivement. Lorsque Content-Length est absent ou invalide, les limites
de taille sont appliquées pendant le streaming.
Réutilisation des Téléchargements
Section intitulée « Réutilisation des Téléchargements »Lorsque le cache est activé, un même téléchargement HTTP peut être réutilisé sur plusieurs envois de notifications.
AttachMemory — stocke entièrement le contenu dans un tampon BytesIO,
sans écrire de fichiers temporaires sur disque. C’est le bon choix lorsque
vous générez du contenu à la volée : rapports, HTML rendu, graphiques ou
octets déjà présents en mémoire.
AttachMemory doit toujours être construit directement ; il n’existe pas de raccourci URL pour lui.
from apprise.attachment import AttachMemoryÀ partir d’un objet bytes :
data = b"<html><body><h1>Report</h1></body></html>"
apobj.notify( body="Monthly report attached", attach=AttachMemory( content=data, name="report.html", mimetype="text/html", ),)À partir d’un objet str — encodé automatiquement en UTF-8 :
csv_text = "date,value\n2026-03-20,42\n"
apobj.notify( body="Today's readings", attach=AttachMemory( content=csv_text, name="readings.csv", mimetype="text/csv", ),)À partir d’un tampon BytesIO — lisez le tampon avant de le passer :
import io
buf = io.BytesIO()buf.write(b"%PDF-1.4 ...") # write your content into the bufferbuf.seek(0)
apobj.notify( body="Generated PDF", attach=AttachMemory( content=buf.read(), name="invoice.pdf", mimetype="application/pdf", ),)Plusieurs pièces jointes en mémoire :
apobj.notify( body="Build summary", attach=[ AttachMemory(content=summary_bytes, name="summary.txt", mimetype="text/plain"), AttachMemory(content=chart_bytes, name="chart.png", mimetype="image/png"), ],)Exemple pratique : graphique Plotly / Matplotlib :
import ioimport apprisefrom apprise.attachment import AttachMemory
# --- generate your chart however you like ---try: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1, 2, 3], [4, 5, 6]) buf = io.BytesIO() fig.savefig(buf, format="png") chart_bytes = buf.getvalue()except ImportError: chart_bytes = b"" # graceful fallback
# --- send it ---apobj = apprise.Apprise()apobj.add("tgram://bottoken/ChatID")
apobj.notify( body="Nightly trend", attach=AttachMemory( content=chart_bytes, name="trend.png", mimetype="image/png", ),)Comportements Clés :
- Accepte
bytesetstr(encodé en UTF-8 par défaut), mais pas directement unBytesIO: appelez d’abord.read()ou.getvalue(). name=prend par défaut un nom de fichier fondé sur un UUID s’il est omis ; définissez-le toujours explicitement pour que le service reçoive un nom parlant.mimetype=vaut par défauttext/plainpour les chaînes etapplication/octet-streampour les octets si rien n’est fourni ; définissez-le explicitement lorsque le service en amont inspecte le type MIME.- Aucune E/S disque n’a lieu à aucun moment.
attachment.open()renvoie directement leBytesIOinterne. len(attachment)renvoie la taille du tampon en octets, que vous pouvez comparer à la limite d’envoi d’un service avant d’appelernotify().
Modes d’Emplacement du Contenu
Section intitulée « Modes d’Emplacement du Contenu »La gestion des pièces jointes est gouvernée par des règles d’emplacement du contenu :
| Valeur | Description |
|---|---|
LOCAL | Autorise les fichiers locaux, les pièces jointes en mémoire et le contenu hébergé. |
HOSTED | Destiné aux services hébergés. Les fichiers locaux et pièces jointes mémoire sont rejetés. |
INACCESSIBLE | Les pièces jointes sont entièrement désactivées. Tous les téléchargements échouent et les objets valent False. |
Paramètres d’URL Partagés par les Types de Pièces Jointes
Section intitulée « Paramètres d’URL Partagés par les Types de Pièces Jointes »| Valeur | Description |
|---|---|
mime | Les URL de pièces jointes prennent en charge un petit ensemble de paramètres de requête communs. Force le type MIME de la pièce jointe en contournant la détection. |
name | Force le nom de fichier présenté au plugin. Cela ne renomme pas le fichier local ; cela modifie seulement les métadonnées (attachment.name) et ce que le plugin peut envoyer en amont. |
Utiliser les Pièces Jointes dans les Plugins
Section intitulée « Utiliser les Pièces Jointes dans les Plugins »Valider l’Accès
Section intitulée « Valider l’Accès »Vérifiez toujours que les pièces jointes sont disponibles avant de les utiliser :
for attachment in attach: if not attachment: self.logger.error( "Could not access attachment %s.", attachment.url(privacy=True), ) return FalseUne pièce jointe peut échouer parce qu’elle est absente, dépasse les limites de taille, est inaccessible depuis l’emplacement d’exécution courant, ou n’a pas pu être téléchargée.
Préférez attachment.open() pour les API d’Upload
Section intitulée « Préférez attachment.open() pour les API d’Upload »De nombreux services exigent des uploads multipart. Utilisez attachment.open()
pour obtenir un objet de type fichier : cela fonctionne correctement pour
tous les types de pièces jointes, y compris AttachMemory, qui n’a
aucun chemin sur disque :
filename = attachment.namemimetype = attachment.mimetype
fh = attachment.open()try: files = {"file": (filename, fh, mimetype)} r = requests.post(url, files=files, ...)finally: fh.close()Ou via la forme avec gestionnaire de contexte :
with attachment as f: files = {"file": (attachment.name, f, attachment.mimetype)} r = requests.post(url, files=files, ...)Base64 Lorsque le Service l’Exige
Section intitulée « Base64 Lorsque le Service l’Exige »Tous les types de pièces jointes prennent en charge l’export Base64. Certaines
API exigent des pièces jointes encodées en base64. Utilisez attachment.base64() :
encoded = attachment.base64() # returns a str by defaultpayload["base64_attachments"].append(encoded)base64()renvoie une chaîne ;base64(encoding=None)renvoie des octets bruts.
Si la pièce jointe ne peut pas être lue, base64() lève une exception Apprise.
Interceptez-la et échouez proprement.
Cela est fréquemment utilisé par les API qui ne prennent pas en charge les uploads multipart.
Streamer par Blocs si Nécessaire
Section intitulée « Streamer par Blocs si Nécessaire »Si vous devez éviter de charger un fichier entier en mémoire, utilisez attachment.chunk() :
for chunk in attachment.chunk(size=5 * 1024 * 1024): # upload / write chunk ...Nettoyage et Cycle de Vie
Section intitulée « Nettoyage et Cycle de Vie »Les plugins n’ont généralement pas besoin de supprimer manuellement les fichiers
temporaires téléchargés. Les objets pièces jointes gèrent eux-mêmes leur
nettoyage via invalidate() et leurs destructeurs.
Si vous conservez des objets pièces jointes au-delà de send(), il vous revient
de bien comprendre leur cycle de vie. En règle générale, traitez-les comme des objets éphémères.
Limites et Sécurité
Section intitulée « Limites et Sécurité »- Les limites de taille sont appliquées par
AttachBase.max_file_size. Si votre service impose une limite plus basse, faites-la respecter dans votre plugin aveclen(attachment)et échouez tôt. - La prise en charge des pièces jointes est activée explicitement par plugin via
attachment_support = True. Si votre service n’accepte pas les fichiers, laissez cette option désactivée. - Utilisez
attachment.url(privacy=True)dans les logs. Cela garantit que les secrets embarqués sont masqués.
Conseils pour les Auteurs de Plugins
Section intitulée « Conseils pour les Auteurs de Plugins »Les plugins devraient valider tôt la taille et le nombre des pièces jointes, et gérer proprement les pièces jointes inaccessibles.
Exemples Pratiques dans les Plugins du Cœur
Section intitulée « Exemples Pratiques dans les Plugins du Cœur »Le projet principal contient des modèles courants que vous pouvez reprendre.
- envoi de pièces jointes sous forme de fichiers (multipart) avec sélection basée sur le MIME ;
- conversion de pièces jointes en base64 pour les API JSON ;
- itération sur les pièces jointes et remontée des échecs partiels.
Consultez les plugins Telegram et Signal API pour des implémentations réelles de ces approches.
Questions ou commentaires ?
Documentation
Vous avez repéré une faute de frappe ou une erreur ? Signalez-la ou proposez une correction .
Problèmes Techniques
Vous rencontrez un problème avec le code ? Ouvrez un ticket sur GitHub :