Flow gmail to telegram

Issue with Multi-line Email Content (Newline Preservation)
Hello everyone,
I'm working on a simple flow: Gmail Trigger → Gmail: Get a Message → Function → Telegram.
The core functionality works (email filtering and Telegram forwarding), but the content I receive on Telegram is always a single, long line, even if the original email had multiple paragraphs.

I suspect the issue is that one of my Gmail nodes is only providing the email's brief snippet (which is always a single line) instead of the full, decoded body that preserves newlines (\n).

i have a fuction node for preparing telegram message and chatid

Disable the “Simplify” Option in Gmail Node:
In both the Gmail Trigger and Gmail nodes, make sure the “Simplify” option is turned off. When “Simplify” is enabled, you only get a brief, single-line snippet. Disabling it will provide the full email content, including newlines and formatting, which you can then process and forward to Telegram.

The Telegram node supports newlines (\n) in messages. If your Function node receives the full email body (with newlines), you can pass it directly to Telegram, and the formatting should be preserved. If you still see a single line, double-check that your Function node is not stripping or replacing newlines.

If the email body is in HTML, you may need to convert <br> or <p> tags to \n before sending to Telegram, as Telegram expects plain text for newlines.

Shring your workflow here(withoud sesnsitive informations), some people here will get a look at the code nodes.

1 Like

The “Simplify” is the way, edit the fuction and now working.

I’m sharing the code, maybe it can be a starting point for someone, or someone more experienced can suggest some optimizations and/or errors

/**
 * Function Node: Email Parsing, Filtering, and Telegram Formatting
 *
 * This script processes incoming email data from the Gmail node, filters the sender,
 * cleans the email body, and formats the output for Telegram using MarkdownV2.
 *
 * Revision Date: 2025-11-13
 */

// --- 1. CONFIGURATION: SENDER MAPPING ---
// Maps verified sender email addresses to the corresponding Telegram Chat ID.
const senderMapping = {
  "[email protected]": "-1234567891234",
  "[email protected]": "-9876543219876",
  "[email protected]": "-147258369"
};

// --- 2. DATA EXTRACTION FROM INPUT JSON ---
// Extracts required data fields from the current n8n item, utilizing the updated JSON structure.

// Extracts the email subject. Uses fallback to "No Subject" if missing.
const subject = $json.subject || "No Subject"; 

// Extracts the clean sender email address for filtering purposes (e.g., "[email protected]").
// Uses optional chaining for robust access to the nested array structure.
const fromAddress = $json.from?.value?.[0]?.address || "unknown_address";

// Extracts the full display string of the sender (e.g., "User Name <[email protected]>")
const fromDisplay = $json.from?.text || "Unknown Sender";

// Extracts the date, prioritizing the ISO-formatted $json.date field.
const date = $json.date
  ? new Date($json.date).toISOString() 
  : new Date().toISOString(); // Fallback to current time if no date is found.

const maxChars = 1500;

// --- 3. BODY EXTRACTION AND CLEANUP ---

// Uses the already decoded multi-line text body ($json.text) provided by the Gmail node.
let textBodyRaw = $json.text || $json.snippet || ""; 

// Step 3.1: Clean up excessive text, typically removing long signatures or previous email replies.
// Removes everything after 5 or more consecutive empty/whitespace-only lines.
textBodyRaw = textBodyRaw.replace(/(\r?\n\s*){5,}.*$/s, '');

// Step 3.2: Remove any residual HTML tags (for robustness).
textBodyRaw = textBodyRaw.replace(/<[^>]*>/g, '');

// Step 3.3: Normalize line spacing: ensures no more than two newlines are present consecutively, 
// preserving paragraphs while eliminating large vertical whitespace blocks.
textBodyRaw = textBodyRaw.replace(/(\r?\n\s*){3,}/g, '\n\n');

// --- 4. MARKDOWNV2 ESCAPING UTILITY ---

/**
 * Escapes all special characters required by Telegram's MarkdownV2 syntax.
 * Optionally converts single newlines to double newlines to force paragraph breaks.
 * @param {string} text - The raw text string.
 * @param {boolean} preserveNewlines - If true, converts '\n' to '\n\n'.
 * @returns {string} The escaped string.
 */
function escapeMarkdownV2(text, preserveNewlines = false) {
  if (!text) return "";
  
  // Escapes all known MarkdownV2 special characters
  let escapedText = text
    .replace(/\\/g, "\\\\")
    .replace(/\*/g, "\\*")
    .replace(/_/g, "\\_")
    .replace(/\[/g, "\\[")
    .replace(/\]/g, "\\]")
    .replace(/\(/g, "\\(")
    .replace(/\)/g, "\\)")
    .replace(/~/g, "\\~")
    .replace(/`/g, "\\`")
    .replace(/>/g, "\\>")
    .replace(/#/g, "\\#")
    .replace(/\+/g, "\\+")
    .replace(/-/g, "\\-")
    .replace(/=/g, "\\=")
    .replace(/\|/g, "\\|")
    .replace(/\{/g, "\\{")
    .replace(/\}/g, "\\}")
    .replace(/\./g, "\\.")
    .replace(/!/g, "\\!");

  // If preserving newlines, replace single breaks with double breaks for Telegram paragraphs
  if (preserveNewlines) {
    escapedText = escapedText.replace(/(\r?\n)/g, '\n\n');
    escapedText = escapedText.trim();
  }
  
  return escapedText;
}

// Prepares the body: limits characters, applies escaping, and formats newlines.
const textBodySegment = textBodyRaw.substring(0, maxChars);
const textBody = escapeMarkdownV2(textBodySegment, true) + (textBodyRaw.length > maxChars ? "\\.\\.\\." : "");


// --- 5. SENDER FILTERING AND MESSAGE CONSTRUCTION ---
let targetChatId = "";
let foundSender = "";

// Check if the extracted sender address matches any configured entry
for (const [sender, chatId] of Object.entries(senderMapping)) {
  if (fromAddress.toLowerCase().includes(sender.toLowerCase())) {
    targetChatId = chatId;
    foundSender = fromDisplay; // Use the display name for the Telegram message
    break;
  }
}

// Proceed only if a target Chat ID was found
if (targetChatId) {
  // Construct the final message text using MarkdownV2 syntax
  const messageText = `
📧 \\*NEW EMAIL FROM ${escapeMarkdownV2(foundSender, false)}\\*

🔍 \\*Subject:\\* ${escapeMarkdownV2(subject, false)}
👤 \\*From:\\* ${escapeMarkdownV2(fromDisplay, false)}
📅 \\*Data:\\* ${escapeMarkdownV2(new Date(date).toLocaleString('it-IT', {
    timeZone: 'Europe/Rome',
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
}), false)}
💬 \\*Content:\\*
${textBody}`;

  let attachments = "";
  if ($json.attachments && $json.attachments.length > 0) {
    attachments = $json.attachments.map(a => escapeMarkdownV2(a.filename || "No Name", false)).join(", ");
  }

  // Return the item with chatId and formatted text for the Telegram node
  return {
    json: {
      chatId: targetChatId,
      text: attachments
        ? messageText + `\n📎 \\*Attachments:\\* ${attachments}`
        : messageText,
      parse_mode: "MarkdownV2"
    }
  };
} else {
  // If no sender matches, discard the item silently to prevent Telegram 400 error.
  console.log(`Email discarded - sender not in list: ${fromAddress}`);
  return { json: { filtered: true } }; 
}
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.