@memberjunction/actions-bizapps-social
Version:
Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer
335 lines • 12.9 kB
JavaScript
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