@tencent-weixin/openclaw-weixin 2.1.2 → 2.1.3
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/package.json
CHANGED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming markdown filter — character-level state machine that strips
|
|
3
|
+
* unsupported markdown syntax on-the-fly.
|
|
4
|
+
*
|
|
5
|
+
* Outputs as much filtered text as possible on each `feed()` call, only
|
|
6
|
+
* holding back the minimum characters needed for pattern disambiguation
|
|
7
|
+
* (e.g. a trailing `*` that might become `***`).
|
|
8
|
+
*
|
|
9
|
+
* States:
|
|
10
|
+
* - **sol** (start-of-line): checks for line-start patterns (```, >, #####, indent)
|
|
11
|
+
* - **body**: scans for inline patterns (`, ![, ~~, ***) and outputs safe chars
|
|
12
|
+
* - **fence**: inside a fenced code block, passes through until closing ```
|
|
13
|
+
* - **inline**: accumulating content inside an inline marker pair
|
|
14
|
+
*/
|
|
15
|
+
export class StreamingMarkdownFilter {
|
|
16
|
+
private buf = "";
|
|
17
|
+
private fence = false;
|
|
18
|
+
private sol = true;
|
|
19
|
+
private inl: { type: "code" | "image" | "strike" | "bold3" | "italic" | "ubold3" | "uitalic" | "table"; acc: string } | null = null;
|
|
20
|
+
|
|
21
|
+
feed(delta: string): string {
|
|
22
|
+
this.buf += delta;
|
|
23
|
+
return this.pump(false);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
flush(): string {
|
|
27
|
+
return this.pump(true);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private pump(eof: boolean): string {
|
|
31
|
+
let out = "";
|
|
32
|
+
while (this.buf) {
|
|
33
|
+
const sLen = this.buf.length;
|
|
34
|
+
const sSol = this.sol;
|
|
35
|
+
const sFence = this.fence;
|
|
36
|
+
const sInl = this.inl;
|
|
37
|
+
|
|
38
|
+
if (this.fence) out += this.pumpFence(eof);
|
|
39
|
+
else if (this.inl) out += this.pumpInline(eof);
|
|
40
|
+
else if (this.sol) out += this.pumpSOL(eof);
|
|
41
|
+
else out += this.pumpBody(eof);
|
|
42
|
+
|
|
43
|
+
if (this.buf.length === sLen && this.sol === sSol &&
|
|
44
|
+
this.fence === sFence && this.inl === sInl) break;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (eof && this.inl) {
|
|
48
|
+
if (this.inl.type === "table") {
|
|
49
|
+
out += StreamingMarkdownFilter.extractTableRow(this.inl.acc);
|
|
50
|
+
} else {
|
|
51
|
+
const markers: Record<string, string> = { code: "`", image: "![", strike: "~~", bold3: "***", italic: "*", ubold3: "___", uitalic: "_" };
|
|
52
|
+
out += (markers[this.inl.type] ?? "") + this.inl.acc;
|
|
53
|
+
}
|
|
54
|
+
this.inl = null;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Inside a code fence: pass content through, watch for closing ``` at SOL. */
|
|
60
|
+
private pumpFence(eof: boolean): string {
|
|
61
|
+
if (this.sol) {
|
|
62
|
+
if (this.buf.length < 3 && !eof) return "";
|
|
63
|
+
if (this.buf.startsWith("```")) {
|
|
64
|
+
this.fence = false;
|
|
65
|
+
const nl = this.buf.indexOf("\n", 3);
|
|
66
|
+
this.buf = nl !== -1 ? this.buf.slice(nl + 1) : "";
|
|
67
|
+
this.sol = true;
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
this.sol = false;
|
|
71
|
+
}
|
|
72
|
+
const nl = this.buf.indexOf("\n");
|
|
73
|
+
if (nl !== -1) {
|
|
74
|
+
const chunk = this.buf.slice(0, nl + 1);
|
|
75
|
+
this.buf = this.buf.slice(nl + 1);
|
|
76
|
+
this.sol = true;
|
|
77
|
+
return chunk;
|
|
78
|
+
}
|
|
79
|
+
const chunk = this.buf;
|
|
80
|
+
this.buf = "";
|
|
81
|
+
return chunk;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** At start of line: detect and consume line-start patterns, then transition to body. */
|
|
85
|
+
private pumpSOL(eof: boolean): string {
|
|
86
|
+
const b = this.buf;
|
|
87
|
+
|
|
88
|
+
if (b[0] === "\n") {
|
|
89
|
+
this.buf = b.slice(1);
|
|
90
|
+
return "\n";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (b[0] === "`") {
|
|
94
|
+
if (b.length < 3 && !eof) return "";
|
|
95
|
+
if (b.startsWith("```")) {
|
|
96
|
+
this.fence = true;
|
|
97
|
+
const nl = b.indexOf("\n", 3);
|
|
98
|
+
this.buf = nl !== -1 ? b.slice(nl + 1) : "";
|
|
99
|
+
this.sol = true;
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
this.sol = false;
|
|
103
|
+
return "";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (b[0] === ">") {
|
|
107
|
+
if (b.length < 2 && !eof) return "";
|
|
108
|
+
this.buf = b.length >= 2 && b[1] === " " ? b.slice(2) : b.slice(1);
|
|
109
|
+
this.sol = false;
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (b[0] === "#") {
|
|
114
|
+
let n = 0;
|
|
115
|
+
while (n < b.length && b[n] === "#") n++;
|
|
116
|
+
if (n === b.length && !eof) return "";
|
|
117
|
+
if (n >= 5 && n <= 6 && n < b.length && b[n] === " ") {
|
|
118
|
+
this.buf = b.slice(n + 1);
|
|
119
|
+
this.sol = false;
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
this.sol = false;
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (b[0] === "|") {
|
|
127
|
+
this.buf = b.slice(1);
|
|
128
|
+
this.inl = { type: "table", acc: "" };
|
|
129
|
+
this.sol = false;
|
|
130
|
+
return "";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (b[0] === " " || b[0] === "\t") {
|
|
134
|
+
if (b.search(/[^ \t]/) === -1 && !eof) return "";
|
|
135
|
+
this.sol = false;
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (b[0] === "-" || b[0] === "*" || b[0] === "_") {
|
|
140
|
+
const ch = b[0];
|
|
141
|
+
let j = 0;
|
|
142
|
+
while (j < b.length && (b[j] === ch || b[j] === " ")) j++;
|
|
143
|
+
if (j === b.length && !eof) return "";
|
|
144
|
+
if (j === b.length || b[j] === "\n") {
|
|
145
|
+
let count = 0;
|
|
146
|
+
for (let k = 0; k < j; k++) if (b[k] === ch) count++;
|
|
147
|
+
if (count >= 3) {
|
|
148
|
+
this.buf = j < b.length ? b.slice(j + 1) : "";
|
|
149
|
+
this.sol = true;
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
this.sol = false;
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.sol = false;
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Scan line body for inline pattern triggers; output safe chars eagerly. */
|
|
162
|
+
private pumpBody(eof: boolean): string {
|
|
163
|
+
let out = "";
|
|
164
|
+
let i = 0;
|
|
165
|
+
while (i < this.buf.length) {
|
|
166
|
+
const c = this.buf[i];
|
|
167
|
+
if (c === "\n") {
|
|
168
|
+
out += this.buf.slice(0, i + 1);
|
|
169
|
+
this.buf = this.buf.slice(i + 1);
|
|
170
|
+
this.sol = true;
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
if (c === "`") {
|
|
174
|
+
out += this.buf.slice(0, i);
|
|
175
|
+
this.buf = this.buf.slice(i + 1);
|
|
176
|
+
this.inl = { type: "code", acc: "" };
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
if (c === "!" && i + 1 < this.buf.length && this.buf[i + 1] === "[") {
|
|
180
|
+
out += this.buf.slice(0, i);
|
|
181
|
+
this.buf = this.buf.slice(i + 2);
|
|
182
|
+
this.inl = { type: "image", acc: "" };
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
if (c === "~" && i + 1 < this.buf.length && this.buf[i + 1] === "~") {
|
|
186
|
+
out += this.buf.slice(0, i);
|
|
187
|
+
this.buf = this.buf.slice(i + 2);
|
|
188
|
+
this.inl = { type: "strike", acc: "" };
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
if (c === "*") {
|
|
192
|
+
if (i + 2 < this.buf.length && this.buf[i + 1] === "*" && this.buf[i + 2] === "*") {
|
|
193
|
+
out += this.buf.slice(0, i);
|
|
194
|
+
this.buf = this.buf.slice(i + 3);
|
|
195
|
+
this.inl = { type: "bold3", acc: "" };
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
if (i + 1 < this.buf.length && this.buf[i + 1] === "*") {
|
|
199
|
+
i += 2;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (i + 1 < this.buf.length && this.buf[i + 1] !== " " && this.buf[i + 1] !== "\n") {
|
|
203
|
+
out += this.buf.slice(0, i);
|
|
204
|
+
this.buf = this.buf.slice(i + 1);
|
|
205
|
+
this.inl = { type: "italic", acc: "" };
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
i++;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (c === "_") {
|
|
212
|
+
if (i + 2 < this.buf.length && this.buf[i + 1] === "_" && this.buf[i + 2] === "_") {
|
|
213
|
+
out += this.buf.slice(0, i);
|
|
214
|
+
this.buf = this.buf.slice(i + 3);
|
|
215
|
+
this.inl = { type: "ubold3", acc: "" };
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
if (i + 1 < this.buf.length && this.buf[i + 1] === "_") {
|
|
219
|
+
i += 2;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (i + 1 < this.buf.length && this.buf[i + 1] !== " " && this.buf[i + 1] !== "\n") {
|
|
223
|
+
out += this.buf.slice(0, i);
|
|
224
|
+
this.buf = this.buf.slice(i + 1);
|
|
225
|
+
this.inl = { type: "uitalic", acc: "" };
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
i++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
i++;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let hold = 0;
|
|
235
|
+
if (!eof) {
|
|
236
|
+
if (this.buf.endsWith("**")) hold = 2;
|
|
237
|
+
else if (this.buf.endsWith("__")) hold = 2;
|
|
238
|
+
else if (this.buf.endsWith("*")) hold = 1;
|
|
239
|
+
else if (this.buf.endsWith("_")) hold = 1;
|
|
240
|
+
else if (this.buf.endsWith("~")) hold = 1;
|
|
241
|
+
else if (this.buf.endsWith("!")) hold = 1;
|
|
242
|
+
}
|
|
243
|
+
out += this.buf.slice(0, this.buf.length - hold);
|
|
244
|
+
this.buf = hold > 0 ? this.buf.slice(-hold) : "";
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Accumulate inline content until closing marker is found. */
|
|
249
|
+
private pumpInline(_eof: boolean): string {
|
|
250
|
+
if (!this.inl) return "";
|
|
251
|
+
this.inl.acc += this.buf;
|
|
252
|
+
this.buf = "";
|
|
253
|
+
|
|
254
|
+
switch (this.inl.type) {
|
|
255
|
+
case "code": {
|
|
256
|
+
const idx = this.inl.acc.indexOf("`");
|
|
257
|
+
if (idx !== -1) {
|
|
258
|
+
const content = this.inl.acc.slice(0, idx);
|
|
259
|
+
this.buf = this.inl.acc.slice(idx + 1);
|
|
260
|
+
this.inl = null;
|
|
261
|
+
return content;
|
|
262
|
+
}
|
|
263
|
+
const nl = this.inl.acc.indexOf("\n");
|
|
264
|
+
if (nl !== -1) {
|
|
265
|
+
const r = "`" + this.inl.acc.slice(0, nl + 1);
|
|
266
|
+
this.buf = this.inl.acc.slice(nl + 1);
|
|
267
|
+
this.inl = null;
|
|
268
|
+
this.sol = true;
|
|
269
|
+
return r;
|
|
270
|
+
}
|
|
271
|
+
return "";
|
|
272
|
+
}
|
|
273
|
+
case "strike": {
|
|
274
|
+
const idx = this.inl.acc.indexOf("~~");
|
|
275
|
+
if (idx !== -1) {
|
|
276
|
+
const content = this.inl.acc.slice(0, idx);
|
|
277
|
+
this.buf = this.inl.acc.slice(idx + 2);
|
|
278
|
+
this.inl = null;
|
|
279
|
+
return content;
|
|
280
|
+
}
|
|
281
|
+
return "";
|
|
282
|
+
}
|
|
283
|
+
case "bold3": {
|
|
284
|
+
const idx = this.inl.acc.indexOf("***");
|
|
285
|
+
if (idx !== -1) {
|
|
286
|
+
const content = this.inl.acc.slice(0, idx);
|
|
287
|
+
this.buf = this.inl.acc.slice(idx + 3);
|
|
288
|
+
this.inl = null;
|
|
289
|
+
return content;
|
|
290
|
+
}
|
|
291
|
+
return "";
|
|
292
|
+
}
|
|
293
|
+
case "ubold3": {
|
|
294
|
+
const idx = this.inl.acc.indexOf("___");
|
|
295
|
+
if (idx !== -1) {
|
|
296
|
+
const content = this.inl.acc.slice(0, idx);
|
|
297
|
+
this.buf = this.inl.acc.slice(idx + 3);
|
|
298
|
+
this.inl = null;
|
|
299
|
+
return content;
|
|
300
|
+
}
|
|
301
|
+
return "";
|
|
302
|
+
}
|
|
303
|
+
case "italic": {
|
|
304
|
+
for (let j = 0; j < this.inl.acc.length; j++) {
|
|
305
|
+
if (this.inl.acc[j] === "\n") {
|
|
306
|
+
const r = "*" + this.inl.acc.slice(0, j + 1);
|
|
307
|
+
this.buf = this.inl.acc.slice(j + 1);
|
|
308
|
+
this.inl = null;
|
|
309
|
+
this.sol = true;
|
|
310
|
+
return r;
|
|
311
|
+
}
|
|
312
|
+
if (this.inl.acc[j] === "*") {
|
|
313
|
+
if (j + 1 < this.inl.acc.length && this.inl.acc[j + 1] === "*") {
|
|
314
|
+
j++;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const content = this.inl.acc.slice(0, j);
|
|
318
|
+
this.buf = this.inl.acc.slice(j + 1);
|
|
319
|
+
this.inl = null;
|
|
320
|
+
return content;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
case "uitalic": {
|
|
326
|
+
for (let j = 0; j < this.inl.acc.length; j++) {
|
|
327
|
+
if (this.inl.acc[j] === "\n") {
|
|
328
|
+
const r = "_" + this.inl.acc.slice(0, j + 1);
|
|
329
|
+
this.buf = this.inl.acc.slice(j + 1);
|
|
330
|
+
this.inl = null;
|
|
331
|
+
this.sol = true;
|
|
332
|
+
return r;
|
|
333
|
+
}
|
|
334
|
+
if (this.inl.acc[j] === "_") {
|
|
335
|
+
if (j + 1 < this.inl.acc.length && this.inl.acc[j + 1] === "_") {
|
|
336
|
+
j++;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const content = this.inl.acc.slice(0, j);
|
|
340
|
+
this.buf = this.inl.acc.slice(j + 1);
|
|
341
|
+
this.inl = null;
|
|
342
|
+
return content;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return "";
|
|
346
|
+
}
|
|
347
|
+
case "image": {
|
|
348
|
+
const cb = this.inl.acc.indexOf("]");
|
|
349
|
+
if (cb === -1) return "";
|
|
350
|
+
if (cb + 1 >= this.inl.acc.length) return "";
|
|
351
|
+
if (this.inl.acc[cb + 1] !== "(") {
|
|
352
|
+
const r = "![" + this.inl.acc.slice(0, cb + 1);
|
|
353
|
+
this.buf = this.inl.acc.slice(cb + 1);
|
|
354
|
+
this.inl = null;
|
|
355
|
+
return r;
|
|
356
|
+
}
|
|
357
|
+
const cp = this.inl.acc.indexOf(")", cb + 2);
|
|
358
|
+
if (cp !== -1) {
|
|
359
|
+
this.buf = this.inl.acc.slice(cp + 1);
|
|
360
|
+
this.inl = null;
|
|
361
|
+
return "";
|
|
362
|
+
}
|
|
363
|
+
return "";
|
|
364
|
+
}
|
|
365
|
+
case "table": {
|
|
366
|
+
const nl = this.inl.acc.indexOf("\n");
|
|
367
|
+
if (nl !== -1) {
|
|
368
|
+
const line = this.inl.acc.slice(0, nl);
|
|
369
|
+
this.buf = this.inl.acc.slice(nl + 1);
|
|
370
|
+
this.inl = null;
|
|
371
|
+
this.sol = true;
|
|
372
|
+
const row = StreamingMarkdownFilter.extractTableRow(line);
|
|
373
|
+
return row ? row + "\n" : "";
|
|
374
|
+
}
|
|
375
|
+
return "";
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return "";
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** Extract cell contents from a table row, or return "" for separator rows. */
|
|
382
|
+
private static extractTableRow(line: string): string {
|
|
383
|
+
if (/^[\s|:\-]+$/.test(line) && line.includes("-")) return "";
|
|
384
|
+
const parts = line.split("|").map(c => c.trim());
|
|
385
|
+
const cells = parts.slice(
|
|
386
|
+
parts[0] === "" ? 1 : 0,
|
|
387
|
+
parts[parts.length - 1] === "" ? parts.length - 1 : parts.length,
|
|
388
|
+
);
|
|
389
|
+
return cells.join("\t");
|
|
390
|
+
}
|
|
391
|
+
}
|
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
} from "./inbound.js";
|
|
29
29
|
import type { WeixinInboundMediaOpts } from "./inbound.js";
|
|
30
30
|
import { sendWeixinMediaFile } from "./send-media.js";
|
|
31
|
-
import {
|
|
31
|
+
import { StreamingMarkdownFilter } from "./markdown-filter.js";
|
|
32
|
+
import { sendMessageWeixin } from "./send.js";
|
|
32
33
|
import { handleSlashCommand } from "./slash-commands.js";
|
|
33
34
|
|
|
34
35
|
const MEDIA_OUTBOUND_TEMP_DIR = path.join(resolvePreferredOpenClawTmpDir(), "weixin/media/outbound-temp");
|
|
@@ -309,7 +310,11 @@ export async function processOneMessage(
|
|
|
309
310
|
humanDelay,
|
|
310
311
|
typingCallbacks,
|
|
311
312
|
deliver: async (payload) => {
|
|
312
|
-
const
|
|
313
|
+
const rawText = payload.text ?? "";
|
|
314
|
+
const text = (() => {
|
|
315
|
+
const f = new StreamingMarkdownFilter();
|
|
316
|
+
return f.feed(rawText) + f.flush();
|
|
317
|
+
})();
|
|
313
318
|
const mediaUrl = payload.mediaUrl ?? payload.mediaUrls?.[0];
|
|
314
319
|
logger.debug(`outbound payload: ${redactBody(JSON.stringify(payload))}`);
|
|
315
320
|
logger.info(
|
|
@@ -414,7 +419,7 @@ export async function processOneMessage(
|
|
|
414
419
|
ctx: finalized,
|
|
415
420
|
cfg: deps.config,
|
|
416
421
|
dispatcher,
|
|
417
|
-
replyOptions: { ...replyOptions, disableBlockStreaming:
|
|
422
|
+
replyOptions: { ...replyOptions, disableBlockStreaming: true },
|
|
418
423
|
}),
|
|
419
424
|
});
|
|
420
425
|
logger.debug(`dispatchReplyFromConfig: done agentId=${route.agentId ?? "(none)"}`);
|
package/src/messaging/send.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
|
|
2
|
-
import { stripMarkdown } from "openclaw/plugin-sdk/text-runtime";
|
|
3
2
|
|
|
4
3
|
import { sendMessage as sendMessageApi } from "../api/api.js";
|
|
5
4
|
import type { WeixinApiOptions } from "../api/api.js";
|
|
@@ -9,32 +8,12 @@ import type { MessageItem, SendMessageReq } from "../api/types.js";
|
|
|
9
8
|
import { MessageItemType, MessageState, MessageType } from "../api/types.js";
|
|
10
9
|
import type { UploadedFileInfo } from "../cdn/upload.js";
|
|
11
10
|
|
|
11
|
+
export { StreamingMarkdownFilter } from "./markdown-filter.js";
|
|
12
|
+
|
|
12
13
|
function generateClientId(): string {
|
|
13
14
|
return generateId("openclaw-weixin");
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
* Convert markdown-formatted model reply to plain text for Weixin delivery.
|
|
18
|
-
* Preserves newlines; strips markdown syntax.
|
|
19
|
-
*/
|
|
20
|
-
export function markdownToPlainText(text: string): string {
|
|
21
|
-
let result = text;
|
|
22
|
-
// Code blocks: strip fences, keep code content
|
|
23
|
-
result = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, code: string) => code.trim());
|
|
24
|
-
// Images: remove entirely
|
|
25
|
-
result = result.replace(/!\[[^\]]*\]\([^)]*\)/g, "");
|
|
26
|
-
// Links: keep display text only
|
|
27
|
-
result = result.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
28
|
-
// Tables: remove separator rows, then strip leading/trailing pipes and convert inner pipes to spaces
|
|
29
|
-
result = result.replace(/^\|[\s:|-]+\|$/gm, "");
|
|
30
|
-
result = result.replace(/^\|(.+)\|$/gm, (_, inner: string) =>
|
|
31
|
-
inner.split("|").map((cell) => cell.trim()).join(" "),
|
|
32
|
-
);
|
|
33
|
-
result = stripMarkdown(result);
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
17
|
/** Build a SendMessageReq containing a single text message. */
|
|
39
18
|
function buildTextMessageReq(params: {
|
|
40
19
|
to: string;
|