UNPKG

n8n-nodes-twitter-uploader-inf

Version:
307 lines 15 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TwitterMediaUpload = void 0; const n8n_workflow_1 = require("n8n-workflow"); const TwitterUtils_1 = require("./TwitterUtils"); const form_data_1 = __importDefault(require("form-data")); const https_1 = require("https"); class TwitterMediaUpload { constructor() { this.description = { displayName: 'Twitter Media Upload', name: 'twitterMediaUpload', icon: 'file:twitter.svg', group: ['transform'], version: 1, description: 'Upload media to Twitter', defaults: { name: 'Twitter Media Upload', color: '#1DA1F2', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'twitterApi', required: true, }, ], properties: [ { displayName: 'Binary Property', name: 'binaryPropertyName', type: 'string', default: 'data', required: true, description: 'Name of the binary property that contains the image', }, { displayName: 'Media Data', name: 'mediaData', type: 'string', default: '', required: false, description: 'Media data parameter' }, { displayName: 'Additional Owners', name: 'additionalOwners', type: 'string', default: '', required: false, description: 'A comma-separated list of user IDs to set as additional owners allowed to use the media. Up to 100 additional owners can be specified.', placeholder: '12345,67890', }, { displayName: 'Media Category', name: 'mediaCategory', type: 'string', default: 'tweet_image', required: false, description: 'The category that represents how the media will be used. For tweets, use tweet_image (default), tweet_gif, or tweet_video.', placeholder: 'tweet_image', }, ], }; } async execute() { var _a, _b, _c; // Get credentials const credentials = await this.getCredentials('twitterApi'); console.log('Credentials Source:', credentials); if (!credentials) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials provided'); } // Validate required OAuth fields const requiredFields = [ { field: 'consumerKey', label: 'Consumer Key' }, { field: 'consumerSecret', label: 'Access Token' }, { field: 'accessToken', label: 'Access Token' }, { field: 'accessSecret', label: 'Access Token Secret' } ]; for (const { field, label } of requiredFields) { if (!((_a = credentials[field]) === null || _a === void 0 ? void 0 : _a.toString())) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Missing required Twitter OAuth credential: ${label}`); } } console.log('Twitter credentials validated successfully'); const items = this.getInputData(); const returnData = []; const logMediaProperties = (binaryPropertyName, mediaData, mediaCategory) => { console.log('Media Properties:'); console.log(` binaryPropertyName: ${binaryPropertyName} (Name of the binary property containing the image data)`); console.log(` mediaData: ${mediaData ? 'Provided' : 'Not Provided'} (Media data as a string, overrides binaryPropertyName if provided)`); console.log(` mediaCategory: ${mediaCategory || 'Not Provided'} (Category that represents how media will be used)`); }; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { // Get binary property name const binaryPropertyName = this.getNodeParameter('binaryPropertyName', itemIndex); // Check if mediaData parameter exists and use it, otherwise use binary data let mediaData; try { mediaData = this.getNodeParameter('mediaData', itemIndex); console.log('Found mediaData parameter'); } catch (error) { // If mediaData parameter doesn't exist, use binary data instead console.log('mediaData parameter not found, using binary data instead'); mediaData = null; } // Get media category if provided let mediaCategory; try { mediaCategory = this.getNodeParameter('mediaCategory', itemIndex); } catch (error) { console.log('Media category not specified, using default'); mediaCategory = null; } logMediaProperties(binaryPropertyName, mediaData, mediaCategory); // Debug log available binary properties console.log('Available binary properties:', Object.keys(items[itemIndex].binary || {})); console.log('Requested binary property:', binaryPropertyName); // Try to find binary data - first check specified property, then look for any image let actualPropertyName = binaryPropertyName; if (!((_b = items[itemIndex].binary) === null || _b === void 0 ? void 0 : _b[binaryPropertyName])) { console.log(`Binary property "${binaryPropertyName}" not found, searching for alternatives...`); const binaryKeys = Object.keys(items[itemIndex].binary || {}); const imageKey = binaryKeys.find(key => { var _a; return (_a = items[itemIndex].binary[key].mimeType) === null || _a === void 0 ? void 0 : _a.startsWith('image/'); }); if (imageKey) { console.log(`Found alternative image property: "${imageKey}"`); actualPropertyName = imageKey; } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No binary data found in property "${binaryPropertyName}" and no image alternatives found`); } } // Get binary data const binaryData = await this.helpers.getBinaryDataBuffer(itemIndex, actualPropertyName); // Extract file information const mediaType = items[itemIndex].binary[actualPropertyName].mimeType; const fileName = items[itemIndex].binary[actualPropertyName].fileName || 'image.jpg'; // Debug log binary data details console.log('Binary data details:', { fileName, mediaType, dataSize: binaryData.length, }); // Validate media type if (!mediaType || !mediaType.startsWith('image/')) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid media type: ${mediaType}. Only images are supported.`); } // Create request options using form data approach const requestUrl = 'https://upload.twitter.com/1.1/media/upload.json'; // Create OAuth helper instance const oauth = new TwitterUtils_1.OAuth1Helper({ consumerKey: credentials.consumerKey, consumerSecret: credentials.consumerSecret, accessToken: credentials.accessToken, accessSecret: credentials.accessSecret }, this.getNode()); // Create params for OAuth header const oauthParams = { command: 'INIT', totalBytes: String(binaryData.length), mediaType: mediaType, }; // Add media_category to params if provided if (mediaCategory) { oauthParams.media_category = mediaCategory; } // Generate authorization header const authHeader = oauth.generateAuthHeader({ url: requestUrl, method: 'POST', params: oauthParams, }); const formData = new form_data_1.default(); formData.append('command', 'INIT'); const totalBytes = String(binaryData.length); if (isNaN(Number(totalBytes))) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'totalBytes must be a number'); } formData.append('totalBytes', totalBytes); if (!mediaType || !mediaType.startsWith('image/')) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid media type: ${mediaType}. Only images are supported.`); } formData.append('mediaType', mediaType); // Add media_category to form data if provided if (mediaCategory) { formData.append('media_category', mediaCategory); } formData.append('media', binaryData, fileName); // Add additional owners if provided try { const additionalOwners = this.getNodeParameter('additionalOwners', itemIndex); if (additionalOwners) { // Clean up and validate the additional owners list const ownersList = additionalOwners .split(',') .map(id => id.trim()) .filter(id => id.length > 0); if (ownersList.length > 100) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Maximum of 100 additional owners allowed'); } formData.append('additional_owners', ownersList.join(',')); } } catch (error) { // If parameter doesn't exist or there's an error, continue without additional owners. if (error instanceof Error) { console.log('No additional owners specified or error occurred:', error.message); } } const requestHeaders = { authorization: authHeader, ...formData.getHeaders(), }; console.log('Sending request to Twitter API with FormData and manual OAuth...', { url: requestUrl, headers: requestHeaders, }); const response = await new Promise((resolve, reject) => { const req = (0, https_1.request)(requestUrl, { method: 'POST', headers: requestHeaders, }, (res) => { let data = ''; res.setEncoding('utf8'); res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { let responseBody; try { responseBody = JSON.parse(data); } catch (e) { responseBody = data; } resolve({ statusCode: res.statusCode, headers: res.headers, body: responseBody }); }); res.on('error', (err) => reject(err)); }); formData.pipe(req); req.on('error', (error) => { reject(error); }); }); console.log('Response received:', { statusCode: response.statusCode, body: JSON.stringify(response.body) }); // Check for error response if (response.statusCode >= 400) { throw new n8n_workflow_1.NodeApiError(this.getNode(), response.body, { httpCode: response.statusCode }); } // Add the response to the output returnData.push({ json: { success: true, ...response.body, }, }); } catch (error) { console.error('Error in TwitterMediaUpload:', error.message); if (error.response) { console.error('Response error details:', { statusCode: error.response.statusCode, body: error.response.body }); } if (error instanceof n8n_workflow_1.NodeOperationError) { console.error('NodeOperationError:', error.message); } else if (error instanceof n8n_workflow_1.NodeApiError) { console.error('NodeApiError:', error.message); } else { console.error('Unexpected error:', error.message); } if (this.continueOnFail()) { returnData.push({ json: { success: false, error: error.message, details: (_c = error.response) === null || _c === void 0 ? void 0 : _c.body } }); continue; } throw error; } } return [returnData]; } } exports.TwitterMediaUpload = TwitterMediaUpload; //# sourceMappingURL=TwitterMediaUpload.node.js.map