UNPKG

@memberjunction/actions-bizapps-social

Version:

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

205 lines (181 loc) 7.39 kB
import { RegisterClass } from '@memberjunction/global'; import { TwitterBaseAction, CreateTweetData } from '../twitter-base.action'; import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base'; import { LogStatus, LogError } from '@memberjunction/core'; import { MediaFile } from '../../../base/base-social.action'; import { BaseAction } from '@memberjunction/actions'; /** * Action to create a tweet on Twitter/X */ @RegisterClass(BaseAction, 'TwitterCreateTweetAction') export class TwitterCreateTweetAction extends TwitterBaseAction { /** * Create a tweet on 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 content = this.getParamValue(Params, 'Content'); const mediaFiles = this.getParamValue(Params, 'MediaFiles'); const replyToTweetId = this.getParamValue(Params, 'ReplyToTweetID'); const quoteTweetId = this.getParamValue(Params, 'QuoteTweetID'); const pollOptions = this.getParamValue(Params, 'PollOptions'); const pollDurationMinutes = this.getParamValue(Params, 'PollDurationMinutes') || 1440; // Default 24 hours // Validate required parameters if (!content) { throw new Error('Content is required'); } // Validate content length (Twitter's limit is 280 characters) if (content.length > 280) { throw new Error(`Content exceeds Twitter's 280 character limit (current: ${content.length} characters)`); } // Build tweet data const tweetData: CreateTweetData = { text: content }; // Add reply if specified if (replyToTweetId) { tweetData.reply = { in_reply_to_tweet_id: replyToTweetId }; } // Add quote tweet if specified if (quoteTweetId) { tweetData.quote_tweet_id = quoteTweetId; } // Add poll if specified if (pollOptions && Array.isArray(pollOptions) && pollOptions.length >= 2) { if (pollOptions.length > 4) { throw new Error('Twitter polls support a maximum of 4 options'); } // Validate poll option lengths (max 25 characters each) for (const option of pollOptions) { if (option.length > 25) { throw new Error(`Poll option "${option}" exceeds 25 character limit`); } } tweetData.poll = { options: pollOptions, duration_minutes: Math.min(Math.max(pollDurationMinutes, 5), 10080) // Min 5 minutes, max 7 days }; } // Upload media if provided if (mediaFiles && Array.isArray(mediaFiles) && mediaFiles.length > 0) { if (mediaFiles.length > 4) { throw new Error('Twitter supports a maximum of 4 media items per tweet'); } LogStatus(`Uploading ${mediaFiles.length} media files...`); const mediaIds = await this.uploadMedia(mediaFiles as MediaFile[]); tweetData.media = { media_ids: mediaIds }; } // Create the tweet LogStatus('Creating tweet...'); const tweet = await this.createTweet(tweetData); // Normalize the created tweet const normalizedPost = this.normalizePost(tweet); // Get user info for additional context const user = await this.getCurrentUser(); // Update output parameters const outputParams = [...Params]; const postParam = outputParams.find(p => p.Name === 'CreatedPost'); if (postParam) postParam.Value = normalizedPost; const tweetIdParam = outputParams.find(p => p.Name === 'TweetID'); if (tweetIdParam) tweetIdParam.Value = tweet.id; const tweetUrlParam = outputParams.find(p => p.Name === 'TweetURL'); if (tweetUrlParam) tweetUrlParam.Value = `https://twitter.com/${user.username}/status/${tweet.id}`; return { Success: true, ResultCode: 'SUCCESS', Message: `Successfully created tweet (ID: ${tweet.id})`, Params: outputParams }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { Success: false, ResultCode: this.getErrorCode(error), Message: `Failed to create tweet: ${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('character limit')) return 'CONTENT_TOO_LONG'; if (error.message.includes('media')) return 'INVALID_MEDIA'; } return 'ERROR'; } /** * Define the parameters this action expects */ public get Params(): ActionParam[] { return [ ...this.commonSocialParams, { Name: 'Content', Type: 'Input', Value: null }, { Name: 'MediaFiles', Type: 'Input', Value: null }, { Name: 'ReplyToTweetID', Type: 'Input', Value: null }, { Name: 'QuoteTweetID', Type: 'Input', Value: null }, { Name: 'PollOptions', Type: 'Input', Value: null }, { Name: 'PollDurationMinutes', Type: 'Input', Value: 1440 // Default 24 hours }, { Name: 'CreatedPost', Type: 'Output', Value: null }, { Name: 'TweetID', Type: 'Output', Value: null }, { Name: 'TweetURL', Type: 'Output', Value: null } ]; } /** * Get action description */ public get Description(): string { return 'Creates a tweet on Twitter/X with optional media attachments, polls, replies, or quote tweets'; } }