@memberjunction/actions-bizapps-social
Version:
Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer
218 lines • 9.57 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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TwitterCreateThreadAction = void 0;
const global_1 = require("@memberjunction/global");
const twitter_base_action_1 = require("../twitter-base.action");
const core_1 = require("@memberjunction/core");
const actions_1 = require("@memberjunction/actions");
/**
* Action to create a thread (series of connected tweets) on Twitter/X
*/
let TwitterCreateThreadAction = class TwitterCreateThreadAction extends twitter_base_action_1.TwitterBaseAction {
/**
* Create a thread on Twitter
*/
async InternalRunAction(params) {
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 tweets = this.getParamValue(Params, 'Tweets');
const mediaFilesByTweet = this.getParamValue(Params, 'MediaFilesByTweet');
const includeNumbers = this.getParamValue(Params, 'IncludeNumbers') !== false; // Default true
const numberFormat = this.getParamValue(Params, 'NumberFormat') || '{n}/{total}'; // Default format
// Validate required parameters
if (!tweets || !Array.isArray(tweets) || tweets.length === 0) {
throw new Error('Tweets array is required and must not be empty');
}
if (tweets.length < 2) {
throw new Error('A thread must contain at least 2 tweets');
}
if (tweets.length > 25) {
throw new Error('Twitter threads are limited to 25 tweets');
}
// Prepare tweets with numbering if requested
const preparedTweets = includeNumbers
? this.addThreadNumbers(tweets, numberFormat)
: tweets;
// Validate all tweet lengths
for (let i = 0; i < preparedTweets.length; i++) {
if (preparedTweets[i].length > 280) {
throw new Error(`Tweet ${i + 1} exceeds Twitter's 280 character limit (current: ${preparedTweets[i].length} characters)`);
}
}
const createdTweets = [];
const createdPosts = [];
let previousTweetId;
try {
// Create tweets in sequence
for (let i = 0; i < preparedTweets.length; i++) {
(0, core_1.LogStatus)(`Creating tweet ${i + 1} of ${preparedTweets.length}...`);
const tweetData = {
text: preparedTweets[i]
};
// Reply to previous tweet in thread
if (previousTweetId) {
tweetData.reply = {
in_reply_to_tweet_id: previousTweetId
};
}
// Add media if provided for this tweet
if (mediaFilesByTweet && mediaFilesByTweet[i]) {
const mediaFiles = mediaFilesByTweet[i];
if (Array.isArray(mediaFiles) && mediaFiles.length > 0) {
if (mediaFiles.length > 4) {
throw new Error(`Tweet ${i + 1}: Twitter supports a maximum of 4 media items per tweet`);
}
(0, core_1.LogStatus)(`Uploading ${mediaFiles.length} media files for tweet ${i + 1}...`);
const mediaIds = await this.uploadMedia(mediaFiles);
tweetData.media = {
media_ids: mediaIds
};
}
}
// Create the tweet
const tweet = await this.createTweet(tweetData);
createdTweets.push(tweet);
createdPosts.push(this.normalizePost(tweet));
previousTweetId = tweet.id;
// Small delay between tweets to avoid rate limiting
if (i < preparedTweets.length - 1) {
await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay
}
}
// Get user info for URLs
const user = await this.getCurrentUser();
// Build thread URL (link to first tweet)
const threadUrl = `https://twitter.com/${user.username}/status/${createdTweets[0].id}`;
// Update output parameters
const outputParams = [...Params];
const postsParam = outputParams.find(p => p.Name === 'CreatedPosts');
if (postsParam)
postsParam.Value = createdPosts;
const tweetIdsParam = outputParams.find(p => p.Name === 'TweetIDs');
if (tweetIdsParam)
tweetIdsParam.Value = createdTweets.map(t => t.id);
const threadUrlParam = outputParams.find(p => p.Name === 'ThreadURL');
if (threadUrlParam)
threadUrlParam.Value = threadUrl;
return {
Success: true,
ResultCode: 'SUCCESS',
Message: `Successfully created thread with ${createdTweets.length} tweets`,
Params: outputParams
};
}
catch (error) {
// If thread creation fails partway through, note which tweets were created
if (createdTweets.length > 0) {
(0, core_1.LogError)(`Thread creation failed after creating ${createdTweets.length} tweets. Tweet IDs: ${createdTweets.map(t => t.id).join(', ')}`);
}
throw error;
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
Success: false,
ResultCode: this.getErrorCode(error),
Message: `Failed to create thread: ${errorMessage}`,
Params
};
}
}
/**
* Add thread numbers to tweets
*/
addThreadNumbers(tweets, format) {
const total = tweets.length;
return tweets.map((tweet, index) => {
const number = index + 1;
const threadNumber = format
.replace('{n}', number.toString())
.replace('{total}', total.toString());
// Add number to beginning of tweet with a space
return `${threadNumber} ${tweet}`;
});
}
/**
* Get error code based on error type
*/
getErrorCode(error) {
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
*/
get Params() {
return [
...this.commonSocialParams,
{
Name: 'Tweets',
Type: 'Input',
Value: null
},
{
Name: 'MediaFilesByTweet',
Type: 'Input',
Value: null
},
{
Name: 'IncludeNumbers',
Type: 'Input',
Value: true
},
{
Name: 'NumberFormat',
Type: 'Input',
Value: '{n}/{total}'
},
{
Name: 'CreatedPosts',
Type: 'Output',
Value: null
},
{
Name: 'TweetIDs',
Type: 'Output',
Value: null
},
{
Name: 'ThreadURL',
Type: 'Output',
Value: null
}
];
}
/**
* Get action description
*/
get Description() {
return 'Creates a thread (series of connected tweets) on Twitter/X with optional media attachments and automatic numbering';
}
};
exports.TwitterCreateThreadAction = TwitterCreateThreadAction;
exports.TwitterCreateThreadAction = TwitterCreateThreadAction = __decorate([
(0, global_1.RegisterClass)(actions_1.BaseAction, 'TwitterCreateThreadAction')
], TwitterCreateThreadAction);
//# sourceMappingURL=create-thread.action.js.map