Features: - Multi-platform bot (Slack, Telegram) - Memory system with SQLite FTS - Tool use capabilities (file ops, commands) - Scheduled tasks system - Dynamic model switching (/sonnet, /haiku) - Prompt caching for cost optimization Optimizations: - Default to Haiku 4.5 (12x cheaper) - Reduced context: 3 messages, 2 memory results - Optimized SOUL.md (48% smaller) - Automatic caching when using Sonnet (90% savings) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
11 KiB
Ajarbot Multi-Platform Adapters
This document describes the adapter system that allows ajarbot to run on multiple messaging platforms simultaneously (Slack, Telegram, and more).
Architecture Overview
The adapter system is inspired by OpenClaw's sophisticated plugin-based architecture but simplified for ajarbot's needs:
┌─────────────────────────────────────────────────────────┐
│ Bot Runner │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Adapter Runtime │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Slack │ │ Telegram │ ... │ │
│ │ │ Adapter │ │ Adapter │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │
│ │ └────────┬────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼───────┐ │ │
│ │ │ Agent Core │ │ │
│ │ │ (Memory+LLM) │ │ │
│ │ └───────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Key Components
-
BaseAdapter (
adapters/base.py)- Abstract interface that all platform adapters implement
- Defines capabilities (threads, reactions, media, markdown, etc.)
- Handles message chunking based on platform limits
- Manages message handler registration
-
AdapterRuntime (
adapters/runtime.py)- Connects messaging adapters to the Agent
- Manages message queue and async processing
- Handles user ID mapping (platform → ajarbot username)
- Supports preprocessors and postprocessors
-
AdapterRegistry (
adapters/base.py)- Manages multiple adapter instances
- Provides lookup by platform name
- Handles bulk start/stop operations
-
ConfigLoader (
config/config_loader.py)- Loads adapter configuration from YAML
- Supports environment variable overrides
- Separates secrets (
.local.yaml) from templates
Supported Platforms
✅ Slack (Socket Mode)
Features:
- Socket Mode (no webhooks needed)
- Thread support
- Reactions
- Media/file attachments
- Markdown (mrkdwn)
- 4000 character limit
Configuration:
slack:
enabled: true
credentials:
bot_token: "xoxb-..."
app_token: "xapp-..."
settings:
auto_react_emoji: "thinking_face" # Optional
Setup Steps:
- Go to https://api.slack.com/apps
- Create new app → "From scratch"
- Enable Socket Mode (Settings → Socket Mode)
- Generate App-Level Token with
connections:writescope - Add Bot Token Scopes:
chat:writechannels:historygroups:historyim:historympim:historyapp_mentions:read
- Install app to workspace
- Copy Bot User OAuth Token (xoxb-...) and App-Level Token (xapp-...)
✅ Telegram
Features:
- Direct polling (no webhooks)
- Reactions (new API)
- Media/file attachments
- Markdown or HTML
- 4096 character limit
- User allowlist support
Configuration:
telegram:
enabled: true
credentials:
bot_token: "123456:ABC-DEF..."
settings:
allowed_users: [] # Optional: [123456789]
parse_mode: "Markdown" # or "HTML"
Setup Steps:
- Open Telegram and message @BotFather
- Send
/newbot - Follow prompts (choose name and username)
- Copy the bot token
- (Optional) Configure privacy settings with
/setprivacy
Quick Start
1. Install Dependencies
pip install -r requirements.txt
2. Generate Configuration Template
python bot_runner.py --init
This creates config/adapters.local.yaml with a template.
3. Edit Configuration
Edit config/adapters.local.yaml:
adapters:
slack:
enabled: true # Change to true
credentials:
bot_token: "xoxb-YOUR-ACTUAL-TOKEN"
app_token: "xapp-YOUR-ACTUAL-TOKEN"
telegram:
enabled: true # Change to true
credentials:
bot_token: "YOUR-ACTUAL-BOT-TOKEN"
4. Run the Bot
python bot_runner.py
Output:
============================================================
🤖 Ajarbot Multi-Platform Runner
============================================================
[Setup] Initializing agent...
[Setup] ✓ Agent initialized
[Setup] Loading Slack adapter...
[Setup] ✓ Slack adapter loaded
[Setup] Loading Telegram adapter...
[Setup] ✓ Telegram adapter loaded
[Setup] ✓ 2 adapter(s) configured
============================================================
🚀 Starting bot...
============================================================
[Slack] Starting Socket Mode connection...
[Slack] ✓ Connected and listening for messages
[Telegram] Starting bot...
[Telegram] ✓ Bot started: @your_bot (Your Bot Name)
[Runtime] Message processing loop started
============================================================
✓ Bot is running! Press Ctrl+C to stop.
============================================================
Environment Variables (Alternative to YAML)
You can use environment variables instead of or in addition to the YAML config:
export AJARBOT_SLACK_BOT_TOKEN="xoxb-..."
export AJARBOT_SLACK_APP_TOKEN="xapp-..."
export AJARBOT_TELEGRAM_BOT_TOKEN="123456:ABC..."
python bot_runner.py
Environment variables take precedence over YAML configuration.
User Mapping
Map platform user IDs to ajarbot usernames for memory consistency:
user_mapping:
slack:U12345ABCDE: "alice"
telegram:123456789: "alice"
Now when Alice messages from either Slack or Telegram, the bot will use the same memory profile.
Advanced Usage
Custom Preprocessors
Add custom logic before messages reach the Agent:
from adapters.runtime import AdapterRuntime
from adapters.base import InboundMessage
def my_preprocessor(message: InboundMessage) -> InboundMessage:
# Example: Auto-expand abbreviations
if message.text == "status":
message.text = "What is your current status?"
return message
runtime.add_preprocessor(my_preprocessor)
Custom Postprocessors
Modify responses before sending to platforms:
def my_postprocessor(response: str, original: InboundMessage) -> str:
# Example: Add platform-specific formatting
if original.platform == "slack":
response = response.replace("**", "*") # Bold
return response
runtime.add_postprocessor(my_postprocessor)
Health Checks
python bot_runner.py --health
Output:
============================================================
Health Check
============================================================
Runtime running: True
Adapters:
SLACK:
platform: slack
running: True
healthy: True
bot_id: B12345
team: T12345
connected: True
TELEGRAM:
platform: telegram
running: True
healthy: True
bot_id: 123456789
username: your_bot
connected: True
Adding New Adapters
To add support for a new platform (Discord, WhatsApp, etc.):
- Create adapter file
adapters/newplatform/adapter.py - Inherit from BaseAdapter and implement required methods:
platform_namepropertycapabilitiespropertyvalidate_config()start()/stop()send_message()
- Register in bot_runner.py
- Add config section to
adapters.yaml
Example skeleton:
from adapters.base import BaseAdapter, AdapterConfig, AdapterCapabilities
class NewPlatformAdapter(BaseAdapter):
@property
def platform_name(self) -> str:
return "newplatform"
@property
def capabilities(self) -> AdapterCapabilities:
return AdapterCapabilities(
supports_threads=True,
max_message_length=2000
)
def validate_config(self) -> bool:
return bool(self.config.credentials.get("api_key"))
async def start(self):
# Initialize connection
self.is_running = True
async def stop(self):
# Cleanup
self.is_running = False
async def send_message(self, message: OutboundMessage):
# Send message to platform
return {"success": True, "message_id": "123"}
Comparison with OpenClaw
| Feature | OpenClaw | Ajarbot Adapters |
|---|---|---|
| Architecture | Plugin-based with 12+ sub-adapters per channel | Simplified single-adapter per platform |
| Type System | TypeScript with structural typing | Python with ABC/dataclasses |
| Adapters | config, gateway, outbound, status, security, pairing, etc. | Combined into BaseAdapter |
| Registry | Two-tier (DOCKS + plugin registry) | Single AdapterRegistry |
| Scope | 20+ platforms, enterprise features | Core platforms, essential features |
| Complexity | High (production-grade) | Medium (developer-friendly) |
What We Adopted from OpenClaw
✅ Plugin-based architecture - Each platform is self-contained ✅ Capability declarations - Platforms declare what they support ✅ Consistent interfaces - All adapters implement the same contract ✅ Gateway pattern - start/stop lifecycle management ✅ Outbound adapter - Message sending abstraction ✅ Status/health checks - Monitoring and diagnostics ✅ Chunking strategies - Platform-aware text splitting
What We Simplified
🔄 Single adapter class instead of 12+ sub-adapters 🔄 Python dataclasses instead of TypeScript interfaces 🔄 YAML config instead of complex config system 🔄 Direct integration instead of full plugin loading system
Troubleshooting
"No adapters enabled"
- Check that
enabled: truein your config - Verify credentials are set correctly
- Try running with
--initto regenerate template
Slack: "invalid_auth"
- Ensure
bot_tokenstarts withxoxb- - Ensure
app_tokenstarts withxapp- - Verify app is installed to workspace
Telegram: Bot not responding
- Check bot token is correct (from @BotFather)
- Ensure no other instance is polling the same bot
- Check
allowed_userssetting isn't blocking you
Import errors
pip install -r requirements.txt --upgrade
License
Same as ajarbot (check main repository).
Credits
Adapter architecture inspired by OpenClaw by Chloe.