@badgerclaw/connect 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SETUP.md CHANGED
@@ -6,6 +6,7 @@ Connect your OpenClaw AI agent to BadgerClaw encrypted chat rooms.
6
6
 
7
7
  - **BadgerClaw iOS app** installed and logged in
8
8
  - **OpenClaw** installed on your machine (`npm install -g openclaw`)
9
+ - Both on the same network (not required, just for initial setup)
9
10
 
10
11
  ---
11
12
 
@@ -13,24 +14,28 @@ Connect your OpenClaw AI agent to BadgerClaw encrypted chat rooms.
13
14
 
14
15
  Open the BadgerClaw app on your phone.
15
16
 
16
- 1. Open any room or DM with BotBadger
17
- 2. Type: `/bot new`
18
- 3. Follow the guided flow to set a bot name and username
19
- 4. BotBadger responds with a pairing code:
17
+ 1. Open any room or DM
18
+ 2. Type: `/bot create <name>`
19
+ - Example: `/bot create jarvis`
20
+ 3. BotBadger responds with:
20
21
 
21
22
  ```
22
23
  🦡 Bot created!
23
24
 
24
25
  Name: jarvis
26
+ ID: @abc123_jarvis_bot:badger.signout.io
27
+
25
28
  Connect to OpenClaw:
29
+ openclaw plugins install @badgerclaw/connect
26
30
  openclaw badgerclaw connect BCK-A8F3-X9K2
27
31
 
28
32
  Code expires in 24 hours.
33
+ Run /bot pair jarvis to generate a new code.
29
34
  ```
30
35
 
31
- 5. **Copy the pairing code** (BCK-XXXX-XXXX)
36
+ 4. **Copy the pairing code** (BCK-XXXX-XXXX)
32
37
 
33
- > **Need a new code?** Type `/bot pair <name>` to regenerate.
38
+ > **Need a new code?** Type `/bot pair jarvis` to regenerate.
34
39
 
35
40
  ---
36
41
 
@@ -39,42 +44,38 @@ Code expires in 24 hours.
39
44
  On your machine (Mac, PC, Linux, Pi), run:
40
45
 
41
46
  ```bash
42
- openclaw plugins install @badgerclaw/connect
43
- ```
44
-
45
- Or install from source:
46
-
47
- ```bash
47
+ # From GitHub (current method)
48
48
  git clone https://github.com/darkstaar4/badgerclaw-plugin.git
49
49
  openclaw plugins install ./badgerclaw-plugin
50
+
51
+ # From npm (coming soon)
52
+ # openclaw plugins install @badgerclaw/connect
50
53
  ```
51
54
 
52
55
  ---
53
56
 
54
57
  ## Step 3: Connect with Pairing Code
55
58
 
56
- ```bash
57
- openclaw badgerclaw connect BCK-XXXX-XXXX
58
- ```
59
-
60
- Or run the full configure wizard:
59
+ Run the OpenClaw configure wizard:
61
60
 
62
61
  ```bash
63
62
  openclaw configure
64
63
  ```
65
64
 
66
- The plugin automatically:
67
- - Validates the code with BadgerClaw servers
68
- - Retrieves your bot's credentials
69
- - Enables end-to-end encryption
70
- - Configures auto-join for room invites
71
- - Sets up auto-reply in group chats
65
+ 1. Select **BadgerClaw** as your channel
66
+ 2. Enter your pairing code: `BCK-XXXX-XXXX`
67
+ 3. The plugin automatically:
68
+ - Validates the code with BadgerClaw servers
69
+ - Retrieves your bot's credentials
70
+ - Enables end-to-end encryption
71
+ - Configures auto-join for room invites
72
+ 4. Done! Your agent is connected.
72
73
 
73
74
  ---
74
75
 
75
76
  ## Step 4: Start Chatting
76
77
 
77
- Restart the OpenClaw gateway:
78
+ Restart the OpenClaw gateway to apply the new config:
78
79
 
79
80
  ```bash
80
81
  openclaw gateway restart
@@ -82,154 +83,31 @@ openclaw gateway restart
82
83
 
83
84
  Now in the BadgerClaw app:
84
85
 
85
- 1. **DM your bot** — find it in your contacts and send a message
86
- 2. **Add to a room** — type `/bot add <name>` in any room
86
+ 1. **DM your bot** — find it in your contacts (`@xxx_jarvis_bot:badger.signout.io`) and send a message
87
+ 2. **Add to a room** — type `/bot add @jarvis` in any room
87
88
  3. **Add to a Space** — add the bot to any room inside a Space
88
89
 
89
- Your OpenClaw agent will receive messages and respond — fully end-to-end encrypted.
90
-
91
- ---
92
-
93
- ## Bot Commands
94
-
95
- Use these commands in any BadgerClaw room or DM where your bot is present.
96
-
97
- | Command | Description |
98
- |---------|-------------|
99
- | `/bot help` | Show all available commands |
100
- | `/bot talk on` | **Enable auto-reply** — bot responds to every message without needing an @mention. Great for 1-on-1 rooms. |
101
- | `/bot talk off` | **Disable auto-reply** — bot only responds when @mentioned. Use in busy group rooms. |
102
- | `/bot add <name>` | Invite a bot to the current room (e.g. `/bot add jarvis`) |
103
- | `/bot list` | List all your bots and their connection status |
104
-
105
- ### How Auto-Reply Works
106
-
107
- - **New installs:** Auto-reply is **ON by default** in all rooms
108
- - **Per-room toggle:** `/bot talk on` and `/bot talk off` override the default for that specific room
109
- - **@mentions always work:** Even with auto-reply off, the bot responds when @mentioned
90
+ Your OpenClaw agent will receive messages and respond through BadgerClaw — fully end-to-end encrypted.
110
91
 
111
92
  ---
112
93
 
113
- ## Group Chat Configuration
114
-
115
- ### Default Behavior
116
-
117
- When you install BadgerClaw with a fresh pairing code, the plugin automatically configures:
118
-
119
- ```json
120
- {
121
- "groupPolicy": "open",
122
- "groups": {
123
- "*": {
124
- "autoReply": true,
125
- "enabled": true,
126
- "systemPrompt": "You are a helpful AI assistant..."
127
- }
128
- }
129
- }
130
- ```
131
-
132
- This means:
133
- - Bot joins any room it's invited to
134
- - Bot responds to all messages (no @mention required)
135
- - Bot uses a helpful assistant personality in groups
136
-
137
- ### Custom Group Personality
138
-
139
- By default, the bot uses a generic helpful assistant prompt in groups — **not** your OpenClaw workspace personality (SOUL.md, IDENTITY.md). This prevents workspace personas from causing the bot to stay silent in groups.
140
-
141
- To customize the group personality, edit your OpenClaw config:
142
-
143
- ```bash
144
- openclaw configure
145
- ```
146
-
147
- Or manually edit `~/.openclaw/openclaw.json`:
148
-
149
- ```json
150
- {
151
- "channels": {
152
- "badgerclaw": {
153
- "groups": {
154
- "*": {
155
- "autoReply": true,
156
- "systemPrompt": "You are Captain Bot, a pirate AI. Answer in pirate speak."
157
- }
158
- }
159
- }
160
- }
161
- }
162
- ```
163
-
164
- ### Per-Room Configuration
165
-
166
- You can configure specific rooms differently:
167
-
168
- ```json
169
- {
170
- "channels": {
171
- "badgerclaw": {
172
- "groups": {
173
- "*": { "autoReply": true },
174
- "!specificRoomId:server": {
175
- "autoReply": false,
176
- "systemPrompt": "Custom prompt for this room only"
177
- }
178
- }
179
- }
180
- }
181
- }
182
- ```
183
-
184
- ---
185
-
186
- ## FAQ
187
-
188
- ### Bot doesn't respond in group chats
189
-
190
- 1. Send `/bot talk on` in the room to enable auto-reply
191
- 2. Make sure the bot is a member of the room (`/bot add <name>`)
192
- 3. Check OpenClaw is running: `openclaw status`
193
-
194
- ### Bot shows typing indicator but never replies
195
-
196
- This usually means the bot's system prompt is telling it to stay silent. Check:
197
-
198
- 1. Your `groups` config has `autoReply: true`
199
- 2. The `systemPrompt` in groups config tells the agent to respond (not stay silent)
200
- 3. If you have a SOUL.md with "stay silent in groups" — the groups `systemPrompt` overrides this, but only if it's set
201
-
202
- Fix: Run `openclaw configure` and reconfigure BadgerClaw, or manually add the groups config shown above.
203
-
204
- ### `/bot talk on` doesn't work
205
-
206
- After changing bot commands, you may need to restart the gateway:
207
-
208
- ```bash
209
- openclaw gateway restart
210
- ```
211
-
212
- ### Bot responds with wrong personality
213
-
214
- The bot uses your **OpenClaw workspace personality** for DMs (SOUL.md, IDENTITY.md) but uses the **groups systemPrompt** for group chats. To change the group personality, edit `channels.badgerclaw.groups.*.systemPrompt` in your config.
215
-
216
- ### Bot keeps saying "NO_REPLY" or stays silent
217
-
218
- Your workspace files (SOUL.md) may instruct the agent to stay silent in groups. The groups `systemPrompt` should override this. If it doesn't:
219
-
220
- 1. Make sure `groups.*.systemPrompt` is set in your config
221
- 2. The prompt should explicitly say to respond to messages
222
- 3. Restart the gateway after config changes
94
+ ## Troubleshooting
223
95
 
224
96
  ### "Invalid pairing code"
97
+ - Code may have expired (24h limit). Run `/bot pair <name>` in the app to get a new one.
98
+ - Make sure you're entering the code exactly as shown (BCK-XXXX-XXXX).
225
99
 
226
- - Code expires after 24 hours. Run `/bot pair <name>` in the app for a new one.
227
- - Enter the code exactly as shown (BCK-XXXX-XXXX).
100
+ ### Bot doesn't respond
101
+ 1. Check OpenClaw is running: `openclaw status`
102
+ 2. Check gateway logs: `openclaw logs --follow`
103
+ 3. Verify the BadgerClaw channel is configured: `openclaw channels status`
228
104
 
229
105
  ### "Pairing code already redeemed"
230
-
231
106
  Each code is one-time use. Run `/bot pair <name>` to generate a fresh one.
232
107
 
108
+ ### Need to reconnect
109
+ Run `openclaw configure` again and enter a new pairing code.
110
+
233
111
  ---
234
112
 
235
113
  ## Architecture
@@ -237,9 +115,9 @@ Each code is one-time use. Run `/bot pair <name>` to generate a fresh one.
237
115
  ```
238
116
  Your Phone (BadgerClaw App)
239
117
 
240
- │ E2EE Messages (Megolm/Olm)
118
+ │ E2EE Messages
241
119
 
242
- BadgerClaw Server (Matrix homeserver)
120
+ BadgerClaw Server (badger.signout.io)
243
121
 
244
122
  │ E2EE Messages
245
123
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@badgerclaw/connect",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "BadgerClaw channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -31,4 +31,4 @@
31
31
  "defaultChoice": "npm"
32
32
  }
33
33
  }
34
- }
34
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "!oVrHNyMFgnOCfmjlLU:badger.signout.io": {
3
+ "autoReply": true
4
+ },
5
+ "!hcLBnfZOzoIHENRxuU:badger.signout.io": {
6
+ "autoReply": true
7
+ }
8
+ }
@@ -47,26 +47,17 @@ export function resolveMatrixGroupRequireMention(params: ChannelGroupContext): b
47
47
  rawGroupId.replace(/^channel:/, ""),
48
48
  rawGroupId.replace(/^room:/, ""),
49
49
  ];
50
- // Case-insensitive lookup: OpenClaw may lowercase the groupId while
51
- // room-config.json stores original-cased Matrix room IDs
52
- const configKeys = Object.keys(roomConfig);
53
50
  for (const variant of variants) {
54
- if (!variant) continue;
55
- const matchedKey = configKeys.find(
56
- (k) => k.toLowerCase() === variant.toLowerCase()
57
- );
58
- if (matchedKey) {
59
- if (roomConfig[matchedKey]?.autoReply === true) {
60
- console.log(`[badgerclaw] room-config autoReply=true for ${matchedKey} (lookup=${variant})`);
61
- return false;
62
- }
63
- if (roomConfig[matchedKey]?.autoReply === false) {
64
- console.log(`[badgerclaw] room-config autoReply=false for ${matchedKey} (lookup=${variant})`);
65
- return true;
66
- }
51
+ if (variant && roomConfig[variant]?.autoReply === true) {
52
+ console.log(`[badgerclaw] room-config autoReply=true for ${variant}`);
53
+ return false;
54
+ }
55
+ if (variant && roomConfig[variant]?.autoReply === false) {
56
+ console.log(`[badgerclaw] room-config autoReply=false for ${variant}`);
57
+ return true;
67
58
  }
68
59
  }
69
- console.log(`[badgerclaw] room-config: no match for groupId="${rawGroupId}", keys=${configKeys.join(",")}`);
60
+ console.log(`[badgerclaw] room-config: no match for groupId="${rawGroupId}", keys=${Object.keys(roomConfig).join(",")}`);
70
61
  } catch (err) {
71
62
  console.log(`[badgerclaw] room-config error: ${err}`);
72
63
  }
@@ -34,39 +34,47 @@ export function registerMatrixAutoJoin(params: {
34
34
  }
35
35
  autoJoinRegistered.add(client);
36
36
 
37
- // After joining a room, send a notice in encrypted rooms to force Megolm session rotation.
38
- // When the bot joins, other clients may use a cached outbound Megolm session that doesn't
39
- // include the bot. Sending a message causes compliant clients to detect the new member and
40
- // rotate their session so subsequent messages are decryptable by the bot.
41
- async function postJoinEncryptionHandshake(roomId: string) {
42
- try {
43
- const encryptionState = await client
44
- .getRoomStateEvent(roomId, "m.room.encryption", "")
45
- .catch(() => null);
46
-
47
- if (!encryptionState) {
48
- logVerbose(`badgerclaw: room ${roomId} is not encrypted, skipping handshake`);
49
- return;
50
- }
51
-
52
- logVerbose(`badgerclaw: room ${roomId} is encrypted, sending notice to trigger key rotation`);
53
- await client.sendNotice(roomId, "🔐 Encryption active — session keys exchanged.");
54
- logVerbose(`badgerclaw: sent encryption handshake notice in room ${roomId}`);
55
- } catch (err) {
56
- runtime.log?.(`badgerclaw: encryption handshake failed for room ${roomId}: ${String(err)}`);
57
- }
58
- }
59
-
60
37
  if (autoJoin === "always") {
61
38
  // Use the built-in autojoin mixin for "always" mode
62
39
  const { AutojoinRoomsMixin } = loadMatrixSdk();
63
40
  AutojoinRoomsMixin.setupOnClient(client);
64
41
  logVerbose("badgerclaw: auto-join enabled for all invites");
65
42
 
66
- // AutojoinRoomsMixin handles the join, so listen for room.join to run post-join logic
67
- client.on("room.join", async (roomId: string, _joinEvent: unknown) => {
68
- logVerbose(`badgerclaw: bot joined room ${roomId} (always mode), running post-join handshake`);
69
- await postJoinEncryptionHandshake(roomId);
43
+ // Also join any rooms with pending invites from before startup
44
+ (async () => {
45
+ try {
46
+ const syncData = await client.doRequest("GET", "/_matrix/client/v3/sync", { timeout: "0" });
47
+ const invitedRooms = Object.keys(syncData?.rooms?.invite ?? {});
48
+ for (const roomId of invitedRooms) {
49
+ try {
50
+ await client.joinRoom(roomId);
51
+ logVerbose(`badgerclaw: joined pending invite room ${roomId}`);
52
+ } catch (err) {
53
+ logVerbose(`badgerclaw: failed to join pending invite ${roomId}: ${err}`);
54
+ }
55
+ }
56
+ } catch {
57
+ // Ignore sync errors
58
+ }
59
+ })();
60
+
61
+ // When bot joins a new room, send a presence message to force Megolm session rotation
62
+ // This ensures all clients create a new session that includes the bot's device
63
+ client.on("room.join", async (roomId: string) => {
64
+ logVerbose(`badgerclaw: bot joined room ${roomId}, requesting key share`);
65
+ try {
66
+ // Send a dummy state event to signal our device presence
67
+ // This forces clients to include our device in the next Megolm session
68
+ await client.sendStateEvent(roomId, "m.room.member", await client.getUserId(), {
69
+ membership: "join",
70
+ displayname: "testbot",
71
+ }).catch(() => {});
72
+
73
+ // Also send a notice message to trigger session rotation
74
+ await client.sendNotice(roomId, "🦡 Bot connected to this room.").catch(() => {});
75
+ } catch (err) {
76
+ logVerbose(`badgerclaw: failed to send join notice in ${roomId}: ${err}`);
77
+ }
70
78
  });
71
79
  return;
72
80
  }
@@ -104,7 +112,6 @@ export function registerMatrixAutoJoin(params: {
104
112
  try {
105
113
  await client.joinRoom(roomId);
106
114
  logVerbose(`badgerclaw: joined room ${roomId}`);
107
- await postJoinEncryptionHandshake(roomId);
108
115
  } catch (err) {
109
116
  runtime.error?.(`badgerclaw: failed to join room ${roomId}: ${String(err)}`);
110
117
  }
@@ -128,9 +128,7 @@ export async function handleBotCommand(params: {
128
128
  case "talk": {
129
129
  if (arg === "on") {
130
130
  const config = loadRoomConfig();
131
- // Store with lowercase key to match OpenClaw's lowercased groupId
132
- const normalizedRoomId = roomId.toLowerCase();
133
- config[normalizedRoomId] = { ...config[normalizedRoomId], autoReply: true };
131
+ config[roomId] = { ...config[roomId], autoReply: true };
134
132
  saveRoomConfig(config);
135
133
  setGroupActivation(roomId, "always");
136
134
  await client.sendMessage(roomId, {
@@ -141,8 +139,7 @@ export async function handleBotCommand(params: {
141
139
  }
142
140
  if (arg === "off") {
143
141
  const config = loadRoomConfig();
144
- const normalizedRoomId = roomId.toLowerCase();
145
- config[normalizedRoomId] = { ...config[normalizedRoomId], autoReply: false };
142
+ config[roomId] = { ...config[roomId], autoReply: false };
146
143
  saveRoomConfig(config);
147
144
  setGroupActivation(roomId, "mention");
148
145
  await client.sendMessage(roomId, {
@@ -472,39 +472,14 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
472
472
  });
473
473
  return;
474
474
  }
475
- // Check room-config.json first (set by /bot talk on|off) — it takes priority
476
- // over the OpenClaw config wildcard entry.
477
- const roomConfigJsonAutoReply = (() => {
478
- try {
479
- const fs = require("fs");
480
- const path = require("path");
481
- const configPath = path.join(
482
- process.env.HOME || "/tmp",
483
- ".openclaw/extensions/badgerclaw/room-config.json"
484
- );
485
- const raw = fs.readFileSync(configPath, "utf-8");
486
- const roomCfg = JSON.parse(raw) as Record<string, { autoReply?: boolean }>;
487
- const normalizedRoomId = roomId.toLowerCase();
488
- const keys = Object.keys(roomCfg);
489
- const matchedKey = keys.find((k) => k.toLowerCase() === normalizedRoomId);
490
- if (matchedKey && typeof roomCfg[matchedKey]?.autoReply === "boolean") {
491
- return roomCfg[matchedKey].autoReply;
492
- }
493
- } catch { /* ignore */ }
494
- return undefined;
495
- })();
496
475
  const shouldRequireMention = isRoom
497
- ? roomConfigJsonAutoReply === true
476
+ ? roomConfig?.autoReply === true
498
477
  ? false
499
- : roomConfigJsonAutoReply === false
478
+ : roomConfig?.autoReply === false
500
479
  ? true
501
- : roomConfig?.autoReply === true
502
- ? false
503
- : roomConfig?.autoReply === false
504
- ? true
505
- : typeof roomConfig?.requireMention === "boolean"
506
- ? roomConfig?.requireMention
507
- : true
480
+ : typeof roomConfig?.requireMention === "boolean"
481
+ ? roomConfig?.requireMention
482
+ : true
508
483
  : false;
509
484
  const shouldBypassMention =
510
485
  allowTextCommands &&
package/src/onboarding.ts CHANGED
@@ -245,17 +245,6 @@ export const badgerclawOnboardingAdapter: ChannelOnboardingAdapter = {
245
245
  autoJoin: "always",
246
246
  // Open policy: bot responds in any room it's invited to
247
247
  groupPolicy: "open",
248
- // Default group config: auto-reply in all rooms with a helpful assistant prompt.
249
- // This prevents the bot from inheriting workspace personas (SOUL.md) that may
250
- // tell it to stay silent in groups or reply with NO_REPLY.
251
- groups: {
252
- "*": {
253
- autoReply: true,
254
- enabled: true,
255
- systemPrompt:
256
- "You are a helpful AI assistant in a BadgerClaw encrypted chat room. Respond to messages directly and helpfully. Be concise and friendly.",
257
- },
258
- },
259
248
  dm: {
260
249
  ...next.channels?.badgerclaw?.dm,
261
250
  policy: "open",