UNPKG

@memberjunction/actions-bizapps-social

Version:

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

234 lines (211 loc) 8.9 kB
import { RegisterClass } from '@memberjunction/global'; import { TwitterBaseAction, Tweet } from '../twitter-base.action'; import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base'; import { LogStatus, LogError } from '@memberjunction/core'; import { SocialPost } from '../../../base/base-social.action'; import { BaseAction } from '@memberjunction/actions'; /** * Action to get mentions (tweets that mention the authenticated user) from Twitter/X */ @RegisterClass(BaseAction, 'TwitterGetMentionsAction') export class TwitterGetMentionsAction extends TwitterBaseAction { /** * Get mentions from Twitter */ 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 maxResults = this.getParamValue(Params, 'MaxResults') || 100; const startTime = this.getParamValue(Params, 'StartTime'); const endTime = this.getParamValue(Params, 'EndTime'); const sinceId = this.getParamValue(Params, 'SinceID'); const untilId = this.getParamValue(Params, 'UntilID'); const includeRetweets = this.getParamValue(Params, 'IncludeRetweets') !== false; // Default true // Get current user info const currentUser = await this.getCurrentUser(); // Build query parameters const queryParams: Record<string, any> = { 'tweet.fields': 'id,text,created_at,author_id,conversation_id,public_metrics,attachments,entities,referenced_tweets,in_reply_to_user_id', 'user.fields': 'id,name,username,profile_image_url,description,created_at,verified', 'media.fields': 'url,preview_image_url,type,width,height', 'expansions': 'author_id,attachments.media_keys,referenced_tweets.id,referenced_tweets.id.author_id,in_reply_to_user_id', 'max_results': Math.min(maxResults, 100) // API limit per request }; // Add time-based filters if provided if (startTime) { queryParams['start_time'] = this.formatTwitterDate(startTime); } if (endTime) { queryParams['end_time'] = this.formatTwitterDate(endTime); } if (sinceId) { queryParams['since_id'] = sinceId; } if (untilId) { queryParams['until_id'] = untilId; } // Get mentions using the mentions timeline endpoint const endpoint = `/users/${currentUser.id}/mentions`; LogStatus(`Getting mentions for @${currentUser.username}...`); const mentions = await this.getPaginatedTweets(endpoint, queryParams, maxResults); // Filter out retweets if requested let filteredMentions = mentions; if (!includeRetweets) { filteredMentions = mentions.filter(tweet => !tweet.referenced_tweets || !tweet.referenced_tweets.some(ref => ref.type === 'retweeted') ); } // Convert to normalized format const normalizedPosts: SocialPost[] = filteredMentions.map(tweet => this.normalizePost(tweet)); // Group mentions by type const mentionTypes = { directMentions: 0, replies: 0, retweets: 0, quotes: 0 }; filteredMentions.forEach(tweet => { if (tweet.referenced_tweets) { const refTypes = tweet.referenced_tweets.map(ref => ref.type); if (refTypes.includes('replied_to')) { mentionTypes.replies++; } else if (refTypes.includes('retweeted')) { mentionTypes.retweets++; } else if (refTypes.includes('quoted')) { mentionTypes.quotes++; } } else { mentionTypes.directMentions++; } }); // Calculate engagement statistics const stats = { totalMentions: normalizedPosts.length, mentionTypes, totalEngagement: 0, averageEngagement: 0, topMentions: [] as any[] }; // Calculate total engagement normalizedPosts.forEach(post => { if (post.analytics) { stats.totalEngagement += post.analytics.engagements; } }); // Calculate average engagement if (normalizedPosts.length > 0) { stats.averageEngagement = Math.round(stats.totalEngagement / normalizedPosts.length); } // Get top 5 mentions by engagement stats.topMentions = normalizedPosts .filter(post => post.analytics) .sort((a, b) => (b.analytics?.engagements || 0) - (a.analytics?.engagements || 0)) .slice(0, 5) .map(post => ({ id: post.id, content: post.content.substring(0, 100) + (post.content.length > 100 ? '...' : ''), engagement: post.analytics?.engagements, author: filteredMentions.find(t => t.id === post.id)?.author_id })); // Update output parameters const outputParams = [...Params]; const postsParam = outputParams.find(p => p.Name === 'Mentions'); if (postsParam) postsParam.Value = normalizedPosts; const tweetsParam = outputParams.find(p => p.Name === 'Tweets'); if (tweetsParam) tweetsParam.Value = filteredMentions; const statsParam = outputParams.find(p => p.Name === 'Statistics'); if (statsParam) statsParam.Value = stats; return { Success: true, ResultCode: 'SUCCESS', Message: `Successfully retrieved ${normalizedPosts.length} mentions`, Params: outputParams }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { Success: false, ResultCode: this.getErrorCode(error), Message: `Failed to get mentions: ${errorMessage}`, Params }; } } /** * Get error code based on error type */ private getErrorCode(error: any): string { if (error instanceof Error) { if (error.message.includes('Rate Limit')) return 'RATE_LIMIT'; if (error.message.includes('Unauthorized')) return 'INVALID_TOKEN'; if (error.message.includes('Forbidden')) return 'INSUFFICIENT_PERMISSIONS'; } return 'ERROR'; } /** * Define the parameters this action expects */ public get Params(): ActionParam[] { return [ ...this.commonSocialParams, { Name: 'MaxResults', Type: 'Input', Value: 100 }, { Name: 'StartTime', Type: 'Input', Value: null }, { Name: 'EndTime', Type: 'Input', Value: null }, { Name: 'SinceID', Type: 'Input', Value: null }, { Name: 'UntilID', Type: 'Input', Value: null }, { Name: 'IncludeRetweets', Type: 'Input', Value: true }, { Name: 'Mentions', Type: 'Output', Value: null }, { Name: 'Tweets', Type: 'Output', Value: null }, { Name: 'Statistics', Type: 'Output', Value: null } ]; } /** * Get action description */ public get Description(): string { return 'Gets tweets that mention the authenticated user on Twitter/X with engagement statistics and filtering options'; } }