@memberjunction/actions-bizapps-social
Version:
Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer
206 lines (183 loc) • 8.21 kB
text/typescript
import { RegisterClass } from '@memberjunction/global';
import { LinkedInBaseAction, LinkedInAnalytics } from '../linkedin-base.action';
import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
import { LogStatus, LogError } from '@memberjunction/core';
import { SocialAnalytics } from '../../../base/base-social.action';
import { BaseAction } from '@memberjunction/actions';
/**
* Action to get analytics for a LinkedIn post
*/
(BaseAction, 'LinkedInGetPostAnalyticsAction')
export class LinkedInGetPostAnalyticsAction extends LinkedInBaseAction {
/**
* Get analytics for a specific LinkedIn post
*/
protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
const { Params, ContextUser } = params;
try {
// Initialize OAuth
const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
if (!await this.initializeOAuth(companyIntegrationId, params)) {
throw new Error('Failed to initialize OAuth connection');
}
// Extract parameters
const postId = this.getParamValue(Params, 'PostID');
const authorType = this.getParamValue(Params, 'AuthorType') || 'organization'; // 'personal' or 'organization'
const organizationId = this.getParamValue(Params, 'OrganizationID');
const timeRange = this.getParamValue(Params, 'TimeRange'); // Optional: {start: Date, end: Date}
// Validate required parameters
if (!postId) {
throw new Error('PostID is required');
}
let analytics: LinkedInAnalytics | null = null;
if (authorType === 'organization') {
// Get organization analytics
let organizationUrn: string;
if (!organizationId) {
// Get first admin organization if not specified
const orgs = await this.getAdminOrganizations();
if (orgs.length === 0) {
throw new Error('No organizations found. Please specify OrganizationID.');
}
organizationUrn = orgs[0].urn;
LogStatus(`Using organization: ${orgs[0].name}`);
} else {
organizationUrn = `urn:li:organization:${organizationId}`;
}
analytics = await this.getOrganizationPostAnalytics(postId, organizationUrn, timeRange);
} else {
// Get personal post analytics (limited)
analytics = await this.getPersonalPostAnalytics(postId);
}
if (!analytics) {
throw new Error('No analytics data available for this post');
}
// Normalize analytics
const normalizedAnalytics = this.normalizeAnalytics(analytics);
// Update output parameters
const outputParams = [...Params];
const analyticsParam = outputParams.find(p => p.Name === 'Analytics');
if (analyticsParam) analyticsParam.Value = normalizedAnalytics;
const rawAnalyticsParam = outputParams.find(p => p.Name === 'RawAnalytics');
if (rawAnalyticsParam) rawAnalyticsParam.Value = analytics;
return {
Success: true,
ResultCode: 'SUCCESS',
Message: `Successfully retrieved analytics for post ${postId}`,
Params: outputParams
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
Success: false,
ResultCode: 'ERROR',
Message: `Failed to get post analytics: ${errorMessage}`,
Params
};
}
}
/**
* Get analytics for an organization post
*/
private async getOrganizationPostAnalytics(shareId: string, organizationUrn: string, timeRange?: any): Promise<LinkedInAnalytics> {
try {
const params: any = {
q: 'organizationalEntity',
organizationalEntity: organizationUrn,
shares: `List(${shareId})`
};
// Add time range if specified
if (timeRange) {
if (timeRange.start) {
params.timeIntervals = `List((start:${new Date(timeRange.start).getTime()},end:${new Date(timeRange.end || Date.now()).getTime()}))`;
}
}
const response = await this.axiosInstance.get('/organizationalEntityShareStatistics', { params });
if (response.data.elements && response.data.elements.length > 0) {
const stats = response.data.elements[0];
return {
totalShareStatistics: stats.totalShareStatistics || {},
timeRange: timeRange ? {
start: new Date(timeRange.start).getTime(),
end: new Date(timeRange.end || Date.now()).getTime()
} : undefined
};
}
throw new Error('No analytics data found');
} catch (error) {
LogError(`Failed to get organization post analytics: ${error instanceof Error ? error.message : 'Unknown error'}`);
throw error;
}
}
/**
* Get analytics for a personal post (limited data available)
*/
private async getPersonalPostAnalytics(shareId: string): Promise<LinkedInAnalytics> {
try {
// For personal posts, we need to fetch the post itself to get basic engagement metrics
const response = await this.axiosInstance.get(`/ugcPosts/${shareId}`);
if (response.data) {
// Extract available metrics from the post data
return {
totalShareStatistics: {
impressionCount: 0, // Not available for personal posts
clickCount: 0, // Not available for personal posts
engagement: response.data.likesSummary?.totalLikes || 0 + response.data.commentsSummary?.totalComments || 0,
likeCount: response.data.likesSummary?.totalLikes || 0,
commentCount: response.data.commentsSummary?.totalComments || 0,
shareCount: 0, // Not directly available
uniqueImpressionsCount: 0 // Not available for personal posts
}
};
}
throw new Error('Post not found');
} catch (error) {
LogError(`Failed to get personal post analytics: ${error instanceof Error ? error.message : 'Unknown error'}`);
throw error;
}
}
/**
* Define the parameters this action expects
*/
public get Params(): ActionParam[] {
return [
...this.commonSocialParams,
{
Name: 'PostID',
Type: 'Input',
Value: null
},
{
Name: 'AuthorType',
Type: 'Input',
Value: 'organization' // 'personal' or 'organization'
},
{
Name: 'OrganizationID',
Type: 'Input',
Value: null
},
{
Name: 'TimeRange',
Type: 'Input',
Value: null // Optional: {start: Date, end: Date}
},
{
Name: 'Analytics',
Type: 'Output',
Value: null
},
{
Name: 'RawAnalytics',
Type: 'Output',
Value: null
}
];
}
/**
* Get action description
*/
public get Description(): string {
return 'Retrieves detailed analytics for a LinkedIn post (organization posts have more detailed analytics than personal posts)';
}
}