# 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.