@ihazz/bitrix24 1.0.0 → 1.0.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/README.md +33 -5
- package/package.json +1 -1
- package/skills/bitrix24/SKILL.md +26 -0
- package/src/channel.ts +53 -3
- package/tests/channel.test.ts +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Create an inbound webhook in Bitrix24:
|
|
|
37
37
|
2. Create a webhook for your OpenClaw bot
|
|
38
38
|
3. Grant scopes:
|
|
39
39
|
- `imbot` — the minimum scope required for full bot operation
|
|
40
|
-
- `im` —
|
|
40
|
+
- `im` — reserved for future `agentMode` support; `agentMode` is not production-ready yet and will be available in future versions
|
|
41
41
|
- you may also grant any extra scopes beyond this set if they are needed for your Bitrix24 scenarios
|
|
42
42
|
4. Save the webhook and copy the URL
|
|
43
43
|
|
|
@@ -94,15 +94,17 @@ If `eventMode` is omitted, the plugin uses:
|
|
|
94
94
|
| `eventMode` | auto | `fetch` or `webhook`. Auto-selects from `callbackUrl` when omitted. |
|
|
95
95
|
| `botName` | `"OpenClaw"` | Bot display name. |
|
|
96
96
|
| `botCode` | auto | Optional explicit bot code. If omitted, the plugin registers the bot as `openclaw_<webhookUserId>`, and if that code is occupied it tries `openclaw_<webhookUserId>_2`, `_3`, and so on. |
|
|
97
|
-
| `botToken` |
|
|
97
|
+
| `botToken` | `md5(webhookUrl)` | Bot token for `imbot.v2` authentication (32-char hex string). If omitted, derived as `md5(webhookUrl)`. **Strongly recommended to set explicitly** if your `webhookUrl` may change (e.g. ngrok, dynamic DNS) — otherwise the plugin loses control of the previously registered bot. You can find the token in the Bitrix24 admin panel or compute it as `md5` of the original webhook URL. |
|
|
98
98
|
| `botAvatar` | — | Optional base64 avatar override. |
|
|
99
99
|
| `dmPolicy` | `"webhookUser"` | Access policy: `webhookUser` or `pairing`. |
|
|
100
100
|
| `showTyping` | `true` | Sends typing indicator before response. |
|
|
101
101
|
| `enabled` | `true` | Enables or disables the account. |
|
|
102
|
-
| `agentMode` | `false` |
|
|
102
|
+
| `agentMode` | `false` | Reserved for future user-event integration. Not working yet; planned for future versions. |
|
|
103
103
|
| `pollingIntervalMs` | `3000` | Base poll interval for `fetch` mode. |
|
|
104
104
|
| `pollingFastIntervalMs` | `100` | Fast follow-up poll interval when more events are pending. |
|
|
105
105
|
|
|
106
|
+
### Agent-Driven Reactions
|
|
107
|
+
|
|
106
108
|
To enable agent-driven reactions, add `reactions` to the channel capabilities in your OpenClaw config:
|
|
107
109
|
|
|
108
110
|
```json
|
|
@@ -118,6 +120,32 @@ To enable agent-driven reactions, add `reactions` to the channel capabilities in
|
|
|
118
120
|
|
|
119
121
|
The plugin maps standard Unicode emoji (👍, 🔥, 👀, etc.) to Bitrix24 reaction codes automatically. B24 reaction codes (like `like`, `fire`, `eyes`) can also be used directly.
|
|
120
122
|
|
|
123
|
+
### Inline Buttons
|
|
124
|
+
|
|
125
|
+
To enable agent-driven inline buttons, add `inlineButtons` to the channel capabilities in your OpenClaw config:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"channels": {
|
|
130
|
+
"bitrix24": {
|
|
131
|
+
"webhookUrl": "...",
|
|
132
|
+
"capabilities": ["inlineButtons"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The plugin supports two button input forms:
|
|
139
|
+
|
|
140
|
+
- native Bitrix24 keyboard payload via `channelData.bitrix24.keyboard`
|
|
141
|
+
- generic OpenClaw button rows via `channelData.telegram.buttons`, which the plugin converts to Bitrix24 `KEYBOARD`
|
|
142
|
+
|
|
143
|
+
For converted generic buttons:
|
|
144
|
+
|
|
145
|
+
- `callback_data` starting with `/` becomes a native Bitrix24 slash command button
|
|
146
|
+
- any other `callback_data` becomes `ACTION=PUT`
|
|
147
|
+
- `primary`, `attention`, and `danger` button styles are mapped to Bitrix24 color tokens
|
|
148
|
+
|
|
121
149
|
## Access Policies
|
|
122
150
|
|
|
123
151
|
### `webhookUser`
|
|
@@ -161,7 +189,7 @@ openclaw pairing approve bitrix24 <CODE>
|
|
|
161
189
|
- Direct messages
|
|
162
190
|
- Typing indicators
|
|
163
191
|
- Text replies with BBCode conversion
|
|
164
|
-
- Inline keyboard buttons
|
|
192
|
+
- Inline keyboard buttons (`channelData.bitrix24.keyboard` and converted generic button rows)
|
|
165
193
|
- Reactions (agent can add/remove reactions on messages via `imbot.v2.Chat.Message.Reaction.*`)
|
|
166
194
|
- File upload to chat via `imbot.v2.File.upload`
|
|
167
195
|
- File download via `imbot.v2.File.download`
|
|
@@ -188,7 +216,7 @@ There is already account-config scaffolding in the plugin, but the current produ
|
|
|
188
216
|
|
|
189
217
|
- Verify `webhookUrl` is valid and active.
|
|
190
218
|
- If you use `eventMode: "webhook"`, verify `callbackUrl` is reachable from the internet over HTTPS.
|
|
191
|
-
-
|
|
219
|
+
- Do not rely on `agentMode` yet. It is not working in the current release and is planned for future versions.
|
|
192
220
|
- Do not expect the bot to work in group chats.
|
|
193
221
|
- If file transfer fails, verify the file size and Bitrix24-side availability of the attachment.
|
|
194
222
|
|
package/package.json
CHANGED
package/skills/bitrix24/SKILL.md
CHANGED
|
@@ -63,8 +63,34 @@ When you see a message from a Bitrix24 user, you can react to acknowledge it bef
|
|
|
63
63
|
|
|
64
64
|
**Important:** Do NOT invent or guess messageId values. Either omit `messageId` to react to the current message, or use a messageId that was explicitly provided to you.
|
|
65
65
|
|
|
66
|
+
## Inline Keyboard Buttons
|
|
67
|
+
|
|
68
|
+
You can attach inline buttons to a message. The `callback_data` value is sent back when the user taps the button.
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"action": "send",
|
|
73
|
+
"channel": "bitrix24",
|
|
74
|
+
"message": "Выберите вариант:",
|
|
75
|
+
"buttons": [
|
|
76
|
+
[
|
|
77
|
+
{ "text": "Да", "callback_data": "Да" },
|
|
78
|
+
{ "text": "Нет", "callback_data": "Нет" }
|
|
79
|
+
]
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Important rules for buttons:**
|
|
85
|
+
|
|
86
|
+
- `callback_data` **must match** the button `text` exactly — when the user taps a button, `callback_data` is sent as a message in the chat. If they differ, the user sees confusing text.
|
|
87
|
+
- Write button labels in the **same language as the conversation** (e.g. Russian if the user writes in Russian).
|
|
88
|
+
- Use `"style": "primary"` for the recommended option, `"style": "danger"` for destructive actions.
|
|
89
|
+
- Each inner array is one row of buttons.
|
|
90
|
+
|
|
66
91
|
## Writing Style (Bitrix24)
|
|
67
92
|
|
|
68
93
|
- Direct, professional, moderate length.
|
|
94
|
+
- **Always reply in the same language the user writes in.** If the user writes in Russian, reply in Russian. If in English, reply in English.
|
|
69
95
|
- Bitrix24 uses BBCode for formatting (conversion is automatic).
|
|
70
96
|
- Inline keyboard buttons are supported for interactive responses.
|
package/src/channel.ts
CHANGED
|
@@ -552,7 +552,7 @@ export function convertButtonsToKeyboard(rows: ChannelButton[][]): B24Keyboard {
|
|
|
552
552
|
b24Btn.COMMAND_PARAMS = parts.slice(1).join(' ');
|
|
553
553
|
}
|
|
554
554
|
} else if (btn.callback_data) {
|
|
555
|
-
b24Btn.ACTION = '
|
|
555
|
+
b24Btn.ACTION = 'SEND';
|
|
556
556
|
b24Btn.ACTION_VALUE = btn.callback_data;
|
|
557
557
|
}
|
|
558
558
|
|
|
@@ -1152,11 +1152,11 @@ export const bitrix24Plugin = {
|
|
|
1152
1152
|
|
|
1153
1153
|
actions: {
|
|
1154
1154
|
listActions: (_params: { cfg: Record<string, unknown> }): string[] => {
|
|
1155
|
-
return ['react'];
|
|
1155
|
+
return ['react', 'send'];
|
|
1156
1156
|
},
|
|
1157
1157
|
|
|
1158
1158
|
supportsAction: (params: { action: string }): boolean => {
|
|
1159
|
-
return params.action === 'react';
|
|
1159
|
+
return params.action === 'react' || params.action === 'send';
|
|
1160
1160
|
},
|
|
1161
1161
|
|
|
1162
1162
|
handleAction: async (ctx: {
|
|
@@ -1167,6 +1167,56 @@ export const bitrix24Plugin = {
|
|
|
1167
1167
|
params: Record<string, unknown>;
|
|
1168
1168
|
[key: string]: unknown;
|
|
1169
1169
|
}): Promise<Record<string, unknown> | null> => {
|
|
1170
|
+
// ─── Send with buttons ──────────────────────────────────────────────
|
|
1171
|
+
if (ctx.action === 'send') {
|
|
1172
|
+
// Only intercept send when buttons are present; otherwise let gateway handle normally
|
|
1173
|
+
const rawButtons = ctx.params.buttons;
|
|
1174
|
+
if (!rawButtons) return null;
|
|
1175
|
+
|
|
1176
|
+
const { config } = resolveAccount(ctx.cfg, ctx.accountId);
|
|
1177
|
+
if (!config.webhookUrl || !gatewayState) return null;
|
|
1178
|
+
|
|
1179
|
+
const bot = gatewayState.bot;
|
|
1180
|
+
const to = String(ctx.params.to ?? '').trim();
|
|
1181
|
+
if (!to) return null;
|
|
1182
|
+
|
|
1183
|
+
const sendCtx: SendContext = { webhookUrl: config.webhookUrl, bot, dialogId: to };
|
|
1184
|
+
const message = String(ctx.params.message ?? '').trim();
|
|
1185
|
+
|
|
1186
|
+
// Parse buttons: may be array or JSON string
|
|
1187
|
+
let buttons: ChannelButton[][] | undefined;
|
|
1188
|
+
try {
|
|
1189
|
+
const parsed = typeof rawButtons === 'string' ? JSON.parse(rawButtons) : rawButtons;
|
|
1190
|
+
if (Array.isArray(parsed)) buttons = parsed;
|
|
1191
|
+
} catch {
|
|
1192
|
+
// invalid buttons JSON — send without keyboard
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
const keyboard = buttons?.length ? convertButtonsToKeyboard(buttons) : undefined;
|
|
1196
|
+
|
|
1197
|
+
const toolResult = (payload: Record<string, unknown>) => ({
|
|
1198
|
+
content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],
|
|
1199
|
+
details: payload,
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
try {
|
|
1203
|
+
const result = await gatewayState.sendService.sendText(
|
|
1204
|
+
sendCtx, message || ' ', keyboard ? { keyboard } : undefined,
|
|
1205
|
+
);
|
|
1206
|
+
return toolResult({
|
|
1207
|
+
channel: 'bitrix24',
|
|
1208
|
+
to,
|
|
1209
|
+
via: 'direct',
|
|
1210
|
+
mediaUrl: null,
|
|
1211
|
+
result: { messageId: String(result.messageId ?? '') },
|
|
1212
|
+
});
|
|
1213
|
+
} catch (err) {
|
|
1214
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1215
|
+
return toolResult({ ok: false, error: errMsg });
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// ─── React ────────────────────────────────────────────────────────────
|
|
1170
1220
|
if (ctx.action !== 'react') return null;
|
|
1171
1221
|
|
|
1172
1222
|
// Helper: wrap payload as gateway-compatible tool result
|
package/tests/channel.test.ts
CHANGED
|
@@ -105,7 +105,7 @@ describe('convertButtonsToKeyboard', () => {
|
|
|
105
105
|
const btn = kb[0];
|
|
106
106
|
if (isButton(btn)) {
|
|
107
107
|
expect(btn.COMMAND).toBeUndefined();
|
|
108
|
-
expect(btn.ACTION).toBe('
|
|
108
|
+
expect(btn.ACTION).toBe('SEND');
|
|
109
109
|
expect(btn.ACTION_VALUE).toBe('some_value');
|
|
110
110
|
}
|
|
111
111
|
});
|