Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions plugins/platforms/bale/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .adapter import register

__all__ = ["register"]
101 changes: 101 additions & 0 deletions plugins/platforms/bale/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import os
from typing import Dict, Any, Optional

from gateway.config import PlatformConfig
from plugins.platforms.telegram.adapter import (
TelegramAdapter,
check_telegram_requirements,
)

def env_enablement_fn() -> Optional[Dict[str, Any]]:
"""Seed PlatformConfig from environment variables before init."""
token = os.getenv("BALE_BOT_TOKEN")
if not token:
return None

return {
"extra": {
"base_url": "https://tapi.bale.ai/bot",
"base_file_url": "https://tapi.bale.ai/file/bot"
},
"home_channel": {
"id": os.getenv("BALE_HOME_CHANNEL"),
"name": os.getenv("BALE_HOME_CHANNEL_NAME", "Bale Home")
} if os.getenv("BALE_HOME_CHANNEL") else None
}

def apply_yaml_config_fn(yaml_cfg: Dict[str, Any], platform_cfg: PlatformConfig) -> Optional[Dict[str, Any]]:
"""Translate config.yaml keys into env vars and extra dict."""
extras = {}
bale_cfg = yaml_cfg.get("bale", {})
if not isinstance(bale_cfg, dict):
return {
"extra": {
"base_url": "https://tapi.bale.ai/bot",
"base_file_url": "https://tapi.bale.ai/file/bot"
}
}

allowed_users = bale_cfg.get("allow_from")
if allowed_users is not None and not os.getenv("BALE_ALLOWED_USERS"):
if isinstance(allowed_users, list):
allowed_users = ",".join(str(v) for v in allowed_users)
os.environ["BALE_ALLOWED_USERS"] = str(allowed_users)

extras["base_url"] = "https://tapi.bale.ai/bot"
extras["base_file_url"] = "https://tapi.bale.ai/file/bot"
return {"extra": extras}

class BaleAdapter(TelegramAdapter):
"""
Bale bot adapter.
Inherits from TelegramAdapter but points to the Bale API servers via base_url.
"""
def __init__(self, config: PlatformConfig):
super().__init__(config)
self.name = "bale"

def _build_adapter(config: PlatformConfig):
return BaleAdapter(config)

def _is_connected(adapter) -> bool:
if not adapter:
return False
return adapter._running

async def _standalone_send(chat_id: str, message: str, **kwargs) -> Dict[str, Any]:
"""Standalone sender for cron jobs and external tool delivery."""
import aiohttp

token = os.getenv("BALE_BOT_TOKEN")
if not token:
raise ValueError("BALE_BOT_TOKEN not set")

async with aiohttp.ClientSession() as session:
url = f"https://tapi.bale.ai/bot{token}/sendMessage"
payload = {"chat_id": chat_id, "text": message}
async with session.post(url, json=payload) as resp:
resp.raise_for_status()
data = await resp.json()
return {"result": "ok", "message_id": data.get("result", {}).get("message_id")}

def register(ctx) -> None:
"""Plugin entry point."""
ctx.register_platform(
name="bale",
label="Bale",
adapter_factory=_build_adapter,
check_fn=check_telegram_requirements,
is_connected=_is_connected,
required_env=["BALE_BOT_TOKEN"],
install_hint="pip install 'hermes-agent[telegram]'",
setup_fn=None,
apply_yaml_config_fn=apply_yaml_config_fn,
allowed_users_env="BALE_ALLOWED_USERS",
allow_all_env="BALE_ALLOW_ALL_USERS",
cron_deliver_env_var="BALE_HOME_CHANNEL",
standalone_sender_fn=_standalone_send,
max_message_length=4096,
emoji="💬",
allow_update_command=True,
)
31 changes: 31 additions & 0 deletions plugins/platforms/bale/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: bale-platform
label: Bale
kind: platform
version: 1.0.0
description: >
Bale gateway adapter for Hermes Agent.
Connects to Bale messenger via the telegram-bot API bridge.
author: Hermes Community
requires_env:
- name: BALE_BOT_TOKEN
description: "Bale bot token from @BotFather in Bale"
prompt: "Bale bot token"
url: "https://ble.ir/botfather"
password: true
optional_env:
- name: BALE_ALLOWED_USERS
description: "Comma-separated Bale user IDs allowed to talk to the bot"
prompt: "Allowed users (comma-separated)"
password: false
- name: BALE_ALLOW_ALL_USERS
description: "Allow any Bale user to trigger the bot"
prompt: "Allow all users? (true/false)"
password: false
- name: BALE_HOME_CHANNEL
description: "Default chat ID for cron / notification delivery"
prompt: "Home channel ID"
password: false
- name: BALE_HOME_CHANNEL_NAME
description: "Display name for the Bale home channel"
prompt: "Home channel display name"
password: false
62 changes: 62 additions & 0 deletions website/docs/user-guide/messaging/bale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
sidebar_position: 10
title: "Bale"
description: "Set up Hermes Agent as a Bale messenger bot"
---

# Bale Setup

Hermes Agent integrates with Bale (بله) messenger as a full-featured conversational bot. Because Bale's Bot API is highly compatible with the Telegram Bot API, the Hermes Bale integration supports almost all the same features as Telegram, including text, media, inline keyboards, and slash commands.

## Step 1: Create a Bot via BotFather in Bale

Every Bale bot requires an API token issued by [@BotFather](https://ble.ir/botfather), Bale's official bot management tool.

1. Open Bale and search for **@BotFather**, or visit [ble.ir/botfather](https://ble.ir/botfather)
2. Send `/newbot`
3. Choose a **display name** (e.g., "Hermes Agent") — this can be anything
4. Choose a **username** — this must be unique and end in `bot` (e.g., `my_hermes_bot`)
5. BotFather replies with your **API token**. It looks like this:

```
123456789:ABCdefGHIjklMNOpqrSTUvwxYZ
```

:::warning
Keep your bot token secret. Anyone with this token can control your bot.
:::

## Step 2: Configure Hermes

Add your Bale token to the Hermes environment or `.env` file:

```bash
BALE_BOT_TOKEN="your-bale-token"
BALE_ALLOWED_USERS="123456789,987654321" # Optional: restrict access
```

If you prefer to configure it via `config.yaml`, add the `bale` platform under `platforms`:

```yaml
platforms:
bale:
allow_from:
- 123456789
```

## Step 3: Run the Gateway

Start the Hermes gateway. It will automatically load the Bale plugin and connect to `tapi.bale.ai`:

```bash
hermes gateway start
```

Your agent is now live on Bale! You can search for its username and send it a message.

## Differences from Telegram

Since Bale uses a separate infrastructure from Telegram, there are a few minor differences:
- Voice/Audio and Document uploads are handled through Bale's specific file servers.
- Some advanced Telegram features (like forum topics/threads or specific interactive markdown rendering) might behave slightly differently depending on the Bale client.
- The base API url for Bale is automatically handled by the plugin (`https://tapi.bale.ai/bot`).
2 changes: 2 additions & 0 deletions website/docs/user-guide/messaging/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Bots need both a model provider and tool providers (TTS, web). A [Nous Portal](/

| Platform | Voice | Images | Files | Threads | Reactions | Typing | Streaming |
|----------|:-----:|:------:|:-----:|:-------:|:---------:|:------:|:---------:|
| Bale | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Telegram | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Discord | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Slack | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Expand Down Expand Up @@ -618,6 +619,7 @@ Defaults to `false`. Only platforms whose adapter implements `delete_message` ho

## Next Steps

- [Bale Setup](bale.md)
- [Telegram Setup](telegram.md)
- [Discord Setup](discord.md)
- [Slack Setup](slack.md)
Expand Down
Loading