Welcome to the nanobot project! You are about to build a sophisticated AI agent, but before our bot can think, plan, or remember, it needs to perform a very basic human function: Listening and Speaking.
However, the internet is a messy place. Telegram speaks one language, Discord speaks another, and Slack speaks a third. If our bot had to learn the specific rules for every single chat app, the code would be a nightmare.
This is where the Channel Gateway comes in.
Imagine you are a diplomat who only speaks "Internal Bot Language." You have people approaching you speaking German, French, and Japanese. You need a translator.
The Channel Gateway is that translator (or in programming terms, an Adapter).
Let's say a user named Alice sends the message "Hello" to your bot.
update_id, message.chat.id, and message.text.d.author.id and d.content. {
"sender": "Alice",
"content": "Hello",
"platform": "telegram" (or "discord")
}
Now, the rest of your bot (specifically The Agent Loop) only has to worry about that clean JSON. It doesn't care if the message came from a pigeon or a supercomputer.
Before we look at the code, let's visualize the journey of a message.
**text** for Markdown or <b>text</b> for HTML).In object-oriented programming, we often create a "Base" class that acts as a template. All our specific channels (Telegram, Slack, etc.) will follow this template.
Here is the BaseChannel from nanobot/channels/base.py. It defines the rule: Every channel must handle messages the same way.
# nanobot/channels/base.py
class BaseChannel(ABC):
async def _handle_message(self, sender_id, chat_id, content, ...):
# 1. Check if user is allowed to talk to bot
if not self.is_allowed(sender_id):
return
# 2. Create the standardized message
msg = InboundMessage(
channel=self.name,
sender_id=str(sender_id),
content=content,
# ... other fields
)
# 3. Send it to the internal bus (The Agent Loop)
await self.bus.publish_inbound(msg)
Explanation:
_handle_message: This is a helper function. No matter which platform we are on, once we have the text, we call this function.InboundMessage: This is our "clean" format.self.bus.publish_inbound: This puts the message on the internal conveyor belt for processing.
Now let's look at a real worker: Telegram. This class inherits from our BaseChannel blueprint. It uses a technique called Long Polling (constantly asking the Telegram server "Do I have mail?").
Here is a simplified look at how nanobot/channels/telegram.py catches a message:
# nanobot/channels/telegram.py
async def _on_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
message = update.message
# 1. Extract the raw text from Telegram's specific format
content = message.text
# 2. Extract the user ID
sender_id = str(message.from_user.id)
# 3. Pass it to the BaseChannel logic we saw above
await self._handle_message(
sender_id=sender_id,
chat_id=str(message.chat_id),
content=content
)
Explanation:
update object (specific to Telegram)..text and .from_user.id. If this were Discord, these property names would be different._handle_message to standardize it.The Gateway also handles sending replies. Telegram requires specific formatting (HTML) if we want bold or italic text.
# nanobot/channels/telegram.py
async def send(self, msg: OutboundMessage) -> None:
# 1. Convert our internal Markdown to Telegram's HTML
html_content = _markdown_to_telegram_html(msg.content)
# 2. Use the Telegram bot API to send the message
await self._app.bot.send_message(
chat_id=int(msg.chat_id),
text=html_content,
parse_mode="HTML"
)
Explanation:
<b>Alice</b>".send method handles this translation automatically.Finally, we have the Channel Manager. If the Channels are workers, the Manager is the boss who hires them based on your configuration file.
# nanobot/channels/manager.py
class ChannelManager:
def _init_channels(self) -> None:
# Check config to see if Telegram is enabled
if self.config.channels.telegram.enabled:
# Create the Telegram Channel
self.channels["telegram"] = TelegramChannel(
self.config.channels.telegram,
self.bus
)
logger.info("Telegram channel enabled")
Explanation:
config and starts the correct ones (Telegram, WhatsApp, Discord, etc.) automatically.In this chapter, we built the ears and mouth of our bot.
InboundMessage objects.Now that our bot can "hear" a message and standardize it, what happens next? The message travels to the brain of the operation.
Next, we will explore how the bot decides what to do with that message in The Agent Loop.
Generated by Code IQ