What It Does
- Displays current time in 3 US timezones (CST, PDT, EST) by updating Discord voice channel names every 5 minutes
- Enables reaction-based meme curationâmembers use đ and â emoji reactions to save content to organized channels
- Monitors specific channels for reactions and automatically reposts curated content
- Handles Discord's API caching limitations with defensive partial message fetching
- Uses modular plugin architecture for easy addition of new reaction handlers
- Provides simple commands like
$pingfor health checks and$pinupfor curation instructions - Deployed on Heroku for continuous operation without dedicated infrastructure
The bot active in a Discord server, monitoring channels for reactions.
Why I Built This
A few friends and I ran a Discord community with members scattered across multiple US timezones. Coordination was a constant headacheâbefore scheduling gaming sessions or meetings, someone always had to ask "What time is it for you?" We also generated tons of memes across various channels, but the best ones got lost in the chaos. Discord's 50-pin limit per channel meant we constantly ran out of space.
Traditional solutions like asking everyone to manually convert timezones or manually pinning messages were inconsistent and required active moderation. We needed something that worked passively in the background: always-visible timezone information and a way for members to collectively curate content without needing moderator permissions.
Simple health check command to verify the bot is responsive.
How It Works
CherryPinBot is built on Node.js with Discord.js v12, using an event-driven architecture that monitors Discord WebSocket events. The timezone display feature cleverly uses Discord voice channels as persistent status boardsâby programmatically updating voice channel names every 5 minutes, the bot creates always-visible time displays without relying on message pins or channel topics (which are less visible).
The reaction system uses a modular plugin pattern: each emoji reaction (đ for casual saves, â for exceptional content) is handled by a separate module that exports onReaction, filter, and channels properties. When someone reacts to a message, the bot fetches the message content (handling Discord's caching limitations), checks if the reaction matches a registered handler, and automatically reposts the content to the designated curation channel.
A critical technical decision involved rate limiting: Discord allows only 2 channel name changes per 10 minutes. I calculated a safe update interval of 5 minutes + 1 second to stay comfortably within limits while providing reasonably current time displays. This constraint also drove the decision to use HH:mm format instead of including seconds.
The bot's Discord profile showing its online status.
Impact
By the numbers:
- Automated 3 timezone displays updating 864 times per day per timezone
- 2 reaction handlers monitoring 2-3 channels each for meme curation
- 59 commits over 6 months showing iterative refinement
- Developed MayâNovember 2021
What changed:
- Eliminated the "What time is it for you?" question entirelyâtimezone info became ambient and always visible
- Empowered community members to curate content collectively without needing moderator permissions
- Created organized meme archives that solved Discord's 50-pin limit problem
- Served as a learning vehicle for event-driven programming and API rate limiting patterns
Challenges & Solutions
The Discord API heavily rate-limits channel name changes (2 per 10 minutes), which initially broke the timezone display feature. I solved this by calculating safe update intervals and simplifying the time format to reduce update frequency needs. The 5-minute + 1-second interval keeps the bot comfortably within limits.
Another challenge was reaction events on uncached messages failing silently. Discord doesn't cache all message history, so reactions on older messages wouldn't trigger handlers. I implemented a defensive partial message fetching pattern that explicitly calls fetch() when reaction.partial === true, ensuring the bot handles old messages correctly.
The clock channel feature required extensive iterationâ30+ commits in November 2021 aloneâto get the asynchronous channel update logic working reliably. Debugging complex async patterns with Discord's API taught me the value of incremental commits and extensive console logging. The commit message "Dad is amazing The Bot works" captures the collaborative debugging moment when the reaction handler finally clicked.
What I Learned
Rate limits should drive architecture, not be an afterthought. Discord's 2 changes per 10 minutes limit forced strategic decisions about update frequency and data format. Understanding external API constraints early saves significant refactoring later.
Event-driven systems need defensive data fetching. The Discord API's caching policies taught me to never assume data is availableâalways implement fallback fetch logic at system boundaries.
Modular architecture scales learning. The plugin pattern for commands and reaction handlers made it easy to add features incrementally without understanding the entire codebase. This approach is valuable for projects where features are added over time or by multiple contributors.
Future improvements (if I revisited this):
- Migrate to Discord.js v14+ for modern slash command support and improved API patterns
- Abstract channel IDs to configuration file for multi-server deployment
- Add database persistence (SQLite/PostgreSQL) to track meme curation stats and user activity
- Implement timezone customization via commands (currently hardcoded to CST/PDT/EST)
- Environment variable management for bot token (currently hardcoded for learning purposes)
- Docker containerization for easier deployment beyond Heroku
Status Update (November 2022)
The platform I was using to host the bot (Heroku) discontinued their free tier services. The bot is now archived and sitting in the repository, waiting for the day I bring it back to life on a new hosting platform. This project served its purpose as both a practical community tool and a deep learning experience with event-driven architecture and API rate limiting.
Links
- Code: github.com/cadleta/CherryPinBot
- Status: Archived (Heroku free tier discontinued)
- Tech Stack: Node.js, Discord.js v12, moment-timezone, Heroku