n8n-nodes-instagram-private-api-wrapped
Version:
n8n community node for Instagram automation using private API capabilities
211 lines (210 loc) • 6.75 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Utils = void 0;
/**
* Utility functions for Instagram n8n integration
*/
class Utils {
/**
* Execute a function with retry logic and exponential backoff
*/
static async executeWithRetry(operation, options = {}) {
const { maxRetries = 3, baseDelay = 1000, maxDelay = 10000 } = options;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
}
catch (error) {
if (attempt === maxRetries - 1) {
throw error;
}
const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
await this.delay(delay);
}
}
throw new Error('Maximum retries exceeded');
}
/**
* Create a delay promise
*/
static async delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Sanitize text input to prevent issues with Instagram API
*/
static sanitizeText(text) {
if (!text)
return '';
// Remove HTML tags and potentially problematic characters
return text
.trim()
.replace(/<[^>]*>/g, '') // Remove HTML tags
// eslint-disable-next-line no-control-regex
.replace(/[\x00-\x1F\x7F-\x9F]/g, '') // Remove control characters
.substring(0, 2200); // Instagram caption limit
}
/**
* Generate random delay to avoid rate limiting
*/
static async randomDelay(min = 1000, max = 3000) {
const delay = Math.floor(Math.random() * (max - min + 1)) + min;
await this.delay(delay);
}
/**
* Formats error messages consistently
*/
static formatError(error) {
if (error instanceof Error) {
return error.message;
}
if (typeof error === 'string') {
return error;
}
return 'Unknown error occurred';
}
/**
* Validates username format
*/
static validateUsername(username) {
if (!username || typeof username !== 'string') {
return false;
}
// Instagram username rules: 1-30 characters, letters, numbers, periods, underscores
const usernameRegex = /^[a-zA-Z0-9._]{1,30}$/;
return usernameRegex.test(username);
}
/**
* Validates hashtag format
*/
static validateHashtag(hashtag) {
if (!hashtag || typeof hashtag !== 'string') {
throw new Error('Invalid hashtag provided');
}
// Remove # if present and validate
const cleanHashtag = hashtag.replace(/^#/, '').trim();
if (cleanHashtag.length === 0) {
throw new Error('Hashtag cannot be empty');
}
// Instagram hashtag rules: letters, numbers, underscores only
const hashtagRegex = /^[a-zA-Z0-9_]+$/;
if (!hashtagRegex.test(cleanHashtag)) {
throw new Error('Hashtag contains invalid characters');
}
return cleanHashtag;
}
/**
* Validates image buffer
*/
static validateImageBuffer(buffer) {
if (!buffer || !Buffer.isBuffer(buffer)) {
return false;
}
// Check minimum size (1KB)
if (buffer.length < 1024) {
return false;
}
// Check maximum size (8MB for Instagram)
if (buffer.length > 8 * 1024 * 1024) {
return false;
}
// Check for common image file signatures
const jpegSignature = buffer.slice(0, 2);
const pngSignature = buffer.slice(0, 8);
const isJPEG = jpegSignature[0] === 0xFF && jpegSignature[1] === 0xD8;
const isPNG = pngSignature.equals(Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]));
return isJPEG || isPNG;
}
/**
* Validates and formats media ID
*/
static validateMediaId(mediaId) {
if (!mediaId || typeof mediaId !== 'string') {
throw new Error('Invalid media ID provided');
}
const cleaned = mediaId.trim();
if (cleaned.length === 0) {
throw new Error('Media ID cannot be empty');
}
return cleaned;
}
/**
* Extracts user ID from Instagram URL
*/
static extractUserIdFromUrl(url) {
if (!url || typeof url !== 'string') {
return null;
}
// Pattern for Instagram profile URLs
const patterns = [
/instagram\.com\/([a-zA-Z0-9._]+)\/?/,
/instagram\.com\/p\/([a-zA-Z0-9._-]+)\/?/
];
for (const pattern of patterns) {
const match = url.match(pattern);
if (match && match[1]) {
return match[1];
}
}
return null;
}
/**
* Formats timestamp to human readable date
*/
static formatTimestamp(timestamp) {
try {
return new Date(timestamp * 1000).toISOString();
}
catch {
return new Date().toISOString();
}
}
/**
* Validates and limits numeric values
*/
static validateLimit(limit, defaultValue, maxValue) {
if (limit === undefined || limit === null) {
return defaultValue;
}
if (typeof limit !== 'number' || isNaN(limit) || limit < 1) {
return defaultValue;
}
return Math.min(limit, maxValue);
}
/**
* Creates a retry mechanism for API calls
*/
static async retry(operation, maxRetries = 3, baseDelay = 1000) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
}
catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt === maxRetries) {
break;
}
const delay = baseDelay * Math.pow(2, attempt);
await this.randomDelay(delay, delay * 1.5);
}
}
throw lastError;
}
/**
* Checks if error is rate limit related
*/
static isRateLimitError(error) {
const errorMsg = this.formatError(error).toLowerCase();
return errorMsg.includes('rate limit') ||
errorMsg.includes('too many requests') ||
errorMsg.includes('429');
}
/**
* Generates a unique request ID for tracking
*/
static generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
exports.Utils = Utils;