2025.02.19

i built too many bots, here is what they taught me

telegram, discord, reddit, trading and monitoring bots look different from outside. underneath, they keep teaching the same lessons.

i may have built too many bots.

Telegram bots, Discord bots, Reddit trackers, keyword monitors, joiner trackers, trading bots and small automation workers that sit somewhere waiting for an event to happen.

from the outside they look like different products. underneath, they are variations of the same stubborn machine:

text
receive event -> decide what it means -> change state -> perform action -> report result

the interesting lessons started appearing when i stopped treating each bot as a completely new project.

the platform should stay near the edge

Telegram calls it an update. Discord has events and interactions. Reddit has posts, comments and API objects. trading systems have ticks, orders and positions.

if platform-specific objects travel through the whole application, every business rule becomes married to the SDK that delivered the event.

a cleaner design translates the external event early:

ts
type IncomingEvent = { source: class="syntax-string">"telegram" | class="syntax-string">"discord" | class="syntax-string">"reddit"; actorId: string; channelId?: string; text?: string; occurredAt: Date; };

the core logic can then answer normal questions. is this actor allowed? does the text match a rule? have we handled this event before? which action should follow?

the adapter handles the platform. the application handles the meaning.

state is where the bot becomes real

a demo bot can respond to /start. a useful bot remembers things.

it remembers subscriptions, permissions, processed event IDs, thresholds, user settings, pending actions and the last successful check. once state appears, duplicate events and restarts become important.

many APIs deliver at least once, not exactly once. if the same update arrives twice, the bot should not send the same trade, payment or notification twice.

idempotency sounds like a large distributed-systems word. sometimes it is simply a table saying, "i have already seen event 8472."

retries need judgement

retrying every failure is not resilience.

a timeout may deserve another attempt. invalid credentials do not. a rate limit wants patience. malformed input wants rejection. a permanent API error repeated in a tight loop wants to ruin the afternoon.

good bot workers classify failure, apply backoff and keep enough context in the logs to explain what happened later.

users experience silence as failure

a background worker can be busy, delayed or waiting on another service. the user sees none of that. they sent a command and the bot became quiet.

small acknowledgements matter:

text
received. checking now...

so do useful error messages. "something went wrong" protects implementation details but gives the user no next move. a better response can say the upstream service is unavailable, the input is invalid or the action will retry.

most bots are integration systems

the clever command handler is rarely the hard part. the work is at the boundaries: unreliable networks, expiring sessions, platform limits, duplicate events, external state and processes expected to remain alive for months.

building many bots did not teach me ten unrelated architectures. it kept showing me the same architecture from different angles.