Let's Build A Buy Bot
Is the HEGE BuyBot an over-engineered monstrosity, or a masterpiece of modular elegance? You be the judge!
“I Mean, How Hard Could It Be?”
Hey Hegends,
It’s Day back at the keyboard again. This one is going to be another full-on nerd assault, so brace yourself!
As you may or may not know, we have our very own HEGE BuyBot running in Hegequarters, delivering our signature style buy messages.
It was in October last year that I felt enough is enough, I’m gonna roll up my proverbial sleeves, and build a buy bot. The external bots we used back then never worked right, and I was tired of seeing everybody complain about it all. the. time.
So I decided to build a whole new one from scratch. Because why not. (And how hard could it be, right?)
In the end, I came up with a bot that’s possibly horribly over-engineered. Possibly a masterpiece of modular elegance. Or most likely, a bit of both.
You be the judge!

What Is the HEGE BuyBot?
The HEGE BuyBot is a Python program that’s constantly monitoring the Solana blockchain for $HEGE buys.
Whenever it detects a buy, it sends off a message to the Hegequarters Telegram group. The exact message will differ depending on the USD value of the buy, and there are a bunch of hidden easter eggs in there for certain USD values, too.
Like this one here, which will bring a smile to any fellow enjoyor of 90s hacker culture cheese:
How Data Flows Through the System
At the top level, HEGE BuyBot is a Python package, split in two parts:
- A bot server that builds and sends messages.
- One or more bot clients that feed buys into the server.
Here’s a diagram showing how data flows through the system:
On the left side of the diagram you have the buy data coming into the system. In the middle you have the actual HEGE BuyBot, consisting of the bot server and the bot clients. On the right side you have the buy messages coming out of the system, into Telegram etc.
On the output side, there is exactly one bot server running. When it starts up, it creates a ZeroMQ socket that it listens to for incoming buys. (ZeroMQ acts as a shared message queue.)
On the input side, there can be one or more bot clients running in parallel, each getting buy data from some source. After processing, the clients forward the buys to the server over the ZeroMQ socket. A client for Helius webhooks is built in. Other clients could be easily added.
When a buy comes in to the bot server over the ZeroMQ socket, the server generates one or more messages, and then sends them out. Support for sending messages to Telegram is built in. Other message sinks could be easily added.
There are two main reasons for this modular design:
- It’s easier to build and test the server and the clients individually.
- New clients can be added with zero changes to the rest of the system.
Internally, the bot server itself is also designed in a modular way, which we’ll dive into next.
Bot Server Architecture
The bot server is built as a pipeline — buys come in at the start of the pipeline (through the ZeroMQ socket), gets processed, and then finally messages are sent out at the end of the pipeline.
This pipeline is connected by queues, and each part of the pipeline runs in its own thread, communicating via explicit interfaces.
This modular setup makes the bot server extremely easy to extend. And it makes it very easy to test, too.
Here is a schematic view of the pipeline:
The buy-transform step adds details to the buy that might not have been available to the bot client. For example the buyer’s wallet balance, the current market cap of $HEGE, and the spot price of $SOL.
Using queues everywhere makes everything work in parallel, without the need for custom locks. This way, the queues also become the interfaces between the different parts of the pipeline, making everything extremely modular and easy to understand and modify.
If an error occurs in some thread, the thread puts an exception on the Errors queue. The error is then handled by an error handler running in its own err‑handler thread. Also, all threads have a reference to a break event, that can be used to shut down the whole bot gracefully.
Note that each message sink runs in its own thread. This means that messages to different sinks will be sent in parallel, and a delay or timeout in one sink will not affect the others. So for example, a delay or timeout in sending messages to Telegram will not affect messages going out to Discord.
Message Building Is Completely Programmable
Let’s zoom in on the get‑messages thread to see how messages are built.
By default, messages are based on the dollar value of the buy. But the message building is completely programmable — it’s trivial to add messages that trigger on other things, like the time of day, the buyer address, etc. etc.
A system of messages sources and message builders make this work:
When a buy comes in, the get‑messages thread loops through each message source, and collects at most one message per source. The process looks something like this:
- Receive a buy on the input queue
- For every installed message source:
- Ask the source if it has a message for the given buy
- If so, put the message on the correct messages queue
In turn, the process inside each message source looks something like this:
- Receive a call from the get-messages thread, asking for a message
- For every installed message builder:
- Ask the builder if it should trigger for the buy
- If so, ask the builder to generate a message for the buy
- Return the message to the get-messages thread
The destination for each message is set on the message level. So it’s up to the message source to specify where the message is going. Typically you would have one source per message sink. So for example you could have one message source for Telegram, one for Discord, and one for X.
The Other Parts Are Not Terribly Interesting
So that covered the big picture architecture, and the design of the bot server.
The bot clients will differ depending on their data sources. The Helius client that we use is a barebones WSGI server running on Gunicorn that listens to webhook events. For sending messages to Telegram we use the Telegram HTTP API.
But those parts are not very interesting to be honest, so I’ll leave it at that and move on to the conclusion.
Conclusion
Am I happy with the end result? Yes.
Starting out on this quest, I wanted:
- A system with as few dependencies as reasonably possible.
- Fully programmable messages.
- An easy way to add or replace buy sources.
- Support for sending (different) messages to multiple services, not just Telegram.
All in all, I think the final result ticks all those boxes pretty well.
Is it overly complex? Possibly. But I like how easy the design is to think about, to modify, and to test.
Thanks For Reading
Well. That’s enough nerdery for today.
Until next time, have a great weekend, and make sure to jump into the Hegequarters and say hi to all the Hegends in there. Good vibes guaranteed!
Over and out,
— Day 👊
