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>
387 lines
11 KiB
Markdown
387 lines
11 KiB
Markdown
# 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](https://github.com/chloebt/openclaw) sophisticated plugin-based architecture but simplified for ajarbot's needs:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Bot Runner │
|
|
│ ┌───────────────────────────────────────────────────┐ │
|
|
│ │ Adapter Runtime │ │
|
|
│ │ ┌──────────────┐ ┌──────────────┐ │ │
|
|
│ │ │ Slack │ │ Telegram │ ... │ │
|
|
│ │ │ Adapter │ │ Adapter │ │ │
|
|
│ │ └──────┬───────┘ └──────┬───────┘ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────┬────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ┌───────▼───────┐ │ │
|
|
│ │ │ Agent Core │ │ │
|
|
│ │ │ (Memory+LLM) │ │ │
|
|
│ │ └───────────────┘ │ │
|
|
│ └───────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Key Components
|
|
|
|
1. **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
|
|
|
|
2. **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
|
|
|
|
3. **AdapterRegistry** (`adapters/base.py`)
|
|
- Manages multiple adapter instances
|
|
- Provides lookup by platform name
|
|
- Handles bulk start/stop operations
|
|
|
|
4. **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:**
|
|
```yaml
|
|
slack:
|
|
enabled: true
|
|
credentials:
|
|
bot_token: "xoxb-..."
|
|
app_token: "xapp-..."
|
|
settings:
|
|
auto_react_emoji: "thinking_face" # Optional
|
|
```
|
|
|
|
**Setup Steps:**
|
|
1. Go to https://api.slack.com/apps
|
|
2. Create new app → "From scratch"
|
|
3. Enable **Socket Mode** (Settings → Socket Mode)
|
|
4. Generate **App-Level Token** with `connections:write` scope
|
|
5. Add **Bot Token Scopes**:
|
|
- `chat:write`
|
|
- `channels:history`
|
|
- `groups:history`
|
|
- `im:history`
|
|
- `mpim:history`
|
|
- `app_mentions:read`
|
|
6. Install app to workspace
|
|
7. 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:**
|
|
```yaml
|
|
telegram:
|
|
enabled: true
|
|
credentials:
|
|
bot_token: "123456:ABC-DEF..."
|
|
settings:
|
|
allowed_users: [] # Optional: [123456789]
|
|
parse_mode: "Markdown" # or "HTML"
|
|
```
|
|
|
|
**Setup Steps:**
|
|
1. Open Telegram and message [@BotFather](https://t.me/botfather)
|
|
2. Send `/newbot`
|
|
3. Follow prompts (choose name and username)
|
|
4. Copy the bot token
|
|
5. (Optional) Configure privacy settings with `/setprivacy`
|
|
|
|
## Quick Start
|
|
|
|
### 1. Install Dependencies
|
|
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
### 2. Generate Configuration Template
|
|
|
|
```bash
|
|
python bot_runner.py --init
|
|
```
|
|
|
|
This creates `config/adapters.local.yaml` with a template.
|
|
|
|
### 3. Edit Configuration
|
|
|
|
Edit `config/adapters.local.yaml`:
|
|
|
|
```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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
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
|
|
|
|
```bash
|
|
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.):
|
|
|
|
1. **Create adapter file** `adapters/newplatform/adapter.py`
|
|
2. **Inherit from BaseAdapter** and implement required methods:
|
|
- `platform_name` property
|
|
- `capabilities` property
|
|
- `validate_config()`
|
|
- `start()` / `stop()`
|
|
- `send_message()`
|
|
3. **Register in bot_runner.py**
|
|
4. **Add config section** to `adapters.yaml`
|
|
|
|
Example skeleton:
|
|
|
|
```python
|
|
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: true` in your config
|
|
- Verify credentials are set correctly
|
|
- Try running with `--init` to regenerate template
|
|
|
|
### Slack: "invalid_auth"
|
|
- Ensure `bot_token` starts with `xoxb-`
|
|
- Ensure `app_token` starts with `xapp-`
|
|
- 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_users` setting isn't blocking you
|
|
|
|
### Import errors
|
|
```bash
|
|
pip install -r requirements.txt --upgrade
|
|
```
|
|
|
|
## License
|
|
|
|
Same as ajarbot (check main repository).
|
|
|
|
## Credits
|
|
|
|
Adapter architecture inspired by [OpenClaw](https://github.com/chloebt/openclaw) by Chloe.
|