@wangpaul667/openclaw-channel-luffa
v1.1.0
Published
Luffa Web3 messaging platform channel plugin for OpenClaw
Maintainers
Readme
OpenClaw Luffa Channel
Luffa Web3 messaging platform channel plugin for OpenClaw.
Features
- HTTP polling for message reception (1 second interval)
- Direct user messages support (type=0)
- Group messages support (type=1)
- Message deduplication via msgId
- Message text sending
- Configurable polling interval
- Allow-list for users and groups
- Multi-account support
- Block streaming with human-like typing rhythm
- Automatic retry with exponential backoff
- Per-session message queue management
- Code block protection for better formatting
- Typing indicator support (optional)
API Endpoints
Based on Luffa Robot API:
- Receive:
POST https://apibot.luffa.im/robot/receive - Send:
POST https://apibot.luffa.im/robot/send
Installation
openclaw plugins install @wangpaul667/openclaw-channel-luffaFor local development:
openclaw plugins install -l /path/to/openclaw-channel-luffaConfiguration
Add to your ~/.openclaw/openclaw.json:
{
"channels": {
"luffa": {
"enabled": true,
"robotKey": "your_robot_secret_key",
"pollInterval": 1000,
"allowFrom": ["user_id_1", "user_id_2"],
"blockStreaming": true,
"humanDelay": {
"enabled": true,
"minMs": 800,
"maxMs": 2500,
"firstBlockDelay": 600,
"codeBlockDelay": 1500
},
"retry": {
"enabled": true,
"maxRetries": 3,
"baseDelayMs": 1000,
"maxDelayMs": 8000
},
"typingIndicator": {
"enabled": false,
"sendPlaceholder": false,
"placeholderText": "_正在思考..._"
}
}
},
"agents": {
"defaults": {
"blockStreamingDefault": "on",
"blockStreamingBreak": "text_end",
"blockStreamingChunk": {
"minChars": 800,
"maxChars": 1200,
"breakPreference": "paragraph"
},
"blockStreamingCoalesce": {
"minChars": 1500,
"maxChars": 2400,
"idleMs": 1200
}
}
}
}Configuration Options
| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Enable/disable the channel | | robotKey | string | - | Luffa robot secret key | | pollInterval | number | 1000 | Polling interval in milliseconds | | allowFrom | string[] | - | Allowed user IDs | | blockStreaming | boolean | true | Enable block streaming for progressive responses |
Advanced Features Configuration
1. Human-like Typing Rhythm (humanDelay)
Simulates natural human typing rhythm with random delays between message blocks.
| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | true | Enable human-like delays | | minMs | number | 800 | Minimum delay between blocks (ms) | | maxMs | number | 2500 | Maximum delay between blocks (ms) | | firstBlockDelay | number | 600 | Initial "thinking" delay (ms) | | codeBlockDelay | number | 1500 | Extra delay for code blocks (ms) |
Example:
{
"humanDelay": {
"enabled": true,
"minMs": 1000,
"maxMs": 2000,
"firstBlockDelay": 500,
"codeBlockDelay": 1200
}
}Effect:
- First message: 500ms delay (simulates thinking)
- Subsequent messages: 1000-2000ms random delay (simulates typing)
- Code blocks: 1200ms delay (extra time for formatting)
2. Automatic Retry (retry)
Automatically retries failed message sends with exponential backoff.
| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | true | Enable automatic retry | | maxRetries | number | 3 | Maximum retry attempts | | baseDelayMs | number | 1000 | Base delay for backoff (ms) | | maxDelayMs | number | 8000 | Maximum delay cap (ms) |
Backoff formula: delay = min(baseDelay * 2^(attempt-1), maxDelay)
Retry timeline:
- Attempt 1: Immediate
- Attempt 2: After 1000ms
- Attempt 3: After 2000ms
- Attempt 4: After 4000ms (capped at 8000ms)
Example:
{
"retry": {
"enabled": true,
"maxRetries": 3,
"baseDelayMs": 1000,
"maxDelayMs": 8000
}
}3. Typing Indicator (typingIndicator)
Sends a placeholder message before the actual response (optional).
| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Enable typing indicator | | sendPlaceholder | boolean | false | Send placeholder text | | placeholderText | string | "正在思考..." | Placeholder message |
Example:
{
"typingIndicator": {
"enabled": true,
"sendPlaceholder": true,
"placeholderText": "_思考中,请稍候..._"
}
}Note: Luffa API doesn't support native typing indicators. This sends a placeholder message instead.
Streaming Configuration
Luffa Channel supports block streaming — AI responses are sent progressively in chunks (800-1200 characters each) instead of waiting for the complete response.
Enable streaming:
{
"channels": {
"luffa": {
"blockStreaming": true
}
},
"agents": {
"defaults": {
"blockStreamingDefault": "on",
"blockStreamingBreak": "text_end",
"blockStreamingChunk": {
"minChars": 800,
"maxChars": 1200,
"breakPreference": "paragraph"
},
"blockStreamingCoalesce": {
"minChars": 1500,
"maxChars": 2400,
"idleMs": 1200
}
}
}
}Streaming modes:
| Mode | Behavior |
|------|----------|
| blockStreaming: true + blockStreamingBreak: "text_end" | Send chunks immediately as they're ready (progressive output) |
| blockStreaming: true + blockStreamingBreak: "message_end" | Wait for complete response, then send all chunks at once |
| blockStreaming: false | Send single message after complete response (default for most channels) |
Coalescing (Smart Merging):
Prevents message spam by merging small chunks:
| Option | Type | Default | Description | |--------|------|---------|-------------| | minChars | number | 1500 | Minimum chars before sending | | maxChars | number | 2400 | Maximum chars (forces send) | | idleMs | number | 1200 | Idle timeout before send (ms) |
Example flow:
Block 1 (500 chars) → Wait
Block 2 (600 chars) → Merge (1100 chars) → Wait
Block 3 (400 chars) → Merge (1500 chars) → Send (reached minChars)
Block 4 (800 chars) → Wait
Block 5 (300 chars) → Merge (1100 chars) → Send (idle timeout)
Result: 2 messages instead of 5Multi-Account Configuration
{
"channels": {
"luffa": {
"accounts": {
"default": {
"robotKey": "key1",
"enabled": true,
"pollInterval": 1000,
"blockStreaming": true,
"humanDelay": {
"enabled": true,
"minMs": 800,
"maxMs": 2000
}
},
"bot2": {
"robotKey": "key2",
"enabled": true,
"allowFrom": ["allowed_user"],
"retry": {
"enabled": true,
"maxRetries": 5
}
}
}
}
}
}Environment Variables
| Variable | Description | |----------|-------------| | LUFFA_ROBOT_KEY | Robot secret key (fallback) |
Message Format
Receive (POST /robot/receive)
Request:
{"secret": "Robot Key"}Response:
[
{
"uid": "Luffa User ID",
"count": 1,
"message": ["{\"atList\":[],\"text\":\"Chat content\",\"urlLink\":null,\"msgId\":\"Message ID\"}"],
"type": 0
},
{
"uid": "Luffa Group ID",
"count": 1,
"message": ["{\"uid\":\"Sender Luffa ID\",\"atList\":[],\"text\":\"Chat content\",\"urlLink\":null,\"msgId\":\"Message ID\"}"],
"type": 1
}
]Send (POST /robot/send)
Request:
{
"secret": "Robot Key",
"uid": "Luffa user ID",
"msg": "{\"text\":\"Message content\"}"
}Development
npm install
npm run build
npm testLocal Testing
# Build and install locally
npm run build
openclaw plugins install -l .
# Or copy to extensions directory
rm -rf ~/.openclaw/extensions/openclaw-channel-luffa
cp -r dist ~/.openclaw/extensions/openclaw-channel-luffa
openclaw gateway restartArchitecture
src/
├── index.ts # Plugin entry point
├── channel/
│ ├── index.ts # Main channel logic (with UX enhancements)
│ ├── config.ts # Configuration adapter
│ ├── dock.ts # Channel dock
│ ├── onboarding.ts # Onboarding adapter
│ └── outbound.ts # Outbound message adapter
├── client/
│ └── LuffaHttpClient.ts # HTTP client for Luffa API
├── commands/
│ ├── CommandHandler.ts # Command parsing
│ ├── LuffaCommands.ts # Luffa-specific commands
│ └── SessionCommands.ts # Session management commands
├── normalizer/
│ └── MessageNormalizer.ts # Message format conversion
└── types/
├── index.ts # TypeScript type definitions
└── plugin-sdk.d.ts # OpenClaw SDK typesUX Enhancements Summary
What's New (v1.0.9+)
Human-like Typing Rhythm
- Random delays between message blocks (800-2500ms)
- Initial "thinking" delay (600ms)
- Extra delay for code blocks (1500ms)
- Configurable per account
Automatic Retry with Exponential Backoff
- Retries failed sends automatically (max 3 attempts)
- Exponential backoff: 1s → 2s → 4s
- Configurable retry limits and delays
Per-Session Message Queue
- Prevents message interleaving
- Maintains order within conversations
- Automatic cleanup after completion
Code Block Protection
- Detects markdown code blocks
- Applies extra delay for proper formatting
- Works with OpenClaw's built-in code fence protection
Typing Indicator (Optional)
- Sends placeholder message before response
- Configurable placeholder text
- Disabled by default
Smart Coalescing
- Merges small chunks to prevent spam
- Waits for idle gaps (1200ms)
- Respects min/max character limits
Performance Impact
| Feature | Latency Impact | User Experience | |---------|---------------|-----------------| | Human Delay | +0.8-2.5s per block | ⭐⭐⭐⭐⭐ Natural rhythm | | Retry | +1-4s on failure | ⭐⭐⭐⭐⭐ Reliability | | Queue | +0.15s between blocks | ⭐⭐⭐⭐ Order preservation | | Coalescing | +1.2s idle wait | ⭐⭐⭐⭐⭐ Reduces spam |
Recommended Configuration
For most users:
{
"channels": {
"luffa": {
"blockStreaming": true,
"humanDelay": {
"enabled": true,
"minMs": 1000,
"maxMs": 2000
},
"retry": {
"enabled": true,
"maxRetries": 3
}
}
},
"agents": {
"defaults": {
"blockStreamingDefault": "on",
"blockStreamingBreak": "text_end",
"blockStreamingCoalesce": {
"minChars": 1500,
"maxChars": 2400,
"idleMs": 1200
}
}
}
}Publishing
This plugin is published to npm under @wangpaul667/openclaw-channel-luffa.
For Maintainers
# 1. Update version
npm version patch|minor|major
# 2. Build and test
npm run build
npm test
# 3. Publish to npm
npm publish --access public
# 4. Push tags
git push --follow-tagsLicense
MIT
