This post is about the chatbot I used to managed my reminders and notes.
Moving from many apps to one bot
Before, when I wanted to be reminded of something, I would use the reminders app on my iPhone, to take notes, I would use the notes app and for todos, wunderlist.
Recently I have tried to simplify my apps in order to focus more and avoid context switching. I have decided to rely on org mode, a mode that ships with emacs (I actually use spacemacs (http://spacemacs.org/).
Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.
I use org mode for my todos, my notes, my reminders and many other things on my laptop.
Contrary to the apps like notes and reminders, org mode does not integrate as well with the iPhone. So, I decided to devise an interface between all my org mode content and my phone, bi-directional and easy to use. This interface is a bot.
Building a bot
There are many resources and tools you can use to build a bot. My favorite is this book: http://shop.oreilly.com/product/0636920057741.do. It highlights some of the basic principles to respect to make your bot useful, friendly and manageable (from a developers perspective).
I decided to build my bot from scratch instead of relying on prebuilt services that make a bot for you as I wanted to learn. My goals were:
improve my knowledge of docker-compose, python3, Digital Ocean and Ansible
learn how to build regression testing with traffic replay
support both telegram and facebook messenger and share code logic
deployable bot on a brand new server in less than 2 minutes
to manage the state externally (separate server)
to have a repl interface for the bot too
<figure style="text-align:center"> <img style="display: block; margin: 0 auto" alt="Architecture of the bot" src ="/assets/archi-bot.svg" /> <figcaption>Architecture of the bot</figcaption> </figure>
I can deploy both the facebook and telegram bot separately using docker-compose. Docker Compose is a way to specify containers configuration and links between containers forming an app. I use it in conjunction with ansible to orchestrate the deployment of the bot. Here is the docker-compose file for the telegram bot (not that I frontend in this file is the same as adapter above):
version: "2" networks: internal: services: telegram_backend: build: ../../APPS/backend image: "backend:1.0" volumes: - ../../CONFIG/backend:/usr/src/app/config environment: - FRONTEND_URL=http://telegram_frontend:5000/reply - REDIS_HOST=redis.laurentcharignon.com networks: internal: aliases: - telegram_backend telegram_nginx: image: "nginx" volumes: - ../../CONFIG/nginx/config/telegram_nginx.conf:/etc/nginx/nginx.conf:ro - ../../CONFIG/nginx/config/ssl-params.conf:/etc/nginx/snippets/ssl-params.conf:ro - ../../CONFIG/nginx/certificates/fullchain.pem:/usr/src/fullchain.pem:ro - ../../CONFIG/nginx/certificates/privkey.pem:/usr/src/privkey.pem:ro - ../../CONFIG/nginx/certificates/dhparam.pem:/etc/ssl/certs/dhparam.pem:ro networks: internal: aliases: - telegram_nginx depends_on: - "telegram_frontend" ports: - "8443:8443" telegram_frontend: build: ../../APPS/telegram_frontend image: "telegram_frontend:1.0" environment: - BACKEND_URL=http://telegram_backend:3000 - ADVERTISE_URL=https://bot.laurentcharignon.com:8443 volumes: - ../../CONFIG/telegram_frontend:/usr/src/app/config networks: internal: aliases: - telegram_frontend depends_on: - "telegram_backend"
How to interact with the bot?
Typically bots use slash commands. I decided not to use that because the feature set of my bot is so limited:
Reminding me of things
Asking me to acknowledge reminders
Log tasks and todos
I can ask
To get some help
To turn on/off a recording session of notes, whatever I send will be persisted for further processing. When recording notes, multiple formats are supported to route the request to different note files.
<figure style="text-align:center"> <img style="display: block; margin: 0 auto" alt="Interaction with the bot" src ="/assets/bot-interaction.png" /> <figcaption>Interaction with the bot</figcaption> </figure>
The bot is named Pascal by the way!.
The main reason why I built Pascal was to be reminded of things to do. I created a module to parse my org-mode files and set up reminders in the bot.
Entries for reminders in my org mode files look like this:
* Columbia CS@CU Happy Hour :PROPERTIES: :REMINDER_DATE: <2017-07-15 Sat 12:00> :REMINDER_TARGET: laurent :REMINDER_QUICK_REPLY: yes :END:
This is plain text. The first line is
* Columbia CS@CU Happy Hour
It is a heading, it starts with a star, in markdown you would achieve a similar result using #
The heading is followed by:
:PROPERTIES: :REMINDER_DATE: <2017-07-15 Sat 12:00> :REMINDER_TARGET: laurent :REMINDER_QUICK_REPLY: yes :END:
Which is called a property drawer, it stores key/value pairs associated with an entry, here we have three key value pairs. The first one is a date (When the reminder will filre), the second is a target (who to notify, me), and the last one configures quick replies. When I toggle Quick Replies it means that the bot should display buttons to make me acknowledge that I acted on the reminder.
Here is another example of a weekly reminder, every Monday at 6pm, I go to the gym:
* Gym Monday :PROPERTIES: :REMINDER_TARGET: laurent :REMINDER_HOUR: 18 :REMINDER_MINUTE: 0 :REMINDER_WEEKDAY: 0 :REMINDER_MESSAGE: It's monday night, workout night! :END:
I create these programmatically with a template and sync them with the datastore powering the bot every day.