Twitter Automation: Scheduled Tweets From Google Sheets
This article explains how to set up a Twitter bot in a Google Sheets spreadsheet. It provides an easy way to efficiently schedule a large number of tweets in a consistent format.
Why Create Our Own Bot?
There are all kinds of ways to schedule tweets. Tweetdeck, for example, allows us to set a publishing time for a tweet (which is actually a function of Twitter Ads). However, I wanted something different. I’m starting a series of #MovieTaglines tweets on my personal account. One tweet per day, consistent format (example). I needed an easy and efficient way to manage those.
How It Works
Our bot uses the Twitter API to interact with the service. That requires us to register as Twitter developers and create a Twitter app, for which we get access tokens.
The bot is basically a spreadsheet with a script. The spreadsheet hosts the data, the script contains the logic, authenticating to Twitter with the tokens mentioned above.
The spreadsheet has multiple sheets, but only one of them is important. For every tweet we want to schedule, we’re adding one row with the details: several fields which are used by the script to build the tweet’s content plus two timestamp field groups. One timestamp stores the scheduled publishing time for the tweet, the other receives the actual publishing time once the row’s data has been tweeted.
Whenever the script is run (which happens regularly through a time-driven trigger), it iterates over the rows in the sheet, looking for an entry where the scheduled time is in the past and the published time is empty. The script uses the first row that matches these criteria, tweets the row’s data, stores the publishing date, and stops.
Setup
Twitter App
Our bot is based on a project from Peter Berthelsen, Make Your Own Twitter Bot With Google Sheets and Google Apps Script (GAS). Peter describes in detail how to create a Twitter app. I won’t repeat that here. The process is a little cumbersome but not difficult. In short:
- Go to the developer portal
- Apply for a Twitter developer account (I selected the use case Hobbyist/Making a bot)
- Create a Twitter app
- Set your app’s permissions to read and write (default is read-only)
- Generate and copy keys and tokens. You need:
- Consumer API key
- Consumer API secret
- Access token (with read and write permissions)
- Access token secret
Google Sheet
Basics
- Click this link to copy the sheet to your Google account.
- Navigate to Tools > Script editor.
- If you see a message asking about the Chrome V8 Apps Script runtime near the top of the window: enable V8, the project is compatible.
- In the lines 50-53 of
Tweet.gs
, fill in our Twitter credentials and save the script by pressing CTRL+S. - Go back to the sheet
SendTweets
and send a test tweet by filling out theTweet text
field and clicking Send tweet.
Script Trigger
We want to script to be executed at regular intervals, e.g., every 10 minutes. To achieve that, we configure a time-driven trigger:
- In Script editor click Edit > Current project’s triggers.
- Click Add trigger and configure the new trigger as follows:
- Function to run:
tweetMovieTaglines
- Event source:
Time-driven
- Type of time-based trigger:
Minutes timer
- Minute interval:
Every 10 minutes
- Function to run:
And that’s it – you’ve completed the setup of your first Twitter bot.
Customization
In all likelihood, you are not interested in tweeting movie taglines. Luckily, modifying the bot only involves adjusting the spreadsheet and a few lines of script code. Let’s take a look at the inner workings to prepare you for that.
Sheets
The spreadsheet contains three sheets:
- SendTweets: only for testing by sending a single tweet.
- MovieTaglines: the script’s data repository (aka, the content for your tweets).
- TweetLog: every successful tweet is logged here.
The MovieTaglines Sheet
The MovieTaglines
sheet has the following columns:
- Scheduled date [UTC]: the planned date for the tweet (number format needs to be
Date
). - Scheduled time [UTC]: the planned time for the tweet (number format needs to be
Time
). - Tagline: part 1 of the tweet’s content.
- Movie name: part 2 of the tweet’s content.
- Movie year: part 3 of the tweet’s content.
- IMDb URL: part 4 of the tweet’s content.
- Scheduled for [UTC epoch]: scheduled tweet timestamp as Unix epoch (auto-calculated by a macro, do NOT modify).
- Tweeted at [UTC epoch]: actual tweet timestamp as Unix epoch (filled out when the row’s content has been tweeted, do NOT modify).
- Tweeted at [UTC time]: actual tweet timestamp in human-readable format (auto-calculated by a macro, do NOT modify; number format needs to be
Date time
).
Scripts
The bot consists of three layers of scripts. From top to bottom:
- TweetMovieTaglines.gs: this is my code. It reads the data from the sheet and tweets by calling routines from
Tweet.gs
. - Tweet.gs & Threads.gs: wrapper around Twitter lib providing logging and functions for tweet replies/threads (not used by me).
- Libraries: Twitter lib and OAuth.
How to Customize the Bot
To customize the bot, modify the content area in the MovieTaglines
sheet. I found it useful to split the content into multiple fields for easier handling (tagline, movie name, movie year, and URL), but for other purposes, a different number of tweet components will be more useful. In the most simple case, a single field for the tweet content might be sufficient.
Once you have configured the content area, adjust the script TweetMovieTaglines.gs
accordingly:
- Column indexes in lines 11-19
- Hashtag in line 22
- Field value access in lines 51-55
- Tweet content format in line 84
To verify that your changed code is working, set a breakpoint by clicking a line number next to a point of interest. Then click the bug icon to manually run the function tweetMovieTaglines
. The script stops at the breakpoint; you can inspect variable values and step through the following instructions by clicking the corresponding icons (the almost-universal keyboard shortcuts F10 and F11 do not work here, unfortunately).
Renaming
Changing names is easily possible, you don’t have to keep my “movie” names around.
Renaming the Sheet
If you rename the MovieTaglines
sheet, remember to update the name in line 26 of TweetMovieTaglines.gs
, too.
Renaming the Script (Function)
The script file TweetMovieTaglines.gs
can be renamed, of course, too. If the script’s function tweetMovieTaglines
is renamed, you need to update it in the trigger, too.
Operations
Adding a Scheduled Tweet
To add a scheduled tweet to the list, simply add a row to the MovieTaglines
sheet. Make sure that the Scheduled for [UTC epoch]
field macro fills it with a large number (like 1597426380).
Draft Content Without Scheduling
To work on content without scheduling it yet, simply leave the first two columns (date & time) empty. Once a tweet is ready to be published, fill out the date & time columns (which populates the Scheduled for [UTC epoch]
field, which in turn is used by the bot).
Resend a Tweet
The bot keeps track of which spreadsheet rows have been tweeted by adding the tweet’s timestamp to the Tweeted at [UTC epoch]
field. If you clear that field, its row’s content will be processed again.
Troubleshooting
The Google Apps Script portal lists every script run on the page My executions. You should see one entry every 10 minutes. If there was nothing for the script to do, the log just contains No logs are available for this execution. If, on the other hand, the bot tried to send a tweet, you should find a detailed log along with error messages, if there were any.
17 Comments
Tweet is undefined. This appears in the Executions page.
Nov 16, 2020, 10:12:12 AM Debug sendTweet Initiated!
Nov 16, 2020, 10:12:12 AM Debug using params:|TweetText: TEST TWEET|IdInResponseTo: |ImageAttachURL:
Nov 16, 2020, 10:12:12 AM Debug Authentication Successful
Nov 16, 2020, 10:12:12 AM Debug No reply ID provided.
Nov 16, 2020, 10:12:12 AM Debug No images detected!
Nov 16, 2020, 10:12:13 AM Info Send tweet failure. Error was:
{“name”:”Exception”}
options were:
{“method”:”POST”,”payload”:”status=TEST%20TWEET”,”headers”:{“Authorization”:”OAuth oauth_consumer_key=\”GACBxsxiFfQ40kblwiUruim5i\”, oauth_nonce=\”CdfQB26nm1INpHHNU1qmuWewVIV4GAGs\”, oauth_signature=\”rJErnKkQBOlefhVCKwAbm%2FyGhmg%3D\”, oauth_signature_method=\”HMAC-SHA1\”, oauth_timestamp=\”1605492733\”, oauth_token=\”3185918180-1qrmUeuiRXtkv8Bz7KUGiqAvUQDy6CHBYHsX3LD\”, oauth_version=\”1.0\””},”escaping”:false}
Nov 16, 2020, 10:12:13 AM Debug No Response
Lou, change the app permission in twitter to Read, Write, and Direct Messages
The exact same keeps happening to me once in a while. It works in few more attempts though. Permission to Read, Write exists. Wondering what exactly could the issue be.
I know this is an old post, but I’m getting this same error. I turned on Read, Write, and Direct Messages. If anyone has any more ideas I’d love to hear them.
Having the same issue! Please help
Hi! I’m using your spreadsheet and everything is working! I would like to know how to implement posting images using one of the columns. Great job, thanks
Thank you so much for this work! I just started using it yesterday and was able to get it up running fine. I’m tweeting from a list of Spotify links and unforunately the album art thumbnail is not being pulled thru to twitter for triggered posts, but it appears fine when I manually run the script. Any idea what might be going on there?
I’ve followed the instructions. I’ve tested through the Send Tweets sheet and it works fine, and my Trigger log is says that it’s running on time, but no tweets are are appearing and none are being logged on the TweetLog. I don’t understand what I’m doing wrong! Help!
Please ignore/delete my comment. I think I’ve figured it out!
How do we add script for auto retweet # tag and content based on same sheet.
Thanks,
Looks just what I’m after! If only there was a way to add an image/images. Is this possible?
Hi, I tried this and it works perfectly I just added some few modifications for my own use. I edited UTC to work on our local timezone by adding * -time(hh; mm; ss;) * on the formula. I also added a character limit counter to check if tweets are too long. May I just ask if there is a workaround on the 1 tweet per minute limitation?
Great work, and thanks for being so kind as to share it. As some have mentioned above I added a character count. I also changed some lines to cater for line breaks in the text, which I thought might be useful for others to know
In the scheduled tweet screipt
// Build the tweet
let tweetText = tweetTextBody.replace(/\n/g, ‘\n’) ;
In the send single tweet
var tweet = ss.getRange(“SendTweets!B7”).getValue().replace(/\n/g, ‘\n’).toString();
Voici mon erreur : Savez-vous ce que signifie ID Twitter?
12:45:13 Avis Exécution démarrée
12:45:13 Infos sendTweet Initiated!
12:45:14 Infos using params:|TweetText: undefined|IdInResponseTo: undefined|ImageAttachURL: undefined
12:45:14 Infos Authentication Successful
12:45:14 Infos No reply ID provided.
12:45:14 Infos No images detected!
12:45:14 Infos Error: TypeError: Cannot read property ‘text’ of undefined
12:45:14 Erreur
TypeError: Cannot read property ‘id_str’ of undefined
sendTweet @ Tweet.gs:138
hi! i have the same problem right now. were you able to figure it out?
Hi guys,
Anyone figured out how to schedule images?
If i pit in “Scheduled date [UTC]” the formula to show Today date it will work?