From 4fed2f9c3c6b4a03871bf734d7a34bc8fe75269b Mon Sep 17 00:00:00 2001 From: Rick Sanchez <101627789+m4tinbeigi-official@users.noreply.github.com> Date: Sat, 27 Jun 2026 00:02:41 +0330 Subject: [PATCH] feat: add Bale messenger platform plugin --- plugins/platforms/bale/__init__.py | 3 + plugins/platforms/bale/adapter.py | 101 +++++++++++++++++++++ plugins/platforms/bale/plugin.yaml | 31 +++++++ website/docs/user-guide/messaging/bale.md | 62 +++++++++++++ website/docs/user-guide/messaging/index.md | 2 + 5 files changed, 199 insertions(+) create mode 100644 plugins/platforms/bale/__init__.py create mode 100644 plugins/platforms/bale/adapter.py create mode 100644 plugins/platforms/bale/plugin.yaml create mode 100644 website/docs/user-guide/messaging/bale.md diff --git a/plugins/platforms/bale/__init__.py b/plugins/platforms/bale/__init__.py new file mode 100644 index 0000000000000..d4f1d7bf0e3fc --- /dev/null +++ b/plugins/platforms/bale/__init__.py @@ -0,0 +1,3 @@ +from .adapter import register + +__all__ = ["register"] diff --git a/plugins/platforms/bale/adapter.py b/plugins/platforms/bale/adapter.py new file mode 100644 index 0000000000000..7767ca811e878 --- /dev/null +++ b/plugins/platforms/bale/adapter.py @@ -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, + ) diff --git a/plugins/platforms/bale/plugin.yaml b/plugins/platforms/bale/plugin.yaml new file mode 100644 index 0000000000000..fb4d530f2215b --- /dev/null +++ b/plugins/platforms/bale/plugin.yaml @@ -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 diff --git a/website/docs/user-guide/messaging/bale.md b/website/docs/user-guide/messaging/bale.md new file mode 100644 index 0000000000000..c295b2783daad --- /dev/null +++ b/website/docs/user-guide/messaging/bale.md @@ -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`). diff --git a/website/docs/user-guide/messaging/index.md b/website/docs/user-guide/messaging/index.md index 289d2eaece4f0..4ae83e7c4275d 100644 --- a/website/docs/user-guide/messaging/index.md +++ b/website/docs/user-guide/messaging/index.md @@ -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 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -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)