Skill: nbdxrycyl/discord-dual-bot-orchestrator (version 1.0.0)

--- name: discord-dual-bot-orchestrator description: Set up and operate dual Discord bots on one machine with isolated memory, channel allowlists, mention-gated reviewer bot behavior, optional one-way reviewer-to-primary context bridge, and safe backup/rollback. Use when configuring two bots (primary + reviewer), tuning reply rules, or recovering from bad config iterations. --- # Discord Dual Bot Orchestrator Use placeholders only. Never store real secrets in this skill. ## Required placeholders - `BOT_A_NAME` - `BOT_B_NAME` - `BOT_A_TOKEN` - `BOT_B_TOKEN` - `BOT_A_ID` - `BOT_B_ID` - `GUILD_ID` - `CHANNEL_ID_LIST` (comma-separated) - `BASE_DIR` (example: `~/.openclaw/bots`) ## Workflow 1. Create isolated workspaces for bot-a and bot-b. 2. Write `.env` files from template using placeholders. 3. Apply channel allowlist policy: - Bot-A: primary chat policy - Bot-B: `requireMention=true` on allowlisted channels 4. Optional: enable one-way bridge (`bot-b -> bot-a`) for reviewer feedback. 5. Create backup snapshot before each risky iteration. 6. If behavior regresses, rollback snapshot and restart both gateways. ## Commands ### Initialize layout Run: ```bash scripts/init_dual_bot.sh ``` ### Apply policy (placeholder-safe) Run: ```bash scripts/apply_policy.sh ``` ### Backup current state Run: ```bash scripts/backup_state.sh ``` ### Rollback from backup dir Run: ```bash scripts/rollback_state.sh <BACKUP_DIR> ``` ## Guardrails - Keep `BOT_B_TOKEN` separate from `BOT_A_TOKEN`. - Keep bot-b mention-gated in group channels. - Never enable bidirectional auto-bridge (avoids reply loops). - Always backup before patching runtime files.

path: scripts/apply_policy.sh
content: #!/usr/bin/env bash
set -euo pipefail

BOTB_CONFIG="${BOTB_CONFIG:-$HOME/.openclaw-bot-b/openclaw.json}"
GUILD_ID="${GUILD_ID:-GUILD_ID}"
CHANNEL_ID_LIST="${CHANNEL_ID_LIST:-CHANNEL_ID_LIST}"

python3 - << PY
import json
from pathlib import Path
cfg=Path("$BOTB_CONFIG")
d=json.loads(cfg.read_text())
channels={}
for cid in "$CHANNEL_ID_LIST".split(','):
    cid=cid.strip()
    if cid and cid!='CHANNEL_ID_LIST':
        channels[cid]={'allow':True,'requireMention':True}
if not channels:
    raise SystemExit('No CHANNEL_ID_LIST provided')

d.setdefault('channels',{}).setdefault('discord',{}).setdefault('guilds',{})["$GUILD_ID"]={'channels':channels}
cfg.write_text(json.dumps(d,ensure_ascii=False,indent=2))
print('Applied bot-b mention-gated allowlist:', list(channels.keys()))
PY

echo "Restart bot-b gateway after applying policy."


path: scripts/backup_state.sh
content: #!/usr/bin/env bash
set -euo pipefail

TS=$(date +%Y%m%d-%H%M%S)
OUT_DIR="${OUT_DIR:-$PWD/backups/dual-bot-$TS}"
mkdir -p "$OUT_DIR"

cp -f "$HOME/.openclaw/openclaw.json" "$OUT_DIR/openclaw.main.json"
cp -f "$HOME/.openclaw-bot-b/openclaw.json" "$OUT_DIR/openclaw.bot-b.json"

echo "$OUT_DIR"


path: scripts/init_dual_bot.sh
content: #!/usr/bin/env bash
set -euo pipefail

BASE_DIR="${BASE_DIR:-$HOME/.openclaw/bots}"

mkdir -p "$BASE_DIR/bot-a/workspace/memory"
mkdir -p "$BASE_DIR/bot-b/workspace/memory"

cat > "$BASE_DIR/bot-a/.env.template" << 'EOF'
DISCORD_BOT_TOKEN=BOT_A_TOKEN
OPENCLAW_WORKSPACE=BASE_DIR/bot-a/workspace
BOT_NAME=BOT_A_NAME
BOT_ID=BOT_A_ID
EOF

cat > "$BASE_DIR/bot-b/.env.template" << 'EOF'
DISCORD_BOT_TOKEN=BOT_B_TOKEN
OPENCLAW_WORKSPACE=BASE_DIR/bot-b/workspace
BOT_NAME=BOT_B_NAME
BOT_ID=BOT_B_ID
EOF

echo "Initialized: $BASE_DIR/{bot-a,bot-b}"
echo "Next: copy .env.template -> .env and replace placeholders."


path: scripts/rollback_state.sh
content: #!/usr/bin/env bash
set -euo pipefail

BK_DIR="${1:-}"
if [[ -z "$BK_DIR" ]]; then
  echo "Usage: $0 <BACKUP_DIR>"
  exit 1
fi

cp -f "$BK_DIR/openclaw.main.json" "$HOME/.openclaw/openclaw.json"
cp -f "$BK_DIR/openclaw.bot-b.json" "$HOME/.openclaw-bot-b/openclaw.json"

echo "Restored configs. Restart gateways manually:"
echo "  openclaw gateway restart"
echo "  openclaw --profile bot-b gateway run --port 28789"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: clean (LOW)