@memberjunction/actions-bizapps-social
Version:
Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer
282 lines (249 loc) • 10 kB
text/typescript
import { RegisterClass } from '@memberjunction/global';
import { HootSuiteBaseAction, HootSuiteAnalytics } from '../hootsuite-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 retrieve analytics data from HootSuite
*/
export class HootSuiteGetAnalyticsAction extends HootSuiteBaseAction {
/**
* Get analytics data from HootSuite
*/
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)) {
throw new Error('Failed to initialize OAuth connection');
}
// Extract parameters
const postId = this.getParamValue(Params, 'PostID');
const profileId = this.getParamValue(Params, 'ProfileID');
const startDate = this.getParamValue(Params, 'StartDate');
const endDate = this.getParamValue(Params, 'EndDate');
const metricsType = this.getParamValue(Params, 'MetricsType') || 'all';
const aggregateByProfile = this.getParamValue(Params, 'AggregateByProfile') || false;
// Determine what analytics to fetch
let analyticsData: any;
if (postId) {
// Get analytics for specific post
analyticsData = await this.getPostAnalytics(postId, startDate, endDate);
} else if (profileId) {
// Get analytics for specific profile
analyticsData = await this.getProfileAnalytics(profileId, startDate, endDate, metricsType);
} else {
// Get analytics for all profiles
analyticsData = await this.getAllProfilesAnalytics(startDate, endDate, metricsType, aggregateByProfile);
}
// Normalize analytics
const normalizedAnalytics = this.processAnalyticsData(analyticsData, metricsType);
// Create summary
const summary = {
period: {
start: startDate || 'Not specified',
end: endDate || 'Not specified'
},
metricsType: metricsType,
totalMetrics: this.calculateTotalMetrics(normalizedAnalytics),
topPerformingPosts: this.getTopPerformingPosts(normalizedAnalytics),
engagementRate: this.calculateEngagementRate(normalizedAnalytics)
};
// Update output parameters
const outputParams = [...Params];
const analyticsParam = outputParams.find(p => p.Name === 'Analytics');
if (analyticsParam) analyticsParam.Value = normalizedAnalytics;
const summaryParam = outputParams.find(p => p.Name === 'Summary');
if (summaryParam) summaryParam.Value = summary;
return {
Success: true,
ResultCode: 'SUCCESS',
Message: 'Successfully retrieved analytics data',
Params: outputParams
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
Success: false,
ResultCode: 'ERROR',
Message: `Failed to get analytics: ${errorMessage}`,
Params
};
}
}
/**
* Get analytics for a specific post
*/
private async getPostAnalytics(postId: string, startDate?: string, endDate?: string): Promise<HootSuiteAnalytics> {
const params: any = {};
if (startDate) params.startTime = this.formatHootSuiteDate(startDate);
if (endDate) params.endTime = this.formatHootSuiteDate(endDate);
const response = await this.axiosInstance.get(`/analytics/posts/${postId}`, { params });
return response.data;
}
/**
* Get analytics for a specific profile
*/
private async getProfileAnalytics(
profileId: string,
startDate?: string,
endDate?: string,
metricsType?: string
): Promise<any> {
const params: any = {
socialProfileIds: profileId
};
if (startDate) params.startTime = this.formatHootSuiteDate(startDate);
if (endDate) params.endTime = this.formatHootSuiteDate(endDate);
if (metricsType && metricsType !== 'all') params.metrics = this.getMetricsList(metricsType);
const response = await this.axiosInstance.get('/analytics/profiles', { params });
return response.data;
}
/**
* Get analytics for all profiles
*/
private async getAllProfilesAnalytics(
startDate?: string,
endDate?: string,
metricsType?: string,
aggregateByProfile?: boolean
): Promise<any> {
// First get all profiles
const profiles = await this.getSocialProfiles();
if (profiles.length === 0) {
return { data: [] };
}
const params: any = {
socialProfileIds: profiles.map(p => p.id).join(',')
};
if (startDate) params.startTime = this.formatHootSuiteDate(startDate);
if (endDate) params.endTime = this.formatHootSuiteDate(endDate);
if (metricsType && metricsType !== 'all') params.metrics = this.getMetricsList(metricsType);
if (aggregateByProfile) params.groupBy = 'socialProfile';
const response = await this.axiosInstance.get('/analytics/profiles', { params });
return response.data;
}
/**
* Get list of metrics based on type
*/
private getMetricsList(metricsType: string): string {
const metricsMap: Record<string, string[]> = {
'engagement': ['likes', 'comments', 'shares', 'engagements'],
'reach': ['impressions', 'reach'],
'clicks': ['clicks', 'linkClicks'],
'all': ['likes', 'comments', 'shares', 'clicks', 'impressions', 'engagements', 'reach']
};
return (metricsMap[metricsType] || metricsMap['all']).join(',');
}
/**
* Process and normalize analytics data
*/
private processAnalyticsData(data: any, metricsType: string): SocialAnalytics[] {
if (!data || !data.data) return [];
const results: SocialAnalytics[] = [];
if (Array.isArray(data.data)) {
data.data.forEach((item: any) => {
results.push(this.normalizeAnalytics(item.metrics));
});
} else if (data.metrics) {
results.push(this.normalizeAnalytics(data.metrics));
}
return results;
}
/**
* Calculate total metrics across all data
*/
private calculateTotalMetrics(analytics: SocialAnalytics[]): SocialAnalytics {
return analytics.reduce((total, current) => ({
impressions: total.impressions + current.impressions,
engagements: total.engagements + current.engagements,
clicks: total.clicks + current.clicks,
shares: total.shares + current.shares,
comments: total.comments + current.comments,
likes: total.likes + current.likes,
reach: total.reach + current.reach,
saves: (total.saves || 0) + (current.saves || 0),
videoViews: (total.videoViews || 0) + (current.videoViews || 0),
platformMetrics: {}
}), {
impressions: 0,
engagements: 0,
clicks: 0,
shares: 0,
comments: 0,
likes: 0,
reach: 0,
saves: 0,
videoViews: 0,
platformMetrics: {}
});
}
/**
* Get top performing posts based on engagement
*/
private getTopPerformingPosts(analytics: SocialAnalytics[]): SocialAnalytics[] {
return analytics
.sort((a, b) => b.engagements - a.engagements)
.slice(0, 5);
}
/**
* Calculate average engagement rate
*/
private calculateEngagementRate(analytics: SocialAnalytics[]): number {
const total = this.calculateTotalMetrics(analytics);
if (total.impressions === 0) return 0;
return (total.engagements / total.impressions) * 100;
}
/**
* Define the parameters this action expects
*/
public get Params(): ActionParam[] {
return [
...this.commonSocialParams,
{
Name: 'PostID',
Type: 'Input',
Value: null
},
{
Name: 'StartDate',
Type: 'Input',
Value: null
},
{
Name: 'EndDate',
Type: 'Input',
Value: null
},
{
Name: 'MetricsType',
Type: 'Input',
Value: null
},
{
Name: 'AggregateByProfile',
Type: 'Input',
Value: null
},
{
Name: 'Analytics',
Type: 'Output',
Value: null
},
{
Name: 'Summary',
Type: 'Output',
Value: null
}
];
}
/**
* Get action description
*/
public get Description(): string {
return 'Retrieves analytics data from HootSuite for posts, profiles, or overall account performance';
}
}