UNPKG

n8n-nodes-duckduckgo-search

Version:

AI Agent-ready n8n community node for DuckDuckGo search. Search the web, images, news, and videos with no API key required and no outbound telemetry. Optionally fetch and extract the main text of result pages or any URL, and get DuckDuckGo Instant Answers

421 lines (420 loc) 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OPERATOR_INFO = exports.SearchOperator = void 0; exports.buildSearchQuery = buildSearchQuery; exports.parseSearchOperators = parseSearchOperators; exports.validateSearchOperators = validateSearchOperators; var SearchOperator; (function (SearchOperator) { SearchOperator["Site"] = "site:"; SearchOperator["Filetype"] = "filetype:"; SearchOperator["Intitle"] = "intitle:"; SearchOperator["Inurl"] = "inurl:"; SearchOperator["InBody"] = "inbody:"; SearchOperator["Cache"] = "cache:"; SearchOperator["Define"] = "define:"; SearchOperator["Related"] = "related:"; SearchOperator["Link"] = "link:"; SearchOperator["Ext"] = "ext:"; SearchOperator["Location"] = "location:"; SearchOperator["Region"] = "region:"; SearchOperator["Language"] = "language:"; SearchOperator["Feed"] = "feed:"; SearchOperator["HasFeed"] = "hasfeed:"; SearchOperator["IPAddress"] = "ip:"; SearchOperator["Prefer"] = "prefer:"; SearchOperator["AllInTitle"] = "allintitle:"; SearchOperator["AllInUrl"] = "allinurl:"; SearchOperator["AllInText"] = "allintext:"; SearchOperator["AllInAnchor"] = "allinanchor:"; })(SearchOperator || (exports.SearchOperator = SearchOperator = {})); exports.OPERATOR_INFO = { site: { description: 'Search within a specific website or domain. Note: site:duckduckgo.com returns no results (DDG does not index itself)', example: 'site:example.com', placeholder: 'example.com', }, filetype: { description: 'Search for specific file types', example: 'filetype:pdf', placeholder: 'pdf, doc, xls, ppt, etc.', }, intitle: { description: 'Search for pages with specific words in the title', example: 'intitle:important', placeholder: 'words to find in title', }, inurl: { description: 'Search for pages with specific words in the URL', example: 'inurl:api', placeholder: 'words to find in URL', }, inbody: { description: 'Search for pages with specific words in the body text', example: 'inbody:content', placeholder: 'words to find in body', }, cache: { description: 'View the cached version of a webpage', example: 'cache:example.com', placeholder: 'example.com', }, define: { description: 'Get definitions for a word or phrase', example: 'define:quantum', placeholder: 'word to define', }, related: { description: 'Find websites related to a specific URL', example: 'related:example.com', placeholder: 'example.com', }, link: { description: 'Find pages that link to a specific URL', example: 'link:example.com', placeholder: 'example.com', }, ext: { description: 'Search for specific file extensions (alias for filetype)', example: 'ext:pdf', placeholder: 'pdf, doc, etc.', }, location: { description: 'Search for results from a specific location', example: 'location:"New York"', placeholder: 'city or country', }, region: { description: 'Search for results from a specific region', example: 'region:us', placeholder: 'country code', }, language: { description: 'Search for results in a specific language', example: 'language:en', placeholder: 'language code', }, feed: { description: 'Find RSS or Atom feeds', example: 'feed:blog', placeholder: 'feed keyword', }, hasfeed: { description: 'Find pages that have RSS or Atom feeds', example: 'hasfeed:news', placeholder: 'keyword', }, ip: { description: 'Search for information about an IP address', example: 'ip:192.168.1.1', placeholder: 'IP address', }, prefer: { description: 'Prefer newer or older results', example: 'prefer:newer', placeholder: 'newer or older', }, allintitle: { description: 'All words must appear in the title', example: 'allintitle:important news', placeholder: 'all words for title', }, allinurl: { description: 'All words must appear in the URL', example: 'allinurl:api docs', placeholder: 'all words for URL', }, allintext: { description: 'All words must appear in the text', example: 'allintext:complete guide', placeholder: 'all words for text', }, allinanchor: { description: 'All words must appear in anchor text', example: 'allinanchor:click here', placeholder: 'all words for anchors', }, exclude: { description: 'Exclude results containing specific words (use - prefix)', example: '-unwanted', placeholder: 'words to exclude', }, exact: { description: 'Search for exact phrase (use quotes)', example: '"exact phrase"', placeholder: 'exact phrase to match', }, or: { description: 'Search for any of the specified terms (use OR)', example: 'term1 OR term2', placeholder: 'alternative terms', }, }; function buildSearchQuery(baseQuery, operators) { if (!operators || Object.keys(operators).length === 0) { return baseQuery; } const queryParts = []; if (baseQuery.trim()) { queryParts.push(baseQuery); } if (operators.site) { queryParts.push(`site:${operators.site}`); } if (operators.filetype) { queryParts.push(`filetype:${operators.filetype}`); } if (operators.intitle) { queryParts.push(`intitle:${operators.intitle}`); } if (operators.inurl) { queryParts.push(`inurl:${operators.inurl}`); } if (operators.inbody) { queryParts.push(`inbody:${operators.inbody}`); } if (operators.cache) { queryParts.push(`cache:${operators.cache}`); } if (operators.define) { queryParts.push(`define:${operators.define}`); } if (operators.related) { queryParts.push(`related:${operators.related}`); } if (operators.link) { queryParts.push(`link:${operators.link}`); } if (operators.ext) { queryParts.push(`ext:${operators.ext}`); } if (operators.location) { const location = operators.location.includes(' ') ? `"${operators.location}"` : operators.location; queryParts.push(`location:${location}`); } if (operators.region) { queryParts.push(`region:${operators.region}`); } if (operators.language) { queryParts.push(`language:${operators.language}`); } if (operators.feed) { queryParts.push(`feed:${operators.feed}`); } if (operators.hasfeed) { queryParts.push(`hasfeed:${operators.hasfeed}`); } if (operators.ip) { queryParts.push(`ip:${operators.ip}`); } if (operators.prefer) { queryParts.push(`prefer:${operators.prefer}`); } if (operators.allintitle) { queryParts.push(`allintitle:${operators.allintitle}`); } if (operators.allinurl) { queryParts.push(`allinurl:${operators.allinurl}`); } if (operators.allintext) { queryParts.push(`allintext:${operators.allintext}`); } if (operators.allinanchor) { queryParts.push(`allinanchor:${operators.allinanchor}`); } if (operators.exclude) { const exclusions = operators.exclude.split(',').map(e => e.trim()); exclusions.forEach(exclusion => { if (exclusion && !exclusion.startsWith('-')) { queryParts.push(`-${exclusion}`); } else if (exclusion) { queryParts.push(exclusion); } }); } if (operators.exact) { const exactPhrase = operators.exact.trim(); if (exactPhrase && !exactPhrase.startsWith('"')) { queryParts.push(`"${exactPhrase}"`); } else if (exactPhrase) { queryParts.push(exactPhrase); } } if (operators.or && operators.or.length > 0) { const orQuery = operators.or.filter(term => term.trim()).join(' OR '); if (orQuery) { queryParts.push(`(${orQuery})`); } } return queryParts.join(' '); } function parseSearchOperators(query) { const operators = {}; const removals = []; const markForRemoval = (startIndex, length) => { removals.push({ start: startIndex, end: startIndex + length }); }; const operatorRegex = /(\w+):("(?:[^"\\]|\\.)*"|\S+)/g; let match; while ((match = operatorRegex.exec(query)) !== null) { if (!match || !match[1] || !match[2]) continue; const [fullMatch, operator, value] = match; const cleanValue = value.replace(/^"|"$/g, ''); switch (operator.toLowerCase()) { case 'site': operators.site = cleanValue; break; case 'filetype': operators.filetype = cleanValue; break; case 'intitle': operators.intitle = cleanValue; break; case 'inurl': operators.inurl = cleanValue; break; case 'inbody': operators.inbody = cleanValue; break; case 'cache': operators.cache = cleanValue; break; case 'define': operators.define = cleanValue; break; case 'related': operators.related = cleanValue; break; case 'link': operators.link = cleanValue; break; case 'ext': operators.ext = cleanValue; break; case 'location': operators.location = cleanValue; break; case 'region': operators.region = cleanValue; break; case 'language': operators.language = cleanValue; break; case 'feed': operators.feed = cleanValue; break; case 'hasfeed': operators.hasfeed = cleanValue; break; case 'ip': operators.ip = cleanValue; break; case 'prefer': operators.prefer = cleanValue; break; case 'allintitle': operators.allintitle = cleanValue; break; case 'allinurl': operators.allinurl = cleanValue; break; case 'allintext': operators.allintext = cleanValue; break; case 'allinanchor': operators.allinanchor = cleanValue; break; } if (match.index !== undefined) { markForRemoval(match.index, fullMatch.length); } } const exclusions = []; const exclusionRegex = /(?:^|\s)-(\w+)(?=\s|$)/g; while ((match = exclusionRegex.exec(query)) !== null) { if (match && match[1] && match.index !== undefined) { exclusions.push(match[1]); markForRemoval(match.index, match[0].length); } } if (exclusions.length > 0) { operators.exclude = exclusions.join(', '); } const exactPhrases = []; const exactRegex = /"([^"]+)"/g; while ((match = exactRegex.exec(query)) !== null) { if (match && match[1] && match.index !== undefined) { const beforeIndex = match.index - 1; if (beforeIndex < 0 || query[beforeIndex] !== ':') { exactPhrases.push(match[1]); markForRemoval(match.index, match[0].length); } } } if (exactPhrases.length > 0) { operators.exact = exactPhrases.join(' '); } const orTerms = []; const orRegex = /(\w+)\s+OR\s+(\w+)/g; while ((match = orRegex.exec(query)) !== null) { if (match && match[1] && match[2] && match.index !== undefined) { orTerms.push(match[1], match[2]); markForRemoval(match.index, match[0].length); } } if (orTerms.length > 0) { operators.or = [...new Set(orTerms)]; } removals.sort((a, b) => b.start - a.start); const queryChars = query.split(''); removals.forEach(removal => { queryChars.splice(removal.start, removal.end - removal.start); }); let baseQuery = queryChars.join(''); baseQuery = baseQuery.replace(/\s+/g, ' ').trim(); return { baseQuery, operators }; } function validateSearchOperators(operators) { const errors = []; if (operators.filetype) { const validFileTypes = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'rtf', 'odt', 'html', 'htm', 'xml', 'json', 'csv', 'zip', 'rar', '7z', 'tar', 'gz']; if (!validFileTypes.includes(operators.filetype.toLowerCase())) { errors.push(`Invalid filetype: ${operators.filetype}. Valid types: ${validFileTypes.join(', ')}`); } } if (operators.region) { const validRegions = ['us', 'uk', 'ca', 'au', 'de', 'fr', 'es', 'it', 'nl', 'br', 'mx', 'ar', 'in', 'jp', 'cn', 'kr', 'ru']; if (!validRegions.includes(operators.region.toLowerCase())) { errors.push(`Invalid region: ${operators.region}. Valid regions: ${validRegions.join(', ')}`); } } if (operators.language) { const validLanguages = ['en', 'es', 'fr', 'de', 'it', 'pt', 'nl', 'ru', 'ja', 'zh', 'ko', 'ar', 'hi', 'tr', 'pl', 'sv', 'da', 'no', 'fi']; if (!validLanguages.includes(operators.language.toLowerCase())) { errors.push(`Invalid language: ${operators.language}. Valid languages: ${validLanguages.join(', ')}`); } } if (operators.prefer && !['newer', 'older'].includes(operators.prefer.toLowerCase())) { errors.push(`Invalid prefer value: ${operators.prefer}. Use 'newer' or 'older'`); } if (operators.ip) { const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; if (!ipRegex.test(operators.ip)) { errors.push(`Invalid IP address format: ${operators.ip}`); } else { const octets = operators.ip.split('.'); const invalidOctets = octets.filter(octet => { const num = parseInt(octet, 10); return num > 255; }); if (invalidOctets.length > 0) { errors.push(`Invalid IP address format: ${operators.ip}`); } } } return errors; }