Turns your content plan into blog posts: you approve the outline, then the finished post, before anything is published on WordPress.
Uses
Google Sheets
OpenAIWordPress
Write blog posts from your content plan, with your sign-off
Every weekday at 08:00, the next topic on your content plan that has not been through review is picked up and an outline is written for it. Approve the outline and the post is written to it, with proper headings and paragraphs for WordPress, and shown to you in full. Reject it and nothing is written: the topic is closed, and any comment you leave is kept on your record tab. Before anything reaches your site you decide again: approve and it goes live, reject or leave it and it is saved in WordPress as a draft.
You end up with one post per run, titled exactly as the topic reads on your plan, either live or waiting as a WordPress draft. Silence never publishes. The topic is recorded as awaiting approval on a second tab before you are asked, and every outcome, including a draft that came back too short to use, is recorded there too, so a topic is never offered twice; to have another go at one, delete its rows from that tab. Your plan tab is only ever read, never changed, however long it grows.
You need Google Sheets, OpenAI and WordPress connections, and one spreadsheet with a plan tab (a 'Topic' column and an optional 'Angle' column) and a record tab headed 'Topic', 'Recorded on', 'Decision', 'Post ID', 'Link' and 'Your note'. Fill in the spreadsheet's link, the two tab names and a description of how your posts should read.
spreadsheetUrl to blank.Set spreadsheetUrl to blank.planTab to blank.Set planTab to blank.recordTab to blank.Set recordTab to blank.houseStyle to blank.Set houseStyle to blank.doneTopics to an empty list.Set doneTopics to an empty list.recordUnreadableIf a step below fails, save the error as recordUnreadabledoneTopics to the values from the Google Sheets “Read column” step.Change doneTopics to the values from the Google Sheets “Read column” step.plan to nothing.Set plan to nothing.planUnreadableIf a step below fails, save the error as planUnreadablespreadsheetUrl. Save it as plan.Read the rows of spreadsheetUrl. Save it as plan.planUnreadable as text.Stop and report a problem: The '@planTab' tab could not be read: planUnreadable as text.planRows to plan.rows.Set planRows to plan.rows.topic to blank.Set topic to blank.angle to blank.Set angle to blank.planRow in planRows:For each planRow in planRows:candidateTopic to blank.Set candidateTopic to blank.noTopicCellIf a step below fails, save the error as noTopicCellplanRow.Topic and save it as candidateTopic.Trim planRow.Topic and save it as candidateTopic.candidateTopic to blank.Change candidateTopic to blank.candidateTopic is nullIf candidateTopic is nullcandidateTopic to blank.Change candidateTopic to blank.candidateTopic is emptyIf candidateTopic is emptydoneTopics contains candidateTopicIf doneTopics contains candidateTopictopic to candidateTopic.Change topic to candidateTopic.noAngleCellIf a step below fails, save the error as noAngleCellplanRow.Angle and save it as angle.Trim planRow.Angle and save it as angle.angle to blank.Change angle to blank.angle is nullIf angle is nullangle to blank.Change angle to blank.topic is emptyIf topic is emptyrecordedOn.Get today's date and save it as recordedOn.outlinePrompt to Plan one blog post. Reply with a title line, then four to six section headings, each with one sentence saying what the section will argue. Reply with the outline only - no preamble, and do not write the post itself.Set outlinePrompt to Plan one blog post. Reply with a title line, then four to six section headings, each with one sentence saying what the section will argue. Reply with the outline only - no preamble, and do not write the post itself.
House style:
houseStyle
Topic: topic
Angle the writer asked for: angle.outline to blank.Set outline to blank.outlineErrorIf a step below fails, save the error as outlineErroroutline.Trim the response text from the OpenAI “Ask OpenAI” step and save it as outline.outlineError as text.Show in the output: The outline for '@topic' could not be written: outlineError as text.outline to blank.Change outline to blank.outline is less than 80If the length of outline is less than 80claimRecordRow to [topic, recordedOn, Outline - awaiting approval, , , ].Set claimRecordRow to [topic, recordedOn, Outline - awaiting approval, , , ].recordTab in spreadsheetUrl.Add a row to recordTab in spreadsheetUrl.outlineAnswer to blank.Set outlineAnswer to blank.outlineApproved to false.Set outlineApproved to false.outlineDecision to Outline - no answer.Set outlineDecision to Outline - no answer.outlineApprovalErrorIf a step below fails, save the error as outlineApprovalError@topic? Save it as outlineReview.Request approval: Approve the outline for @topic? Save it as outlineReview.Approve and the post is written to this outline and brought back to you before anything reaches WordPress. Reject it and nothing is written: the topic is closed, any comment you leave is kept on the record tab, and the topic is not offered again. To try it again, delete its rows...outlineReview.approved is yesIf outlineReview.approved is yesoutlineApproved to true.Change outlineApproved to true.outlineDecision to Outline - rejected.Change outlineDecision to Outline - rejected.outlineReview.comment and save it as outlineAnswer.Trim outlineReview.comment and save it as outlineAnswer.outlineApprovalError as text.Show in the output: The outline review for '@topic' could not be sent: outlineApprovalError as text.outlineApproved is noIf outlineApproved is nooutlineRecordRow to [topic, recordedOn, outlineDecision, , , outlineAnswer].Set outlineRecordRow to [topic, recordedOn, outlineDecision, , , outlineAnswer].recordTab in spreadsheetUrl.Add a row to recordTab in spreadsheetUrl.draftPrompt to Write the blog post below to the outline you are given. Follow the outline's sections in order. Reply with the post body only - no title line, no commentary. Write it as HTML for WordPress: <h2> for each section heading, <p> for paragraphs, <ul><li> for lists and <strong> for emphasis. Use no Markdown (no #, ** or -) and do not wrap the HTML in a code block. Do not state a statistic, price, date or named source unless it is in the brief.Set draftPrompt to Write the blog post below to the outline you are given. Follow the outline's sections in order. Reply with the post body only - no title line, no commentary. Write it as HTML for WordPress: <h2> for each section heading, <p> for paragraphs, <ul><li> for lists and <strong> for emphasis. Use no Markdown (no #, ** or -) and do not wrap the HTML in a code block. Do not state a statistic, price, date or named source unless it is in the brief.
House style:
houseStyle
Topic: topic
Approved outline:
outline.draft to blank.Set draft to blank.draftErrorIf a step below fails, save the error as draftErrordraft.Trim the response text from the OpenAI “Ask OpenAI” step and save it as draft.draftError as text.Show in the output: The post for '@topic' could not be written: draftError as text.draft to blank.Change draft to blank.^\s*```[a-zA-Z]*\s*|\s*```\s*$ with in draft and save it as draft.Replace the pattern ^\s*```[a-zA-Z]*\s*|\s*```\s*$ with in draft and save it as draft.draft is less than 400If the length of draft is less than 400shortDraftRecordRow to [topic, recordedOn, Draft too short - nothing sent to WordPress, , , ].Set shortDraftRecordRow to [topic, recordedOn, Draft too short - nothing sent to WordPress, , , ].recordTab in spreadsheetUrl.Add a row to recordTab in spreadsheetUrl.(?i)(\s*(</(p|h[1-6]|li|ul|ol|blockquote)\s*>|<br\s*/?>)\s*)+ with "Replace the pattern (?i)(\s*(</(p|h[1-6]|li|ul|ol|blockquote)\s*>|<br\s*/?>)\s*)+ with
in draft and save it as draftText.(?i)<li[^>]*> with - in draftText and save it as draftText.Replace the pattern (?i)<li[^>]*> with - in draftText and save it as draftText.<[^>]+> with in draftText and save it as draftText.Replace the pattern <[^>]+> with in draftText and save it as draftText.draftText back from HTML and save it as draftText.Make draftText back from HTML and save it as draftText.publishAnswer to blank.Set publishAnswer to blank.postStatus to draft.Set postStatus to draft.publishApprovalErrorIf a step below fails, save the error as publishApprovalError@topic on your site? Save it as publishReview.Request approval: Publish @topic on your site? Save it as publishReview.Approve and it goes live on your site. Reject it, or leave it, and it is saved in WordPress as a draft for you to finish by hand.
draftTextpublishReview.approved is yesIf publishReview.approved is yespostStatus to publish.Change postStatus to publish.publishReview.comment and save it as publishAnswer.Trim publishReview.comment and save it as publishAnswer.publishApprovalError as text.Show in the output: The publish review for '@topic' could not be sent, so it will be saved as a draft: publishApprovalError as text.createdPost to nothing.Set createdPost to nothing.wordpressErrorIf a step below fails, save the error as wordpressErrortopic. Save it as createdPost.Create the post topic. Save it as createdPost.draftwordpressError as text.Show in the output: WordPress refused the post for '@topic': wordpressError as text.draft.Show in the output: draft.refusedRecordRow to [topic, recordedOn, WordPress refused the post - nothing created, , , wordpressError.message].Set refusedRecordRow to [topic, recordedOn, WordPress refused the post - nothing created, , , wordpressError.message].refusedRecordErrorIf a step below fails, save the error as refusedRecordErrorrecordTab in spreadsheetUrl.Add a row to recordTab in spreadsheetUrl.refusedRecordError as text.Show in the output: The outcome for '@topic' could not be recorded on the '@recordTab' tab: refusedRecordError as text.postId to blank.Set postId to blank.noPostIdIf a step below fails, save the error as noPostIdcreatedPost.id and save it as postId.Trim createdPost.id and save it as postId.postId to blank.Change postId to blank.postId is nullIf postId is nullpostId to blank.Change postId to blank.postLink to blank.Set postLink to blank.noPostLinkIf a step below fails, save the error as noPostLinkcreatedPost.link and save it as postLink.Trim createdPost.link and save it as postLink.postLink to blank.Change postLink to blank.postLink is nullIf postLink is nullpostLink to blank.Change postLink to blank.finalDecision to Saved in WordPress as a draft.Set finalDecision to Saved in WordPress as a draft.postStatus is publishIf postStatus is publishfinalDecision to Published.Change finalDecision to Published.publishedRecordRow to [topic, recordedOn, finalDecision, postId, postLink, publishAnswer].Set publishedRecordRow to [topic, recordedOn, finalDecision, postId, postLink, publishAnswer].recordTab in spreadsheetUrl.Add a row to recordTab in spreadsheetUrl.finalDecision. postLink.Show in the output: '@topic' - finalDecision. postLink.