One day I tried to show a post of mine to a coworker and then I realised my website was down. I had nothing to warn me if that happens at that time and it was kinda embarrassing. Solving it was as easy as login to DigitalOcean and restart the droplet, but it looked very unprofessional.
For how long was my site down? How many people were unable to read my awesome posts?
That was when I decided that I needed some kind of automatic monitor for my website up time.
The documentation looked awesome so I joined as a free 10 days trial to check if it was useful for me. It turned out to be the perfect solution I needed. Taking a look at the docs and learning how to use it (pretty easy indeed) I saw the API section. It ended up being a well structured and simple API. I've worked building API's and consuming them for almost 3 years in the past and I wanted to work on something new. Then this project came to my mind.
A chatbot to interact with this API. To check your website status and receive messages if something is wrong.
I always create my bots to interact just with Telegram by now, I know how it works and it's easy to test the result. This project could work with Facebook Messenger in a future, I'm not discarding that feature 🤔
Once I've installed a fresh copy of BotMan and prepare the app I realised I'd never tested code that interacts with an API that was not mine. I learned TDD and Unit Testing after that API developer period. BotMan offers a great Testing Solution to work with. In fact, it's easier to develop a chatbot with tests since you can check the result instantly.
Then I remembered a few lessons in Adam Wathan's course Test Driven Laravel regarding this matter. You create a class that will return the same payload as the API you want to integrate. Usually your
Fake class will extend the current working class to override those methods that perform the actual API call. And that's what I've done for this test suite. This way I was able to develop the entire project without querying OhDear's app.
OhDear offers a PHP Open Source package to interact with their API. I needed to extend a few classes to adapt them into my needs but the rest was pretty easy.
They also have a package to interact with OhDear WebHooks, but this time I was unable to use this package. It's built to work with a secret key saved in a configuration file. My app, otherwise, it's made to interact with multiple users at a time, and each users has a different secret key. I'm currently storing that information encrypted into the database. When a webhook request is received it already contains the username. For example
https://deployedbot.com/freekmurze. Then I check the payload with the user's secret key in order to proceed or return an authorization error.
I built this project using the Single Action Controllers approach and I'm pretty happy with the result, It's something I'll consider in future projects for sure.
Most of the times there are just one controller per namespace in this project. But the benefit is also visible in the routes file, since it looks neat and you can use your IDE to click on the controllers name and go directly.
When you work on a chatbot that you want to test, you have two options:
- Write the message twice, in controller and tests, and remember to update both if something is modified.
- Store your messages somewhere you can call in your controller and tests.
I decided to go with number 2, and also decided to store the messages in Laravel translations files. This project is not multi-lingual (for now 😉) but I think that having the messages there makes it easy to:
- Refactor the message.
- Read code, it's easy to see
ohdear.token.storedthan a whole sentence.
- Test the message is sent to the user.
- Re-use the message in another conversation.
- Translate the whole app in a future, since you have all the texts in a single place.
This bot is currently running and you can talk to it here or by searching
Mr. Dear on Telegram. You can also deploy it into your own server since it's Open Source. You would find instructions about how to do that in the README.md of the project.