Skip to content
Open
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
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ OPENROUTER_API_KEY=sk-or-v1-your-key-here
# Telegram Bot Token (from @BotFather)
TELEGRAM_BOT_TOKEN=1234567890:your-token-here

# LINE Messaging API credentials (optional)
# Channel Access Token + Channel Secret from LINE Developers Console
LINE_CHANNEL_ACCESS_TOKEN=
LINE_CHANNEL_SECRET=

# Gateway Token (set any strong password)
OPENCLAW_GATEWAY_TOKEN=your-secret-gateway-token

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
[![OpenRouter](https://img.shields.io/badge/OpenRouter-Free%20Tier-ff6b35?logoColor=white)](https://openrouter.ai)
[![OpenClaw](https://img.shields.io/badge/OpenClaw-2026-00e5cc?logoColor=white)](https://openclaw.bot)
[![Telegram](https://img.shields.io/badge/Telegram-Bot-26a5e4?logo=telegram&logoColor=white)](https://telegram.org)
[![LINE](https://img.shields.io/badge/LINE-Bot-00c300?logo=line&logoColor=white)](https://line.me)

One-command deployment of an [OpenClaw](https://openclaw.bot) AI agent as a Telegram bot on DigitalOcean. After `terraform apply`, the bot is fully operational with no manual SSH steps required.
One-command deployment of an [OpenClaw](https://openclaw.bot) AI agent on DigitalOcean with Telegram support and optional LINE support. After `terraform apply`, the bot is fully operational with no manual SSH steps required.

## Features

- Telegram bot with DM and group chat support
- Optional LINE Messaging API channel
- Web search via Brave Search (falls back to DuckDuckGo)
- 8 switchable free LLM models via `/model <alias>`
- Secrets managed via `.env` — never committed
Expand All @@ -26,6 +28,7 @@ One-command deployment of an [OpenClaw](https://openclaw.bot) AI agent as a Tele
- DigitalOcean account + API token
- OpenRouter API key
- Telegram bot token (from [@BotFather](https://t.me/BotFather))
- LINE channel access token + channel secret (optional)

## Setup

Expand All @@ -41,6 +44,8 @@ Edit `.env` and fill in your values:
|---|---|
| `OPENROUTER_API_KEY` | From [openrouter.ai/keys](https://openrouter.ai/keys) |
| `TELEGRAM_BOT_TOKEN` | From [@BotFather](https://t.me/BotFather) |
| `LINE_CHANNEL_ACCESS_TOKEN` | From LINE Developers Console (optional, enables LINE channel) |
| `LINE_CHANNEL_SECRET` | From LINE Developers Console (optional, enables LINE channel) |
| `OPENCLAW_GATEWAY_TOKEN` | Any strong random string |
| `BRAVE_API_KEY` | From [api.search.brave.com](https://api.search.brave.com) — optional, falls back to DuckDuckGo |
| `TELEGRAM_OWNER_ID` | Your Telegram user ID from [@userinfobot](https://t.me/userinfobot) — grants `/model` and other privileged commands |
Expand Down
2 changes: 2 additions & 0 deletions terraform/digitalOcean/.envrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ dotenv ../../.env

export TF_VAR_openrouter_api_key="$OPENROUTER_API_KEY"
export TF_VAR_telegram_bot_token="$TELEGRAM_BOT_TOKEN"
export TF_VAR_line_channel_access_token="${LINE_CHANNEL_ACCESS_TOKEN:-}"
export TF_VAR_line_channel_secret="${LINE_CHANNEL_SECRET:-}"
export TF_VAR_openclaw_gateway_token="$OPENCLAW_GATEWAY_TOKEN"
export TF_VAR_brave_api_key="${BRAVE_API_KEY:-}"
export TF_VAR_telegram_owner_id="${TELEGRAM_OWNER_ID:-}"
14 changes: 12 additions & 2 deletions terraform/digitalOcean/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ cat > /root/.openclaw/openclaw.json << JSONEOF
"plugins": {
"load": {
"paths": [
"/usr/lib/node_modules/openclaw/dist/extensions/telegram"
"/usr/lib/node_modules/openclaw/dist/extensions/telegram"${line_channel_access_token != "" && line_channel_secret != "" ? ",\n \"/usr/lib/node_modules/openclaw/dist/extensions/line\"" : ""}
]
},
"entries": {
"telegram": { "enabled": true },
${line_channel_access_token != "" && line_channel_secret != "" ? "\"line\": { \"enabled\": true }," : ""}
"openrouter": { "enabled": true },
"brave": {
"enabled": true,
Expand All @@ -112,7 +113,7 @@ cat > /root/.openclaw/openclaw.json << JSONEOF
"groupPolicy": "open"${telegram_owner_id != "" ? ",\n \"allowFrom\": [\"${telegram_owner_id}\"]" : ""}
}
}
}
}${line_channel_access_token != "" && line_channel_secret != "" ? ",\n \"line\": {\n \"enabled\": true,\n \"accounts\": {\n \"default\": {\n \"channelAccessToken\": \"${line_channel_access_token}\",\n \"channelSecret\": \"${line_channel_secret}\"\n }\n }\n }" : ""}
}
}
JSONEOF
Expand All @@ -124,6 +125,8 @@ write_config
cat > /root/.openclaw/.env << ENVEOF
OPENROUTER_API_KEY=${openrouter_api_key}
TELEGRAM_BOT_TOKEN=${telegram_bot_token}
LINE_CHANNEL_ACCESS_TOKEN=${line_channel_access_token}
LINE_CHANNEL_SECRET=${line_channel_secret}
OPENCLAW_GATEWAY_TOKEN=${openclaw_gateway_token}
BRAVE_API_KEY=${brave_api_key}
OPENCLAW_ONBOARD_NON_INTERACTIVE=1
Expand All @@ -132,12 +135,19 @@ ENVEOF
# ── 8. Export env vars for subsequent commands ───────────────
export OPENROUTER_API_KEY=${openrouter_api_key}
export TELEGRAM_BOT_TOKEN=${telegram_bot_token}
export LINE_CHANNEL_ACCESS_TOKEN=${line_channel_access_token}
export LINE_CHANNEL_SECRET=${line_channel_secret}
export OPENCLAW_GATEWAY_TOKEN=${openclaw_gateway_token}
export BRAVE_API_KEY=${brave_api_key}

# ── 9. Install Telegram plugin ───────────────────────────────
openclaw plugins install @openclaw/telegram

# Install LINE plugin when LINE credentials are configured.
if [ -n "${line_channel_access_token}" ] && [ -n "${line_channel_secret}" ]; then
openclaw plugins install @openclaw/line || echo "LINE plugin install skipped (package unavailable in this release)."
fi

# ── 10. Auto-fix common config issues ────────────────────────
openclaw doctor --fix || true

Expand Down
45 changes: 38 additions & 7 deletions terraform/digitalOcean/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,36 @@ provider "digitalocean" {
# ── SSH Key ──────────────────────────────────────────────────

resource "digitalocean_ssh_key" "openclaw" {
count = var.existing_ssh_key_fingerprint == "" ? 1 : 0
name = "openclaw-key"
public_key = file(var.ssh_public_key_path)
}

locals {
openclaw_ssh_key_fingerprint = var.existing_ssh_key_fingerprint != "" ? var.existing_ssh_key_fingerprint : digitalocean_ssh_key.openclaw[0].fingerprint

# Build plugin paths based on enabled channels
plugin_paths = join(
",\n ",
concat(
["\"/usr/lib/node_modules/openclaw/dist/extensions/telegram\""],
var.line_channel_access_token != "" && var.line_channel_secret != "" ? ["\"/usr/lib/node_modules/openclaw/dist/extensions/line\""] : []
)
)

# Build plugin entries based on enabled channels
plugin_entries = join(
"\n ",
concat(
["\"telegram\": { \"enabled\": true },"],
var.line_channel_access_token != "" && var.line_channel_secret != "" ? ["\"line\": { \"enabled\": true },"] : []
)
)

# Build LINE channel JSON section (empty if not configured)
line_channel_json = var.line_channel_access_token != "" && var.line_channel_secret != "" ? ",\n \"line\": {\n \"enabled\": true,\n \"accounts\": {\n \"default\": {\n \"channelAccessToken\": \"${var.line_channel_access_token}\",\n \"channelSecret\": \"${var.line_channel_secret}\"\n }\n }\n }" : ""
}

# ── Firewall ─────────────────────────────────────────────────

resource "digitalocean_firewall" "openclaw" {
Expand Down Expand Up @@ -59,15 +85,20 @@ resource "digitalocean_droplet" "openclaw" {
region = var.region
size = var.droplet_size
image = "ubuntu-24-04-x64"
ssh_keys = [digitalocean_ssh_key.openclaw.fingerprint]
ssh_keys = [local.openclaw_ssh_key_fingerprint]

user_data = templatefile("${path.module}/bootstrap.sh", {
openrouter_api_key = var.openrouter_api_key
telegram_bot_token = var.telegram_bot_token
openclaw_gateway_token = var.openclaw_gateway_token
brave_api_key = var.brave_api_key
swap_size = var.swap_size
telegram_owner_id = var.telegram_owner_id
openrouter_api_key = var.openrouter_api_key
telegram_bot_token = var.telegram_bot_token
line_channel_access_token = var.line_channel_access_token
line_channel_secret = var.line_channel_secret
openclaw_gateway_token = var.openclaw_gateway_token
brave_api_key = var.brave_api_key
swap_size = var.swap_size
telegram_owner_id = var.telegram_owner_id
plugin_paths = local.plugin_paths
plugin_entries = local.plugin_entries
line_channel_json = local.line_channel_json
})
}

Expand Down
5 changes: 5 additions & 0 deletions terraform/digitalOcean/terraform.tfvars.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ do_token = "your-digitalocean-api-token"
# ── SSH ──────────────────────────────────────────────────────
ssh_public_key_path = "~/.ssh/id_rsa.pub"

# Optional: reuse an existing SSH key in your DO account to avoid duplicate-key errors.
# existing_ssh_key_fingerprint = "3b:16:bf:e5:8f:89:02:8c:67:63:6a:73:b4:7d:85:4d"

# ── Droplet ──────────────────────────────────────────────────
# Regions: tor1 (Toronto), sfo3 (San Francisco), nyc3 (New York), sgp1 (Singapore), ams3 (Amsterdam)
region = "tor1"
Expand All @@ -26,6 +29,8 @@ swap_size = "3G"
# ── Secrets ──────────────────────────────────────────────────
openrouter_api_key = "sk-or-v1-your-key-here"
telegram_bot_token = "1234567890:your-token-here"
line_channel_access_token = ""
line_channel_secret = ""
openclaw_gateway_token = "your-secret-gateway-token"

# ── Web Search ───────────────────────────────────────────────
Expand Down
17 changes: 17 additions & 0 deletions terraform/digitalOcean/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ variable "ssh_public_key_path" {
default = "~/.ssh/id_rsa.pub"
}

variable "existing_ssh_key_fingerprint" {
description = "Existing DigitalOcean SSH key fingerprint to reuse. Leave empty to create a new SSH key from ssh_public_key_path."
default = ""
}

# ── Droplet ──────────────────────────────────────────────────
variable "region" {
description = "DigitalOcean region slug (e.g. tor1, sfo3, nyc3, sgp1, ams3)"
Expand Down Expand Up @@ -46,6 +51,18 @@ variable "telegram_bot_token" {
sensitive = true
}

variable "line_channel_access_token" {
description = "LINE Messaging API channel access token. Leave empty to disable LINE channel."
sensitive = true
default = ""
}

variable "line_channel_secret" {
description = "LINE Messaging API channel secret. Leave empty to disable LINE channel."
sensitive = true
default = ""
}

variable "openclaw_gateway_token" {
sensitive = true
}
Expand Down
Loading