Publishes the posts in your spreadsheet queue on the day each one is due, and writes the date and link back so none goes out twice.
Uses
Google Sheets
X
Post your queued content to X on schedule
queueSheetUrl to blank.Set queueSheetUrl to blank.queueTabName to blank.Set queueTabName to blank.maxPostsThisRun to 5.Set maxPostsThisRun to 5.queueSheetUrl. Save it as queue.Read the rows of queueSheetUrl. Save it as queue.queueHeaders to queue.headers.Set queueHeaders to queue.headers.headerLine to queueHeaders joined by ||.Set headerLine to queueHeaders joined by ||.headerLine doesn't start with Posted On|Link|If headerLine doesn't start with Posted On|Link|Yes
No
No steps
queueHeaders doesn't contain PostIf queueHeaders doesn't contain PostYes
No
No steps
queueHeaders doesn't contain Publish DateIf queueHeaders doesn't contain Publish DateYes
No
No steps
todaysDate.Get today's date and save it as todaysDate.profile.Get your account. Save it as profile.authorHandle to i/web.Set authorHandle to i/web.profileProblemIf a step below fails, save the error as profileProblemTry
profile.user.username and save it as foundHandle.Trim profile.user.username and save it as foundHandle.foundHandle isn't emptyIf foundHandle isn't emptyYes
authorHandle to foundHandle.Change authorHandle to foundHandle.No
No steps
On error
Could not read your X username, so the links written back to the queue use the generic form..Show in the output: Could not read your X username, so the links written back to the queue use the generic form..postsPublished to 0.Set postsPublished to 0.published to an empty record.Set published to an empty record.dueLeft to 0.Set dueLeft to 0.problems to an empty list.Set problems to an empty list.row in queue.rows:For each row in queue.rows:rowProblemIf a step below fails, save the error as rowProblemTry
rowNumber to row.row_number.Set rowNumber to row.row_number.row["Posted On"] and save it as postedOn.Trim row["Posted On"] and save it as postedOn.postedOn isn't emptyIf postedOn isn't emptyYes
No
No steps
row.Post and save it as postText.Trim row.Post and save it as postText.postText is emptyIf postText is emptyYes
No
No steps
row["Publish Date"] and save it as publishDate.Trim row["Publish Date"] and save it as publishDate.publishDate is emptyIf publishDate is emptyYes
No
No steps
daysSinceDue to -1.Set daysSinceDue to -1.dateProblemIf a step below fails, save the error as dateProblemTry
publishDate to todaysDate and save it as daysSinceDue.Count the days from publishDate to todaysDate and save it as daysSinceDue.On error
rowNumber: the publish date '@publishDate' is not a date, so the post was left in the queue. Write it as 2026-09-01. to the end of problems and save it as problems.Add Row rowNumber: the publish date '@publishDate' is not a date, so the post was left in the queue. Write it as 2026-09-01. to the end of problems and save it as problems.daysSinceDue is less than 0If daysSinceDue is less than 0Yes
No
No steps
https?://\S+ with https://t.co/LinkLength in postText and save it as countedText.Replace the pattern https?://\S+ with https://t.co/LinkLength in postText and save it as countedText.countedText and save it as countedLength.Count the characters in countedText and save it as countedLength.countedLength is greater than 280If countedLength is greater than 280Yes
rowNumber: the post is countedLength characters as X counts them (a link counts as 23), over the 280 X accepts, so it was left in the queue. Shorten it. to the end of problems and save it as problems.Add Row rowNumber: the post is countedLength characters as X counts them (a link counts as 23), over the 280 X accepts, so it was left in the queue. Shorten it. to the end of problems and save it as problems.No
No steps
postsPublished is greater than or equal to maxPostsThisRunIf postsPublished is greater than or equal to maxPostsThisRunYes
dueLeft.Add 1 to dueLeft.No
No steps
rowNumber of queueTabName in queueSheetUrl.Overwrite row rowNumber of queueTabName in queueSheetUrl.postProblemIf a step below fails, save the error as postProblemTry
postText. Save it as published.Post: postText. Save it as published.On error
rowNumber: X did not publish it - postProblem.message to the end of problems and save it as problems.Add Row rowNumber: X did not publish it - postProblem.message to the end of problems and save it as problems.rowNumber of queueTabName in queueSheetUrl.Overwrite row rowNumber of queueTabName in queueSheetUrl.postsPublished.Add 1 to postsPublished.postLink to https://x.com/@authorHandle/status/@published.tweet.id.Set postLink to https://x.com/@authorHandle/status/@published.tweet.id.linkProblemIf a step below fails, save the error as linkProblemTry
rowNumber of queueTabName in queueSheetUrl.Overwrite row rowNumber of queueTabName in queueSheetUrl.On error
rowNumber was published (postLink) but its link could not be written back, so the row still reads 'in progress'. Paste the link into it. Google Sheets said: linkProblem.message to the end of problems and save it as problems.Add Row rowNumber was published (postLink) but its link could not be written back, so the row still reads 'in progress'. Paste the link into it. Google Sheets said: linkProblem.message to the end of problems and save it as problems.On error
row.row_number could not be handled, so nothing was posted from it: rowProblem.message to the end of problems and save it as problems.Add Row row.row_number could not be handled, so nothing was posted from it: rowProblem.message to the end of problems and save it as problems.summaryLines to an empty list.Set summaryLines to an empty list.postsPublished post(s) from the queue. to the end of summaryLines and save it as summaryLines.Add Published postsPublished post(s) from the queue. to the end of summaryLines and save it as summaryLines.dueLeft is 1If dueLeft is 1Yes
maxPostsThisRun. to the end of summaryLines and save it as summaryLines.Add 1 due post left for tomorrow, because 'maxPostsThisRun' is maxPostsThisRun. to the end of summaryLines and save it as summaryLines.Else if @dueLeft is greater than 1
dueLeft due posts left for tomorrow, because 'maxPostsThisRun' is maxPostsThisRun. to the end of summaryLines and save it as summaryLines.Add dueLeft due posts left for tomorrow, because 'maxPostsThisRun' is maxPostsThisRun. to the end of summaryLines and save it as summaryLines.Otherwise
No steps
problems with a new line and save it as problemText.Join problems with a new line and save it as problemText.problems isn't emptyIf problems isn't emptyYes
problemText to the end of summaryLines and save it as summaryLines.summaryLines joined by a blank line.Stop and report a problem: summaryLines joined by a blank line.No
No steps
summaryLines joined by a blank line.Finish the run: summaryLines joined by a blank line.