Chat: Kunde bekommt Mail, wenn das Team schreibt (K17)

Neue Vorlage K17 im Stil von K16 mit Nachricht, Absender und Link
auf den Portal-Chat. Hoechstens eine Mail je Kunde im Zeitfenster
CHAT_NOTIFY_THROTTLE_MIN (15 Min.), ohne BCC ans Team, nicht ohne
E-Mail-Adresse oder Portalzugang. Das Admin-Panel zeigt unter dem
Kundennamen, ob der Hinweis rausging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 19:00:33 +00:00
1 parent 37489aa866
commit 00ea4baf65
6 files changed
+176 -7

No files matched your search

+44 -3
View File
@@ -78,6 +78,46 @@ export async function sendChatNotification(customer, text) {
}
}
// Gleiches Zeitfenster in Gegenrichtung: das Team schreibt, der Kunde bekommt K17
const lastCustomerNoticeByCustomer = new Map() // customerId -> timestamp
/**
* Benachrichtigt den Kunden per E-Mail ueber eine neue Chatnachricht vom Team (K17), ohne BCC.
* Wirft nie; das Ergebnis zeigt das Admin-Panel unter der Nachricht an.
* @returns {Promise<{ sent: boolean, reason?: string, to?: string }>}
*/
export async function sendCustomerChatNotice(customer, { sender, text }) {
const to = String(customer.email || '').trim()
if (!to) return { sent: false, reason: 'keine E-Mail-Adresse' }
if (!customer.portalAccessEnabled) return { sent: false, reason: 'kein Portalzugang' }
const now = Date.now()
const last = lastCustomerNoticeByCustomer.get(customer.$id) || 0
if (now - last < config.chatNotify.throttleMin * 60 * 1000) return { sent: false, reason: 'throttled', to }
try {
lastCustomerNoticeByCustomer.set(customer.$id, now)
await sendCustomerMail(to, {
template: 'K17',
bccTeam: false,
values: {
kunde_name: customer.name || to,
absender: sender || 'Dein WEBklar-Team',
nachricht: text,
portal_url: `${config.publicBaseUrl}/dashboard.html#chat`,
},
})
return { sent: true, to }
} catch (err) {
lastCustomerNoticeByCustomer.delete(customer.$id)
console.error(`[mailer] Chat-Hinweis an Kunde ${customer.$id} fehlgeschlagen:`, err.message)
return { sent: false, reason: err.message, to }
}
}
/** Nur fuer Tests: Zeitfenster der Kunden-Hinweise zuruecksetzen. */
export function resetChatNoticeThrottleForTests() {
lastCustomerNoticeByCustomer.clear()
}
// ---------------------------------------------------------------------------
// Wartungs-Abo: Vertrags-/Kuendigungs-/Widerrufsbestaetigungen
// ---------------------------------------------------------------------------
@@ -112,9 +152,10 @@ export async function sendAdminNotice(message) {
* Vertragsunterlagen als PDF.
* Ergebnis: nodemailer-Info (messageId, Antwort des Mailservers, abgelehnte Empfaenger).
* @param {string} to
* @param {{ template: string, values?: object, blocks?: object, attachments?: object[] }} message
* `bccTeam: false` laesst die Kopie ans Team weg (K17: das Team hat die Nachricht selbst geschrieben).
* @param {{ template: string, values?: object, blocks?: object, attachments?: object[], bccTeam?: boolean }} message
*/
export async function sendCustomerMail(to, { attachments = [], ...message }) {
export async function sendCustomerMail(to, { attachments = [], bccTeam = true, ...message }) {
const mailer = getTransporter()
if (!mailer) throw new Error('SMTP nicht konfiguriert')
const mail = render(message)
@@ -122,7 +163,7 @@ export async function sendCustomerMail(to, { attachments = [], ...message }) {
from: `"${CUSTOMER_SENDER}" <${config.smtp.from}>`,
replyTo: config.smtp.replyTo || undefined,
to,
bcc: config.chatNotify.emails.join(', ') || undefined,
bcc: (bccTeam && config.chatNotify.emails.join(', ')) || undefined,
subject: mail.subject,
text: mail.text,
html: mail.html,