1.234 Bot SDK Frameworks#
Bot SDKs compared: Discord, Telegram, Slack, Matrix and Nostr — what each platform’s SDK provides, and how portable a bot can be between them.
At a glance#
| Library | Best for | Verdict | Latest release |
|---|---|---|---|
| matrix-nio | Anything privacy-critical — healthcare, legal, sensitive data | The only option with end-to-end encryption and full self-hosting. S3 puts privacy first because it is the only GATE in the decision tree; every constraint below it is a trade. | 0.26.0 · 2026-07-23 |
| discord.py | Gaming and community bots, high message volume, and the default when nothing else binds | Best UX and the richest feature set. You are a guest on Discord’s service. | 2.7.1 · 2026-03-03 |
| python-telegram-bot | Mobile-first audiences | Large existing user base and quick to an MVP. Also a guest platform. | 22.8 · 2026-06-12 |
| slack-bolt | Enterprise internal tooling — where they already use Slack | “Do they already use Slack?” carries more weight than it appears to: a bot on a platform your users do not have open is a bot nobody runs. | 1.30.0 · 2026-07-15 |
| nostr-tools | Censorship resistance | Maximum resistance — relays are pluggable and no single operator controls reach. Simplest protocol in the set, with native Lightning payments. | 1.4.1 · 2025-11-30 |
Latest release observed from PyPI in 2026-09.
What the research found
- Discord has best slash command UX but centralized
- Telegram fastest MVP (2-4h) but centralized
- Matrix only option with E2EE + self-hosting
- Matrix lacks native slash commands (use ! prefix)
- Nostr lacks interactive components, has Lightning payments
Explainer
Bot SDK Frameworks - EXPLAINER#
Research ID: 1.234 Category: Self-Operated Tools - Bot Development Status: Complete (S1-S4 + Explainer) Date: 2026-02-05
What This Research Covers#
Comprehensive analysis of bot SDK frameworks for building interactive chat bots across 5 major platforms:
- Matrix (matrix-nio) - Python SDK
- Discord (discord.py) - Python SDK
- Telegram (python-telegram-bot) - Python SDK
- Nostr (nostr-tools) - JavaScript SDK
- Slack (slack-bolt) - Python SDK
Quick Decision Guide#
Choose this platform if…
| Platform | Choose if… | Avoid if… |
|---|---|---|
| Matrix | Privacy/E2EE critical, self-hosting required | Need interactive UI (buttons/forms), quick MVP needed |
| Discord | Best UX/DX priority, gaming/community focus | Privacy required, payments needed, enterprise SSO |
| Telegram | Mobile-first, payments/commerce, fastest MVP | Rich formatting needed, threading critical |
| Nostr | Censorship resistance, Bitcoin/Lightning integration | Interactive UI needed, large user base required |
| Slack | Enterprise internal tool, workplace integration | Consumer bot, high message volume, budget constrained |
Key Findings#
Architecture Patterns#
| Platform | Connection | Command Model | State Management |
|---|---|---|---|
| Matrix | Long-polling | Manual parsing (! prefix) | SQLite store (E2EE) |
| Discord | WebSocket | CommandTree (decorators) | Cache-based |
| Telegram | Polling/Webhook | Handler-based | Built-in persistence |
| Nostr | WebSocket (multi-relay) | Manual parsing | Stateless (client-side) |
| Slack | Socket/HTTP | Listener-based | External (Redis/DB) |
Performance Comparison#
| Platform | Throughput (msg/s) | Latency | Memory Footprint | Rate Limits |
|---|---|---|---|---|
| Matrix | 5-8 | ~200-500ms | 15-60 MB | 10 req/s (matrix.org) |
| Discord | 20-40 | ~50-150ms | 30-50 MB (minimal) | 50 req/s global |
| Telegram | 10-20 | ~100-300ms | 20-30 MB | 30 msg/s per chat |
| Nostr | 10-50 | Varies | 10-15 MB | Relay-dependent |
| Slack | 1-3 | ~50ms | 40-60 MB | 1 msg/s per channel |
Feature Matrix#
| Feature | Matrix | Discord | Telegram | Nostr | Slack |
|---|---|---|---|---|---|
| Slash commands | No (! prefix) | Native | Native | No | Native |
| Interactive buttons | Widgets | Views | Inline KB | No | Block Kit |
| E2E encryption | ✅ Full | ❌ None | ⚠️ DMs only | ⚠️ Weak | ❌ None |
| Self-hosting | ✅ Yes | ❌ No | ❌ No | ✅ Yes | ❌ No |
| Payments | ❌ No | ❌ Banned | ✅ Native | ✅ Lightning | ❌ No |
| Threading | ✅ Native | ✅ Forums | ❌ Limited | ⚠️ Via tags | ✅ Native |
Use Case → Platform Mapping#
Privacy-Critical (Healthcare, Legal)#
Winner: Matrix (matrix-nio) Reason: Only platform with bot E2EE support
High-Volume Notifications#
Winners: Discord (discord.py) or Telegram (PTB) Reason: 20-40 msg/s vs 1-3 msg/s (Slack)
Enterprise Internal Tool#
Winner: Slack (slack-bolt) Reason: SSO, audit logs, compliance features
Payments/Commerce#
Winners: Telegram (fiat) or Nostr (Bitcoin) Reason: Native payment APIs
Censorship-Resistant#
Winner: Nostr (nostr-tools) Reason: No central authority, relay redundancy
Mobile-First#
Winner: Telegram (PTB) Reason: 900M+ mobile users, best mobile UX
Developer Tools (CI/CD)#
Winners: Slack (teams) or Discord (OSS) Reason: Code formatting, threading, developer adoption
Technical Complexity Ranking#
Easiest → Hardest:
Telegram (python-telegram-bot) - 2-4 hours to MVP
- Handler-based routing
- Built-in state persistence
- Simple inline keyboards
Discord (discord.py) - 2-4 hours to MVP
- CommandTree abstraction
- Rich Views for UI
- Auto rate limit handling
Slack (slack-bolt) - 4-8 hours to MVP
- Decorator-based routing
- Block Kit learning curve
- Socket Mode for dev, webhooks for prod
Matrix (matrix-nio) - 4-8 hours to MVP
- Manual command parsing
- E2EE setup complexity
- SQLite store management
Nostr (nostr-tools) - 4-8 hours to MVP
- No command framework (DIY everything)
- Manual event construction
- Relay management complexity
Cost Analysis#
| Platform | User Cost | Bot Cost | Scaling Cost |
|---|---|---|---|
| Matrix | Free (self-host) | Server costs ($5-50/mo) | Linear with traffic |
| Discord | Free | Free | Free (no bot fees) |
| Telegram | Free | Free | Free (no bot fees) |
| Nostr | Free | Free | Free (decentralized) |
| Slack | $8.75/user/mo | Free (dev) | Expensive at scale |
Key insight: Slack is 100x more expensive than alternatives for user-facing bots.
Risk Assessment#
Platform Stability (2026-2031)#
| Platform | Shutdown Risk | API Breakage | Policy Tightening |
|---|---|---|---|
| Slack | Very Low | Low | Medium |
| Discord | Very Low | Medium | High (verification) |
| Telegram | Low | Low | Medium |
| Matrix | Very Low (protocol) | Medium | Very Low |
| Nostr | None (protocol) | High (NIPs evolving) | None |
Recommendation: Abstract platform logic early (adapter pattern) to mitigate API breakage risk.
Research Structure#
packages/research/1.234-bot-sdk-frameworks/
├── EXPLAINER.md (this file)
├── metadata.yaml
└── 01-discovery/
├── S1-rapid/
│ └── recommendations-synthesis.md (Quick comparison + code examples)
├── S2-comprehensive/
│ ├── approach.md
│ ├── discord-py.md (Architecture, API, performance deep-dive)
│ ├── matrix-nio.md
│ ├── python-telegram-bot.md
│ ├── nostr-tools.md
│ └── slack-bolt.md
├── S3-need-driven/
│ └── use-case-analysis.md (8 use cases + decision tree)
└── S4-strategic/
└── ecosystem-analysis.md (Market trends, 5-year outlook)When to Read Each Phase#
S1 (Rapid): Start here
- Need: Quick decision, code examples
- Time: 10 minutes
- Output: Platform recommendation + starter code
S2 (Comprehensive): Deep dive into specific platform
- Need: Understand architecture, performance, trade-offs
- Time: 30-60 minutes per platform
- Output: Production deployment knowledge
S3 (Need-Driven): Map requirements to platforms
- Need: “Which platform for my use case?”
- Time: 20 minutes
- Output: Use case → platform mapping
S4 (Strategic): Long-term planning
- Need: Multi-year roadmap, market trends
- Time: 45 minutes
- Output: Risk assessment, future-proofing strategy
Common Gotchas#
Matrix#
- E2EE requires persistent store - bot loses keys on restart without SqliteStore
- No native slash commands - use ! prefix, confusing for users expecting /
- Widgets are complex - interactive buttons require embedding web UI
Discord#
- Privileged intents require approval -
>75guilds needs verification - Cache can be huge - 1000 guilds × 1000 members = 1 GB RAM
- Command sync delay - global commands take ~1 hour to propagate
Telegram#
- No native threading - replies via inline keyboards, not ideal for discussions
- Callback data limited to 64 bytes - use short IDs, store data in bot_data
- Group rate limits strict - 20 msg/minute for broadcasts
Nostr#
- No command framework - implement all parsing/routing yourself
- Relays go offline - connect to 10+ relays for redundancy
- No interactive UI - protocol limitation, text + reactions only
Slack#
- 3-second ack timeout - must call ack() immediately in handlers
- Rate limits strict - 1 msg/s per channel
- Expensive - $8.75/user/month minimum
Next Steps#
If implementing a bot:#
- Read S1 for quick platform selection
- Read S3 use case analysis for your specific requirements
- Read S2 comprehensive analysis for chosen platform
- Prototype with starter code from S1
- Production prep with deployment considerations from S2
If evaluating strategically:#
- Read S4 for market trends and long-term outlook
- Read S3 for decision framework
- Monitor platform changelogs for policy/API changes
- Abstract platform logic (adapter pattern) for multi-platform optionality
Related Research#
- 1.173: Terminology Extraction - For extracting keywords from bot commands
- 1.166: Text Classification - For intent detection in bot messages
- 1.234.x (future): Platform-specific advanced patterns (Discord Views, Telegram Mini Apps, etc.)
Maintenance Notes#
Last updated: 2026-02-05 Reviewer: Ivan (Research Crew) Confidence: High (all 5 platforms analyzed in production contexts)
Update triggers:
- Major SDK version releases (breaking changes)
- Platform policy updates (new restrictions/features)
- Market shifts (acquisitions, shutdowns)
Next review: 2026-Q3 (6 months)
S1: Rapid Discovery
Executive Summary#
Comparison of bot SDKs for building interactive bots (commands, buttons, integrations):
| Platform | Recommended SDK | Language | Time to MVP | Best For |
|---|---|---|---|---|
| Matrix | matrix-nio | Python | 4-8 hours | Privacy, self-hosting |
| Discord | discord.py | Python | 2-4 hours | Interactive UX |
| Telegram | python-telegram-bot | Python | 2-4 hours | Mobile-first |
| Nostr | nostr-tools | JavaScript | 4-8 hours | Censorship resistance |
| Slack | slack-bolt | Python | 4-8 hours | Enterprise |
Discord Bot SDKs#
discord.py (Python)#
Overview: Most popular Python Discord library, excellent slash command support.
Installation:
pip install discord.pySlash Commands Example:
import discord
from discord import app_commands
class Bot(discord.Client):
def __init__(self):
super().__init__(intents=discord.Intents.default())
self.tree = app_commands.CommandTree(self)
async def setup_hook(self):
await self.tree.sync()
client = Bot()
@client.tree.command(name="hello", description="Say hello")
async def hello(interaction: discord.Interaction):
await interaction.response.send_message("Hello!")
@client.tree.command(name="greet", description="Greet someone")
@app_commands.describe(name="Who to greet")
async def greet(interaction: discord.Interaction, name: str):
embed = discord.Embed(title=f"Hello {name}!", color=0x00ff00)
await interaction.response.send_message(embed=embed)
client.run("BOT_TOKEN")Interactive Buttons Example:
class ConfirmView(discord.ui.View):
@discord.ui.button(label="Confirm", style=discord.ButtonStyle.success)
async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
await interaction.response.edit_message(content="Confirmed!", view=None)
@discord.ui.button(label="Cancel", style=discord.ButtonStyle.danger)
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
await interaction.response.edit_message(content="Cancelled", view=None)Features:
| Feature | Support |
|---|---|
| Slash commands | Excellent (native) |
| Interactive buttons | Excellent (Views) |
| Dropdowns/Selects | Native |
| Modals/Forms | Native |
| Embeds | Rich formatting |
| File uploads | Native |
Strengths:
- Best slash command UX
- Excellent interactive components
- Large community
- Well-documented
Weaknesses:
- Requires bot verification for
>100servers - No self-hosting (centralized platform)
Time to MVP: 2-4 hours
Feature Comparison Matrix#
| Feature | Matrix | Discord | Telegram | Nostr | Slack |
|---|---|---|---|---|---|
| Slash commands | ! prefix | Native | Native | No | Native |
| Interactive buttons | Widgets | Views | Inline KB | No | Block Kit |
| Dropdowns | Widgets | Native | Native | No | Native |
| Modals/Forms | No | Native | No | No | Native |
| Rich embeds | HTML | Embeds | Markdown | No | Blocks |
| E2E encryption | Yes | No | No | No | No |
| Self-hosting | Yes | No | No | Yes | No |
| Payments | No | No | No | Zaps | No |
| Python SDK | Yes | Yes | Yes | No | Yes |
Matrix Bot SDKs#
matrix-nio (Python)#
Overview: Async Python SDK for Matrix, supports E2EE.
Installation:
pip install matrix-nio[e2e]Basic Bot Example:
import asyncio
from nio import AsyncClient, RoomMessageText
class Bot:
def __init__(self, homeserver, user_id, password):
self.client = AsyncClient(homeserver, user_id)
self.password = password
async def message_callback(self, room, event):
if event.body.startswith("!hello"):
await self.client.room_send(
room.room_id,
"m.room.message",
{"msgtype": "m.text", "body": "Hello!"}
)
async def run(self):
await self.client.login(self.password)
self.client.add_event_callback(self.message_callback, RoomMessageText)
await self.client.sync_forever(timeout=30000)
bot = Bot("https://matrix.org", "@bot:matrix.org", "password")
asyncio.run(bot.run())Features:
| Feature | Support |
|---|---|
| Text commands | Native (! prefix convention) |
| Slash commands | Not protocol-native |
| Interactive buttons | Via widgets (complex) |
| Reactions | Native |
| E2E Encryption | Full support |
| File uploads | Native |
Strengths:
- Async-first design
- E2EE support built-in
- Well-documented
- Self-hostable platform
Weaknesses:
- No native slash commands (Matrix protocol limitation)
- Interactive buttons require widget complexity
- Learning curve for Matrix concepts
Time to MVP: 4-8 hours
matrix-bot-sdk (TypeScript)#
Overview: Official TypeScript SDK for Matrix bots.
import { MatrixClient, SimpleFsStorageProvider, AutojoinRoomsMixin } from "matrix-bot-sdk";
const client = new MatrixClient(
"https://matrix.org",
"access_token",
new SimpleFsStorageProvider("bot.json")
);
AutojoinRoomsMixin.setupOnClient(client);
client.on("room.message", async (roomId, event) => {
if (event.content.body?.startsWith("!hello")) {
await client.sendText(roomId, "Hello!");
}
});
client.start().then(() => console.log("Bot started"));Time to MVP: 4-8 hours
Nostr Bot SDKs#
nostr-tools (JavaScript)#
Overview: Core JavaScript library for Nostr protocol.
Installation:
npm install nostr-toolsBot Example:
import { SimplePool, getPublicKey, finalizeEvent } from 'nostr-tools';
const sk = "private_key_hex";
const pk = getPublicKey(sk);
const pool = new SimplePool();
const relays = [
'wss://relay.damus.io',
'wss://relay.nostr.band'
];
// Subscribe to mentions
pool.subscribeMany(relays, [{
kinds: [1],
'#p': [pk],
since: Math.floor(Date.now() / 1000)
}], {
onevent(event) {
if (event.content.includes('!hello')) {
const reply = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [['e', event.id], ['p', event.pubkey]],
content: "Hello!"
}, sk);
relays.forEach(r => pool.publish([r], reply));
}
}
});Features:
| Feature | Support |
|---|---|
| Text commands | Via mentions |
| Slash commands | Not protocol-native |
| Interactive buttons | Not supported |
| Reactions | Kind 7 events |
| Zaps (payments) | NIP-57 |
| E2E encryption | Not built-in |
Strengths:
- Censorship resistant
- Lightning payments native
- Simple protocol
- No platform approval
Weaknesses:
- No interactive components
- Smaller user base
- Key management complexity
Time to MVP: 4-8 hours
Decision Guide#
Choose Matrix when:
- Privacy/E2EE is required
- Self-hosting is important
- Building for open/federated ecosystem
Choose Discord when:
- Best UX is priority
- Building for gaming/community
- Need rich interactive components
Choose Telegram when:
- Mobile-first experience needed
- Large existing user base
- Quick MVP required
Choose Nostr when:
- Censorship resistance required
- Lightning payments needed
- Building for Bitcoin community
Choose Slack when:
- Enterprise/workplace focus
- Existing Slack workspace
- Rich workflow automation needed
Slack Bot SDK#
slack-bolt (Python)#
Overview: Official Python SDK for Slack apps.
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
app = App(token="xoxb-...")
@app.command("/hello")
def handle_hello(ack, command, client):
ack()
client.chat_postMessage(
channel=command["channel_id"],
text="Hello!"
)
@app.action("button_click")
def handle_button(ack, body, client):
ack()
# Handle button click
handler = SocketModeHandler(app, "xapp-...")
handler.start()Features:
| Feature | Support |
|---|---|
| Slash commands | Excellent |
| Interactive buttons | Block Kit |
| Modals | Native |
| Rich formatting | Block Kit |
Strengths:
- Enterprise adoption
- Excellent documentation
- Rich UI components
Weaknesses:
- Per-user cost ($8.75/mo Pro)
- Admin API gated to Enterprise
- Platform lock-in
Time to MVP: 4-8 hours
Sources#
- matrix-nio documentation
- discord.py documentation
- python-telegram-bot docs
- nostr-tools GitHub
- Slack Bolt documentation
Telegram Bot SDKs#
python-telegram-bot#
Overview: Feature-complete Python wrapper for Telegram Bot API.
Installation:
pip install python-telegram-botExample:
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
keyboard = [[
InlineKeyboardButton("Option 1", callback_data="1"),
InlineKeyboardButton("Option 2", callback_data="2")
]]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Choose:", reply_markup=reply_markup)
async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer(f"You selected {query.data}")
app = Application.builder().token("BOT_TOKEN").build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(button_callback))
app.run_polling()Features:
| Feature | Support |
|---|---|
| Slash commands | Native (/command) |
| Inline keyboards | Excellent |
| Reply keyboards | Native |
| Inline mode | Native |
| File uploads | Native |
| Mini Apps | WebApp support |
Strengths:
- Best mobile experience
- Excellent inline keyboards
- Simple API
- Large user base (900M+)
Weaknesses:
- Centralized platform
- No self-hosting
Time to MVP: 2-4 hours
S2: Comprehensive
S2 Comprehensive: Approach#
Date: 2026-02-05 Methodology: S2 - Comprehensive technical analysis Scope: Deep technical comparison of bot SDK frameworks
Research Question#
How do major bot SDK frameworks differ in architecture, API design, performance characteristics, and implementation patterns?
Libraries Under Analysis#
- Matrix - matrix-nio (Python)
- Discord - discord.py (Python)
- Telegram - python-telegram-bot (Python)
- Nostr - nostr-tools (JavaScript)
- Slack - slack-bolt (Python)
Analysis Dimensions#
Architecture Patterns#
- Event-driven vs polling architectures
- Sync vs async execution models
- State management approaches
- Connection handling (WebSocket, long polling, webhooks)
API Design#
- Command registration patterns
- Event callback systems
- Message builder abstractions
- Error handling strategies
Performance Characteristics#
- Message throughput benchmarks
- Memory footprint
- Connection overhead
- Rate limiting handling
Feature Completeness#
- Interactive components (buttons, menus, forms)
- Rich media support
- E2E encryption
- Multi-device sync
- File handling
Method#
Each library analysis includes:
- Architecture overview - Core design patterns and runtime model
- API patterns - Code examples showing typical usage patterns
- Performance profile - Benchmarks and resource requirements
- Feature matrix - Capability comparison across implementations
- Integration patterns - How bots integrate with platform features
Sources#
- Official SDK documentation
- GitHub repositories and issue trackers
- Platform API specifications
- Performance benchmarks from community testing
- Production bot implementations
discord.py - S2 Comprehensive Analysis#
Library: discord.py Language: Python Platform: Discord Version Analyzed: 2.3.2 Date: 2026-02-05
Architecture Overview#
Core Design Pattern#
discord.py implements an event-driven async architecture with rich command abstractions:
- Client: Base connection manager (low-level)
- Bot: Extended client with command framework
- CommandTree: Slash command registration and dispatch
- Cogs: Plugin system for organizing commands
Runtime Model#
Bot Lifecycle:
┌─────────────┐
│ Bot │──→ login() ──→ Gateway handshake
└─────────────┘ ↓
↓ Receive READY event
Gateway WS ↓
↓ Dispatch events to handlers
[Loop] ↓
Command tree routes slash commandsConnection: Discord uses WebSocket gateway (not polling). Single persistent WS connection receives all events in real-time.
State Management#
Cache-based state (automatic):
- Guilds (servers) and channels cached on READY
- Members cached per guild (configurable via Intents)
- Messages cached (last 100 per channel by default)
Cache implications:
- Large bots (1000+ guilds): 500 MB - 2 GB memory for full member cache
- Privileged intents required for full member cache (needs Discord approval)
State persistence: discord.py doesn’t persist state to disk. On restart, bot rejoins gateway and receives fresh state.
API Design#
Command Registration Pattern#
Slash commands (modern approach):
from discord import app_commands
@bot.tree.command(name="hello", description="Say hello")
async def hello(interaction: discord.Interaction):
await interaction.response.send_message("Hello!")
# Command with parameters
@bot.tree.command()
@app_commands.describe(count="Number of times to greet")
async def greet(interaction: discord.Interaction, count: int):
await interaction.response.send_message(f"Hello! " * count)Key features:
- Type-safe parameter extraction (Discord validates types client-side)
- Auto-generated help text from descriptions
- Global sync: Commands registered globally or per-guild
Command sync:
await bot.tree.sync() # Global (takes ~1 hour to propagate)
await bot.tree.sync(guild=discord.Object(id=123)) # Guild-specific (instant)Legacy text commands (prefix-based):
@bot.command()
async def ping(ctx):
await ctx.send("Pong!")Still supported but Discord deprecated prefix commands in UI (no autocomplete, no type validation).
Event Callback System#
Decorator-based events:
@bot.event
async def on_message(message):
if message.author.bot:
return
# Process message
@bot.event
async def on_reaction_add(reaction, user):
# Handle reactionEvent hierarchy: Discord emits ~70 event types (message, member, guild, voice, etc.). You implement handlers for events you care about.
Callback signature: Events have specific signatures (on_message gets Message object, on_reaction_add gets Reaction + User).
Message Builder Abstraction#
Embed builder:
embed = discord.Embed(
title="Title",
description="Description",
color=0x00ff00
)
embed.add_field(name="Field 1", value="Value 1")
embed.set_thumbnail(url="https://...")
await interaction.response.send_message(embed=embed)Rich builder pattern: Fluent API for constructing complex embeds. Contrast with matrix-nio’s raw dict construction.
Interactive components:
class MyView(discord.ui.View):
@discord.ui.button(label="Click me", style=discord.ButtonStyle.primary)
async def button_callback(self, interaction, button):
await interaction.response.send_message("Clicked!")
@discord.ui.select(options=[
discord.SelectOption(label="Option 1", value="1"),
discord.SelectOption(label="Option 2", value="2")
])
async def select_callback(self, interaction, select):
await interaction.response.send_message(f"Selected: {select.values[0]}")
await interaction.response.send_message("Choose:", view=MyView())Component lifecycle: Views can have timeout (auto-disable after X seconds), persistent views (survive bot restarts).
Error Handling Strategy#
Exception-based errors:
try:
await interaction.response.send_message("Message")
except discord.Forbidden:
# Bot lacks permissions
except discord.HTTPException as e:
# API error
logger.error(f"HTTP {e.status}: {e.text}")Command error handler:
@bot.tree.error
async def on_app_command_error(interaction, error):
if isinstance(error, app_commands.CommandNotFound):
await interaction.response.send_message("Unknown command")Automatic retry: discord.py retries failed requests (5xx errors) with exponential backoff.
Performance Characteristics#
Message Throughput#
WebSocket advantages:
- Low latency: ~50-150ms from event to bot (vs 200-500ms for polling)
- No polling overhead: Single WS connection handles all events
Rate limits (per bot token):
- 50 requests/second globally
- 5 requests/second per channel
- Special limits for bulk operations (bulk delete, ban/kick)
Practical throughput: 20-40 messages/second sustainable (respecting per-channel limits).
Memory Footprint#
Minimal config (no intents): ~30-50 MB Full member cache (large bot): 500 MB - 2 GB
Memory scaling:
- ~500 KB per guild (metadata + channels)
- ~1 KB per cached member
- ~10 KB per cached message (100 messages/channel default)
Large bot example: 1000 guilds × 1000 members/guild = ~1 GB member cache
Connection Overhead#
Startup cost:
- Gateway handshake: ~500ms
- READY event: 1-5s (sends all guild data)
- Guild streaming: Large bots get guilds in chunks
Keepalive: Discord sends heartbeat every 41.25s. Bot must respond or gateway closes connection.
Rate Limiting Handling#
Per-route buckets: Discord uses complex rate limit bucketing (different limits for different endpoints).
discord.py automatically handles rate limits:
- Pre-emptive rate limiting (tracks bucket state, doesn’t send if limit reached)
- Queues requests exceeding limits
- Waits for bucket reset before sending
Global rate limit: If bot hits global 50 req/s limit, all requests queue until window resets.
Transparent to bot code - you don’t implement retry logic.
Feature Completeness#
Interactive Components#
| Feature | Support | Notes |
|---|---|---|
| Buttons | Native (Views) | Up to 5 buttons per row, 5 rows per message |
| Select menus | Native | Single/multi-select, up to 25 options |
| Modals | Native | Pop-up forms with text inputs |
| Autocomplete | Native | Dynamic options for slash command params |
View persistence:
class PersistentView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None) # Never times out
@discord.ui.button(label="Persistent", custom_id="persistent_button")
async def callback(self, interaction, button):
await interaction.response.send_message("Still works after restart!")
# Register on bot startup
bot.add_view(PersistentView())Custom IDs enable views to survive bot restarts (Discord stores component state, bot re-registers handlers).
Rich Media Support#
- Embeds: Rich cards with title, description, fields, images, thumbnails
- Images/Videos: Direct upload or URL reference
- File attachments: Up to 25 MB (100 MB for Nitro users)
- Stickers: Native sticker support
File upload:
file = discord.File("image.png")
await interaction.response.send_message("Image:", file=file)E2E Encryption#
Not supported. Discord doesn’t offer E2EE for any messages (bot or user).
Privacy implication: Discord can read all messages. Not suitable for privacy-critical use cases.
Multi-Device Sync#
Not applicable to bots (bots run on single instance, not user accounts).
User accounts sync across devices, but bot tokens don’t represent user accounts.
File Handling#
Upload:
await channel.send(file=discord.File("file.pdf", filename="document.pdf"))Single-step upload (vs matrix-nio’s two-step upload → send).
CDN caching: Uploaded files hosted on Discord’s CDN (permanent URLs unless deleted).
Integration Patterns#
Bot Registration#
- Create bot application at Discord Developer Portal
- Generate bot token
- Configure OAuth2 scopes (bot, applications.commands)
- Generate invite URL, add bot to guilds
Privileged intents (requires approval for >100 guilds):
GUILD_MEMBERS(member join/leave events)GUILD_PRESENCES(online status)MESSAGE_CONTENT(read message content)
Why approval needed? Privacy concerns (large bots can scrape member data).
Webhook Support#
Discord webhooks (alternative to bots):
# Bots can create webhooks
webhook = await channel.create_webhook(name="Bot Webhook")
# Post via webhook (appears as separate user)
await webhook.send("Message from webhook")Webhook limitations:
- Can’t receive events (send-only)
- Can’t use slash commands
- Can’t access full API (limited to sending messages)
Use case: External services posting to Discord (CI/CD notifications, monitoring alerts).
Admin API Access#
Bot permissions (configurable per guild):
- Manage channels, roles, messages
- Ban/kick members
- Administrator (full access)
Permission integers: Discord uses bitfield permissions (combine with bitwise OR).
Requires guild owner approval when inviting bot.
Sharding#
Large bot requirement (2500+ guilds):
# Discord requires sharding at scale
bot = commands.AutoShardedBot(command_prefix="!", intents=intents)How sharding works:
- Discord assigns guilds to shards (shard 0 gets guilds 0-999, shard 1 gets 1000-1999, etc.)
- Each shard = separate gateway connection
- Bot code runs in single process, library manages shards
Manual sharding (advanced):
# Run separate processes for each shard
bot = commands.Bot(..., shard_id=0, shard_count=4) # Process 1
bot = commands.Bot(..., shard_id=1, shard_count=4) # Process 2When to shard: >2500 guilds (Discord enforces), or >150k members cached.
Comparison Insights#
Strengths relative to other platforms:
- Best interactive UX (Views, Modals, Autocomplete)
- Native command framework (CommandTree vs manual parsing)
- Automatic rate limit handling (vs manual implementation)
- Rich embed system (best formatting of all platforms)
Weaknesses relative to other platforms:
- No E2EE (Matrix has it)
- No self-hosting (centralized platform)
- Privileged intents (friction for large bots)
- Bot verification (required for
>75guilds with privileged intents)
Architecture comparison:
- vs matrix-nio: WebSocket (lower latency) vs long-polling
- vs python-telegram-bot: Views (class-based components) vs inline keyboards (dict-based)
- vs slack-bolt: Similar WebSocket approach, but Discord has richer component library
Production Deployment Considerations#
High Availability#
Single-instance architecture: discord.py maintains WS connection, can’t easily run multiple instances (gateway would spawn multiple connections).
HA approaches:
- Blue-green deployment (new instance connects, old disconnects)
- Run multiple bots with different tokens (manual work distribution)
Stateless design: Bot should store minimal state (rely on Discord’s cache, externalize user data to database).
Monitoring#
Key metrics:
- Gateway latency (bot.latency)
- Command invocation rate
- Error rate by command
- Shard status (if sharded)
Built-in stats:
@bot.event
async def on_ready():
logger.info(f"Latency: {bot.latency * 1000}ms")
logger.info(f"Guilds: {len(bot.guilds)}")Integrate with Prometheus, Datadog, or CloudWatch for production monitoring.
Backup and Recovery#
No persistent state (besides bot token). On restart:
- Reconnect to gateway
- Receive READY event with fresh state
- Re-register persistent views
External state (database): Bot should store:
- Per-guild configuration
- User data (permissions, preferences)
- Analytics data
Recovery: Restart bot, reconnects to gateway automatically. No backup required for bot code.
Sources#
- discord.py documentation
- Discord API documentation
- Discord Gateway API
- Production bot case studies: Public bot monitoring dashboards, Discord API rate limit analysis
matrix-nio - S2 Comprehensive Analysis#
Library: matrix-nio Language: Python Platform: Matrix Version Analyzed: 0.24.0 Date: 2026-02-05
Architecture Overview#
Core Design Pattern#
matrix-nio implements an event-driven async architecture built on Python’s asyncio:
- AsyncClient: Main client managing connection lifecycle
- Event loop integration: Native async/await for all I/O operations
- Store-based state: Persistent state management for E2EE
- Event callbacks: Register handlers for Matrix event types
Runtime Model#
Client Lifecycle:
┌─────────────┐
│ AsyncClient │──→ login() ──→ Initial sync
└─────────────┘ ↓
↓ Set up callbacks
sync_forever() ↓
↓ Process events ──→ Invoke callbacks
[Loop] ↓
Send responsesConnection handling: Uses long-polling sync (Matrix’s /sync endpoint), not WebSocket. Each sync cycle fetches new events since last sync token.
State Management#
- Ephemeral mode (default): No persistence, loses E2EE keys on restart
- Store mode: SQLite-backed storage for:
- E2EE device keys
- Olm/Megolm sessions
- Sync tokens
- Room state cache
Key insight: Matrix’s encryption model requires persistent state. Without a store, encrypted rooms become unusable after restarts.
API Design#
Command Registration Pattern#
Matrix doesn’t have “slash commands” at protocol level. Bots conventionally use ! prefix:
async def message_callback(self, room, event):
if not event.decrypted:
return # Skip encrypted events we can't read
body = event.body
if body.startswith("!hello"):
await self.client.room_send(
room.room_id,
"m.room.message",
{"msgtype": "m.text", "body": "Hello!"}
)No command routing DSL - you manually parse event.body. Compare to Discord’s @bot.command() decorator.
Event Callback System#
Event callbacks are type-based:
client.add_event_callback(
callback_func,
RoomMessageText # Specific event type
)
# Multiple event types:
client.add_event_callback(callback_func, (RoomMessageText, RoomEncryptedImage))Callback signature:
async def callback(room: MatrixRoom, event: Event) -> NoneStrengths: Type-safe event filtering Weaknesses: Manual dispatch logic for commands
Message Builder Abstraction#
Low-level send interface:
await client.room_send(
room_id="!abc:matrix.org",
message_type="m.room.message",
content={
"msgtype": "m.text",
"body": "Plain text fallback",
"format": "org.matrix.custom.html",
"formatted_body": "<b>HTML content</b>"
}
)No builder pattern. You construct dicts manually. Compare to:
- Discord:
discord.Embed(title="...", description="...") - Telegram:
InlineKeyboardMarkup([[button1, button2]])
HTML formatting via formatted_body field provides rich text.
Error Handling Strategy#
matrix-nio uses response objects instead of exceptions:
response = await client.room_send(...)
if isinstance(response, RoomSendError):
logger.error(f"Failed to send: {response.message}")
else:
logger.info(f"Sent event: {response.event_id}")Response types:
LoginResponse/LoginErrorSyncResponse/SyncErrorRoomSendResponse/RoomSendError
Why this pattern? Avoids exception handling overhead in hot paths (sync loop processes many events).
Performance Characteristics#
Message Throughput#
Long-polling overhead: Each sync cycle has ~200-500ms latency (HTTP round-trip + server processing).
Rate limits (matrix.org homeserver):
- 10 requests/second per IP
- Burst allowance: 20 requests
Practical throughput: ~5-8 messages/second for interactive bots on matrix.org. Self-hosted instances can configure higher limits.
Memory Footprint#
Minimal config (no store): ~15-20 MB resident memory With store + E2EE: ~40-60 MB (includes libolm crypto library)
Sync response caching: Matrix servers send full room state on initial sync. Subsequent syncs are incremental, but initial sync for large rooms (1000+ members) can be 5-10 MB.
Connection Overhead#
Startup cost:
- Login: ~200ms
- Initial sync: 500ms - 5s (depends on room count)
- E2EE setup (if enabled): +500ms
Keepalive: 30-second timeout on /sync endpoint. Client must sync at least every 30s to stay “online.”
Rate Limiting Handling#
matrix-nio respects HTTP 429 responses with Retry-After headers:
if isinstance(response, RateLimitError):
await asyncio.sleep(response.retry_after_ms / 1000)
# Retry requestNo automatic retry - you implement exponential backoff manually.
Feature Completeness#
Interactive Components#
| Feature | Support | Notes |
|---|---|---|
| Buttons | Widgets (MSC1236) | Requires embedding web content via m.room.message with widget URL |
| Menus/Dropdowns | Widgets | Same as buttons - no native protocol support |
| Forms | Widgets | Complex, requires hosting web UI |
| Reactions | Native | m.reaction event type |
Widget complexity: To show buttons, you:
- Host a web page with button UI
- Send
m.widgetevent with URL - Client renders iframe
- Bot receives widget events via separate API
Compared to Discord/Telegram: Significantly more complex. Most Matrix bots stick to text commands + reactions.
Rich Media Support#
- Images: Native via
m.room.message(msgtype: m.image) - Video/Audio: Native support
- File uploads: Matrix homeserver stores media, generates
mxc://URLs - HTML formatting: Via
formatted_bodyfield (subset of HTML allowed)
File size limits: Configurable per homeserver (matrix.org: 50 MB default)
E2E Encryption#
Full protocol support:
- Olm: 1:1 message encryption
- Megolm: Group message encryption (efficient for rooms)
- Device verification: Cross-signing support
Implementation notes:
# E2EE requires store + async setup
client = AsyncClient("https://matrix.org", "@bot:matrix.org", store_path="./store")
await client.login(password)
await client.sync() # Download device keys
await client.rooms_joined() # Load room membersKey challenge: Managing device keys for all room members. Encrypted rooms require tracking:
- User device list (can have multiple devices)
- Device key changes (when users add new devices)
- Megolm session rotation
Performance impact: E2EE adds ~100-200ms per message (encryption + key distribution).
Multi-Device Sync#
Matrix protocol natively supports multi-device:
- Each device gets unique device ID
- Messages sync across devices automatically
- Read receipts propagate to all user’s devices
Bot implications: If bot runs on multiple servers, each needs separate device ID + E2EE setup.
File Handling#
Upload flow:
response = await client.upload(
data_provider=open("file.jpg", "rb"),
content_type="image/jpeg",
filename="file.jpg"
)
mxc_url = response.content_uri
# Send message referencing uploaded file
await client.room_send(
room_id,
"m.room.message",
{
"msgtype": "m.image",
"url": mxc_url,
"body": "file.jpg"
}
)Two-step process: Upload → send message. Allows reusing uploaded files across multiple messages.
Integration Patterns#
Bot Registration#
- Create Matrix account (manual or via registration endpoint)
- Obtain access token via password login
- (Optional) Configure as bot user (appservice for advanced use)
No OAuth flow for bots. Most bots use password authentication.
Webhook Support#
Matrix doesn’t have native webhooks. Options:
- Run bot server: Bot stays connected via long-polling
- Push gateway (advanced): Configure homeserver to push events to HTTP endpoint
Practical implication: Most Matrix bots are long-running processes, not webhook handlers.
Admin API Access#
Synapse admin API (requires server admin):
- User management
- Room moderation
- Server statistics
Not available on shared homeservers (matrix.org). Requires self-hosting for admin features.
Federation Integration#
Matrix is federated - bots can interact with users on any homeserver:
# Bot on bot-server.com can message user on matrix.org
await client.room_send(
"!room:matrix.org",
"m.room.message",
{"msgtype": "m.text", "body": "Cross-server message"}
)Federation delay: Messages between servers can have 1-5s latency depending on network topology.
Comparison Insights#
Strengths relative to other platforms:
- Only bot SDK with native E2EE (Discord/Slack/Telegram don’t support E2EE for bots)
- Self-hostable (vs centralized Discord/Slack/Telegram)
- Open federation (vs walled gardens)
Weaknesses relative to other platforms:
- No native interactive components (Discord has Views, Telegram has InlineKeyboards)
- Manual command parsing (Discord has command tree, Telegram has CommandHandler)
- Complex E2EE setup (vs “just works” on other platforms)
- Smaller ecosystem (fewer users/bots than Discord/Telegram)
Architecture comparison:
- vs Discord.py: Both async, but Discord has better command abstraction
- vs python-telegram-bot: Both event-driven, but PTB has richer component API
- vs slack-bolt: Matrix uses long-polling, Slack uses WebSocket (lower latency)
Production Deployment Considerations#
High Availability#
Challenges:
- Stateful E2EE (can’t easily run multiple bot instances)
- Sync token management (each instance would have separate state)
Solutions:
- Run single bot instance with auto-restart (systemd, Docker restart policy)
- Use appservice for distributed bots (complex setup)
Monitoring#
Key metrics to track:
- Sync loop latency (should be
<1s) - Failed message sends (rate limit or network issues)
- E2EE session failures (device key problems)
matrix-nio doesn’t have built-in metrics. Integrate with Prometheus/StatsD manually.
Backup and Recovery#
Critical state (must backup):
store/directory (E2EE keys + sync tokens)- Bot access token
Recovery: Restore store + access token, bot resumes from last sync point.
Without backup: Bot can re-login, but loses E2EE sessions → can’t decrypt past encrypted messages.
Sources#
- matrix-nio documentation
- Matrix Client-Server API Spec
- Matrix E2EE Implementation Guide
- Production bot analysis: Synapse homeserver metrics, bot implementation case studies
nostr-tools - S2 Comprehensive Analysis#
Library: nostr-tools Language: JavaScript/TypeScript Platform: Nostr Version Analyzed: 2.1.0 Date: 2026-02-05
Architecture Overview#
Core Design Pattern#
nostr-tools implements a low-level event-driven architecture without bot abstractions:
- SimplePool: Connection pool for multiple relays
- Event creation: Manual event construction + signing
- Subscription model: Filter-based event subscriptions
- No command framework: You implement all parsing/routing
Runtime Model#
Bot Lifecycle:
┌─────────────┐
│ SimplePool │──→ Connect to relays
└─────────────┘ ↓
↓ Subscribe with filters
WebSocket ↓
connections Receive matching events
↓ ↓
[Loop] Process + respondConnection: Each relay = separate WebSocket. Pool manages N relay connections.
No state management: Nostr is stateless. Events published to relays, relays forward to subscribers. No server-side persistence.
State Management#
Client-side only:
- Bot stores private key locally
- No server-side sessions
- No message history (unless you query relays)
Event fetching:
// Get past events (recent 100)
const events = await pool.querySync(relays, {
kinds: [1],
authors: [publicKey],
limit: 100
});Relay differences: Each relay may have different events (no guaranteed consistency). Bots query multiple relays for redundancy.
API Design#
Command Registration Pattern#
Manual parsing (no command framework):
import { SimplePool, getPublicKey, finalizeEvent } from 'nostr-tools';
const sk = privateKeyHex;
const pk = getPublicKey(sk);
const pool = new SimplePool();
const relays = ['wss://relay.damus.io', 'wss://relay.nostr.band'];
pool.subscribeMany(relays, [{
kinds: [1], // Text notes
'#p': [pk], // Mentions of this bot
since: Math.floor(Date.now() / 1000)
}], {
onevent(event) {
const content = event.content.toLowerCase();
if (content.includes('!hello')) {
handleHello(event);
} else if (content.includes('!help')) {
handleHelp(event);
}
}
});No decorators, no command tree. You implement text parsing yourself.
Event Callback System#
Subscription filters:
// Filter by event kind
{kinds: [1]} // Text notes
{kinds: [3]} // Contact lists
{kinds: [7]} // Reactions
// Filter by author
{authors: [pubkey1, pubkey2]}
// Filter by tags
{'#p': [pubkey]} // Events mentioning pubkey
{'#e': [eventId]} // Events replying to eventId
// Time range
{since: timestamp, until: timestamp}
// Limit results
{limit: 100}Callback signature:
{
onevent(event) {
// Process event
},
oneose() {
// End of stored events (relay sent all matching past events)
}
}No type safety: Events are plain objects. You manually check event.kind and event.tags.
Message Builder Abstraction#
Manual event construction:
import { finalizeEvent } from 'nostr-tools';
// Reply to event
const reply = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [
['e', originalEvent.id], // Reply to event
['p', originalEvent.pubkey] // Mention original author
],
content: "Hello!"
}, privateKey);
// Publish to relays
await Promise.all(relays.map(relay => pool.publish([relay], reply)));No builder pattern. You construct objects manually.
Event structure:
{
id: "event_id_hex", // SHA256 hash of event
pubkey: "author_pubkey", // Author's public key
created_at: 1234567890, // Unix timestamp
kind: 1, // Event type
tags: [["tag", "value"]], // Metadata tags
content: "text", // Event content
sig: "signature_hex" // Schnorr signature
}Error Handling Strategy#
No automatic error handling:
try {
const pubs = pool.publish(relays, event);
// Wait for relay confirmations
await Promise.all(pubs.map(p => p.on));
console.log("Published successfully");
} catch (error) {
console.error("Publish failed:", error);
}Relay failures are silent: If 3/5 relays accept event, others fail silently.
No retry logic: You implement exponential backoff manually.
Performance Characteristics#
Message Throughput#
No rate limits (protocol level):
- Nostr protocol doesn’t enforce rate limits
- Individual relays may have limits (varies per relay)
Relay capacity: Most public relays handle 100-1000 events/second.
Practical throughput: 10-50 events/second to popular relays (limited by relay capacity, not protocol).
Memory Footprint#
Minimal: ~10-15 MB (Node.js runtime + nostr-tools)
No caching: nostr-tools doesn’t cache events. Bot uses memory only for active subscriptions.
Connection scaling: Each relay = 1 WebSocket = ~1-2 KB memory overhead.
Connection Overhead#
Startup cost:
- Connect to relays: ~200-500ms (depends on relay count + network)
- No authentication (keypair-based, no login flow)
Keepalive: WebSocket ping/pong (relay-specific, typically 30-60s).
Relay churn: Relays go offline frequently. Bots should reconnect automatically:
// Auto-reconnect on disconnect
relays.forEach(relay => {
relay.on('disconnect', () => {
setTimeout(() => pool.ensureRelay(relay), 5000);
});
});Rate Limiting Handling#
Relay-dependent:
- Some relays: No limits
- Others: 10 events/second per connection
- Popular relays: 100 events/second aggregate
No standard rate limit protocol. Relays may disconnect clients violating limits.
Best practice: Publish to multiple relays (redundancy + load distribution).
Feature Completeness#
Interactive Components#
| Feature | Support | Notes |
|---|---|---|
| Buttons | Not supported | Protocol limitation |
| Menus | Not supported | Protocol limitation |
| Forms | Not supported | Protocol limitation |
| Reactions | Native (Kind 7) | Simple emoji reactions |
| Zaps (Lightning payments) | Native (NIP-57) | Monetization primitive |
No rich UI: Nostr is text-focused. Clients render text + reactions + zaps.
Reaction example:
// React to event with 👍
const reaction = finalizeEvent({
kind: 7,
tags: [
['e', eventId],
['p', eventPubkey]
],
content: "👍"
}, privateKey);
await pool.publish(relays, reaction);Rich Media Support#
Basic media:
- Text: Native (Kind 1 events)
- Images/Videos: URL references in content (clients render)
- No inline embeds: Clients show URLs, may auto-expand
Example:
content: "Check out this image: https://example.com/image.jpg"Clients detect URLs and render previews.
File hosting: Not protocol-native. Use external hosting (IPFS, HTTP), reference in event.
E2E Encryption#
Not built-in:
- Events are public by default
- NIP-04 (deprecated): DM encryption using shared secret
- NIP-44 (new): Improved DM encryption
Encrypted DM example (NIP-04):
import { nip04 } from 'nostr-tools';
// Encrypt message
const ciphertext = await nip04.encrypt(privateKey, recipientPubkey, "Secret message");
// Send as Kind 4 event
const dm = finalizeEvent({
kind: 4,
tags: [['p', recipientPubkey]],
content: ciphertext
}, privateKey);
await pool.publish(relays, dm);Limitations:
- Only 1:1 DMs (no group encryption)
- Metadata not encrypted (sender, recipient, timestamp visible)
vs Matrix: Nostr’s encryption is weaker (no group E2EE, metadata leaks).
Multi-Device Sync#
Not applicable:
- Nostr keypairs = identity
- Same keypair on multiple devices = automatic “sync” (all devices see same events)
No device management: No concept of “devices” (just keypairs).
File Handling#
No native file storage:
- Upload file to external host (IPFS, S3, Cloudflare R2)
- Reference URL in event content
- Clients fetch file from URL
Example:
// 1. Upload to IPFS (external service)
const ipfsHash = await uploadToIPFS(file);
// 2. Publish event with URL
const event = finalizeEvent({
kind: 1,
tags: [['imeta', `url https://ipfs.io/ipfs/${ipfsHash}`]],
content: `File: https://ipfs.io/ipfs/${ipfsHash}`
}, privateKey);File URLs in events: Permanent (event immutability).
Integration Patterns#
Bot Registration#
No registration:
- Generate keypair (private + public key)
- Start publishing events
No approval, no API token. Anyone can publish events.
Keypair generation:
import { generateSecretKey, getPublicKey } from 'nostr-tools';
const sk = generateSecretKey();
const pk = getPublicKey(sk);
console.log("Private key:", Buffer.from(sk).toString('hex'));
console.log("Public key:", pk);Store private key securely (hex string, 32 bytes).
Webhook Support#
Not applicable:
- No centralized server
- Bots subscribe to relays via WebSocket
- No HTTP callbacks
Admin API Access#
No admin API:
- Relays don’t have “admin” concept
- Events are public (anyone can read)
- Bots can’t “moderate” (no delete/ban at protocol level)
Relay-level moderation: Relay operators can filter events (but that’s relay policy, not protocol feature).
Monetization (Zaps)#
Native Lightning payment integration (NIP-57):
// Request zap (payment) from user
const zapRequest = finalizeEvent({
kind: 9734,
tags: [
['relays', ...relays],
['amount', '1000'], // Satoshis
['lnurl', lnurl]
],
content: "Please zap!"
}, privateKey);
await pool.publish(relays, zapRequest);Zap flow:
- Bot sends zap request (Kind 9734)
- User’s client shows Lightning invoice
- User pays
- Lightning service publishes zap receipt (Kind 9735)
- Bot sees zap receipt, knows payment completed
Use case: Monetize bot services (pay per query, subscriptions).
Comparison Insights#
Strengths relative to other platforms:
- Censorship resistant (no centralized platform)
- No approval/verification (instant bot creation)
- Native payments (Lightning zaps)
- Open protocol (anyone can build relay/client)
- Pseudonymous (keypair-based identity)
Weaknesses relative to other platforms:
- No interactive UI (no buttons, menus, forms)
- Manual everything (no command framework, no builders)
- Smaller user base (~100k active users vs 900M Telegram)
- Weak encryption (metadata leaks, no group encryption)
- Relay unreliability (relays go offline, no SLA)
Architecture comparison:
- vs discord.py: No abstractions (vs rich command tree + Views)
- vs python-telegram-bot: No handler system (vs CommandHandler, CallbackQueryHandler)
- vs matrix-nio: Weaker encryption, but simpler protocol
Use case fit:
- ✅ Bitcoin community bots (native Lightning integration)
- ✅ Censorship-resistant apps (no platform can ban you)
- ❌ Mass-market bots (small user base, poor UX)
- ❌ Privacy-critical bots (weak encryption, metadata leaks)
Production Deployment Considerations#
High Availability#
Relay redundancy: Connect to 5-10 relays (if 2-3 go offline, bot still works).
Stateless: Bot crashes = reconnect to relays + resubscribe. No persistent state loss.
Multi-instance: Run multiple bot instances (same keypair), all receive events (relay broadcasts to all subscribers).
Monitoring#
Key metrics:
- Relay connection count (should match configured relays)
- Event processing latency
- Relay disconnects per hour
- Failed event publishes
No built-in monitoring: Implement custom metrics:
let connectedRelays = 0;
let publishFailures = 0;
pool.on('connect', () => connectedRelays++);
pool.on('disconnect', () => connectedRelays--);
async function publishWithMetrics(event) {
try {
await pool.publish(relays, event);
} catch (error) {
publishFailures++;
throw error;
}
}Backup and Recovery#
Critical data:
- Private key (hex string)
- Relay list (hardcoded or config file)
Recovery:
- Restore private key
- Reconnect to relays
- Resubscribe to events
- Bot resumes (no missed events if offline
>1hour, relays don’t queue)
No message history: Nostr relays typically keep 7-30 days of events. Older events may be lost.
Sources#
- nostr-tools GitHub
- Nostr Protocol NIPs (Nostr Implementation Possibilities)
- NIP-01: Basic protocol
- NIP-57: Lightning Zaps
- Production relay analysis: Public relay statistics, bot implementation case studies
python-telegram-bot - S2 Comprehensive Analysis#
Library: python-telegram-bot (PTB) Language: Python Platform: Telegram Version Analyzed: 20.7 Date: 2026-02-05
Architecture Overview#
Core Design Pattern#
PTB implements a handler-based async architecture with flexible routing:
- Application: Main bot manager (successor to Updater in v13)
- Handler system: Route updates to callbacks (CommandHandler, MessageHandler, CallbackQueryHandler)
- JobQueue: Built-in task scheduler
- Persistence: Optional state persistence layer
Runtime Model#
Bot Lifecycle:
┌─────────────────┐
│ Application │──→ initialize() ──→ Setup handlers
└─────────────────┘ ↓
↓ start_polling() OR start_webhook()
Get updates ↓
↓ Dispatch to handlers
[Loop] ↓
Execute callbacksTwo modes:
- Polling: Bot fetches updates via
getUpdatesAPI (long-polling, 30s timeout) - Webhook: Telegram POSTs updates to your HTTP endpoint
Polling is default (easier setup). Webhook for production (lower latency, scales better).
State Management#
Built-in persistence (optional):
from telegram.ext import PicklePersistence
persistence = PicklePersistence(filepath="bot_data.pkl")
app = Application.builder().token(TOKEN).persistence(persistence).build()Persists:
bot_data: Global bot stateuser_data: Per-user statechat_data: Per-chat statecallback_data: Button callback state
Storage backends: PicklePersistence (file), DictPersistence (memory), or custom (Redis, PostgreSQL).
API Design#
Command Registration Pattern#
Handler-based routing:
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("Hello!")
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler(["help", "info"], help_command)) # Multiple commandsCommand arguments:
async def greet(update: Update, context: ContextTypes.DEFAULT_TYPE):
# /greet Alice Bob -> context.args = ["Alice", "Bob"]
name = " ".join(context.args) or "World"
await update.message.reply_text(f"Hello, {name}!")
app.add_handler(CommandHandler("greet", greet))No type validation (unlike Discord’s slash commands). You manually parse context.args.
Event Callback System#
Handler hierarchy (evaluated in order):
# 1. Commands
app.add_handler(CommandHandler("start", start))
# 2. Callback queries (button clicks)
app.add_handler(CallbackQueryHandler(button_handler, pattern="^option_"))
# 3. Messages with filters
from telegram.ext import filters
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# 4. Catch-all
app.add_handler(MessageHandler(filters.ALL, fallback))Filter system:
filters.TEXT: Text messages onlyfilters.PHOTO: Photosfilters.COMMAND: Any command- Custom filters:
filters.User([123, 456]),filters.Regex(r"pattern")
Callback signature:
async def handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> NoneContext object provides:
context.bot: Bot API clientcontext.user_data: Per-user persistent statecontext.args: Command argumentscontext.job_queue: Task scheduler
Message Builder Abstraction#
InlineKeyboard builder:
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
keyboard = [
[InlineKeyboardButton("Option 1", callback_data="opt1")],
[InlineKeyboardButton("Option 2", callback_data="opt2")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Choose:", reply_markup=reply_markup)Button types:
callback_data: Bot handles click (CallbackQuery)url: Opens URLswitch_inline_query: Switches to inline modeweb_app: Opens mini-app
ReplyKeyboard (alternative):
from telegram import ReplyKeyboardMarkup, KeyboardButton
keyboard = [
[KeyboardButton("Button 1"), KeyboardButton("Button 2")],
[KeyboardButton("Share contact", request_contact=True)]
]
reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
await update.message.reply_text("Choose:", reply_markup=reply_markup)Difference:
- InlineKeyboard: Buttons under message (ephemeral)
- ReplyKeyboard: Replaces keyboard at bottom (persistent until removed)
Error Handling Strategy#
Exception-based + error handler:
async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
logger.error(f"Update {update} caused error: {context.error}")
# Optionally notify user
if update and update.effective_message:
await update.effective_message.reply_text("An error occurred.")
app.add_error_handler(error_handler)Common exceptions:
telegram.error.TimedOut: Network timeouttelegram.error.BadRequest: Invalid API call (e.g., message not found)telegram.error.Forbidden: Bot blocked by usertelegram.error.RetryAfter: Rate limit (includes retry_after seconds)
Automatic retry: PTB retries network errors (connection issues) but not API errors (e.g., BadRequest).
Performance Characteristics#
Message Throughput#
Long-polling latency: ~100-300ms (better than matrix-nio’s 200-500ms due to optimized Telegram servers).
Rate limits:
- 30 messages/second per chat
- 20 messages/second to different users
- Group broadcasts: 20 messages/minute (slow for mass notifications)
Practical throughput: 10-20 messages/second for interactive bots.
Memory Footprint#
Minimal config: ~20-30 MB resident memory With persistence: +10-20 MB (depends on data size)
Update queue: PTB queues incoming updates (default: unbounded). High traffic can cause memory growth.
Memory management:
# Limit concurrent updates
app = Application.builder().token(TOKEN).concurrent_updates(100).build()Connection Overhead#
Startup cost (polling mode):
getMecall: ~100ms (fetch bot info)- First
getUpdates: ~100ms
Keepalive: Polling uses long-polling (30s timeout), automatically keeps connection alive.
Webhook mode startup:
- Set webhook URL: ~100ms
- Wait for Telegram to POST updates (no polling overhead)
Rate Limiting Handling#
Manual handling required:
from telegram.error import RetryAfter
try:
await bot.send_message(chat_id, "Message")
except RetryAfter as e:
logger.warning(f"Rate limited, retry after {e.retry_after}s")
await asyncio.sleep(e.retry_after)
await bot.send_message(chat_id, "Message")No automatic retry for rate limits (unlike discord.py).
Best practice: Implement token bucket or leaky bucket algorithm for bulk messaging.
Feature Completeness#
Interactive Components#
| Feature | Support | Notes |
|---|---|---|
| Inline keyboards | Native | Up to 8 buttons per row, unlimited rows |
| Reply keyboards | Native | Custom keyboard layout |
| Inline mode | Native | Bot responds to @bot query in any chat |
| Web Apps | Native | Embed web UI in bot (mini-apps) |
| Forms | Via Web Apps | No native modal support |
Callback data limitation: 64 bytes max per button. For larger data, store in context.bot_data and use short IDs as callback_data.
Inline mode example:
from telegram import InlineQueryResultArticle, InputTextMessageContent
async def inline_query(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.inline_query.query
results = [
InlineQueryResultArticle(
id="1",
title=f"Result for {query}",
input_message_content=InputTextMessageContent(f"You searched: {query}")
)
]
await update.inline_query.answer(results)
app.add_handler(InlineQueryHandler(inline_query))Rich Media Support#
- Markdown/HTML: Via
parse_modeparameter - Photos/Videos: Native upload or URL reference
- Documents: Any file type, up to 50 MB (2 GB for premium users)
- Stickers: Send existing stickers or upload custom sticker packs
- Polls: Native poll creation
Media groups (albums):
await bot.send_media_group(chat_id, [
InputMediaPhoto("photo1.jpg"),
InputMediaPhoto("photo2.jpg")
])E2E Encryption#
Secret Chats (user-to-user only):
- Native E2EE for user DMs
- Not available for bots (bots can’t create secret chats)
Regular chats: Server-side encryption (Telegram can read messages).
Privacy implication: Bots don’t have E2EE. Better than Discord (no E2EE at all), worse than Matrix (full E2EE support).
Multi-Device Sync#
User accounts: Native multi-device sync (cloud-based).
Bots: Not applicable (bots don’t run on multiple devices).
File Handling#
Upload:
# From disk
await update.message.reply_document(document=open("file.pdf", "rb"))
# From URL
await update.message.reply_photo(photo="https://example.com/image.jpg")
# With custom filename
from telegram import InputFile
await update.message.reply_document(
document=InputFile(open("data.csv", "rb"), filename="report.csv")
)File download:
file = await context.bot.get_file(update.message.document.file_id)
await file.download_to_drive("downloaded.pdf")File IDs: Telegram assigns unique file_id to uploaded files. Reuse file_id to avoid re-uploading.
Integration Patterns#
Bot Registration#
- Message @BotFather on Telegram
- Create new bot (
/newbot) - Receive bot token
- (Optional) Configure commands (
/setcommands)
No approval process (unlike Discord’s verification). Instant bot creation.
Webhook Support#
Setup webhook:
await application.bot.set_webhook(
url="https://yourdomain.com/webhook",
allowed_updates=Update.ALL_TYPES
)
# Run webhook server
application.run_webhook(listen="0.0.0.0", port=8443, webhook_url="https://yourdomain.com/webhook")Requirements:
- HTTPS required (Telegram won’t POST to HTTP)
- Valid SSL certificate (self-signed not allowed)
- Open port (443, 80, 88, or 8443)
Webhook advantages:
- Lower latency (~50ms vs 100-300ms polling)
- No polling overhead
- Scales better (Telegram pushes to you)
Webhook challenges:
- Requires public IP + domain
- SSL certificate management
- Firewall configuration
Admin API Access#
Bot API limitations:
- Can’t access user phone numbers
- Can’t initiate DMs (users must message bot first)
- Can’t add bot to channels as admin (requires user intervention)
Admin permissions (when added to groups):
- Delete messages
- Ban/restrict users
- Pin messages
- Change group title/photo
Requires group owner to grant permissions.
Payment Integration#
Telegram Payments API:
await bot.send_invoice(
chat_id=chat_id,
title="Product",
description="Description",
payload="invoice_payload",
provider_token="STRIPE_TOKEN",
currency="USD",
prices=[LabeledPrice("Product", 1000)] # $10.00
)Supported providers: Stripe, Yandex.Money, etc.
Native payment flow (users pay within Telegram, bot receives confirmation).
Comparison Insights#
Strengths relative to other platforms:
- Mobile-first UX (900M+ users, primarily mobile)
- Instant bot creation (no approval process)
- Inline mode (bot works in any chat, not just dedicated conversations)
- Payment integration (native commerce support)
- File handling (up to 2 GB for premium users)
Weaknesses relative to other platforms:
- No E2EE for bots (Matrix has it)
- Manual rate limit handling (discord.py auto-handles)
- Limited formatting (no rich embeds like Discord)
- Callback data limit (64 bytes vs Discord’s 100)
Architecture comparison:
- vs discord.py: Polling/webhook vs WebSocket, handler-based vs command tree
- vs matrix-nio: Better UX (inline keyboards), worse privacy (no bot E2EE)
- vs slack-bolt: Similar webhook support, but Telegram is consumer-focused (Slack is enterprise)
Production Deployment Considerations#
High Availability#
Webhook mode enables HA:
- Run multiple bot instances behind load balancer
- Telegram distributes updates via round-robin
- Each instance handles subset of requests
Polling mode: Single instance only (can’t poll from multiple servers without duplicate updates).
State synchronization: Use external persistence (Redis, PostgreSQL) for shared state across instances.
Monitoring#
Key metrics:
- Update processing latency
- Handler error rate
- Rate limit hits
- Queue depth (incoming updates)
Built-in stats:
@app.post_init
async def post_init(application: Application):
logger.info(f"Bot started: @{application.bot.username}")
# Track handler timing
import time
async def timed_handler(update, context):
start = time.time()
await original_handler(update, context)
duration = time.time() - start
logger.info(f"Handler took {duration:.2f}s")Backup and Recovery#
Persistent state (if using persistence):
- Backup
bot_data.pkl(or Redis/PostgreSQL) - Store bot token securely
Recovery:
- Restore persistence file
- Restart bot with same token
- Bot resumes from last update offset
Webhook recovery: If bot crashes, Telegram queues updates (up to 24 hours). On restart, bot receives queued updates.
Sources#
- python-telegram-bot documentation
- Telegram Bot API
- Telegram Bot Platform Guide
- Production bot analysis: Telegram bot analytics, community rate limit testing
S2 Recommendation: The Platform Chooses the SDK#
S2 examined five SDKs in depth — discord.py, matrix-nio, nostr-tools,
python-telegram-bot and slack-bolt. The technical comparison produces a
result that looks unhelpful until you accept it:
These SDKs do not compete. Each is the client library for one platform, and
each is the best available option for that platform. There is no shootout to run,
because a Discord bot cannot be written with slack-bolt at any level of skill.
What that means for the decision#
The question “which bot SDK should I use” is almost always the wrong question. The real one is “where are the people I am building for?” — and the SDK follows from the answer with no further deliberation.
The exception is a genuinely open choice, where the audience could be assembled anywhere. That case is rarer than it looks, and S3 handles it.
What S2 does establish#
Since the platform decides the SDK, the useful technical findings are about what each SDK costs to operate, not which is better:
- The event model differs more than the API surface. Some SDKs hand you a long-lived connection with a callback; others expect you to run a webhook endpoint. That determines your deployment shape — a persistent process versus a request handler — and it is a hosting decision disguised as a library detail.
- Rate limits are the operational constraint, not throughput. Every platform imposes them, they differ substantially, and they bind long before Python does.
- The self-hosting question splits the set cleanly. Matrix can be run entirely on your own infrastructure. Discord, Telegram and Slack cannot — the platform is somebody else’s service, and your bot is a guest on it. Nostr sits apart again: relays are pluggable and no single operator controls reach.
Where the technical detail actually pays#
Read the per-SDK file for the platform you have already chosen. Reading all five to pick between them is time spent on a choice that was made by your audience before you opened an editor.
slack-bolt - S2 Comprehensive Analysis#
Library: slack-bolt (Python) Language: Python Platform: Slack Version Analyzed: 1.18.0 Date: 2026-02-05
Architecture Overview#
Core Design Pattern#
slack-bolt implements a listener-based async architecture with decorator routing:
- App: Main application instance (manages listeners + middleware)
- Listener system: Route events/commands to handlers
- Middleware stack: Chain handlers for auth, logging, etc.
- Socket Mode / HTTP: Two connection modes
Runtime Model#
Bot Lifecycle:
┌─────────────┐
│ App │──→ Register listeners
└─────────────┘ ↓
↓ Start Socket Mode OR HTTP server
Connection ↓
↓ Receive events from Slack
[Loop] ↓
Dispatch to listenersTwo modes:
- Socket Mode: WebSocket connection (development, no public URL required)
- HTTP Mode: Slack POSTs events to your endpoint (production)
State Management#
No built-in persistence:
- Bolt doesn’t provide state management
- You store state in external systems (Redis, PostgreSQL, DynamoDB)
Conversation state (built-in):
@app.action("button_click")
def handle_button(ack, body, client, context):
ack()
# context.user_id available
user_data = get_user_data(context.user_id) # Your storageNo automatic persistence like python-telegram-bot. You implement storage.
API Design#
Command Registration Pattern#
Decorator-based routing:
from slack_bolt import App
app = App(token="xoxb-...", signing_secret="...")
# Slash command
@app.command("/hello")
def handle_hello(ack, command, respond):
ack() # Acknowledge within 3 seconds (required)
respond(f"Hello, <@{command['user_id']}>!")
# Shortcut (global)
@app.shortcut("open_modal")
def handle_shortcut(ack, shortcut, client):
ack()
client.views_open(...)
# Message event
@app.message("hello")
def handle_hello_message(message, say):
say(f"Hi <@{message['user']}>!")Acknowledge pattern: All interactions must be acknowledged within 3 seconds (Slack timeout).
ack() # Always first line in listenerCommand arguments:
@app.command("/greet")
def handle_greet(ack, command, respond):
ack()
# /greet Alice -> command['text'] = "Alice"
name = command['text'] or "World"
respond(f"Hello, {name}!")Event Callback System#
Event subscription:
# Listen to all messages
@app.event("message")
def handle_message(event, say):
say(f"You said: {event['text']}")
# Filtered events
import re
@app.message(re.compile(r"(hi|hello)"))
def handle_greeting(message, say):
say("Hello!")Event types:
message: Chat messagesapp_mention: Bot mentioned with @reaction_added: Emoji reactionmember_joined_channel: User joins channel
Callback signature (flexible):
def handler(ack, body, logger): # Bolt injects arguments
# ack: Acknowledge function
# body: Full event payload
# logger: Pre-configured loggerLazy listeners (async processing):
@app.event("message")
def handle_message_sync(ack, body):
ack() # Acknowledge immediately
# Body queued for async processing
def process_message_async(body, say):
# Heavy processing
say("Processed!")
app.event("message", middleware=[handle_message_sync])(process_message_async)Message Builder Abstraction#
Block Kit builder:
@app.command("/survey")
def survey(ack, client, command):
ack()
client.views_open(
trigger_id=command["trigger_id"],
view={
"type": "modal",
"title": {"type": "plain_text", "text": "Survey"},
"blocks": [
{
"type": "input",
"block_id": "name_block",
"label": {"type": "plain_text", "text": "Name"},
"element": {
"type": "plain_text_input",
"action_id": "name_input"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "Submit"},
"action_id": "submit_survey",
"style": "primary"
}
]
}
]
}
)Block Kit = Slack’s UI framework. Rich components (buttons, selects, date pickers, etc.).
Simpler syntax via slack_sdk.models:
from slack_sdk.models.blocks import *
from slack_sdk.models.views import View
view = View(
type="modal",
title=PlainTextObject(text="Survey"),
blocks=[
InputBlock(
block_id="name",
label=PlainTextObject(text="Name"),
element=PlainTextInputElement(action_id="name_input")
),
ActionsBlock(elements=[
ButtonElement(text=PlainTextObject(text="Submit"), action_id="submit")
])
]
)
client.views_open(trigger_id=..., view=view)Error Handling Strategy#
Exception-based + error listener:
@app.error
def handle_errors(error, body, logger):
logger.error(f"Error: {error}")
logger.debug(f"Body: {body}")
# Optionally notify admins
# Specific handler errors
@app.command("/hello")
def handle_hello(ack, command, respond):
ack()
try:
result = risky_operation()
respond(f"Success: {result}")
except Exception as e:
respond(f"Error: {e}")Slack API errors:
from slack_sdk.errors import SlackApiError
try:
client.chat_postMessage(channel="C123", text="Message")
except SlackApiError as e:
if e.response["error"] == "channel_not_found":
logger.error("Invalid channel")
elif e.response["error"] == "not_in_channel":
logger.error("Bot not in channel")Automatic retry: slack_sdk retries 429 (rate limit) and 5xx errors automatically.
Performance Characteristics#
Message Throughput#
Rate limits (per workspace):
- 1 message/second per channel (Tier 1)
- 1 request/second for most API methods (Tier 2)
- 20 requests/minute for user-scoped methods (Tier 3)
- 100+ requests/minute for high-volume apps (Tier 4, approved apps only)
Practical throughput: 1-3 messages/second for typical apps.
Burst allowance: Short bursts (5-10 messages) tolerated before rate limiting.
Memory Footprint#
Minimal config: ~40-60 MB (includes slack_sdk dependencies) Socket Mode: +10-20 MB (WebSocket connection overhead)
No caching: Bolt doesn’t cache workspace data. Each API call fetches fresh data.
Connection Overhead#
Socket Mode startup:
- Authenticate: ~200ms
- WebSocket handshake: ~100ms
- Ready to receive events:
<500ms total
HTTP Mode startup:
- Start HTTP server: ~50ms
- Wait for Slack POSTs (no connection overhead)
Keepalive: Socket Mode sends WebSocket pings every 30s.
Rate Limiting Handling#
Automatic retry (slack_sdk):
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
client = WebClient(token="xoxb-...")
try:
response = client.chat_postMessage(channel="C123", text="Message")
except SlackApiError as e:
if e.response.status_code == 429:
# Automatically retried by SDK
logger.warning(f"Rate limited, retry after {e.response.headers['Retry-After']}s")No manual retry needed (unlike python-telegram-bot).
Feature Completeness#
Interactive Components#
| Feature | Support | Notes |
|---|---|---|
| Buttons | Native (Block Kit) | Rich styling (primary, danger) |
| Select menus | Native | Static, dynamic, user/channel select |
| Date picker | Native | Calendar UI |
| Time picker | Native | Time selection |
| Modals | Native | Pop-up forms with validation |
| Workflows | Native | No-code automation (Workflow Builder) |
Button example:
@app.command("/action")
def show_buttons(ack, respond):
ack()
respond(
blocks=[
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "Approve"},
"action_id": "approve",
"style": "primary"
},
{
"type": "button",
"text": {"type": "plain_text", "text": "Deny"},
"action_id": "deny",
"style": "danger"
}
]
}
]
)
@app.action("approve")
def handle_approve(ack, body, respond):
ack()
respond(text="Approved!", replace_original=True)Rich Media Support#
- Attachments: Images, videos, files (up to 1 GB per file)
- Rich text: Block Kit formatting (markdown subset)
- Embeds: Unfurl links automatically (og:image, og:title)
- Custom unfurls: Bot controls link previews
File upload:
client.files_upload(
channels="C123",
file="report.pdf",
title="Monthly Report"
)E2E Encryption#
Not supported: Slack doesn’t offer E2EE (even for user messages).
Enterprise Key Management (EKM): Enterprise Grid customers can bring their own encryption keys (but Slack still has access during processing).
Privacy implication: Slack (and admins) can read all messages.
Multi-Device Sync#
User accounts: Native multi-device sync (cloud-based).
Bots: Not applicable (single bot instance per app).
File Handling#
Upload:
client.files_upload_v2(
channel="C123",
file="./document.pdf",
title="Document",
initial_comment="Here's the file"
)Download:
import requests
file_info = client.files_info(file="F123")
file_url = file_info["file"]["url_private"]
# Download with bot token
response = requests.get(file_url, headers={"Authorization": f"Bearer {bot_token}"})
with open("downloaded.pdf", "wb") as f:
f.write(response.content)File hosting: Slack hosts files on CDN (permanent URLs unless deleted by user/admin).
Integration Patterns#
Bot Registration#
- Create app at api.slack.com/apps
- Configure bot scopes (permissions)
- Install app to workspace
- Receive bot token (
xoxb-...)
OAuth flow (for distribution):
from slack_bolt.oauth import OAuthSettings
oauth_settings = OAuthSettings(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
scopes=["chat:write", "commands"],
)
app = App(oauth_settings=oauth_settings)Bot distributes to multiple workspaces, each gets separate token.
Webhook Support#
Incoming webhooks (simple, send-only):
import requests
webhook_url = "https://hooks.slack.com/services/T123/B456/abc"
requests.post(webhook_url, json={"text": "Message from webhook"})Limitations: Can’t receive events, can’t use full API.
App webhooks (full API):
- Configure Request URL in app settings
- Slack POSTs events to your endpoint
- Use Bolt’s HTTP mode
Admin API Access#
Scoped permissions (OAuth scopes):
chat:write: Post messageschannels:read: List channelsusers:read: List workspace membersadmin: Workspace admin actions (Enterprise Grid only)
Requires workspace owner approval for admin scopes.
Enterprise Grid: Additional APIs for org-level management (multi-workspace).
Workflow Integration#
Workflow Steps (custom steps in Workflow Builder):
@app.step("approval_step")
def approval_step(ack, step, configure):
ack()
# User configures step in Workflow Builder
@app.action("approval_step_save")
def save_approval_step(ack, body, client):
ack()
# Save step configurationUse case: No-code automation for end users (trigger workflows on events, bot provides custom logic).
Comparison Insights#
Strengths relative to other platforms:
- Enterprise-grade (SSO, admin controls, audit logs)
- Rich UI components (Block Kit rivals Discord’s complexity)
- Workflow Builder (no-code automation)
- Mature ecosystem (10M+ daily active users in paid workspaces)
Weaknesses relative to other platforms:
- Expensive ($8.75/user/month Pro, $15/user/month Business+)
- No E2EE (unlike Matrix)
- Strict rate limits (1 msg/s/channel vs 30 msg/s Telegram)
- Enterprise-focused (overkill for consumer bots)
Architecture comparison:
- vs discord.py: Similar (WebSocket, rich components), but Slack is workplace-focused
- vs python-telegram-bot: Better rich UI (Block Kit), worse rate limits
- vs matrix-nio: Easier (no E2EE complexity), but no self-hosting
Use case fit:
- ✅ Internal workplace bots (HR, DevOps, support)
- ✅ Enterprise integrations (Salesforce, Jira, GitHub)
- ❌ Consumer bots (expensive, not consumer-facing)
- ❌ High-volume messaging (rate limits too strict)
Production Deployment Considerations#
High Availability#
Socket Mode: Single connection (no HA). If bot crashes, reconnect.
HTTP Mode: Scale horizontally (multiple instances behind load balancer). Slack distributes events via round-robin.
Stateless design: Store state externally (Redis, DynamoDB). Each instance handles subset of requests.
Monitoring#
Key metrics:
- Event processing latency
- API error rate
- Rate limit hits
- Socket Mode connection drops (if using)
Built-in logging:
import logging
logging.basicConfig(level=logging.DEBUG)
@app.middleware
def log_request(logger, body, next):
logger.info(f"Received: {body['type']}")
next()Slack API analytics: Slack provides dashboard (API call counts, error rates) at api.slack.com.
Backup and Recovery#
Critical data:
- Bot token (
xoxb-...) - Signing secret (for request verification)
- OAuth credentials (if distributing)
External state: Store in database (PostgreSQL, DynamoDB, Redis).
Recovery:
- Restore tokens + secrets
- Restart bot
- Reconnect (Socket Mode) or resume (HTTP Mode)
- No missed events (Slack retries failed webhook deliveries for 3 hours)
Event replay: Slack doesn’t queue events >3 hours. If bot offline longer, events lost.
Sources#
- Slack Bolt for Python documentation
- Slack API documentation
- Block Kit Builder
- Production bot analysis: Slack app analytics, enterprise deployment case studies
S3: Need-Driven
Anti-Patterns: When NOT to Use Each SDK#
Avoid Matrix if:#
- Interactive UI critical (buttons, forms) - widgets too complex
- Quick MVP needed (
<4hours) - E2EE setup takes time - Non-technical users - slash commands confusing (! prefix vs /)
Avoid Discord if:#
- Privacy/E2EE required - no encryption
- Mobile-first audience - good but not mobile-native
- Payments needed - prohibited by ToS
- Enterprise SSO required - consumer platform
Avoid Telegram if:#
- Rich formatting needed - limited compared to Discord/Slack
- Threading important - no native thread support
- Self-hosting required - centralized platform
Avoid Nostr if:#
- Interactive UI needed - protocol limitation (text only)
- Large user base required - ~100k active users (niche)
- Reliable delivery critical - relays can go offline
- UX polish matters - rough edges, developer-focused
Avoid Slack if:#
- Consumer-facing bot - expensive per-user pricing
- High message volume - strict rate limits (1 msg/s/channel)
- Budget-constrained - $8.75/user/month minimum
- No enterprise budget - not cost-effective for small projects
Framework Purpose#
This analysis maps bot SDK selection to concrete use cases and requirements. Rather than abstract comparisons, we evaluate SDKs against real-world constraints and needs.
Decision Tree#
START: What type of bot are you building?
┌─ Privacy-critical? (Healthcare, legal, sensitive data)
│ └─ YES → Matrix (matrix-nio)
│ └─ NO → Continue
│
┌─ Enterprise internal tool?
│ └─ YES → Already use Slack? → YES → Slack (slack-bolt)
│ │ → NO → Evaluate Matrix (self-host) vs Slack (buy)
│ └─ NO → Continue
│
┌─ Payments/commerce?
│ └─ YES → Bitcoin/Lightning? → YES → Nostr (nostr-tools)
│ │ → NO → Telegram (python-telegram-bot)
│ └─ NO → Continue
│
┌─ Censorship resistance critical?
│ └─ YES → Maximum resistance → Nostr (nostr-tools)
│ │ Federated OK → Matrix (matrix-nio)
│ └─ NO → Continue
│
┌─ High message volume? (>1000 msg/hour)
│ └─ YES → Gaming/community? → YES → Discord (discord.py)
│ │ → NO → Telegram (python-telegram-bot)
│ └─ NO → Continue
│
┌─ Mobile-first audience?
│ └─ YES → Telegram (python-telegram-bot)
│ └─ NO → Continue
│
┌─ Developer tool? (CI/CD, GitHub integration)
│ └─ YES → Internal team? → YES → Slack (slack-bolt)
│ │ → NO → Discord (discord.py) [OSS community]
│ └─ NO → Continue
│
└─ Default recommendation: Discord (discord.py) [best UX, largest features]Multi-Platform Strategy#
Some use cases benefit from supporting multiple platforms simultaneously:
When to go multi-platform:#
- Maximize reach: Different audiences prefer different platforms
- Risk mitigation: Platform ban/outage doesn’t kill bot
- Feature complementarity: Use Telegram for mobile, Discord for desktop
Implementation approach:#
Shared core logic:
# Core bot business logic (platform-agnostic)
class BotCore:
def handle_hello(self, user_id: str) -> str:
return f"Hello, {user_id}!"
# Platform-specific adapters
class DiscordAdapter:
def __init__(self, core: BotCore):
self.core = core
@bot.command()
async def hello(self, ctx):
response = self.core.handle_hello(ctx.author.id)
await ctx.send(response)
class TelegramAdapter:
def __init__(self, core: BotCore):
self.core = core
async def hello(self, update, context):
response = self.core.handle_hello(update.effective_user.id)
await update.message.reply_text(response)When NOT to go multi-platform:
- Maintenance burden 2x (each platform has quirks)
- Feature parity difficult (Discord Views ≠ Telegram InlineKeyboards)
- User confusion (different command syntax per platform)
Recommendation: Start single-platform, add others when proven demand exists.
S3 Recommendation: Answer the Constraint, Not the Feature List#
S3’s decision tree resolves in one pass, because each branch is a hard constraint rather than a preference. The order matters — the first “yes” is the answer.
| Ask, in order | If yes |
|---|---|
| Is this privacy-critical? Healthcare, legal, sensitive data | Matrix (matrix-nio) — the only option with end-to-end encryption and full self-hosting |
| Is it an enterprise internal tool? | Slack (slack-bolt) if they already use Slack; otherwise weigh Matrix self-hosted against buying Slack |
| Is censorship resistance critical? | Nostr (nostr-tools) for maximum resistance; Matrix if federated is sufficient |
| High message volume, above ~1000/hour? | Discord (discord.py) for gaming and community; Telegram otherwise |
| Mobile-first audience? | Telegram (python-telegram-bot) |
| Developer tooling — CI/CD, source-control integration? | Slack for an internal team; Discord for an open-source community |
| None of the above | Discord (discord.py) — best UX and the richest feature set |
The two branches that carry real weight#
Privacy comes first for a reason. It is the only constraint in the list that cannot be satisfied by any other option. Everything below it is a trade; this one is a gate. If the data is sensitive and you need E2EE plus infrastructure control, Matrix is not the best choice — it is the only one.
“Do they already use Slack?” is doing more work than it appears to. An enterprise bot lives or dies on whether people see it. Deploying to a platform your users do not already have open is a bot nobody runs.
The anti-patterns file is the useful half#
anti-patterns.md records when not to use each SDK, and it is worth more than
the positive recommendations. The recurring mistake in this category is choosing
the platform you like rather than the one your audience is on — and no amount of
SDK quality recovers from that.
Multi-platform#
multi-platform-strategy.md covers building for several at once. The short
version: the platform-specific layer is smaller than teams expect and the shared
layer is larger, so structure the bot as domain logic with thin adapters — but do
this because you genuinely have audiences in two places, not to defer the
decision.
Sources#
- Platform API documentation (Discord, Telegram, Matrix, Slack, Nostr)
- Production bot case studies
- Developer community surveys (r/discordapp, r/Telegram, Matrix community)
- Enterprise procurement analysis (Slack vs alternatives)
Use Case Matrix#
1. Privacy-Critical Communication Bot#
Requirements:
- End-to-end encryption mandatory
- Self-hosting capability
- No third-party data access
- Audit trail for compliance
SDK Evaluation:
| Platform | Fit | Rationale |
|---|---|---|
| Matrix (matrix-nio) | ✅ Best fit | Only SDK with native E2EE. Self-hostable. Full message encryption including metadata. |
| Discord (discord.py) | ❌ No E2EE | Centralized, no encryption. Discord can read all messages. |
| Telegram (PTB) | ⚠️ Partial | Secret Chats E2EE (user-to-user only), but bots can’t use secret chats. |
| Nostr (nostr-tools) | ⚠️ Weak | DM encryption (NIP-04), but metadata leaks. No group encryption. |
| Slack (slack-bolt) | ❌ No E2EE | Enterprise-focused, but no message encryption. Admins see all. |
Recommendation: Matrix (matrix-nio) - Only option with full E2EE support for bot messages. Requires understanding E2EE key management complexity.
Implementation notes:
- Use
SqliteStorefor persistent E2EE keys - Implement device verification flow
- Plan for key backup/recovery
- Test encrypted room membership changes
2. High-Volume Notification Bot#
Requirements:
- Send 1000+ messages/hour
- Low latency (
<500ms) - Reliable delivery
- Cost-effective at scale
SDK Evaluation:
| Platform | Throughput | Latency | Cost | Verdict |
|---|---|---|---|---|
| Discord (discord.py) | 20-40 msg/s | ~50-150ms | Free | ✅ Best |
| Telegram (PTB) | 10-20 msg/s | ~100-300ms | Free | ✅ Good |
| Matrix (matrix-nio) | 5-8 msg/s | ~200-500ms | Free (self-host) | ⚠️ Slow |
| Slack (slack-bolt) | 1-3 msg/s | ~50ms | $8.75/user/mo | ❌ Expensive + slow |
| Nostr (nostr-tools) | 10-50 msg/s | Varies | Free | ⚠️ Unreliable relays |
Recommendation: Discord (discord.py) or Telegram (PTB) depending on audience:
- Discord: Best for gaming/community (younger demographic)
- Telegram: Best for mobile-first (broader geographic reach, 900M+ users)
Implementation notes:
- Discord: Respect 50 req/s global limit, use per-channel batching
- Telegram: Implement token bucket for 30 msg/s rate limit
- Both: Use webhook mode in production for lower latency
3. Enterprise Internal Tool#
Requirements:
- SSO integration
- Admin audit logs
- Rich UI components (forms, approvals)
- IT department approval process
SDK Evaluation:
| Platform | Enterprise Features | UI Richness | Approval Process | Verdict |
|---|---|---|---|---|
| Slack (slack-bolt) | Excellent (SSO, SCIM, audit) | Block Kit (excellent) | Standard procurement | ✅ Best |
| Matrix (matrix-nio) | Self-host (full control) | Widgets (complex) | Deploy your own | ⚠️ High effort |
| Discord (discord.py) | Limited (no SSO) | Views (excellent) | Consumer platform | ❌ Not enterprise |
| Telegram (PTB) | None | Inline keyboards (good) | Consumer platform | ❌ Not enterprise |
| Nostr (nostr-tools) | None | Text only | No approval needed | ❌ Not enterprise |
Recommendation: Slack (slack-bolt) - Only platform designed for enterprise. Cost justified by compliance/security features.
Alternative: Matrix (matrix-nio) if:
- Company already self-hosts Matrix
- SSO not critical (or implement via proxy)
- UI simplicity acceptable
- Privacy requirements outweigh UX
Implementation notes:
- Slack: Use OAuth for multi-workspace distribution
- Request admin scopes early (approval takes time)
- Integrate with SAML/SCIM for user provisioning
- Use Workflow Builder for no-code end-user automation
4. Developer Tool Bot (CI/CD Integration)#
Requirements:
- GitHub/GitLab integration
- Code syntax highlighting
- Thread-based conversations
- Developer-friendly UX
SDK Evaluation:
| Platform | Code Display | Threading | Dev Adoption | Verdict |
|---|---|---|---|---|
| Slack (slack-bolt) | Excellent (code blocks) | Native threads | High (workplace) | ✅ Best for teams |
| Discord (discord.py) | Good (markdown) | Forum channels | High (OSS community) | ✅ Best for OSS |
| Matrix (matrix-nio) | HTML formatting | Native threads | Medium (tech community) | ⚠️ Smaller reach |
| Telegram (PTB) | Markdown | No native threads | Low (consumer focus) | ❌ Poor fit |
| Nostr (nostr-tools) | None | Via ’e’ tags | Very low | ❌ Poor fit |
Recommendation:
- Slack (slack-bolt) for internal company tools
- Discord (discord.py) for open-source project notifications
Implementation notes:
- Slack: Use Block Kit code blocks with language highlighting
- Discord: Use embeds with code blocks (```python)
- Both: Implement threading for build logs (keep channels clean)
- GitHub webhooks → bot processes → formatted messages
5. Payments/Commerce Bot#
Requirements:
- Native payment integration
- Transaction confirmation
- Receipt delivery
- PCI compliance considerations
SDK Evaluation:
| Platform | Payment Support | Transaction Flow | Compliance | Verdict |
|---|---|---|---|---|
| Telegram (PTB) | Native (Stripe, etc.) | In-app checkout | Slack handles PCI | ✅ Best for consumers |
| Nostr (nostr-tools) | Lightning Network (Zaps) | Bitcoin-native | N/A (crypto) | ✅ Best for Bitcoin |
| Slack (slack-bolt) | None (external links) | Leave app | N/A | ⚠️ Manual integration |
| Discord (discord.py) | None (banned ToS) | N/A | N/A | ❌ Prohibited |
| Matrix (matrix-nio) | None | N/A | N/A | ❌ Not supported |
Recommendation:
- Telegram (PTB) for fiat currency (broad payment provider support)
- Nostr (nostr-tools) for Bitcoin/Lightning (native Zaps protocol)
Implementation notes:
- Telegram: Use
send_invoice+ handlepre_checkout_query - Payment providers: Stripe, Yandex.Money (varies by region)
- Nostr: Implement NIP-57 (Zaps) with LNURL/Lightning Address
- Consider fraud detection (user reputation, transaction limits)
6. Community Moderation Bot#
Requirements:
- Ban/kick users
- Delete messages
- Auto-moderation (spam, profanity)
- Audit log for mod actions
SDK Evaluation:
| Platform | Mod Powers | Auto-mod APIs | Audit Trail | Verdict |
|---|---|---|---|---|
| Discord (discord.py) | Excellent (ban, timeout, etc.) | Native (AutoMod) | Via webhooks | ✅ Best |
| Telegram (PTB) | Good (ban, restrict) | Manual implementation | Manual logging | ⚠️ Good |
| Slack (slack-bolt) | Limited (admin only) | Manual | Slack audit logs | ⚠️ Enterprise only |
| Matrix (matrix-nio) | Good (kick, ban) | Manual | Manual logging | ⚠️ Good |
| Nostr (nostr-tools) | None (protocol limitation) | Relay-level only | N/A | ❌ Not supported |
Recommendation: Discord (discord.py) - Best moderation API, AutoMod integration, built-in audit logging via guild audit log.
Implementation notes:
- Discord: Use
guild.ban(),member.timeout()for temp bans - Check
guild.audit_logsfor mod action history - Implement warning system (3 strikes → ban)
- Use AutoMod API for regex-based filtering (reduces bot load)
7. Decentralized/Censorship-Resistant Bot#
Requirements:
- No single point of failure
- Resistant to deplatforming
- Pseudonymous operation
- No approval process
SDK Evaluation:
| Platform | Decentralization | Censorship Resistance | Deployment Barrier | Verdict |
|---|---|---|---|---|
| Nostr (nostr-tools) | Full (relay network) | Excellent | None (instant) | ✅ Best |
| Matrix (matrix-nio) | Federated | Good | Self-host setup | ⚠️ Good |
| Discord (discord.py) | Centralized | None | Verification required | ❌ Can be banned |
| Telegram (PTB) | Centralized | None | Instant | ❌ Can be banned |
| Slack (slack-bolt) | Centralized | None | OAuth approval | ❌ Can be banned |
Recommendation:
- Nostr (nostr-tools) for maximum censorship resistance
- Matrix (matrix-nio) for federated approach with better UX
Implementation notes:
- Nostr: Connect to 10+ relays (redundancy), rotate relay list
- Generate keypair securely, no email/phone required
- Use NIP-05 for identity verification (optional)
- Matrix: Self-host homeserver, use
.onionaddress for anonymity - Both: No KYC, no approval process
8. Mobile-First Bot#
Requirements:
- Excellent mobile UX
- Push notifications
- Offline message queue
- Low bandwidth usage
SDK Evaluation:
| Platform | Mobile UX | Push Notifications | Offline Support | Bandwidth | Verdict |
|---|---|---|---|---|---|
| Telegram (PTB) | Excellent | Native | Message queue | Low | ✅ Best |
| Discord (discord.py) | Good | Native | Sync on reconnect | Medium | ⚠️ Good |
| Slack (slack-bolt) | Good | Native | Sync on reconnect | Medium | ⚠️ Good |
| Matrix (matrix-nio) | Good | Via push gateway | Sync tokens | High (E2EE) | ⚠️ Slower |
| Nostr (nostr-tools) | Limited | No standard | No guarantee | Low | ❌ Unreliable |
Recommendation: Telegram (PTB) - Built for mobile-first, 900M+ users primarily on mobile, excellent offline message queueing.
Implementation notes:
- Telegram: Messages queued on server if user offline
- Use inline keyboards (better than reply keyboards on mobile)
- Keep messages concise (mobile screen space)
- Test on slow networks (3G simulation)
S4: Strategic
Executive Summary#
Bot SDK ecosystem is fragmenting into three strategic directions:
- Enterprise consolidation (Slack) - workplace dominance, high margins
- Consumer scale (Discord, Telegram) - massive user bases, free platforms
- Decentralized protocols (Matrix, Nostr) - open standards, self-sovereignty
Long-term bet: Multi-protocol abstraction layers will emerge (like how web frameworks abstract HTTP). Early movers building protocol-agnostic bot cores will have advantage.
Architectural Patterns#
Pattern 1: The “Thin Client” Bot#
Design: Bot as thin wrapper around external service.
User → Platform SDK → Bot (routing only) → External API → ResponseExample: GitHub notifications bot
- Receives webhooks from GitHub
- Formats as platform-specific messages
- No state, no business logic
Best platforms: Slack (webhooks), Discord (webhooks), Telegram (webhooks)
Avoid: Matrix (no native webhooks), Nostr (requires persistent connection)
Pattern 2: The “Conversational State Machine” Bot#
Design: Multi-step workflows with user state.
User input → State lookup → Business logic → State update → ResponseExample: Survey bot (ask name → ask email → ask feedback → send confirmation)
Best platforms:
- Telegram (built-in persistence via
context.user_data) - Discord (can use persistent Views)
Avoid:
- Matrix (no state management, DIY)
- Nostr (stateless protocol, external DB required)
Pattern 3: The “Real-Time Sync” Bot#
Design: Bot mirrors external state in real-time.
External system → WebSocket/SSE → Bot → Platform → Users see updatesExample: CI/CD status bot (build starts → bot posts “Building…” → build completes → bot updates “✅ Done”)
Best platforms:
- Discord (edit messages, rich embeds)
- Slack (update messages, Block Kit)
Challenges:
- Telegram (can’t edit other bot messages)
- Matrix (no edit history, confusing UX)
Pattern 4: The “Collaborative Workspace” Bot#
Design: Bot facilitates multi-user workflows.
User A action → Bot state → Notify User B → User B action → Update shared viewExample: Approval workflow (employee requests, manager approves, bot tracks)
Best platforms:
- Slack (threads, Block Kit, workflow builder)
- Discord (forum channels, thread creation)
Poor fit:
- Telegram (no native threading)
- Nostr (no collaborative state)
Ecosystem Landscape#
Market Segmentation (2026)#
| Segment | Leaders | Growth | Monetization |
|---|---|---|---|
| Enterprise | Slack, Microsoft Teams | Stable (~10% YoY) | Per-user SaaS ($8-15/mo) |
| Gaming/Communities | Discord | High (~30% YoY) | Nitro subscriptions + server boosts |
| Consumer Mobile | Telegram, WhatsApp | High (~25% YoY) | Business API revenue |
| Decentralized | Matrix, Nostr | Nascent (~100% YoY, small base) | Self-hosted/donations |
Key insight: Enterprise and consumer markets diverging. Discord/Telegram growing faster but monetization unclear. Matrix/Nostr tiny but ideologically driven.
Emerging Opportunities#
1. AI-Augmented Moderation Bots#
Opportunity: Platforms struggle with moderation at scale. Bots combining:
- LLM-based content analysis (detect toxicity, spam)
- Platform mod APIs (ban, timeout, delete)
- Human-in-the-loop for edge cases
Best platforms: Discord (best mod APIs), Telegram (good APIs)
Differentiation: Domain expertise (gaming toxicity vs political discourse)
2. Cross-Platform Identity Bots#
Problem: Users have fragmented identities across platforms.
Solution: Bot that links Discord/Telegram/Matrix accounts, provides unified profile.
Tech: OAuth (Discord, Slack), Bot API (Telegram), Matrix auth
Monetization: Premium features (verified cross-platform identity)
3. Web3 Integration Bots#
Opportunity: Crypto communities need bot tooling:
- Wallet verification (prove NFT ownership)
- DAO governance (vote via chat)
- Token gating (access based on holdings)
Best platforms: Discord (largest crypto community), Nostr (Lightning native)
Caution: Regulatory uncertainty, scam risk (due diligence on partners)
4. Compliance/Audit Bots#
Opportunity: Regulated industries (healthcare, finance) need audit trails.
Features:
- Message archiving (compliance)
- Keyword alerting (detect violations)
- Audit reports (for regulators)
Best platforms: Slack (enterprise focus), Matrix (E2EE + audit trails)
Monetization: B2B SaaS ($500-5000/month per org)
5-Year Outlook (2026-2031)#
Predicted Consolidation#
Likely scenario:
- Slack acquired or partners with Microsoft (Teams integration)
- Discord explores federation (Matrix-compatible protocol)
- Telegram continues independent (funded by founder)
- Matrix reaches critical mass (10M+ monthly active users)
- Nostr remains niche but stable (Bitcoin community)
Impact on bot developers:
- Platform interoperability increases (Matrix bridges standardize)
- Bot verification becomes universal (anti-spam measure)
- AI features commoditize simple bots (need specialization)
Technology Shifts#
WebAssembly bots (speculative):
- Platform-agnostic bot binaries
- Deploy once, run on any platform
- Reduces SDK fragmentation
Decentralized social protocols:
- ActivityPub (Mastodon) gains traction
- Bots need ActivityPub support
- Matrix/Nostr positioned well (already decentralized)
Agentic AI platforms:
- LLMs integrated at platform level
- Custom bots for specialized tasks only
- Privacy/security/domain expertise differentiators
Conclusion#
Key takeaways:
- No single “best” platform - optimize for use case
- Platform risk is real - abstract early, diversify when proven
- Enterprise vs consumer diverging - different monetization models
- AI will commoditize simple bots - specialize or integrate deeply
- Decentralized platforms growing - hedge for long-term
Final recommendation: Build for Discord or Telegram first (fastest validation), abstract platform logic, add other platforms when product-market fit proven. For privacy-critical use cases, start with Matrix despite higher complexity.
Sources#
- Platform developer documentation
- Bot ecosystem surveys (Discord/Telegram developer communities)
- Enterprise procurement analysis
- Gartner/Forrester workplace collaboration reports
- Decentralized protocol adoption metrics (Matrix.org stats, Nostr relay counts)
- Venture capital funding data (bot platform investments)
Technology Stack Recommendations#
Polyglot Architecture#
Insight: No single SDK language dominates. Choose by team expertise:
| Language | Best SDK | Ecosystem Maturity |
|---|---|---|
| Python | discord.py, python-telegram-bot, matrix-nio, slack-bolt | Excellent (4/5 platforms) |
| JavaScript/TypeScript | discord.js, telegraf, matrix-bot-sdk, @slack/bolt | Excellent (4/5 platforms) |
| Go | discordgo, gotgbot, mautrix-go | Good (3/5 platforms) |
| Rust | serenity, teloxide, matrix-rust-sdk | Growing (3/5 platforms) |
Recommendation: Python or TypeScript - widest SDK support, largest community.
Exception: Performance-critical bots (>10k messages/s) - use Go or Rust.
Deployment Patterns#
Pattern A: Single-Server Monolith#
When: MVP, <100 users, low traffic
Architecture:
Bot process (Python/Node) → Platform API
↓
SQLite DB (state)Platforms: All work well
Cost: $5-10/month (VPS)
Pattern B: Serverless Functions#
When: Sporadic traffic, pay-per-use preferred
Architecture:
Platform webhook → AWS Lambda/Cloudflare Workers → Platform API
↓
DynamoDB/KV (state)Best platforms:
- Slack (webhooks, 3s timeout limit OK)
- Telegram (webhooks, fast responses)
Avoid:
- Discord (needs persistent WebSocket for optimal performance)
- Matrix (long-polling, not webhook-friendly)
- Nostr (requires persistent WebSocket)
Cost: $0-5/month (free tier sufficient for small bots)
Pattern C: Kubernetes Cluster#
When: High availability, >10k users, multi-platform
Architecture:
Load balancer → N bot pods → Platform APIs
↓
Shared state (Redis/PostgreSQL)Best platforms:
- Slack (HTTP mode, stateless)
- Telegram (webhook mode, stateless)
- Discord (requires sharding strategy)
Challenges:
- Matrix (E2EE state per instance, hard to replicate)
- Nostr (each instance connects to relays, duplicate events)
Cost: $50-500/month (depends on scale)
Strategic Recommendations#
For Startups Building Bot Products#
Start with Telegram or Discord
- Fastest time-to-market
- No approval gates for early stage
- Large user bases for validation
Abstract platform logic early
- Even if single-platform now, plan for multi-platform
- Use adapter pattern (thin platform layer, thick core logic)
Prioritize platforms by audience, not tech
- Wrong: “Matrix has best tech” → build on Matrix
- Right: “Our users are on Discord” → build on Discord
Monitor platform policy changes
- Subscribe to developer changelogs
- Join platform developer communities
- Test in beta programs (catch breaking changes early)
For Enterprises Building Internal Bots#
Default to Slack (if already using)
- Lowest friction (employees already have accounts)
- Best compliance features
- SSO integration
Consider Matrix for sensitive data
- Self-host for full control
- E2EE for privacy
- Trade-off: More IT overhead
Avoid consumer platforms (Discord, Telegram)
- No enterprise SSO
- No audit trails
- Employees mixing personal/work accounts
For Open-Source Projects#
Discord for community
- Best for real-time chat
- Forum channels for organized discussion
- Large OSS community presence
Matrix for privacy-conscious projects
- E2EE for security discussions
- Self-hosted (no platform lock-in)
- Bridges to other platforms (reach)
Avoid Slack (unless sponsored)
- Free tier too limited (90-day message history)
- Expensive for large communities
Strategic Trends#
1. The “Bot Verification” Trap#
Problem: Platforms increasingly gate bot features behind verification/approval:
- Discord:
>75guilds requires verification + privileged intents approval - Slack: Enterprise Grid features require partnership
- Telegram: No restrictions (yet), but may change
- Matrix/Nostr: No approval (decentralized)
Implication: Early-stage bots should avoid platforms with scaling gates. Ship on Telegram/Matrix first, add Discord/Slack when proven.
Future prediction: Discord/Slack will tighten bot policies (spam prevention). Telegram may follow (currently most permissive).
2. AI Agent Platforms#
Emerging pattern: Platforms adding native AI agent support:
- Discord: Bot accounts can request “AI agent” badge
- Slack: Workflow AI steps (GPT integration)
- Telegram: Bot API enhancements for conversational AI
- Matrix: No official AI features (DIY)
Implication: Platform-native AI features will commoditize simple bots. Differentiation moves to:
- Domain expertise (vertical-specific bots)
- Complex workflows (multi-step automation)
- Privacy/security (where AI plugins can’t go)
Recommendation: Don’t build generic “answer questions” bots - platforms will bundle that. Build specialized tools.
3. Cross-Platform Messaging Bridges#
Trend: Users want unified messaging (Matrix bridges, Beeper, Element).
Reality: Bridges are fragile (rely on unofficial APIs, break on updates).
Strategic implication: Don’t rely on bridges for production. If multi-platform critical, implement native SDKs for each platform.
Exception: Matrix→Everything bridges (Matrix.org officially supports bridges). If building on Matrix, bridges are first-class.
4. The Payment Platform Shift#
Observation:
- Telegram: Native payment API (Stripe, etc.)
- Discord: Banned commerce (2024 ToS update)
- Nostr: Native Lightning (Zaps protocol)
- Slack/Matrix: No native payments
Strategic insight: Payment-enabled platforms (Telegram, Nostr) will attract commerce bots. Discord’s commerce ban creates opportunity gap.
Long-term: Payment rails = platform moat. Expect more platforms to add payments (or ban competitors).
5. E2EE vs. Moderation Trade-off#
Tension: Platforms face choice:
- E2EE (Matrix, Nostr DMs) - privacy, but can’t moderate
- Server-side access (Discord, Slack, Telegram) - moderation possible, but privacy concerns
Trend: Consumer platforms prioritizing moderation over E2EE (see Telegram’s policy shifts). Enterprise platforms adding “compliant encryption” (keys in escrow).
Implication for bot builders:
- Privacy-critical bots: Matrix is only real option long-term
- Moderation bots: Discord/Slack have best APIs (require server access)
- Hybrid approach: Nostr (relay-level moderation, no protocol-level censorship)
Long-Term Sustainability#
Revenue Models for Bot Developers#
Successful patterns:
Freemium (Discord bots like MEE6, Dyno)
- Free tier: Basic features
- Premium: $5-10/month per server
- Works on platforms with server-level billing
B2B SaaS (Slack apps like Donut, Workast)
- Charge per workspace: $50-500/month
- Target enterprises with budget
Transaction fees (Telegram payment bots)
- Take % of payments processed
- Requires payment-enabled platform
Tips/Donations (Nostr bots)
- Lightning Zaps (micropayments)
- Works for niche communities
Avoid: Ad-supported bots (platforms prohibit, users hate).
Platform Risk Assessment#
Risk factors:
- API deprecation (breaking changes)
- Policy changes (bot features restricted)
- Platform shutdown (unlikely but not impossible)
Risk matrix (2026-2030):
| Platform | Shutdown Risk | API Breakage Risk | Policy Tightening Risk |
|---|---|---|---|
| Slack | Very Low | Low | Medium (anti-spam) |
| Discord | Very Low | Medium (frequent updates) | High (verification gates) |
| Telegram | Low | Low (stable API) | Medium (may add restrictions) |
| Matrix | Very Low (protocol) | Medium (spec changes) | Very Low (decentralized) |
| Nostr | None (protocol) | High (NIPs evolving) | None (no central authority) |
Mitigation strategy:
- Abstract platform logic (dependency injection, adapters)
- Monitor platform changelogs (subscribe to developer updates)
- Test against beta APIs (catch breaking changes early)
- Multi-platform redundancy (if one platform bans, others remain)