Files
zeekay acaeda400a Eradicate OpenClaw — resolve merge conflicts and finish the rebrand
Three coordinated things:

1. Resolve the committed upstream/main merge conflicts. 94 files were
   sitting with raw <<<<<<< HEAD ... >>>>>>> upstream/main blocks. Every
   conflict had HanzoBot/Bot on HEAD and OpenClaw on upstream. Resolved
   by keeping HEAD everywhere — 540 conflict blocks.

2. Rewrite remaining OpenClaw text refs.
     OPENCLAW   → HANZO_BOT
     OpenClaw   → HanzoBot          (PascalCase type identifiers)
     openclaw   → bot               (codebase convention — botToken,
                                    botUsername etc.; avoids invalid
                                    hyphenated JS identifiers)
     openclaw[_]/openclaw[A-Z] → bot[_]/bot[A-Z]   (compound)
     ai.openclaw.x → ai.hanzo.bot.x  (JVM package path)

3. Delete or rename openclaw-named file paths (116 of them). Dead
   duplicates with a Bot-named canonical from the partial migration:
   deleted. Otherwise renamed.
     ai/openclaw/**           → deleted (ai/hanzo/bot/** canonical)
     Sources/OpenClaw*/       → deleted (Sources/Bot* canonical)
     Tests/OpenClawIPCTests/  → deleted (Tests/BotIPCTests canonical)
     OpenClawKit/             → deleted (BotKit canonical)
     openclaw-tools.*.ts      → deleted (bot-tools.*.ts canonical)
     openclaw-root.ts etc.    → deleted (bot-root.ts canonical)
     types.openclaw.ts        → deleted (types.bot.ts canonical)
     extensions/*/openclaw.plugin.json → renamed hanzo-bot.plugin.json
     docs/start/openclaw.md   → renamed hanzo-bot.md (+ zh-CN)
     docs/assets/openclaw-*.png, whatsapp-openclaw*.jpg → deleted
     scripts/.../openclaw-*   → deleted (bot/hanzo-bot parallels)
     openclaw.mjs             → deleted (hanzo-bot.mjs is package.bin)

Then patched 65 broken JS/TS import strings where hanzo-bot had ended
up inside an import path ('./types.hanzo-bot.js' → './types.bot.js'
etc., since the on-disk filename uses the brand-neutral bot- prefix).

Pre-existing lint debt cleaned up to get oxlint --type-aware to 0/0:
removed the dead i18n test that referenced a path no longer existing;
defined the missing DIDConfig and WalletConfig types in types.base.ts
(they were imported but never declared); dropped unused imports;
collapsed redundant type assertion and a tautological meta.bot lookup
that the openclaw→bot rename made redundant.

The a2ui.bundle.js generated artifact is kept at its pre-rebrand
contents — it gets regenerated by 'pnpm canvas:a2ui:bundle' from
sources I cannot rebuild in this commit; the source side is clean.

Verified: 0 occurrences of openclaw (case-insensitive) in tree source
content, 0 file or directory paths with openclaw in the name; oxlint
--type-aware src test reports 0/0.
2026-05-11 17:56:29 -07:00

9.6 KiB

summary, read_when, title
summary read_when title
Twitch chat bot configuration and setup
Setting up Twitch chat integration for HanzoBot
Twitch

Twitch (plugin)

Twitch chat support via IRC connection. HanzoBot connects as a Twitch user (bot account) to receive and send messages in channels.

Plugin required

Twitch ships as a plugin and is not bundled with the core install.

Install via CLI (npm registry):

hanzo-bot plugins install @hanzo/bot-twitch

Local checkout (when running from a git repo):

hanzo-bot plugins install ./extensions/twitch

Details: Plugins

Quick setup (beginner)

  1. Create a dedicated Twitch account for the bot (or use an existing account).
  2. Generate credentials: Twitch Token Generator
    • Select Bot Token
    • Verify scopes chat:read and chat:write are selected
    • Copy the Client ID and Access Token
  3. Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
  4. Configure the token:
    • Env: BOT_TWITCH_ACCESS_TOKEN=... (default account only)
    • Or config: channels.twitch.accessToken
    • If both are set, config takes precedence (env fallback is default-account only).
  5. Start the gateway.

⚠️ Important: Add access control (allowFrom or allowedRoles) to prevent unauthorized users from triggering the bot. requireMention defaults to true.

Minimal config:

{
  channels: {
    twitch: {
      enabled: true,
      username: "hanzo-bot", // Bot's Twitch account
      accessToken: "oauth:abc123...", // OAuth Access Token (or use BOT_TWITCH_ACCESS_TOKEN env var)
      clientId: "xyz789...", // Client ID from Token Generator
      channel: "vevisk", // Which Twitch channel's chat to join (required)
      allowFrom: ["123456789"], // (recommended) Your Twitch user ID only - get it from https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
    },
  },
}

What it is

  • A Twitch channel owned by the Gateway.
  • Deterministic routing: replies always go back to Twitch.
  • Each account maps to an isolated session key agent:<agentId>:twitch:<accountName>.
  • username is the bot's account (who authenticates), channel is which chat room to join.

Setup (detailed)

Generate credentials

Use Twitch Token Generator:

  • Select Bot Token
  • Verify scopes chat:read and chat:write are selected
  • Copy the Client ID and Access Token

No manual app registration needed. Tokens expire after several hours.

Configure the bot

Env var (default account only):

BOT_TWITCH_ACCESS_TOKEN=oauth:abc123...

Or config:

{
  channels: {
    twitch: {
      enabled: true,
      username: "hanzo-bot",
      accessToken: "oauth:abc123...",
      clientId: "xyz789...",
      channel: "vevisk",
    },
  },
}

If both env and config are set, config takes precedence.

{
  channels: {
    twitch: {
      allowFrom: ["123456789"], // (recommended) Your Twitch user ID only
    },
  },
}

Prefer allowFrom for a hard allowlist. Use allowedRoles instead if you want role-based access.

Available roles: "moderator", "owner", "vip", "subscriber", "all".

Why user IDs? Usernames can change, allowing impersonation. User IDs are permanent.

Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/ (Convert your Twitch username to ID)

Token refresh (optional)

Tokens from Twitch Token Generator cannot be automatically refreshed - regenerate when expired.

For automatic token refresh, create your own Twitch application at Twitch Developer Console and add to config:

{
  channels: {
    twitch: {
      clientSecret: "your_client_secret",
      refreshToken: "your_refresh_token",
    },
  },
}

The bot automatically refreshes tokens before expiration and logs refresh events.

Multi-account support

Use channels.twitch.accounts with per-account tokens. See gateway/configuration for the shared pattern.

Example (one bot account in two channels):

{
  channels: {
    twitch: {
      accounts: {
        channel1: {
          username: "hanzo-bot",
          accessToken: "oauth:abc123...",
          clientId: "xyz789...",
          channel: "vevisk",
        },
        channel2: {
          username: "hanzo-bot",
          accessToken: "oauth:def456...",
          clientId: "uvw012...",
          channel: "secondchannel",
        },
      },
    },
  },
}

Note: Each account needs its own token (one token per channel).

Access control

Role-based restrictions

{
  channels: {
    twitch: {
      accounts: {
        default: {
          allowedRoles: ["moderator", "vip"],
        },
      },
    },
  },
}

Allowlist by User ID (most secure)

{
  channels: {
    twitch: {
      accounts: {
        default: {
          allowFrom: ["123456789", "987654321"],
        },
      },
    },
  },
}

Role-based access (alternative)

allowFrom is a hard allowlist. When set, only those user IDs are allowed. If you want role-based access, leave allowFrom unset and configure allowedRoles instead:

{
  channels: {
    twitch: {
      accounts: {
        default: {
          allowedRoles: ["moderator"],
        },
      },
    },
  },
}

Disable @mention requirement

By default, requireMention is true. To disable and respond to all messages:

{
  channels: {
    twitch: {
      accounts: {
        default: {
          requireMention: false,
        },
      },
    },
  },
}

Troubleshooting

First, run diagnostic commands:

hanzo-bot doctor
hanzo-bot channels status --probe

Bot doesn't respond to messages

Check access control: Ensure your user ID is in allowFrom, or temporarily remove allowFrom and set allowedRoles: ["all"] to test.

Check the bot is in the channel: The bot must join the channel specified in channel.

Token issues

"Failed to connect" or authentication errors:

  • Verify accessToken is the OAuth access token value (typically starts with oauth: prefix)
  • Check token has chat:read and chat:write scopes
  • If using token refresh, verify clientSecret and refreshToken are set

Token refresh not working

Check logs for refresh events:

Using env token source for mybot
Access token refreshed for user 123456 (expires in 14400s)

If you see "token refresh disabled (no refresh token)":

  • Ensure clientSecret is provided
  • Ensure refreshToken is provided

Config

Account config:

  • username - Bot username
  • accessToken - OAuth access token with chat:read and chat:write
  • clientId - Twitch Client ID (from Token Generator or your app)
  • channel - Channel to join (required)
  • enabled - Enable this account (default: true)
  • clientSecret - Optional: For automatic token refresh
  • refreshToken - Optional: For automatic token refresh
  • expiresIn - Token expiry in seconds
  • obtainmentTimestamp - Token obtained timestamp
  • allowFrom - User ID allowlist
  • allowedRoles - Role-based access control ("moderator" | "owner" | "vip" | "subscriber" | "all")
  • requireMention - Require @mention (default: true)

Provider options:

  • channels.twitch.enabled - Enable/disable channel startup
  • channels.twitch.username - Bot username (simplified single-account config)
  • channels.twitch.accessToken - OAuth access token (simplified single-account config)
  • channels.twitch.clientId - Twitch Client ID (simplified single-account config)
  • channels.twitch.channel - Channel to join (simplified single-account config)
  • channels.twitch.accounts.<accountName> - Multi-account config (all account fields above)

Full example:

{
  channels: {
    twitch: {
      enabled: true,
      username: "hanzo-bot",
      accessToken: "oauth:abc123...",
      clientId: "xyz789...",
      channel: "vevisk",
      clientSecret: "secret123...",
      refreshToken: "refresh456...",
      allowFrom: ["123456789"],
      allowedRoles: ["moderator", "vip"],
      accounts: {
        default: {
          username: "mybot",
          accessToken: "oauth:abc123...",
          clientId: "xyz789...",
          channel: "your_channel",
          enabled: true,
          clientSecret: "secret123...",
          refreshToken: "refresh456...",
          expiresIn: 14400,
          obtainmentTimestamp: 1706092800000,
          allowFrom: ["123456789", "987654321"],
          allowedRoles: ["moderator"],
        },
      },
    },
  },
}

Tool actions

The agent can call twitch with action:

  • send - Send a message to a channel

Example:

{
  action: "twitch",
  params: {
    message: "Hello Twitch!",
    to: "#mychannel",
  },
}

Safety & ops

  • Treat tokens like passwords - Never commit tokens to git
  • Use automatic token refresh for long-running bots
  • Use user ID allowlists instead of usernames for access control
  • Monitor logs for token refresh events and connection status
  • Scope tokens minimally - Only request chat:read and chat:write
  • If stuck: Restart the gateway after confirming no other process owns the session

Limits

  • 500 characters per message (auto-chunked at word boundaries)
  • Markdown is stripped before chunking
  • No rate limiting (uses Twitch's built-in rate limits)