Fix garbled ticket text from HTML emails (Outlook VML/entities)
extractBody() used a naive tag-stripping regex as a fallback, which
strips <style> tags but leaves their CSS content behind as visible
text, and never decodes HTML entities. Outlook's VML style block
(v\:* {behavior:url(#default#VML);} ...) and /« entities
were leaking straight into ticket bodies as a result.
Now uses html-to-text (already a transitive dep via mailparser,
promoted to direct) to convert the HTML part whenever present, which
correctly drops <head>/<style> content and decodes entities. Also
stopped trusting parsed.text unconditionally — some senders' plain-text
alternative isn't actually clean, so HTML is now the primary source
whenever available.
This commit is contained in:
1 parent
8844323cad
commit
87cf045715
4 files changed
+20
-2
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { ImapFlow } from "imapflow";
|
||||
import { simpleParser, type ParsedMail } from "mailparser";
|
||||
import { htmlToText } from "html-to-text";
|
||||
import { getMailboxSettings, saveMailboxSettings, disableMailboxConfig, type MailboxSettings } from "@/lib/mail/config";
|
||||
import { recordEmailInboundMessage } from "@/lib/tickets/service";
|
||||
import { saveAttachment } from "@/lib/attachments/storage";
|
||||
@@ -28,11 +29,16 @@ function createClient(settings: MailboxSettings): ImapFlow {
|
||||
});
|
||||
}
|
||||
|
||||
// Prefer converting the HTML part ourselves rather than trusting parsed.text
|
||||
// blindly — some senders (Outlook in particular) produce a text/plain
|
||||
// alternative that isn't actually clean, while html-to-text reliably drops
|
||||
// <head>/<style> content (e.g. Outlook's VML behavior: url(...) rules) and
|
||||
// decodes entities, which a naive tag-stripping regex cannot do.
|
||||
function extractBody(parsed: ParsedMail): string {
|
||||
if (parsed.text) return parsed.text;
|
||||
if (typeof parsed.html === "string") {
|
||||
return parsed.html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
||||
return htmlToText(parsed.html, { baseElements: { selectors: ["body"] }, wordwrap: false }).trim();
|
||||
}
|
||||
if (parsed.text) return parsed.text;
|
||||
return "(пустое письмо)";
|
||||
}
|
||||
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
declare module "html-to-text" {
|
||||
interface HtmlToTextOptions {
|
||||
wordwrap?: number | false;
|
||||
baseElements?: { selectors?: string[] };
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export function htmlToText(html: string, options?: HtmlToTextOptions): string;
|
||||
export function convert(html: string, options?: HtmlToTextOptions): string;
|
||||
}
|
||||
Reference in new issue
Block a user