Setting Up Your Agent Environment

Communication Channels: Telegram & Discord

4 min read

An agent without a communication channel is like a brilliant assistant locked in a room with no door. You need a way to talk to your agent — and more importantly, a way for your agent to reach you. Telegram and Discord are the two most practical channels for agent communication, each with distinct strengths.

Before you write a line of this: CrewAI has no channel layer. There is no channels: block in any CrewAI config file, and no Telegram or Discord integration ships with crewai or crewai-tools. What you are building in this lesson is a small program you own that holds the platform connection and calls crew.kickoff() — roughly 40 lines per platform. Module 1 Lesson 4 covers why the split lands where it does. Everything below is real code against the platforms' own libraries, so it will run.

Why These Two Channels

Telegram is ideal for personal, one-on-one agent interaction. It works on every device, supports rich media (photos, documents, voice notes), and its Bot API is one of the most straightforward to work with. When you want your agent to feel like a personal assistant in your pocket, Telegram is the right choice.

Discord excels at parallel, multi-context work. Each Discord channel provides a separate conversation context, which means you can have your agent working on different tasks in different channels simultaneously — research in one channel, code review in another, email drafting in a third. This channel-based separation is uniquely powerful for agent orchestration.

Setting Up a Telegram Bot

Telegram bots are created through BotFather, Telegram's official bot management tool.

Step 1: Create the bot

  1. Open Telegram and search for @BotFather
  2. Send the command /newbot
  3. Choose a display name (e.g., "My AI Agent")
  4. Choose a username ending in bot (e.g., my_ai_agent_bot)
  5. BotFather responds with your bot token — save this securely

Step 2: Write the adapter

pip install python-telegram-bot
export TELEGRAM_BOT_TOKEN="your-bot-token-here"
export TELEGRAM_ALLOWED_USERS="123456789"      # comma-separated numeric IDs
# telegram_agent.py
import asyncio, os
from telegram import Update
from telegram.ext import Application, MessageHandler, ContextTypes, filters

from my_crew import build_crew          # your Crew — see the capstone in Module 5

ALLOWED = {int(u) for u in os.environ["TELEGRAM_ALLOWED_USERS"].split(",") if u.strip()}


async def on_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if update.effective_user.id not in ALLOWED:
        return                            # silent drop — do not confirm the bot exists

    await update.message.chat.send_action("typing")

    # crew.kickoff() is synchronous and can run for minutes. Never await it directly
    # on the event loop — it would block every other update the bot is handling.
    crew = build_crew(update.message.text)
    result = await asyncio.to_thread(crew.kickoff)

    await update.message.reply_text(str(result)[:4000])   # Telegram caps messages ~4096 chars


app = Application.builder().token(os.environ["TELEGRAM_BOT_TOKEN"]).build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, on_message))
app.run_polling()

That is the whole channel layer. The two lines worth pausing on are the allow-list check (an unrestricted bot is a public endpoint to your API budget) and asyncio.to_thread — a crew run is blocking work, and calling it directly inside an async handler freezes the bot for everyone else until it finishes. That second one is the bug people ship, because it works fine with one user.

Step 3: Set bot commands (optional but recommended)

Send these to BotFather to define your bot's command menu:

/setcommands
status - Check agent status
help - Show available commands
task - Assign a new task

Step 4: Get your user ID

To restrict your bot to only respond to you, you need your Telegram user ID. Send a message to @userinfobot on Telegram — it will reply with your numeric user ID.

Setting Up a Discord Bot

Discord bots require a few more steps because of Discord's permission and intent system.

Step 1: Create a Discord application

  1. Go to the Discord Developer Portal (discord.com/developers/applications)
  2. Click "New Application" and give it a name
  3. Navigate to the "Bot" section and click "Add Bot"
  4. Copy the bot token — save this securely
  5. Under "Privileged Gateway Intents," enable Message Content Intent (required for reading messages)

Step 2: Invite the bot to your server

  1. In the Developer Portal, go to "OAuth2" then "URL Generator"
  2. Select scopes: bot, applications.commands
  3. Select permissions: Send Messages, Read Message History, Embed Links, Attach Files
  4. Copy the generated URL and open it in your browser to invite the bot

Step 3: Write the adapter

pip install discord.py
export DISCORD_BOT_TOKEN="your-discord-bot-token-here"
# discord_agent.py
import asyncio, os
import discord
from discord.ext import commands

from my_crew import build_crew

# Per-channel briefs: the channel a message arrives in decides the agent's job.
CHANNEL_PURPOSE = {
    "research":    "Search the web and summarise findings with sources.",
    "code-review": "Review the pasted diff and report correctness risks only.",
    "email-drafts": "Draft a reply for human review. Never send anything.",
}
OPERATOR_ROLE = "agent-operator"

intents = discord.Intents.default()
intents.message_content = True          # must also be enabled in the Developer Portal
bot = commands.Bot(command_prefix="!", intents=intents)


@bot.event
async def on_message(message: discord.Message) -> None:
    if message.author.bot:
        return                          # or two bots will talk to each other forever
    purpose = CHANNEL_PURPOSE.get(message.channel.name)
    if purpose is None:
        return                          # bot is inert outside the channels you listed
    if not any(r.name == OPERATOR_ROLE for r in getattr(message.author, "roles", [])):
        return

    async with message.channel.typing():
        crew = build_crew(f"{purpose}\n\nRequest: {message.content}")
        result = await asyncio.to_thread(crew.kickoff)

    await message.reply(str(result)[:1900])   # Discord caps messages at 2000 chars


bot.run(os.environ["DISCORD_BOT_TOKEN"])

Note if message.author.bot: return. Without it, two agents sharing a server will answer each other's messages indefinitely — a cheap mistake that gets expensive overnight.

Discord's Parallel Context Advantage

This is where Discord becomes particularly valuable for agent work. Consider this setup:

ChannelPurposeAgent Behavior
#researchWeb research tasksAgent searches, summarizes, and saves findings
#code-reviewCode analysisAgent reviews PRs and provides feedback
#email-draftsEmail compositionAgent drafts emails for your review
#daily-briefingMorning reportsAgent posts daily summaries on schedule
#logsAgent activity logsAgent logs all actions for audit

Each channel maintains its own conversation context. When you message the agent in #research, it does not confuse that with the conversation happening in #code-review. This natural isolation means you can run multiple workflows in parallel without context pollution.

That isolation is not something the platform gives you for free — it is the CHANNEL_PURPOSE dictionary in the adapter above. The channel name selects which brief gets prepended to the request, so a message in #code-review and a message in #research reach the crew with different instructions. Adding a workflow is one dictionary entry plus one Discord channel.

The shape of every channel adapter

Both bots above are the same four steps. Once you can see the shape, adding Slack, SMS, or a web form is the same work with a different library:

Message in, answer out — what your adapter owns

allowednot allowedMessage arrivesTelegram update / Discord on_me…AuthorizeAllow-list user ID or role — dr…Add the briefChannel or command selects the …crew.kickoff() off-threadBlocking call — asyncio.to_thre…Reply, truncatedRespect the platform's message …IgnoreNo error message — never confir…

The framework owns exactly one box in that diagram. Everything else is yours, which is also why swapping CrewAI for another orchestrator later costs you one line in this file.

Security Considerations

Messaging channels are a direct interface to your agent's capabilities. Securing them is critical.

Allow lists: Always restrict your bot to known users (Telegram) or specific servers and roles (Discord). An unrestricted bot is a security liability — anyone who finds it can issue commands.

Message validation: Validate incoming messages before processing. Check that the sender is authorized, the message format is expected, and the requested action is within allowed boundaries.

Token security: Bot tokens are equivalent to passwords. Never commit them to version control. Use environment variables or a secrets manager.

# WRONG: Token hardcoded in config
bot_token: "7234567890:AAF..."

# RIGHT: Token loaded from environment
bot_token: ${TELEGRAM_BOT_TOKEN}

Rate limiting: Implement rate limits to prevent accidental or malicious message floods from triggering excessive API calls and running up costs.

Audit logging: Log all commands received and actions taken. If something goes wrong, you need a clear trail of what happened and who triggered it.

Key takeaway: Telegram gives you a personal assistant in your pocket. Discord gives you a multi-channel command center. Use Telegram for direct interaction and Discord for parallel workflows — and always secure both with allow lists and proper token management.

Next: Expanding your agent's capabilities with memory, voice, and email integration. :::

Quiz

Module 2 Quiz: Setting Up Your Agent Environment

Take Quiz
Was this lesson helpful?

Sign in to rate