Action
Tweet Storm Multi-line
Based upon Tweet Storm action by agiletortoise.
Divides up a draft into separate tweets using == as a tweet separator, allowing for multi-line tweets in a thread. Removes tweet numbers.
Builds a preview of the tweets as they will be posted and displays it. If “Continue” tapped from preview, posts thread.
Steps
-
script
// set identifier for Twitter credentials you wish to use const twitterIdentifier = ""; // max characters per tweet. Leave room for /n. const maxCharacters = 248; // Utility functions function divideTweets(input) { var paragraphs = input.split("=="); return paragraphs; } function countString(current, total) { return ` `; } const urlRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/g; function isTweetValid(tweet) { var unlinked = tweet.replace(urlRegex, "00000000000000000000000"); return unlinked.length <= maxCharacters && unlinked.length > 0; } function areAllTweetsValid(tweets) { if (tweets.length == 0) { return false; } for (var tweet of tweets) { if (!isTweetValid(tweet)) { return false; } } return true; } function htmlSafe(s) { return s.replace(/</g, "<").replace(/\n/g, "<br>\n"); } // build HTML preview display the resulting tweets. var html = ["<html><head>"]; html.push("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"); html.push("<style>"); html.push("body { background: #666; color: #444; font-family: system-ui, -apple-system; margin: 1em auto; max-width:360px; }"); html.push("p {padding: 1em; background-color: #eee; }"); html.push("p.error{ background-color: #FFCDDD; color: maroon; font-weight: bold; padding: 1.5em; }\n"); html.push("p.invalid{ color: maroon; }\n"); html.push("p.reply { border-left: 5px solid #bbb; }"); html.push("p.preview{ font-style:italic; background-color: #444; color: #aaa; font-size: .9em; }\n"); html.push("span.note{ background-color: maroon; color: white; font-weight: bold; border-radius:3px; padding: .25em; font-size: .8em; }\n"); html.push("</style>"); html.push("</head><body>"); const tweets = divideTweets(draft.content); var isValid = areAllTweetsValid(tweets); if (!isValid) { html.push("<p class='error'>"); html.push("This draft cannot be posted as a tweet storm. Be sure to divide the draft into paragraphs of " + maxCharacters + " characters or shorter."); html.push("</p>"); } else { html.push("<p class='preview'>"); html.push("Preview tweets as they will be posted below. Tap continue to post the tweet storm, cancel to go back without posting"); html.push("</p>"); } if (tweets.length > 0) { var ct = 1; for (var tweet of tweets) { if (isTweetValid(tweet)) { html.push("<p class='valid" + (ct > 1 ? " reply" : "") + "'>"); html.push(htmlSafe(tweet)); html.push(countString(ct, tweets.length)); html.push("</p>"); } else { html.push("<p class='invalid" + (ct > 1 ? " reply" : "") + "'>"); html.push("<span class='note'>" + tweet.length + " characters</span> "); html.push(htmlSafe(tweet)); html.push(countString(ct, tweets.length)); html.push("</p>"); } ct++; } } else { html.push("<p>Nothing to tweet</p>"); } html.push("</body></html>"); draft.setTemplateTag("html", html.join("\n"));
-
htmlpreview
[[html]]
-
script
// If we reach this script // We have a valid set of tweets ready // in the `tweets` const, we just need to post them... // a few base values const updateURL = "https://api.twitter.com/1.1/statuses/update.json"; // sleep method to use to avoid rate limiting const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) if (isValid) { var twitter = Twitter.create(twitterIdentifier); var ct = 1; var inReplyTo = ""; var hasError = false; for (var tweet of tweets) { const tweetContent = tweet + countString(ct, tweets.length); var params = { "status": tweetContent } // if this is the first tweet // set the reply information to thread tweet storm if (ct != 1) { params["in_reply_to_status_id"] = inReplyTo; console.log(inReplyTo); } // make API request var response = twitter.request({ "url": updateURL, "method": "POST", "parameters": params }); if (response.statusCode == 200 || response.statusCode == 201) { var id = response.responseData.id_str; var sn = response.responseData.user.screen_name var tweetURL = `http://twitter.com/${sn}/status/${id}`; inReplyTo = id; console.log("Tweet posted: " + tweetURL); } else { console.log("Error: " + response.error); hasError = true; break; } ct++; sleep(400); // avoid rate limiting with little pause } if (!hasError) { console.log("Tweet storm posted"); } else { console.log("Tweet storm failed"); context.fail(); } } else { context.cancel(); }
Options
-
After Success Default Notification Info Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.