Initial commit: Ajarbot with optimizations
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>
This commit is contained in:
386
docs/README_ADAPTERS.md
Normal file
386
docs/README_ADAPTERS.md
Normal file
@@ -0,0 +1,386 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user