feat(mail-templates): render ACKnowledge SOC emails from markdown (1.0.0)
render-mail-template turns a template, a subject and a markdown
description into the dark ACKnowledge SOC mail (header badge, optional
callout and action button, signature with the logo, SOC or client
footer, fr/en). Nothing is sent: subject, html_body and text_body go to
exchange-send-mail or mail-sender, which embed the data-URI logo.
Eight templates (sla_breach, incident_notification, incident_update,
incident_closure, action_required, escalation, threat_bulletin, info).
The markdown covers GFM tables, code blocks and {{level}} pills; raw
HTML in the inputs is escaped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJu1jvaug4qY1kMgz8wx41
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
id: mail_templates
|
||||||
|
name: Mail Templates (ACKnowledge)
|
||||||
|
version: 1.0.0
|
||||||
|
description: "Turns a subject and a markdown description into an ACKnowledge SOC email (dark theme, header badge, signature with the logo, footer) — nothing is sent: chain the output into exchange-send-mail or mail-sender (`subject` → subject, `html_body` → html_body, `text_body` → body). Eight templates, each with its own badge and colour: sla_breach, incident_notification (colour follows `severity`), incident_update, incident_closure, action_required, escalation, threat_bulletin, info. The description takes markdown: headings (## is the blue capitalised section title), bold, italic, links, lists, quotes, rules, GFM tables, `code` and ``` blocks (hashes, IPs, command lines), and level pills `{{critical}}` `{{high}}` `{{medium}}` `{{low}}` `{{info}}` `{{ok}}` — `{{high:Élevée}}` changes the label. Raw HTML in the inputs is escaped, so alert data can be interpolated safely. The logo is an inline data-URI PNG: exchange-send-mail and mail-sender embed it; the Gmail integration does not, and the logo is lost there. No connection, no secret; stdlib-only."
|
||||||
|
changelog: "1.0.0 — Initial release: render-mail-template (8 templates, markdown description with tables, code and level pills, optional callout, action button, case reference and severity, SOC or client footer, French or English) and a self-test."
|
||||||
|
category: email
|
||||||
|
|
||||||
|
# Nothing to connect to. The only setting names the bot in the SOC footer
|
||||||
|
# ("generated automatically by the <bot_name> bot").
|
||||||
|
config_schema:
|
||||||
|
properties:
|
||||||
|
bot_name:
|
||||||
|
type: string
|
||||||
|
description: "Bot named in the footer of SOC mails (default Riposte)"
|
||||||
|
default: Riposte
|
||||||
|
required: []
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- id: render_mail_template
|
||||||
|
name: mail-render-template
|
||||||
|
description: "Render an ACKnowledge SOC email from a template, a subject and a markdown description. Returns `subject` (prefixed with [case_ref] when case_ref is given and not already in it), `html_body` (table layout with inline styles, for Outlook and webmail), `text_body` (the plain-text part) and `html_bytes`. Sends nothing: pass the outputs to exchange-send-mail or mail-sender."
|
||||||
|
risk: read
|
||||||
|
inputs_schema:
|
||||||
|
properties:
|
||||||
|
template:
|
||||||
|
type: string
|
||||||
|
enum: [sla_breach, incident_notification, incident_update, incident_closure, action_required, escalation, threat_bulletin, info]
|
||||||
|
description: "Layout: sla_breach (red, SLA ALERT), incident_notification (colour of `severity`), incident_update (blue, STATUS UPDATE), incident_closure (green, CLOSED), action_required (orange), escalation (magenta), threat_bulletin (violet), info (grey, default)"
|
||||||
|
subject: { type: string, description: "Subject, also shown as the title of the mail" }
|
||||||
|
description: { type: string, description: "Body in markdown — ## section titles, **bold**, lists, tables, `code`, ``` blocks, {{medium}} pills" }
|
||||||
|
callout_text: { type: string, description: "Highlighted box above the description (markdown). Empty: no box" }
|
||||||
|
callout_title: { type: string, description: "Title of the box (default: the template badge, e.g. ⚠ SLA ALERT)" }
|
||||||
|
button_label: { type: string, description: "Action button text, e.g. Open the case in Riposte (needs button_url)" }
|
||||||
|
button_url: { type: string, description: "Action button link (http or https)" }
|
||||||
|
case_ref: { type: string, description: "Case or ticket reference, shown under the title and prefixed to the subject" }
|
||||||
|
severity:
|
||||||
|
type: string
|
||||||
|
enum: [critical, high, medium, low, info]
|
||||||
|
description: "Severity pill under the title; for incident_notification it sets the badge colour and label"
|
||||||
|
audience:
|
||||||
|
type: string
|
||||||
|
enum: [soc, client]
|
||||||
|
description: "Footer: soc (generated by the bot, default) or client (confidentiality notice)"
|
||||||
|
lang:
|
||||||
|
type: string
|
||||||
|
enum: [fr, en]
|
||||||
|
description: "Language of the badges, date and footer (default fr)"
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
description: "ISO 8601 date shown under the title, in UTC (default: now)"
|
||||||
|
required: [template, subject, description]
|
||||||
|
outputs_schema:
|
||||||
|
properties:
|
||||||
|
subject: { type: string }
|
||||||
|
html_body: { type: string }
|
||||||
|
text_body: { type: string }
|
||||||
|
template: { type: string }
|
||||||
|
lang: { type: string }
|
||||||
|
audience: { type: string }
|
||||||
|
html_bytes: { type: number }
|
||||||
|
|
||||||
|
- id: test_connection
|
||||||
|
name: mail-templates-test
|
||||||
|
description: "Render every template with a sample description, without sending anything (used by the Test button)."
|
||||||
|
risk: read
|
||||||
|
inputs_schema:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
outputs_schema: { properties: {} }
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,27 @@
|
|||||||
|
import json, os, sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
SAMPLE = "## Sample\n\n**1 ticket** waiting.\n\n| Ticket | Priority |\n|---|---|\n| SOC-1 | {{medium}} |\n\n```\n8.8.8.8\n```"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
import render_mail_template as r
|
||||||
|
except ImportError:
|
||||||
|
# The runner ships one script per command: say so instead of failing the Test button.
|
||||||
|
print(json.dumps({"ok": True, "detail": "No connection to test; render a template to check the output."}))
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
cfg = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||||
|
sizes = {}
|
||||||
|
for name in r.TEMPLATES:
|
||||||
|
out = r.render(cfg, {"template": name, "subject": "Test", "description": SAMPLE, "severity": "high"})
|
||||||
|
sizes[name] = out["html_bytes"]
|
||||||
|
print(json.dumps({"ok": True, "templates": sizes}))
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({"error": str(e)}))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user