Skip to content

Commit 2bc16f2

Browse files
author
Derek Zen
committed
feat: add custom labels support, limit parameters, and enhanced email fields
- Add custom labels support in search operations and label management - Add limit parameters to 'Get Many' operations for emails and mailboxes - Add enhanced email fields with structured output (title, from, to, cc, bcc, content) - Add new 'Manage Labels' operation for custom label management - Enhance 'Set Flags' operation with custom labels support - Rename node to 'IMAP Enhanced' to avoid conflicts with original package - Update package name to 'n8n-nodes-imap-enhanced' - Add simplified HTML content conversion - Improve performance with limit parameters
1 parent 8f1cfc4 commit 2bc16f2

File tree

11 files changed

+1241
-1503
lines changed

11 files changed

+1241
-1503
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
## [2.11.1](https://github.com/callzhang/n8n-nodes-imap/compare/v2.11.0...v2.11.1) (2025-01-XX)
2+
3+
### Bug Fixes
4+
5+
* **Node Name Conflict**: Fixed node name conflict with original IMAP package by changing node name to "IMAP Enhanced" and internal name to "imapEnhanced"
6+
7+
## [2.11.0](https://github.com/callzhang/n8n-nodes-imap/compare/v2.10.0...v2.11.0) (2025-01-XX)
8+
9+
### Package Rename
10+
* **Package Name**: Renamed from `n8n-nodes-imap` to `n8n-nodes-imap-enhanced` to avoid conflicts with the original package
11+
12+
### Features
13+
14+
* **Enhanced Email Fields**: Added structured email fields including title, from, to, cc, bcc, labels, content (text and HTML)
15+
* **Custom Labels Support**: Added support for custom labels in search operations and label management
16+
* **Limit Parameters**: Added limit parameters to "Get Many" operations for both emails and mailboxes
17+
* **New Manage Labels Operation**: Added dedicated operation for managing custom labels (add, remove, set)
18+
* **Simplified HTML Content**: Added automatic conversion of text content to simplified HTML format
19+
* **Enhanced Set Flags**: Extended existing Set Flags operation to support custom labels alongside system flags
20+
21+
### Improvements
22+
23+
* **Performance**: Limit parameters prevent excessive data fetching
24+
* **User Experience**: Structured email fields provide better data organization
25+
* **Flexibility**: Custom labels support advanced email organization and searching
26+
127
## [2.10.0](https://github.com/umanamente/n8n-nodes-imap/compare/v2.9.0...v2.10.0) (2025-06-22)
228

329
### Features

README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# <img src="nodes/Imap/node-imap-icon.svg" height="40"> n8n-nodes-imap
22

3-
This is an n8n community node that adds support for [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol) email servers.
3+
This is an enhanced n8n community node that adds support for [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol) email servers with advanced features including custom labels, limit parameters, and structured email fields.
44

55
* [Installation](#installation)
66
* [Operations](#operations)
@@ -11,30 +11,48 @@ This is an n8n community node that adds support for [IMAP](https://en.wikipedia.
1111

1212
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
1313

14-
Use `n8n-nodes-imap` in N8N settings to install the stable version.
14+
Use `n8n-nodes-imap-enhanced` in N8N settings to install the stable version.
1515

16-
To install beta version, use `n8n-nodes-imap@beta`.
16+
To install beta version, use `n8n-nodes-imap-enhanced@beta`.
1717

18-
NPMJS: [n8n-nodes-imap](https://www.npmjs.com/package/n8n-nodes-imap)
18+
NPMJS: [n8n-nodes-imap-enhanced](https://www.npmjs.com/package/n8n-nodes-imap-enhanced)
1919

2020

2121
## Operations
2222

2323
* Mailbox
24-
* Get list of mailboxes (including status information like number of messages)
24+
* Get list of mailboxes (including status information like number of messages) **with limit parameter**
2525
* Get status of a mailbox (number of messages, etc.)
2626
* Create a mailbox
2727
* Rename a mailbox
2828
* ~Delete a mailbox~ (disabled due to danger of accidental data loss and no apparent use case)
2929
* Email
30-
* Get list of emails in a mailbox
30+
* Get list of emails in a mailbox **with limit parameter and enhanced structured fields**
3131
* Download attachments from an email
3232
* Move an email to another mailbox
3333
* Copy an email into another mailbox
34-
* Set/remove flags on an email ("seen", "answered", "flagged", "deleted", "draft")
34+
* Set/remove flags on an email ("seen", "answered", "flagged", "deleted", "draft") **with custom labels support**
35+
* **Manage custom labels** (add, remove, set custom labels/keywords)
3536
* Create email draft in a mailbox
3637
* Use `n8n-nodes-eml` node to create complex emails. It supports attachments and other features.
3738

39+
## New Features
40+
41+
### Enhanced Email Fields
42+
- **Structured Output**: Returns title, from, to, cc, bcc, labels, content (text and HTML)
43+
- **Simplified HTML**: Converts text content to simplified HTML format
44+
- **All Email Metadata**: Includes date, messageId, size, and other envelope fields
45+
46+
### Custom Labels Support
47+
- **Search by Custom Labels**: Search emails using custom labels/keywords
48+
- **Label Management**: Add, remove, or set custom labels on emails
49+
- **Flexible Format**: Support for `labelName:labelValue` format (e.g., "Priority:High")
50+
51+
### Limit Parameters
52+
- **Email List Limit**: Control maximum number of emails returned
53+
- **Mailbox List Limit**: Control maximum number of mailboxes returned
54+
- **Performance Optimization**: Prevents excessive data fetching
55+
3856
## Credentials
3957

4058
Currently, this node supports only basic authentication (username and password).

nodes/Imap/Imap.node.ts renamed to nodes/Imap/ImapEnhanced.node.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ import { loadMailboxList } from './utils/SearchFieldParameters';
88
import { CREDENTIALS_TYPE_CORE_IMAP_ACCOUNT, CREDENTIALS_TYPE_THIS_NODE, credentialNames, getImapCredentials } from './utils/CredentialsSelector';
99

1010

11-
export class Imap implements INodeType {
11+
export class ImapEnhanced implements INodeType {
1212
description: INodeTypeDescription = {
13-
displayName: 'IMAP',
14-
name: 'imap',
13+
displayName: 'IMAP Enhanced',
14+
name: 'imapEnhanced',
1515
icon: 'file:node-imap-icon.svg',
1616
group: ['transform'],
1717
version: 1,
1818
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
19-
description: 'Retrieve emails via IMAP',
19+
description: 'Enhanced IMAP node with custom labels, limit parameters, and structured email fields',
2020
defaults: {
21-
name: 'IMAP',
21+
name: 'IMAP Enhanced',
2222
},
2323
inputs: [NodeConnectionType.Main],
2424
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong

nodes/Imap/operations/email/OperationsList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { downloadAttachmentOperation } from './functions/EmailDownloadAttachment
77
import { getEmailsListOperation } from './functions/EmailGetList';
88
import { moveEmailOperation } from './functions/EmailMove';
99
import { setEmailFlagsOperation } from './functions/EmailSetFlags';
10+
import { manageEmailLabelsOperation } from './functions/EmailManageLabels';
1011

1112
export const emailResourceDefinitions: IResourceDef = {
1213
resource: resourceEmail,
@@ -18,5 +19,6 @@ export const emailResourceDefinitions: IResourceDef = {
1819
copyEmailOperation,
1920
setEmailFlagsOperation,
2021
createDraftOperation,
22+
manageEmailLabelsOperation,
2123
],
2224
};

nodes/Imap/operations/email/functions/EmailGetList.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,31 @@ function streamToString(stream: Readable): Promise<string> {
3535
});
3636
}
3737

38+
function textToSimplifiedHtml(text: string): string {
39+
if (!text) return '';
40+
41+
// Convert text to simplified HTML
42+
return text
43+
.replace(/&/g, '&amp;')
44+
.replace(/</g, '&lt;')
45+
.replace(/>/g, '&gt;')
46+
.replace(/\n/g, '<br>')
47+
.replace(/\r\n/g, '<br>')
48+
.replace(/\r/g, '<br>');
49+
}
50+
51+
function formatEmailAddresses(addresses: any[]): string[] {
52+
if (!addresses || !Array.isArray(addresses)) return [];
53+
54+
return addresses.map(addr => {
55+
if (typeof addr === 'string') return addr;
56+
if (addr.name && addr.address) {
57+
return `${addr.name} <${addr.address}>`;
58+
}
59+
return addr.address || '';
60+
}).filter(addr => addr);
61+
}
62+
3863

3964
export const getEmailsListOperation: IResourceOperationDef = {
4065
operation: {
@@ -116,6 +141,24 @@ export const getEmailsListOperation: IResourceOperationDef = {
116141
],
117142
},
118143
},
144+
},
145+
{
146+
displayName: 'Limit',
147+
name: 'limit',
148+
type: 'number',
149+
typeOptions: {
150+
minValue: 1,
151+
},
152+
default: 50,
153+
description: 'Max number of results to return',
154+
placeholder: '100',
155+
},
156+
{
157+
displayName: 'Enhanced Email Fields',
158+
name: 'enhancedFields',
159+
type: 'boolean',
160+
default: true,
161+
description: 'Whether to return structured email fields (title, from, to, cc, bcc, labels, content)',
119162
}
120163
],
121164
async executeImapAction(context: IExecuteFunctions, itemIndex: number, client: ImapFlow): Promise<INodeExecutionData[] | null> {
@@ -175,15 +218,28 @@ export const getEmailsListOperation: IResourceOperationDef = {
175218
context.logger?.debug(`Search object: ${JSON.stringify(searchObject)}`);
176219
context.logger?.debug(`Fetch query: ${JSON.stringify(fetchQuery)}`);
177220

221+
// get limit parameter
222+
const limit = context.getNodeParameter('limit', itemIndex) as number;
223+
178224
// wait for all emails to be fetched before processing them
179225
// because we might need to fetch the body parts for each email,
180226
// and this will freeze the client if we do it in parallel
181227
const emailsList: FetchMessageObject[] = [];
228+
let count = 0;
182229
for await (let email of client.fetch(searchObject, fetchQuery)) {
183230
emailsList.push(email);
231+
count++;
232+
// apply limit if specified
233+
if (limit > 0 && count >= limit) {
234+
context.logger?.info(`Reached limit of ${limit} emails, stopping fetch`);
235+
break;
236+
}
184237
}
185238
context.logger?.info(`Found ${emailsList.length} emails`);
186239

240+
// get enhanced fields parameter
241+
const enhancedFields = context.getNodeParameter('enhancedFields', itemIndex) as boolean;
242+
187243
// process the emails
188244
for (const email of emailsList) {
189245
context.logger?.info(` ${email.uid}`);
@@ -192,6 +248,33 @@ export const getEmailsListOperation: IResourceOperationDef = {
192248
// add mailbox path to the item
193249
item_json.mailboxPath = mailboxPath;
194250

251+
// add enhanced email fields if requested
252+
if (enhancedFields) {
253+
// Extract structured fields from envelope
254+
if (email.envelope) {
255+
item_json.title = email.envelope.subject || '';
256+
item_json.from = formatEmailAddresses(email.envelope.from || []);
257+
item_json.to = formatEmailAddresses(email.envelope.to || []);
258+
item_json.cc = formatEmailAddresses(email.envelope.cc || []);
259+
item_json.bcc = formatEmailAddresses(email.envelope.bcc || []);
260+
item_json.replyTo = formatEmailAddresses(email.envelope.replyTo || []);
261+
item_json.date = email.envelope.date;
262+
item_json.messageId = email.envelope.messageId;
263+
item_json.inReplyTo = email.envelope.inReplyTo;
264+
// item_json.references = email.envelope.references; // references property not available in envelope
265+
}
266+
267+
// Extract labels/flags
268+
if (email.flags) {
269+
item_json.labels = email.flags;
270+
}
271+
272+
// Add size information
273+
if (email.size) {
274+
item_json.size = email.size;
275+
}
276+
}
277+
195278
// process the headers
196279
if (includeParts.includes(EmailParts.Headers)) {
197280
if (email.headers) {
@@ -239,7 +322,7 @@ export const getEmailsListOperation: IResourceOperationDef = {
239322
} else {
240323
// if there is only one part, to sometimes it has no partId
241324
// in that case, ImapFlow uses "TEXT" as partId to download the only part
242-
if (partInfo.type === 'text/plain') {
325+
if (partInfo.type === 'text/plain') {
243326
textPartId = partInfo.partId || "TEXT";
244327
}
245328
if (partInfo.type === 'text/html') {
@@ -265,6 +348,12 @@ export const getEmailsListOperation: IResourceOperationDef = {
265348
});
266349
if (textContent.content) {
267350
item_json.textContent = await streamToString(textContent.content);
351+
352+
// if enhanced fields are enabled, also provide simplified HTML version
353+
if (enhancedFields) {
354+
item_json.contentText = item_json.textContent;
355+
item_json.contentHtml = textToSimplifiedHtml(item_json.textContent);
356+
}
268357
}
269358
}
270359
}
@@ -277,9 +366,25 @@ export const getEmailsListOperation: IResourceOperationDef = {
277366
});
278367
if (htmlContent.content) {
279368
item_json.htmlContent = await streamToString(htmlContent.content);
369+
370+
// if enhanced fields are enabled, also provide the HTML content
371+
if (enhancedFields) {
372+
item_json.contentHtml = item_json.htmlContent;
373+
// if we don't have text content, create simplified HTML from the HTML content
374+
if (!item_json.contentText) {
375+
// Strip HTML tags for text version
376+
item_json.contentText = item_json.htmlContent.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ').trim();
377+
}
378+
}
280379
}
281380
}
282381
}
382+
383+
// if enhanced fields are enabled but no content was found, set empty values
384+
if (enhancedFields && !item_json.contentText && !item_json.contentHtml) {
385+
item_json.contentText = '';
386+
item_json.contentHtml = '';
387+
}
283388
}
284389

285390
returnData.push({

0 commit comments

Comments
 (0)