I'm a lazy person. Beyond being lazy, I'm also forgetful. That's a killer combo when it comes to repetitive tasks that I have to do week after week.
You may or may not know that I'm a frequent blogger. Over the past 5 years I've written over 200 blog posts. Despite the repetition, there's still one thing that I dread every time I launch a new post - social media.
Advertising my new content on socials like X and LinkedIn is one of the main ways I drive traffic to my site. But that takes time, creativity, and consistency that I don't always have. Sometimes I forget to post and my blog gets almost no views. Other times I intentionally don't post anything because I can't think of a good way to articulate what I wrote about in short form. It's tough!
To foster my laziness, I've spent a considerable amount of time building automations into my website to do things so I don't have to. I have automatic cross-posting, future post scheduling, text to speech (for accessibility purposes), writer analytics and trends, and engagement tracking across all cross-posts and articles I've ever written. But I don't have anything for social media.
Until now.
As part of my "new blog post workflow", I've added social media promotion creation and scheduling. Thank goodness!
Let's talk about how it works and I'll point you at the source code.
Social Post Creation
I've very quickly become dependent on ChatGPT and other generative AI services to do my work that I have no idea how I'd code for. It's enabled me to solve use cases that were actually impossible for me to implement without it.
Part of my existing workflow takes the Markdown format of my blog posts and passes it to OpenAI to run some analytics. It would create SEO optimized meta descriptions, analyze writing style and tone, and do basic metrics like word count.
It never occurred to me to also ask OpenAI to come up with a tweet that promoted the content. So I asked it to do that. But while I was at it, why not ask it for several posts?
I realized very recently that I was promoting my blog posts all wrong. I would follow an "at-most-once" approach to social media. I'd write one tweet and copy that over to LinkedIn and be done. But what about the people that missed that message?
Replaying your content is exactly like syndicating and replaying a TV show. The majority of people who watch an episode of a show don't see it during its original airing. It's the replay of the episode that gets the viewers. The episode is played at different times, on different days, and on different networks, therefore reaching a larger audience. So why not do that with social media posts about my blogs?
So I asked OpenAI to create 8 posts about my content that I can share over the course of a couple months. Adding this functionality into my workflow was as easy as making an extra call to my "Ask ChatGPT" lambda function in my Step Function workflow, formatting the output, and publishing an event.
Now that I had the social posts, I had to get them scheduled at meaningful times. And I definitely didn't want to do that manually. So I built a scheduler.
Scheduling Posts Programmatically
Now was time for the hard part - figuring out how to programmatically schedule and send social media posts. If you follow me on LinkedIn or Twitter, you might have seen me post about my struggles with this process.
Through a bunch of trials, errors, and discovery, I was able to figure out how to send a tweet via the X API inside of a Lambda function. Once I had that, I built the first iteration of my scheduler.
The process was triggered by an EventBridge event that kicked off a Step Function workflow. The workflow would look at the event input for a date and create a one-time EventBridge schedule. If the schedule already existed, it would update the time and message. Then it would fire an event saying the post was scheduled.
When the schedule would run, it would start a separate Step Function workflow that calls the "Send Tweet" Lambda function and sends a post successful event containing a link to the newly created tweet.
This worked fine, but it still required manual activity. I had to provide a time in the event payload for it to work. The whole point of this was to bolster my laziness, so that wouldn't do.
Bedrock To The Rescue
I genuinely enjoy trying out new AWS services when they become available. Since Amazon Bedrock recently went generally available, I figured it was time to give it a shot and see if it could solve my problems for me.
I needed to have some brains behind a scheduler. AI is the perfect tool for the job. I could provide it some posting rules and get back a suggested time for the post. So I grabbed a template from Serverless Land that had a deployable example of using Bedrock in a Lambda function and began tinkering.
There were a few rules I wanted to incorporate into my scheduler that ended up making an impact on the design:
- Post at most one time a day
- Don't post on the weekends
- Post when my audience is awake and usually online
- Space out posts for the same blog a minimum of 5 days apart
- Posts for a different blog can be scheduled on the next available day
To do this, I was going to need to track the schedules. Simply creating one-time EventBridge schedules wasn't going to cut it because I needed to know what was scheduled when.
So I created a simple data model in DynamoDB that allowed me to get all scheduled posts in the future sorted by date. When a tweet is posted, I remove the record from DynamoDB. Easy enough.
Access Patterns
- Get social post by id
- Get all social posts for a specific platform
pk | sk | type (GSI Hash Key) | sort (GSI Range Key) | campaign |
---|---|---|---|---|
{ postid1 } | "post" | "twitter" | 2023-11-01T14:00:00 | { blog name 1} |
{ postid2 } | "post" | "twitter" | 2023-11-03T14:00:00 | { blog name 2 } |
{ postid3 } | "post" | "linkedin" | 2023-11-07T18:45:00 | { blog name 1 } |
I group social posts under a campaign
property, which is a marketing term for a set of related tasks. Each blog post is it's own campaign, so when I build the prompt for the generative AI model, I can do some preprocessing and modify the rules before I ask it to do its thing.
Now that I had the data model, I had to update the existing workflows to create, update, and delete the records when they were run. This was handled by direct integrations to DynamoDB from my Step Function workflow.
With the data model fully in place, I could now provide Bedrock with all the information it needed to determine a schedule date. The workflow shaped up like this:
First, I'd load the post schedule from DynamoDB. Then I formatted the prompt to include my rules, information about the post I wanted to schedule, and a list of all future posts. After the prompt is assembled I simply pass that along to Bedrock using the Claude V2 model and get my recommended date as the output!
Here's an example of the prompt I'm providing to Bedrock:
You are a master calender planner. I want to schedule a social post.
I need you to give me a time in UTC in the format of YYYY-MM-DDTHH:MM:SS.
My audience is mostly in the USA, some in Europe and is highly technical.
Return ONLY the schedule date and nothing else.
Rules:
- The post must be at least one day after ${state.timestamp}
- Do not schedule a post on the weekend.
- Only one post is allowed to be scheduled a day
- Select the next available day in the future that matches the above rules unless otherwise specified
- The scheduled time of day must vary between high engagement times in the morning and afternoon for the target audience.
Schedule:
- 2023-11-01T14:00:00
- 2023-11-06T10:30:00
- 2023-11-07T18:45:00
Now that I have a generated date, the workflow can continue how it always did - create the one-time schedule and pass along the message to be tweeted.
Of course, I didn't want to take away functionality. So I branched the logic in the workflow based on the presence of the scheduleDate
property in the event. If it's provided, bypass the posting rules and schedule the tweet. If a date is not provided, use the rules to come up with the next available date.
Conclusion
I expect this blog post to have mixed reactions. Not because of the tech backing the workflow - that part is super cool. But because of the polarizing opinions we have about relying on GenAI to create marketing material on its own.
Honestly, I have mixed feelings about it. All my blogs, newsletters, and podcasts are done by hand with no generative AI involvement. This is a big leap of faith for me to let go and rely on it to create trustworthy, meaningful social posts with engaging call to actions (CTA).
We'll see how it goes. I might regret it and back out of this plan. Or I could love it and try to use it for more things. Time will tell.
The good thing is that regardless of where the social post content comes from, the automatic scheduler will work and take that burden from me. It probably knows about your behavioral patterns way better than I do.
The value in this project is the ability to get the next best date to do something given a set of rules
This is a highly extensible workflow. You can change the rules based on your needs and get completely different behavior. You can even change the workflow to have it do some other action besides sending a tweet.
So try it out, it's all open source. Let me know what you think!
Happy coding!
Top comments (3)
This is a really cool setup you’ve put together. Thank you very much for sharing!
Thanks Darryl! I'm really glad you like it!
Inspiring post! I'm going to try something similar with C#.