Refurbot: a serverless bot that tweetsFeb 14, 2021
The Serverless Opportunity
Writing bots is fun. Sometimes it is even useful, but you write a bot most of the time because it is entertaining.
But once you have written one, traditionally, you had to deal with the hassle of keeping them running indefinitely on your computer, on some random Pis, or on an EC2 instance.
Are there other solutions to run a bot?
Hell, yeah: a serverless bot! ⚡️
Even better, bots are one kind of application that can be run efficiently within most cloud providers' free tiers.
In this article, I’ll share with you how I built @Refurbot (code is available at https://github.com/zmoog/refurbot), a Twitter bot that every day tweets the best deal available in the refurbished products section of the Apple Store.
Every morning at 9 am (CET), Refurbot will wake up and have breakfast. It will then access the Apple Store to fetch the latest deals, find the best one (percentage-wise), and tweet it to its vast audience (it’s just me).
Refurbot is a simple serverless application written in Python. It has been designed using the clean architecture principles, even if such a simple project should not really deserve this special attention.
Sometimes, we do such things “just because we can." Still, in this case, I think it’s useful to test or practice using simple context and then progressively move up to something more complex No, I’m lying, I just read the two books Architecture Patterns with Python and Clean Architectures in Python and I want to practice!)
Commands and Events
A more detailed overview of the Refurbot architecture.
Step 1 — Schedule it
We are using the CloudWatch Event to fire up an event and trigger the lambda execution:
functions: run-scheduled: handler: refurbot/entrypoints/aws/cloudwatch/events.run_scheduled timeout: 10 # sometimes the Apple's refurbished store hangs for a few seconds. events: - schedule: name: run-scheduled rate: cron(00 8 ? * MON-SUN *) enabled: true input: commands: SearchDeals: country: us product: mac
The lambda function starts and publishes a
SearchDeals command in the MessageBus:
def run_scheduled(event: Dict, config: Any): cmd = commands.SearchDeals( country='us', product='mac' ) messagebus.handle(cmd)
In a real project the lambda should use the
event data to build the command, but in this sample project we’ll just build a
The MessageBus looks up the right command handler and invoke it. The command handler contains the business logic to fullfil the command.
Step 4 and 5
The handler executes the real business logic: in this case, search the Apple Store end create a
def search_deals(cmd: commands.SearchDeals, uow: UnitOfWork, _: Dict[str, Any]) -> List[events.Event]: with uow: deals = uow.refurbished_store.search(cmd.country, cmd.product) if not deals: return [events.DealsNotFound( country=cmd.country, product=cmd.product, )] # get the deal, arbitrarily defined as the one with the # max saving percentage the_best_deal = max(deals, key=lambda deal: deal.saving_percentage) return [events.DealsFound( country=cmd.country, product=cmd.product, deals=list(deals), best_deal=the_best_deal, )]
Each command handler exececution usually created one or more events.
Step 6 and 7
Now the MessageBus handles the
DealsFound event, updating the status on Twitter:
def tweet_deals(event: events.DealsFound, uow: UnitOfWork, context: Dict[str, Any]): with uow: text = "Hey, ... [cut]" uow.twitter.update_status(text)
Refurbot is using a few libraries:
- tweepy, to post status updates to Twitter
- click, to build a [command line version of che bot].
- pytest and pytest-mock, for testing.
But there is another one that is dear to me (just becouse I’m the author).
Refurbot is using it from the Adapter:
>>> from refurbished import Store >>> store = Store(country="us") >>> ipads = store.get_ipads() >>> >>> print(next(ipads).name) Refurbished iPad mini 4 Wi-Fi + Cellular 64GB - Silver
Use the CLI Luke!
% rfrb it macs --min-saving=300 1979 1679 300 (15%) MacBook Pro 13,3" ricondizionato con Intel Core i5 quad-core a 2,4GHz e display Retina - Argento ... (more)
Serverless is a good fit for bots: there aren’t any process running idle waiting for events consuming resources (CPU, RAM, etc). The application is started when needed, and when it’s done that’s it.
But you know: there ain’t no such thing as a free lunch. Serverless solutions come with their own sets of trafe offs, like lambda functions cold start, potential “vendor lock in”, and more.