@inetafrica/open-claudia 2.6.28 → 2.6.29
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/channels/kazee/adapter.js +59 -9
- package/channels/kazee/format.js +22 -4
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ const { TEMP_DIR, FILES_DIR } = require("../../core/config");
|
|
|
12
12
|
const { canonicalForChannel } = require("../../core/identity");
|
|
13
13
|
const { inlineKeyboardToPortable } = require("../types");
|
|
14
14
|
const { createKazeeSocket } = require("./socket");
|
|
15
|
+
const { normalize } = require("./format");
|
|
15
16
|
|
|
16
17
|
class KazeeAdapter {
|
|
17
18
|
constructor({ id = "kazee", url, token, ownerUserId, botUserId }) {
|
|
@@ -98,7 +99,7 @@ class KazeeAdapter {
|
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
_handleInboundMessage(msg) {
|
|
102
|
+
async _handleInboundMessage(msg) {
|
|
102
103
|
if (!msg) return;
|
|
103
104
|
const senderId = String(msg.sender?._id || msg.sender?.id || msg.senderId || "");
|
|
104
105
|
if (this.botUserId && senderId === String(this.botUserId)) return; // ignore our own echoes
|
|
@@ -169,6 +170,37 @@ class KazeeAdapter {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
// Voice/audio distinction: chat-central has no "voice" type, so voice
|
|
174
|
+
// notes arrive as "audio". Detect them by mimeType (ogg/opus) and
|
|
175
|
+
// promote to "voice" so the router can transcribe them correctly.
|
|
176
|
+
if (envelope.type === "audio" && envelope.media?.length) {
|
|
177
|
+
const mime = (envelope.media[0].mimeType || "").toLowerCase();
|
|
178
|
+
if (mime.includes("ogg") || mime.includes("opus")) {
|
|
179
|
+
envelope.type = "voice";
|
|
180
|
+
envelope.media[0].type = "voice";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Reply-to context: fetch the replied-to message content so the router
|
|
185
|
+
// has the same context Telegram provides. Best-effort — failure is silent.
|
|
186
|
+
if (msg.replyTo) {
|
|
187
|
+
try {
|
|
188
|
+
const data = await this._request("GET", `/message/${encodeURIComponent(String(msg.replyTo))}`);
|
|
189
|
+
const m = data?.message || data;
|
|
190
|
+
if (m && (m._id || m.id)) {
|
|
191
|
+
envelope.reply = {
|
|
192
|
+
text: m.content || "",
|
|
193
|
+
from: {
|
|
194
|
+
id: String(m.sender?._id || m.sender?.id || ""),
|
|
195
|
+
name: m.sender?.name || m.sender?.displayName || "",
|
|
196
|
+
username: m.sender?.username || "",
|
|
197
|
+
},
|
|
198
|
+
messageId: m._id || m.id,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
} catch (e) { /* best-effort — envelope is still valid without reply context */ }
|
|
202
|
+
}
|
|
203
|
+
|
|
172
204
|
this._emit("message", envelope);
|
|
173
205
|
}
|
|
174
206
|
|
|
@@ -184,24 +216,42 @@ class KazeeAdapter {
|
|
|
184
216
|
const body = {
|
|
185
217
|
chat_id: channelId,
|
|
186
218
|
sender: this.botUserId,
|
|
187
|
-
content: text,
|
|
219
|
+
content: normalize(text),
|
|
188
220
|
type: buttons ? "interactive" : "text",
|
|
189
221
|
findMeCode: `oc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
190
222
|
};
|
|
191
223
|
if (opts.replyTo) body.replyTo = opts.replyTo;
|
|
192
224
|
if (buttons) body.interactive = { buttons };
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
225
|
+
|
|
226
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
227
|
+
try {
|
|
228
|
+
const res = await this._request("POST", `/chat/${encodeURIComponent(channelId)}/message`, body);
|
|
229
|
+
return res?.message?._id || res?.messageId || res?._id || null;
|
|
230
|
+
} catch (e) {
|
|
231
|
+
const errMsg = e.message || "";
|
|
232
|
+
// Don't retry on client errors (4xx) — they won't self-heal.
|
|
233
|
+
const statusMatch = errMsg.match(/→ (\d{3})/);
|
|
234
|
+
const status = statusMatch ? parseInt(statusMatch[1], 10) : 0;
|
|
235
|
+
if (status >= 400 && status < 500) {
|
|
236
|
+
console.error("Kazee send error (not retrying):", errMsg);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
if (attempt < 2) {
|
|
240
|
+
const delay = (attempt + 1) * 1000;
|
|
241
|
+
console.error(`Kazee send error (attempt ${attempt + 1}/3, retrying in ${delay}ms):`, errMsg);
|
|
242
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
console.error("Kazee send error (exhausted retries):", errMsg);
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
199
248
|
}
|
|
249
|
+
return null;
|
|
200
250
|
}
|
|
201
251
|
|
|
202
252
|
async edit(channelId, messageId, text, opts = {}) {
|
|
203
253
|
const buttons = this._normalizeKeyboard(opts.keyboard);
|
|
204
|
-
const body = { content: text };
|
|
254
|
+
const body = { content: normalize(text) };
|
|
205
255
|
if (buttons) {
|
|
206
256
|
body.type = "interactive";
|
|
207
257
|
body.interactive = { buttons };
|
package/channels/kazee/format.js
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
-
// Kazee
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Kazee renders standard markdown end-to-end. This module normalizes text
|
|
2
|
+
// before sending: converts any stray Telegram HTML tags (which the model may
|
|
3
|
+
// accidentally emit) into their markdown equivalents so they render correctly
|
|
4
|
+
// instead of appearing as raw tag noise.
|
|
5
|
+
|
|
6
|
+
function htmlToMarkdown(text) {
|
|
7
|
+
return String(text || "")
|
|
8
|
+
.replace(/<pre><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, code) => "```\n" + code + "\n```")
|
|
9
|
+
.replace(/<pre>([\s\S]*?)<\/pre>/gi, (_, code) => "```\n" + code + "\n```")
|
|
10
|
+
.replace(/<code>([\s\S]*?)<\/code>/gi, "`$1`")
|
|
11
|
+
.replace(/<b>([\s\S]*?)<\/b>/gi, "**$1**")
|
|
12
|
+
.replace(/<strong>([\s\S]*?)<\/strong>/gi, "**$1**")
|
|
13
|
+
.replace(/<i>([\s\S]*?)<\/i>/gi, "_$1_")
|
|
14
|
+
.replace(/<em>([\s\S]*?)<\/em>/gi, "_$1_")
|
|
15
|
+
.replace(/<s>([\s\S]*?)<\/s>/gi, "~~$1~~")
|
|
16
|
+
.replace(/<strike>([\s\S]*?)<\/strike>/gi, "~~$1~~")
|
|
17
|
+
.replace(/<del>([\s\S]*?)<\/del>/gi, "~~$1~~")
|
|
18
|
+
.replace(/<a href="([^"]+)">([\s\S]*?)<\/a>/gi, "[$2]($1)")
|
|
19
|
+
.replace(/<[^>]+>/g, "")
|
|
20
|
+
.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n)));
|
|
21
|
+
}
|
|
4
22
|
|
|
5
23
|
function normalize(text) {
|
|
6
|
-
return String(text || "");
|
|
24
|
+
return htmlToMarkdown(String(text || ""));
|
|
7
25
|
}
|
|
8
26
|
|
|
9
27
|
module.exports = { normalize };
|
package/package.json
CHANGED