1.234 Bot SDK Frameworks#
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
S1 Rapid Discovery: Bot SDK Frameworks#
Date: 2025-12-22 Methodology: S1 - Rapid practical comparison Category: 1.234 (Bot SDK Frameworks) Scope: Bot development SDKs for major chat platforms
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 |
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
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
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
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
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
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 |
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
Sources#
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
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
S3 Need-Driven: Bot SDK Use Case Analysis#
Date: 2026-02-05 Methodology: S3 - Need-driven selection framework Category: 1.234 (Bot SDK Frameworks)
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.
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)
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]Anti-Patterns: When NOT to Use Each SDK#
Avoid Matrix if:#
- Interactive UI critical (buttons, forms) - widgets too complex
- Quick MVP needed (<4 hours) - 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
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.
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)
S4: Strategic
S4 Strategic: Bot SDK Ecosystem Analysis#
Date: 2026-02-05 Methodology: S4 - Strategic long-term analysis Category: 1.234 (Bot SDK Frameworks)
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.
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.
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)
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)
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)
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)
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)
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
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)