@dendaio/n8n-nodes-collection
Version:
🚀 Comprehensive n8n node collection for financial analysis and automation. Features 55+ technical indicators (RSI, MACD, Bollinger Bands), 32+ candlestick patterns (Doji, Hammer, Engulfing), automated trading strategies, RSS feed processing, and OAuth2 v
355 lines • 17.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiRssFeedRead = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const rss_parser_1 = __importDefault(require("rss-parser"));
const axios_1 = __importDefault(require("axios"));
function validateURL(url) {
try {
const urlPattern = new RegExp('^(https?:\\/\\/)?' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
'((\\d{1,3}\\.){3}\\d{1,3}))' +
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
'(\\?[;&a-z\\d%_.~+=-]*)?' +
'(\\#[-a-z\\d_]*)?$', 'i');
return urlPattern.test(url);
}
catch (err) {
return false;
}
}
class MultiRssFeedRead {
constructor() {
this.description = {
displayName: 'Multi RSS Read',
name: 'multiRssFeedRead',
icon: 'fa:rss',
iconColor: 'orange-red',
group: ['input'],
version: [1],
description: 'Reads data from multiple RSS Feeds in parallel',
defaults: {
name: 'Multi RSS Read',
color: '#b02020',
},
usableAsTool: true,
inputs: ["main"],
outputs: ["main", "main"],
outputNames: ['Success', 'Error'],
properties: [
{
displayName: 'URLs',
name: 'urls',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
placeholder: 'Add RSS URL',
options: [
{
name: 'urlValues',
displayName: 'URL',
values: [
{
displayName: 'URL',
name: 'url',
type: 'string',
default: '',
required: true,
description: 'URL of the RSS feed',
placeholder: 'https://example.com/rss',
},
],
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
options: [
{
displayName: 'Continue On Error',
name: 'continueOnError',
type: 'boolean',
default: true,
description: 'Whether to continue processing other feeds if one fails',
},
{
displayName: 'Custom Fields',
name: 'customFields',
type: 'string',
default: '',
description: 'A comma-separated list of custom fields to include in the output. For example, "author, contentSnippet".',
},
{
displayName: 'Custom Headers',
name: 'customHeaders',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
placeholder: 'Add custom header',
description: 'Add custom HTTP headers to the request',
options: [
{
name: 'headerValues',
displayName: 'Header',
values: [
{
displayName: 'Header Name',
name: 'name',
type: 'string',
default: '',
required: true,
description: 'Name of the HTTP header',
placeholder: 'X-Custom-Header',
},
{
displayName: 'Header Value',
name: 'value',
type: 'string',
default: '',
required: true,
description: 'Value of the HTTP header',
placeholder: 'custom-value',
},
],
},
],
},
{
displayName: 'Ignore SSL Issues (Insecure)',
name: 'ignoreSSL',
type: 'boolean',
default: false,
description: 'Whether to ignore SSL/TLS certificate issues or not',
},
{
displayName: 'Include Source URL',
name: 'includeSourceUrl',
type: 'boolean',
default: true,
description: 'Whether to include the source URL in each item',
},
{
displayName: 'User Agents',
name: 'userAgents',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
placeholder: 'Add user agent',
description: 'Add multiple User-Agent strings (one will be randomly selected for each request)',
options: [
{
name: 'userAgentValues',
displayName: 'User Agent',
values: [
{
displayName: 'User Agent String',
name: 'userAgent',
type: 'string',
default: '',
required: true,
description: 'User-Agent string to use',
placeholder: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...',
},
],
},
],
},
],
},
],
};
}
async execute() {
var _a, _b;
const successData = [];
const errorData = [];
const items = this.getInputData();
for (let i = 0; i < items.length; i++) {
try {
const urlsData = this.getNodeParameter('urls', i);
const options = this.getNodeParameter('options', i);
const ignoreSSL = Boolean(options.ignoreSSL);
const continueOnError = Boolean((_a = options.continueOnError) !== null && _a !== void 0 ? _a : true);
const includeSourceUrl = Boolean((_b = options.includeSourceUrl) !== null && _b !== void 0 ? _b : true);
const urlValues = urlsData.urlValues || [];
const urls = urlValues.map((item) => item.url).filter(Boolean);
if (urls.length === 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one URL must be provided!', {
itemIndex: i,
});
}
const invalidUrls = urls.filter((url) => !validateURL(url));
if (invalidUrls.length > 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The following URLs are not valid: ${invalidUrls.join(', ')}`, {
itemIndex: i,
});
}
const parserOptions = {};
if (options.customFields) {
const customFields = options.customFields;
parserOptions.customFields = {
item: customFields.split(',').map((field) => field.trim()),
};
}
const fetchRssWithAxios = async (url) => {
try {
const userAgentsData = options.userAgents;
let availableUserAgents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
];
if (userAgentsData && userAgentsData.userAgentValues) {
const userAgentValues = userAgentsData.userAgentValues;
const customUserAgents = userAgentValues
.map((ua) => ua.userAgent)
.filter(Boolean);
if (customUserAgents.length > 0) {
availableUserAgents = customUserAgents;
}
}
const selectedUserAgent = availableUserAgents[Math.floor(Math.random() * availableUserAgents.length)];
const headers = {
Accept: 'application/rss+xml, application/rdf+xml;q=0.8, application/atom+xml;q=0.6, application/xml;q=0.4, text/xml;q=0.4, */*;q=0.1',
'User-Agent': selectedUserAgent,
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
DNT: '1',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
};
const customHeadersData = options.customHeaders;
if (customHeadersData && customHeadersData.headerValues) {
const headerValues = customHeadersData.headerValues;
for (const header of headerValues) {
if (header.name && header.value) {
headers[header.name] = header.value;
}
}
}
const axiosConfig = {
timeout: 30000,
headers,
httpsAgent: ignoreSSL
? new (require('https').Agent)({ rejectUnauthorized: false })
: undefined,
};
const response = await axios_1.default.get(url, axiosConfig);
if (response.status !== 200) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `HTTP ${response.status}: ${response.statusText}`, { itemIndex: i });
}
const parser = new rss_parser_1.default(parserOptions);
const feed = await parser.parseString(response.data);
if (feed.items) {
const feedItems = feed.items.map((item) => {
const json = { ...item };
if (includeSourceUrl) {
json.sourceUrl = url;
}
return { json };
});
return {
url,
items: feedItems,
};
}
return {
url,
items: [],
};
}
catch (error) {
if (continueOnError) {
let errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage.includes('Status code 403')) {
errorMessage =
'Access forbidden (403) - This site may be blocking automated requests. Try adding custom headers or contact the site administrator.';
}
else if (errorMessage.includes('Status code 429')) {
errorMessage = 'Too many requests (429) - Rate limit exceeded. Try again later.';
}
else if (errorMessage.includes('Status code 404')) {
errorMessage =
'RSS feed not found (404) - The URL may be incorrect or the feed may have been moved.';
}
else if (errorMessage.includes('ENOTFOUND')) {
errorMessage =
'Domain not found - Check if the URL is correct and the domain exists.';
}
else if (errorMessage.includes('ECONNREFUSED')) {
errorMessage =
'Connection refused - The server may be down or blocking connections.';
}
else if (errorMessage.includes('ETIMEDOUT')) {
errorMessage = 'Connection timeout - The server took too long to respond.';
}
else if (errorMessage.includes('Network Error')) {
errorMessage = 'Network error - Check your internet connection and try again.';
}
return {
url,
items: [],
error: errorMessage,
};
}
throw error;
}
};
const results = await Promise.all(urls.map(fetchRssWithAxios));
for (const result of results) {
if (result.error && !continueOnError) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to fetch RSS feed from ${result.url}: ${result.error}`, {
itemIndex: i,
});
}
if (result.error) {
errorData.push({
json: {
error: result.error,
sourceUrl: result.url,
message: `Failed to fetch RSS feed from ${result.url}`,
},
pairedItem: [{ item: i }],
});
}
else {
const itemData = [{ item: i }];
const executionData = this.helpers.constructExecutionMetaData(result.items, {
itemData,
});
successData.push(...executionData);
}
}
}
catch (error) {
if (this.continueOnFail()) {
errorData.push({
json: { error: error instanceof Error ? error.message : String(error) },
pairedItem: [{ item: i }],
});
continue;
}
throw error;
}
}
return [successData, errorData];
}
}
exports.MultiRssFeedRead = MultiRssFeedRead;
//# sourceMappingURL=MultiRssFeedRead.node.js.map