UNPKG

@memberjunction/actions-bizapps-social

Version:

Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer

275 lines 11.2 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HootSuiteBaseAction = void 0; const global_1 = require("@memberjunction/global"); const base_social_action_1 = require("../../base/base-social.action"); const axios_1 = __importDefault(require("axios")); const core_1 = require("@memberjunction/core"); const actions_1 = require("@memberjunction/actions"); /** * Base class for all HootSuite actions. * Handles HootSuite-specific authentication, API interactions, and rate limiting. */ let HootSuiteBaseAction = class HootSuiteBaseAction extends base_social_action_1.BaseSocialMediaAction { get platformName() { return 'HootSuite'; } get apiBaseUrl() { return 'https://platform.hootsuite.com/v1'; } /** * Axios instance for making HTTP requests */ _axiosInstance = null; /** * Get or create axios instance with interceptors */ get axiosInstance() { if (!this._axiosInstance) { this._axiosInstance = axios_1.default.create({ baseURL: this.apiBaseUrl, timeout: 30000, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } }); // Add request interceptor for auth this._axiosInstance.interceptors.request.use((config) => { const token = this.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error)); // Add response interceptor for rate limit handling this._axiosInstance.interceptors.response.use((response) => { // Log rate limit info const rateLimitInfo = this.parseRateLimitHeaders(response.headers); if (rateLimitInfo) { (0, core_1.LogStatus)(`HootSuite Rate Limit - Remaining: ${rateLimitInfo.remaining}/${rateLimitInfo.limit}, Reset: ${rateLimitInfo.reset}`); } return response; }, async (error) => { if (error.response?.status === 429) { // Rate limit exceeded const retryAfter = error.response.headers['retry-after']; const waitTime = retryAfter ? parseInt(retryAfter) : 60; await this.handleRateLimit(waitTime); // Retry the request return this._axiosInstance.request(error.config); } return Promise.reject(error); }); } return this._axiosInstance; } /** * Refresh the access token using the refresh token */ async refreshAccessToken() { const refreshToken = this.getRefreshToken(); if (!refreshToken) { throw new Error('No refresh token available for HootSuite'); } try { const response = await axios_1.default.post('https://platform.hootsuite.com/oauth2/token', { grant_type: 'refresh_token', refresh_token: refreshToken, client_id: this.getCustomAttribute(2), // Client ID stored in CustomAttribute2 client_secret: this.getCustomAttribute(3) // Client Secret stored in CustomAttribute3 }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const { access_token, refresh_token: newRefreshToken, expires_in } = response.data; // Update stored tokens await this.updateStoredTokens(access_token, newRefreshToken || refreshToken, expires_in); (0, core_1.LogStatus)('HootSuite access token refreshed successfully'); } catch (error) { (0, core_1.LogError)(`Failed to refresh HootSuite access token: ${error instanceof Error ? error.message : 'Unknown error'}`); throw error; } } /** * Upload media to HootSuite */ async uploadSingleMedia(file) { try { // First, request an upload URL const uploadRequest = await this.axiosInstance.post('/media', { mimeType: file.mimeType, sizeBytes: file.size }); const { uploadUrl, mediaId } = uploadRequest.data; // Upload the file to the provided URL const fileData = typeof file.data === 'string' ? Buffer.from(file.data, 'base64') : file.data; await axios_1.default.put(uploadUrl, fileData, { headers: { 'Content-Type': file.mimeType, 'Content-Length': file.size.toString() } }); // Wait for processing await this.waitForMediaProcessing(mediaId); return mediaId; } catch (error) { (0, core_1.LogError)(`Failed to upload media to HootSuite: ${error instanceof Error ? error.message : 'Unknown error'}`); throw error; } } /** * Wait for media to finish processing */ async waitForMediaProcessing(mediaId, maxAttempts = 30) { for (let i = 0; i < maxAttempts; i++) { try { const response = await this.axiosInstance.get(`/media/${mediaId}`); const { state } = response.data; if (state === 'READY') { return; } else if (state === 'FAILED') { throw new Error('Media processing failed'); } // Wait 2 seconds before next check await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { if (i === maxAttempts - 1) { throw new Error(`Media processing timeout for ${mediaId}`); } } } } /** * Get social profiles for the authenticated user */ async getSocialProfiles() { try { const response = await this.axiosInstance.get('/socialProfiles'); return response.data.data || []; } catch (error) { (0, core_1.LogError)(`Failed to get social profiles: ${error instanceof Error ? error.message : 'Unknown error'}`); throw error; } } /** * Make a paginated request to HootSuite API */ async makePaginatedRequest(endpoint, params = {}) { const results = []; let cursor = null; const limit = params.limit || 50; do { const queryParams = { ...params, limit }; if (cursor) { queryParams.cursor = cursor; } const response = await this.axiosInstance.get(endpoint, { params: queryParams }); const data = response.data; if (data.data && Array.isArray(data.data)) { results.push(...data.data); } cursor = data.cursor || null; // Check if we've reached the desired number of results if (params.maxResults && results.length >= params.maxResults) { return results.slice(0, params.maxResults); } } while (cursor); return results; } /** * Format date for HootSuite API (ISO 8601) */ formatHootSuiteDate(date) { if (typeof date === 'string') { date = new Date(date); } return date.toISOString(); } /** * Parse HootSuite date string */ parseHootSuiteDate(dateString) { return new Date(dateString); } /** * Convert HootSuite post to common format */ normalizePost(hootsuitePost) { return { id: hootsuitePost.id, platform: 'HootSuite', profileId: hootsuitePost.socialProfileIds.join(','), // Multiple profiles possible content: hootsuitePost.text, mediaUrls: hootsuitePost.mediaIds || [], publishedAt: this.parseHootSuiteDate(hootsuitePost.createdTime), scheduledFor: hootsuitePost.scheduledTime ? this.parseHootSuiteDate(hootsuitePost.scheduledTime) : undefined, platformSpecificData: { state: hootsuitePost.state, tags: hootsuitePost.tags, location: hootsuitePost.location, socialProfileIds: hootsuitePost.socialProfileIds } }; } /** * Search for posts - implemented in search action */ async searchPosts(params) { // This is implemented in the search-posts.action.ts throw new Error('Search posts is implemented in HootSuiteSearchPostsAction'); } /** * Handle HootSuite-specific errors */ handleHootSuiteError(error) { if (error.response) { const { status, data } = error.response; const errorData = data; switch (status) { case 400: throw new Error(`Bad Request: ${errorData.message || 'Invalid request parameters'}`); case 401: throw new Error('Unauthorized: Invalid or expired access token'); case 403: throw new Error('Forbidden: Insufficient permissions'); case 404: throw new Error('Not Found: Resource does not exist'); case 429: throw new Error('Rate Limit Exceeded: Too many requests'); case 500: throw new Error('Internal Server Error: HootSuite service error'); default: throw new Error(`HootSuite API Error (${status}): ${errorData.message || 'Unknown error'}`); } } else if (error.request) { throw new Error('Network Error: No response from HootSuite'); } else { throw new Error(`Request Error: ${error.message}`); } } }; exports.HootSuiteBaseAction = HootSuiteBaseAction; exports.HootSuiteBaseAction = HootSuiteBaseAction = __decorate([ (0, global_1.RegisterClass)(actions_1.BaseAction, 'HootSuiteBaseAction') ], HootSuiteBaseAction); //# sourceMappingURL=hootsuite-base.action.js.map