Intro
I'm in the process of moving all my stuff from Azure to AWS. One of the things I currently run in Azure is a twitter bot that tweets every hour (it even made the local news a couple of years ago...).
Currently it's beeing triggered by a cron job on a VM. It has worked perfectly fine for 6 years now, but since I'm moving stuff and don't wan't to maintain a VM anymore, I decided to "convert it" to run in a AWS Lambda instead.
Solution
The bot is written in Python and has two pip dependencies, twitter
and pytz
.
Code changes
Before the code looked liked this:
tweet.py
from twitter import OAuth, Twitter
from datetime import datetime, time
import pytz, random
TOKEN = "SECRET"
TOKEN_KEY = "SECRET"
API_KEY = "SECRET"
API_SECRET = "SECRET"
MY_TZ = pytz.timezone('Europe/Stockholm')
now = datetime.now(MY_TZ).time()
retries = 0
def get_current_hour(timestamp):
if timestamp.hour == 0 or timestamp.hour == 12:
return 12
return timestamp.hour % 12
def compose_tweet(timestamp):
number_of_rings = get_current_hour(timestamp)
# Creates a tweet, e.g. "BONG BONG BONG #03:00
alert_sound = get_bell_sound(retries)
tweet = " ".join([alert_sound] * number_of_rings)
hashtag = "#%s:%s" %(str(timestamp.hour).zfill(2), str(timestamp.minute).zfill(2))
return "%s %s" %(tweet, hashtag)
def send_tweet(tweet):
global retries
auth = OAuth(TOKEN, TOKEN_KEY, API_KEY, API_SECRET)
t = Twitter(auth=auth)
try:
t.statuses.update(status=tweet)
retries = 0
except:
retries += 1
if retries <= 7:
main()
else:
raise
def get_bell_sound(index):
sounds = ('BONG', 'DONG', 'DING', 'BING-BONG', 'RING', 'PING', 'JINGLE', 'DING-DONG')
return sounds[index]
def main(timestamp = now):
send_tweet(compose_tweet(timestamp))
if __name__ == "__main__":
main()
To make it "Lambda compatible", I only needed to add the following method
def lambda_handler(event, context):
main()
The lambda_handler
function is called by the lambda runtime. You get access to both the event that triggered the function and also a context. I don't need any of it though.
Packaging
Here the documentation lacked a little when it comes to how to deploy a python function with external (pip) dependencies. It wasn't that hard to figure out but it took me like 30 minutes that I could have spent on something else, so that's why I'm writing this post :).
Lambda supports uploading a zip file, so let's create one. It needs to contain the pip packages and the entrypoint, in our case the entrypoint is tweet.py
.
The easiest way dealing with PIP imo is to have a requirements.txt
file, so let's create one.
requirements.txt
twitter==1.18.0
pytz==2018.5
Here we have specified which packages and what version to use. I use quite old packages, I don't know if newer versions work and I don't have time to try it out either, so bare with me.
After creating a requirements.txt
, we can install the packages to the current folder like this:
pip install -r requirements.txt -t .
This will create the following folder structure:
Now the only thing left to do is create the zip file.
I added the following files/folders to the zip file:
Then I uploaded it by choosing Upload a .zip file
The UI will now look like this:
Here I've also specified the entrypoint for the function by entering tweet.lambda_handler
in the Handler input field (tweet is the name of the file and lambda_handler is the name of the method).
Scheduling/triggering
Now the only thing that's left is to make the function run every hour (08:00, 09:00 and so on).
It's really easy, all we need to do is add a CloudWatch Event Trigger with the following schedule expression: cron(0 * * * ? *)
.
That's it, we're now tweeting from the (aws) cloud!
BONG BONG BONG BONG BONG BONG BONG BONG BONG BONG BONG BONG #12:00
— Domkyrkan (@skara_domkyrka) February 9, 2020