UNPKG

@memberjunction/actions-bizapps-social

Version:

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

335 lines 12.9 kB
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; }; import { RegisterClass } from '@memberjunction/global'; import { FacebookBaseAction } from '../facebook-base.action.js'; import { LogStatus, LogError } from '@memberjunction/core'; import axios from 'axios'; import { BaseAction } from '@memberjunction/actions'; /** * Boosts (promotes) a Facebook post to reach a wider audience. * Creates a simple ad campaign to increase post visibility. */ let FacebookBoostPostAction = class FacebookBoostPostAction extends FacebookBaseAction { /** * Get action description */ get Description() { return 'Boosts (promotes) a Facebook post to reach a wider audience through paid advertising'; } /** * Define the parameters for this action */ get Params() { return [ ...this.commonSocialParams, { Name: 'PostID', Type: 'Input', Value: null, }, { Name: 'AdAccountID', Type: 'Input', Value: null, }, { Name: 'Budget', Type: 'Input', Value: null, }, { Name: 'Duration', Type: 'Input', Value: 7, }, { Name: 'Objective', Type: 'Input', Value: 'POST_ENGAGEMENT', }, { Name: 'AudienceType', Type: 'Input', Value: 'AUTO', }, { Name: 'TargetingSpec', Type: 'Input', Value: null, }, { Name: 'StartTime', Type: 'Input', Value: null, }, { Name: 'CallToAction', Type: 'Input', Value: null, } ]; } /** * Execute the action */ async InternalRunAction(params) { const { Params, ContextUser } = params; try { // Validate required parameters const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID'); const postId = this.getParamValue(Params, 'PostID'); const adAccountId = this.getParamValue(Params, 'AdAccountID'); const budget = this.getParamValue(Params, 'Budget'); if (!companyIntegrationId) { return { Success: false, Message: 'CompanyIntegrationID is required', ResultCode: 'INVALID_TOKEN' }; } if (!postId) { return { Success: false, Message: 'PostID is required', ResultCode: 'MISSING_REQUIRED_PARAM' }; } if (!adAccountId) { return { Success: false, Message: 'AdAccountID is required', ResultCode: 'MISSING_REQUIRED_PARAM' }; } if (!budget || budget <= 0) { return { Success: false, Message: 'Budget must be a positive number', ResultCode: 'INVALID_BUDGET' }; } // Initialize OAuth if (!await this.initializeOAuth(companyIntegrationId, params)) { return { Success: false, Message: 'Failed to initialize Facebook OAuth connection', ResultCode: 'INVALID_TOKEN' }; } // Get parameters const duration = this.getParamValue(Params, 'Duration') || 7; const objective = this.getParamValue(Params, 'Objective') || 'POST_ENGAGEMENT'; const audienceType = this.getParamValue(Params, 'AudienceType') || 'AUTO'; const targetingSpec = this.getParamValue(Params, 'TargetingSpec'); const startTime = this.getParamValue(Params, 'StartTime'); const callToAction = this.getParamValue(Params, 'CallToAction'); // Validate duration if (duration < 1 || duration > 30) { return { Success: false, Message: 'Duration must be between 1 and 30 days', ResultCode: 'INVALID_DURATION' }; } // Extract page ID from post ID const pageId = postId.split('_')[0]; // Get page access token const pageToken = await this.getPageAccessToken(pageId); LogStatus(`Creating boost campaign for post ${postId}...`); // Step 1: Create campaign const campaignName = `Boost Post ${postId} - ${new Date().toISOString()}`; const campaign = await this.createCampaign(adAccountId, campaignName, objective); // Step 2: Create ad set with targeting const adSetName = `Ad Set for ${postId}`; const targeting = this.buildTargeting(audienceType, targetingSpec, pageId); const adSet = await this.createAdSet(adAccountId, campaign.id, adSetName, budget, duration, targeting, startTime); // Step 3: Create ad creative from post const creative = await this.createCreativeFromPost(adAccountId, postId, pageToken, callToAction); // Step 4: Create the ad const adName = `Boosted Post ${postId}`; const ad = await this.createAd(adAccountId, adSet.id, creative.id, adName); // Get boost summary const boostSummary = { campaignId: campaign.id, adSetId: adSet.id, adId: ad.id, creativeId: creative.id, postId, budget, duration, objective, audienceType, startTime: adSet.start_time, endTime: adSet.end_time, status: ad.status, reviewStatus: ad.review_feedback?.global_review_status || 'PENDING', previewUrl: `https://www.facebook.com/ads/manager/account/campaigns?act=${adAccountId}&selected_campaign_ids=${campaign.id}` }; LogStatus(`Successfully created boost campaign for post ${postId}`); return { Success: true, Message: 'Post boost created successfully', ResultCode: 'SUCCESS', Params }; } catch (error) { LogError(`Failed to boost Facebook post: ${error instanceof Error ? error.message : 'Unknown error'}`); if (this.isAuthError(error)) { return this.handleOAuthError(error); } // Check for specific ad-related errors if (error instanceof Error) { if (error.message.includes('permissions')) { return { Success: false, Message: 'Insufficient permissions. Ensure the token has ads_management permission.', ResultCode: 'INSUFFICIENT_PERMISSIONS' }; } if (error.message.includes('budget')) { return { Success: false, Message: 'Invalid budget. Check minimum budget requirements for your currency.', ResultCode: 'INVALID_BUDGET' }; } } return { Success: false, Message: error instanceof Error ? error.message : 'Unknown error occurred', ResultCode: 'ERROR' }; } } /** * Create a campaign */ async createCampaign(adAccountId, name, objective) { const response = await this.axiosInstance.post(`/${adAccountId}/campaigns`, { name, objective, status: 'PAUSED', // Start paused for safety special_ad_categories: [] // Required field }); return response.data; } /** * Create an ad set */ async createAdSet(adAccountId, campaignId, name, budget, durationDays, targeting, startTime) { const now = new Date(); const start = startTime ? new Date(startTime) : now; const end = new Date(start); end.setDate(end.getDate() + durationDays); // Calculate daily budget const dailyBudget = Math.ceil((budget * 100) / durationDays); // Convert to cents const response = await this.axiosInstance.post(`/${adAccountId}/adsets`, { name, campaign_id: campaignId, daily_budget: dailyBudget, billing_event: 'IMPRESSIONS', optimization_goal: this.getOptimizationGoal(campaignId), bid_strategy: 'LOWEST_COST_WITHOUT_CAP', targeting, start_time: start.toISOString(), end_time: end.toISOString(), status: 'PAUSED' }); return response.data; } /** * Create creative from existing post */ async createCreativeFromPost(adAccountId, postId, pageToken, callToAction) { const creativeData = { name: `Creative for ${postId}`, object_story_id: postId }; if (callToAction) { // Get post details to add CTA const postResponse = await axios.get(`${this.apiBaseUrl}/${postId}`, { params: { access_token: pageToken, fields: 'permalink_url' } }); creativeData.call_to_action = { type: callToAction, value: { link: postResponse.data.permalink_url } }; } const response = await this.axiosInstance.post(`/${adAccountId}/adcreatives`, creativeData); return response.data; } /** * Create the ad */ async createAd(adAccountId, adSetId, creativeId, name) { const response = await this.axiosInstance.post(`/${adAccountId}/ads`, { name, adset_id: adSetId, creative: { creative_id: creativeId }, status: 'PAUSED' }); return response.data; } /** * Build targeting specification */ buildTargeting(audienceType, customTargeting, pageId) { const baseTargeting = { geo_locations: { countries: ['US'] // Default to US, can be overridden } }; switch (audienceType) { case 'FANS': baseTargeting.connections = [pageId]; break; case 'FANS_AND_CONNECTIONS': baseTargeting.connections = [pageId]; baseTargeting.friends_of_connections = [pageId]; break; case 'CUSTOM': if (customTargeting) { return { ...baseTargeting, ...customTargeting }; } break; case 'AUTO': default: // Facebook will automatically optimize targeting break; } // Add any custom targeting on top if (customTargeting && audienceType !== 'CUSTOM') { Object.assign(baseTargeting, customTargeting); } return baseTargeting; } /** * Get optimization goal based on objective */ getOptimizationGoal(objective) { const goalMap = { 'POST_ENGAGEMENT': 'POST_ENGAGEMENT', 'REACH': 'REACH', 'LINK_CLICKS': 'LINK_CLICKS', 'PAGE_LIKES': 'PAGE_LIKES', 'BRAND_AWARENESS': 'AD_RECALL_LIFT', 'VIDEO_VIEWS': 'VIDEO_VIEWS' }; return goalMap[objective] || 'POST_ENGAGEMENT'; } }; FacebookBoostPostAction = __decorate([ RegisterClass(BaseAction, 'FacebookBoostPostAction') ], FacebookBoostPostAction); export { FacebookBoostPostAction }; //# sourceMappingURL=boost-post.action.js.map