n8n-nodes-hdw
Version:
n8n node to access Horizon Data Wave API services
513 lines • 24.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.HdwTwitter = void 0;
class HdwTwitter {
constructor() {
this.description = {
displayName: 'HDW Twitter',
name: 'hdwTwitter',
icon: 'file:hdw_logo.png',
group: ['transform'],
version: 1,
usableAsTool: true,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Integrate with Horizon Data Wave Twitter (X) API',
defaults: {
name: 'HDW Twitter',
},
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'hdwLinkedinApi',
required: true,
},
],
requestDefaults: {
baseURL: 'https://api.horizondatawave.ai',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{ name: 'User', value: 'user' },
{ name: 'Search', value: 'search' },
],
default: 'user',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: { show: { resource: ['user'] } },
options: [
{
name: 'Get Profile',
value: 'getProfile',
description: 'Get Twitter user profile',
action: 'Get twitter user profile',
routing: {
request: {
method: 'POST',
url: '/api/twitter/user',
body: {
user: '={{$parameter["user"]}}',
timeout: '={{$parameter["timeout"]}}',
},
},
},
},
{
name: 'Get Posts',
value: 'getPosts',
description: 'Get Twitter user posts',
action: 'Get twitter user posts',
routing: {
request: {
method: 'POST',
url: '/api/twitter/user/posts',
body: {
user: '={{$parameter["user"]}}',
count: '={{$parameter["count"]}}',
timeout: '={{$parameter["timeout"]}}',
},
},
},
},
],
default: 'getProfile',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: { show: { resource: ['search'] } },
options: [
{
name: 'Search Users',
value: 'searchUsers',
description: 'Search for Twitter users',
action: 'Search for twitter users',
routing: {
request: {
method: 'POST',
url: '/api/twitter/search/users',
body: {
query: '={{$parameter["query"]}}',
count: '={{$parameter["count"]}}',
timeout: '={{$parameter["timeout"]}}',
},
},
},
},
{
name: 'Search Posts',
value: 'searchPosts',
description: 'Search for Twitter posts (tweets)',
action: 'Search for twitter posts',
routing: {
request: {
method: 'POST',
url: '/api/twitter/search/posts',
body: {
query: '={{$parameter["query"]}}',
count: '={{$parameter["count"]}}',
timeout: '={{$parameter["timeout"]}}',
},
},
},
},
],
default: 'searchUsers',
},
{
displayName: 'User',
name: 'user',
type: 'string',
required: true,
default: '',
placeholder: 'elonmusk',
description: 'Twitter username, handle, or URL',
displayOptions: { show: { resource: ['user'] } },
},
{
displayName: 'Count',
name: 'count',
type: 'number',
default: 10,
description: 'Maximum number of posts to return',
displayOptions: { show: { resource: ['user'], operation: ['getPosts'] } },
},
{
displayName: 'Query',
name: 'query',
type: 'string',
required: true,
default: '',
placeholder: 'crypto OR bitcoin',
description: 'Search query',
displayOptions: { show: { resource: ['search'] } },
},
{
displayName: 'Count',
name: 'count',
type: 'number',
default: 20,
description: 'Maximum number of results to return',
displayOptions: { show: { resource: ['search'] } },
},
{
displayName: 'Advanced Search Options',
name: 'advancedOptions',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: { show: { resource: ['search'], operation: ['searchPosts'] } },
options: [
{
displayName: 'Exact Phrase',
name: 'exact_phrase',
type: 'string',
default: '',
description: 'Tweets containing this exact phrase',
routing: { request: { body: { exact_phrase: '={{$value}}' } } },
},
{
displayName: 'Any of These Words',
name: 'any_of_these_words',
type: 'string',
default: '',
description: 'Tweets containing any of these words (comma-separated)',
routing: {
send: {
type: 'body',
property: 'any_of_these_words',
value: '={{ $value.split(",").map(item => item.trim()) }}',
},
},
},
{
displayName: 'None of These Words',
name: 'none_of_these_words',
type: 'string',
default: '',
description: 'Tweets containing none of these words (comma-separated)',
routing: {
send: {
type: 'body',
property: 'none_of_these_words',
value: '={{ $value.split(",").map(item => item.trim()) }}',
},
},
},
{
displayName: 'These Hashtags',
name: 'these_hashtags',
type: 'string',
default: '',
description: 'Tweets containing these hashtags (comma-separated, without #)',
routing: {
send: {
type: 'body',
property: 'these_hashtags',
value: '={{ $value.split(",").map(item => item.trim()) }}',
},
},
},
{
displayName: 'Language',
name: 'language',
type: 'options',
options: [
{ name: 'English', value: 'English' },
{ name: 'Arabic', value: 'Arabic' },
{ name: 'Chinese', value: 'Simplified Chinese' },
{ name: 'French', value: 'French' },
{ name: 'German', value: 'German' },
{ name: 'Hindi', value: 'Hindi' },
{ name: 'Japanese', value: 'Japanese' },
{ name: 'Korean', value: 'Korean' },
{ name: 'Portuguese', value: 'Portuguese' },
{ name: 'Russian', value: 'Russian' },
{ name: 'Spanish', value: 'Spanish' },
],
default: 'English',
description: 'Language of tweets to search for',
routing: { request: { body: { language: '={{$value}}' } } },
},
{
displayName: 'From These Accounts',
name: 'from_these_accounts',
type: 'string',
default: '',
description: 'Tweets from these accounts (comma-separated usernames)',
routing: {
send: {
type: 'body',
property: 'from_these_accounts',
value: '={{ $value.split(",").map(item => item.trim()) }}',
},
},
},
{
displayName: 'To These Accounts',
name: 'to_these_accounts',
type: 'string',
default: '',
description: 'Tweets to these accounts (comma-separated usernames)',
routing: {
send: {
type: 'body',
property: 'to_these_accounts',
value: '={{ $value.split(",").map(item => item.trim()) }}',
},
},
},
{
displayName: 'Mentioning These Accounts',
name: 'mentioning_these_accounts',
type: 'string',
default: '',
description: 'Tweets mentioning these accounts (comma-separated, with @)',
routing: {
send: {
type: 'body',
property: 'mentioning_these_accounts',
value: '={{ $value.split(",").map(item => item.trim()) }}',
},
},
},
{
displayName: 'Minimum Replies',
name: 'min_replies',
type: 'number',
default: 0,
description: 'Minimum number of replies',
routing: { request: { body: { min_replies: '={{$value}}' } } },
},
{
displayName: 'Minimum Likes',
name: 'min_likes',
type: 'number',
default: 0,
description: 'Minimum number of likes',
routing: { request: { body: { min_likes: '={{$value}}' } } },
},
{
displayName: 'Minimum Retweets',
name: 'min_retweets',
type: 'number',
default: 0,
description: 'Minimum number of retweets',
routing: { request: { body: { min_retweets: '={{$value}}' } } },
},
{
displayName: 'From Date',
name: 'from_date',
type: 'dateTime',
default: '',
description: 'Starting date for tweets search',
routing: {
send: {
type: 'body',
property: 'from_date',
value: '={{ Math.round(new Date($value).getTime() / 1000) }}',
},
},
},
{
displayName: 'To Date',
name: 'to_date',
type: 'dateTime',
default: '',
description: 'Ending date for tweets search',
routing: {
send: {
type: 'body',
property: 'to_date',
value: '={{ Math.round(new Date($value).getTime() / 1000) }}',
},
},
},
{
displayName: 'Search Type',
name: 'search_type',
type: 'options',
options: [
{ name: 'Top', value: 'Top' },
{ name: 'Latest', value: 'Latest' },
],
default: 'Top',
description: 'Type of search results',
routing: { request: { body: { search_type: '={{$value}}' } } },
},
],
},
{
displayName: 'Timeout',
name: 'timeout',
type: 'number',
default: 300,
description: 'Timeout in seconds (20-1500)',
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
const credentials = await this.getCredentials('hdwLinkedinApi');
if (!credentials) {
throw new Error('No credentials provided!');
}
const accountId = credentials.accountId;
if (!accountId) {
throw new Error('Account ID is missing in credentials!');
}
const baseURL = 'https://api.horizondatawave.ai';
const delayInMs = 100;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
for (let i = 0; i < items.length; i++) {
try {
const resource = this.getNodeParameter('resource', i);
const operation = this.getNodeParameter('operation', i);
const timeout = this.getNodeParameter('timeout', i);
let endpoint = '';
const body = {
timeout,
account_id: accountId,
};
if (resource === 'user') {
if (operation === 'getProfile') {
endpoint = '/api/twitter/user';
body.user = this.getNodeParameter('user', i);
}
else if (operation === 'getPosts') {
endpoint = '/api/twitter/user/posts';
body.user = this.getNodeParameter('user', i);
body.count = this.getNodeParameter('count', i);
}
}
else if (resource === 'search') {
if (operation === 'searchUsers') {
endpoint = '/api/twitter/search/users';
body.query = this.getNodeParameter('query', i);
body.count = this.getNodeParameter('count', i);
}
else if (operation === 'searchPosts') {
endpoint = '/api/twitter/search/posts';
body.query = this.getNodeParameter('query', i);
body.count = this.getNodeParameter('count', i);
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
for (const key in advancedOptions) {
if (advancedOptions[key] !== '') {
body[key] = advancedOptions[key];
}
}
}
}
const options = {
method: 'POST',
url: `${baseURL}${endpoint}`,
body,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
json: true,
};
const responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'hdwLinkedinApi', options);
if (Array.isArray(responseData)) {
for (const element of responseData) {
returnData.push({ json: element });
}
}
else {
returnData.push({ json: responseData });
}
if (i < items.length - 1) {
await sleep(delayInMs);
}
}
catch (error) {
// Enhanced error handling to extract information from headers and response body
let errorMessage = error.message;
let errorDetails = 'No detailed error information available';
let httpStatus = '';
let apiError = '';
let requestId = '';
let executionTime = '';
let tokenPoints = '';
// Extract information from HTTP response if available
if (error.response) {
httpStatus = error.response.status || '';
// Extract custom headers from HDW API
if (error.response.headers) {
apiError = error.response.headers['x-error'] || '';
requestId = error.response.headers['x-request-id'] || '';
executionTime = error.response.headers['x-execution-time'] || '';
tokenPoints = error.response.headers['x-token-points'] || '';
}
// Try to get error details from response body
if (error.response.data) {
if (typeof error.response.data === 'string') {
errorDetails = error.response.data;
}
else if (typeof error.response.data === 'object') {
errorDetails = JSON.stringify(error.response.data);
}
}
// If we have API error from headers, use it as the main error message
if (apiError) {
errorMessage = `${apiError} (HTTP ${httpStatus})`;
}
// Build comprehensive error details
const detailParts = [];
if (apiError)
detailParts.push(`API Error: ${apiError}`);
if (httpStatus)
detailParts.push(`HTTP Status: ${httpStatus}`);
if (requestId)
detailParts.push(`Request ID: ${requestId}`);
if (executionTime)
detailParts.push(`Execution Time: ${executionTime}s`);
if (tokenPoints)
detailParts.push(`Token Points: ${tokenPoints}`);
if (error.response.data && error.response.data !== '{}') {
detailParts.push(`Response Body: ${typeof error.response.data === 'object' ? JSON.stringify(error.response.data) : error.response.data}`);
}
if (detailParts.length > 0) {
errorDetails = detailParts.join(' | ');
}
}
if (this.continueOnFail()) {
returnData.push({
json: {
error: errorMessage,
details: errorDetails,
httpStatus: httpStatus,
apiError: apiError,
requestId: requestId,
executionTime: executionTime,
tokenPoints: tokenPoints,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.HdwTwitter = HdwTwitter;
//# sourceMappingURL=HdwTwitter.node.js.map