Case Study: Daily Reflections
A project analysis of the Daily Reflections Discord bot.
Discord bot that posts the Daily Reflections on recovery in Discord on a schedule or when users invoke the bot with /reflections
.
Overview
Tech Stack
- Discord.js SDK
- Netlify serverless platform
- Netlify functions scheduled jobs
- TypeScript
- AVA node test runner with TypeScript support
- Supabase PostgreSQL database
Motivation
This side-project spawned from being active in several community Discord servers focused on sobriety and discussions referencing the Daily Reflections but users still uploading and sharing PDF files in chat. I didn’t have a well-formatted version of the Daily Reflections and thought about doing OCR on the PDFs but realized that would involved a fair number of OCR errors that would need manual correction. I found an easier solution by scraping an unsecured XML database for a well-structured version of the Daily Reflections that I persisted in Supabase. The Daily Reflections do not change, so a one-time scrape was sufficient and the project is not dependent on continuing access to the other database.
I knew from messing with Carlbot and Discohook as a Discord server admin that Discord embeds allow for well-structured formatting of rich text in Discord chat. I decided to build a bot to post the Daily Reflections as a structured embed into the Discord servers that I am active in.
Features and Functionality
- Scheduled posting of the Daily Reflections to Discord servers
- Users invoke the bot to post the reflection of that day using
/reflections
command in Discord chat - Supports multiple Discord servers via webhook URLs
example reflection post in Discord
Technical Choices
Leveraged the Discordjs SDK because it’s well documented and that made transforming the JSON data from my Supabase data API into Discord embeds straightforward.
Adopted hosted Supabase as the PostgreSQL database because of its ease of use, integrated data API, and uptime guarantees.
I combined my learnings from Tabletop’s API data fetching and transformation with Discordjs to rapidly develop and deploy a Minimum Viable Product.
Designed an event-driven architecture to leverage Discord’s webhook infrastructure and enable just-in-time delivery of messages and scheduled posts.
Deployed on Netlify over Vercel because of Vercel’s limits on scheduled cron jobs. I knew I wanted the ability to post the Daily Reflections into multiple servers (and make it configurable) so multiple crons would be helpful.
Challenges and Solutions
- Required a copy of the Daily Reflections - Evaluated doing Optical Character Recognition (OCR) on the Daily Reflection PDFs but knew that would have errors that would need manual correction. Instead I found and scraped an unsecured XML database for a well-structured version of the Daily Reflections that I persisted in Supabase.
- Local devlopment and testing of cron jobs - Leveraged the Netlify CLI for cron testing locally along with manually testing the endpoints with authorized requests sent from Postman.
- Debugging event-driven functions across services - Leveraged AVA with TypeScript for unit tests along with integration testing to ensure consistent and reliable performance.
- Transforming data into Discord Embeds - Implemented TypeScript just-in-time data transformation using Supabase table return types and a custom Discord embed type.
Lessons Learned
- Leverage social platforms for distribution - Pushing interactions to where users are is easier than getting them to leave platforms to use web clients. Discord is popular because how easy it is to use it as a frontend as opposed to developing your own web client and frontend.
- Serverless is designed for lightweight, asynchronous, and event-driven projects - the Lambda function infrastructure of serverless platforms are event-driven: they only listen for invocations to start their request cycle and shut down again after. It’s great to push for asynchronous, event-driven execution as much as possible for on-demand compute as opposed to paying for continuous access to cloud resources.
- AVA is much easier to run TypeScript tests than Jest - I tried to use Jest initially for its established ecosystem but quickly ended up in config hell. AVA TS support is just adding a
tsimp
dev dependency and one config object in package.json.
Future Considerations
- Explore potential monetization strategies such as premium subscription for advanced interaction features.
- Add admin configuration options:
- Scheduled post time
- Channel to post into
Impact
- The Daily Reflections bot posts daily to over 1000 Discord users through Discord.
Links
- repository
- Discordjs
- Netlify
- AVA node test runner with TypeScript support
- Supabase