@ihazz/bitrix24 1.1.5 → 1.1.7
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/dist/src/channel.d.ts +1 -0
- package/dist/src/channel.d.ts.map +1 -1
- package/dist/src/channel.js +86 -20
- package/dist/src/channel.js.map +1 -1
- package/dist/src/i18n.d.ts +10 -0
- package/dist/src/i18n.d.ts.map +1 -1
- package/dist/src/i18n.js +58 -0
- package/dist/src/i18n.js.map +1 -1
- package/dist/src/message-utils.d.ts.map +1 -1
- package/dist/src/message-utils.js +73 -7
- package/dist/src/message-utils.js.map +1 -1
- package/dist/src/send-service.d.ts.map +1 -1
- package/dist/src/send-service.js +2 -6
- package/dist/src/send-service.js.map +1 -1
- package/package.json +1 -1
- package/src/channel.ts +107 -23
- package/src/i18n.ts +70 -0
- package/src/message-utils.ts +94 -7
- package/src/send-service.ts +2 -7
package/src/channel.ts
CHANGED
|
@@ -31,12 +31,14 @@ import { OPENCLAW_COMMANDS, buildCommandsHelpText, formatModelsCommandReply } fr
|
|
|
31
31
|
import {
|
|
32
32
|
accessApproved,
|
|
33
33
|
accessDenied,
|
|
34
|
+
commandKeyboardLabels,
|
|
34
35
|
groupPairingPending,
|
|
35
36
|
mediaDownloadFailed,
|
|
36
37
|
groupChatUnsupported,
|
|
37
38
|
onboardingMessage,
|
|
38
39
|
ownerAndAllowedUsersOnly,
|
|
39
40
|
personalBotOwnerOnly,
|
|
41
|
+
replyGenerationFailed,
|
|
40
42
|
watchOwnerDmNotice,
|
|
41
43
|
} from './i18n.js';
|
|
42
44
|
import { HistoryCache } from './history-cache.js';
|
|
@@ -923,14 +925,20 @@ export function __setGatewayStateForTests(state: GatewayState | null): void {
|
|
|
923
925
|
// ─── Default command keyboard ────────────────────────────────────────────────
|
|
924
926
|
|
|
925
927
|
/** Default keyboard shown with command responses and welcome messages. */
|
|
926
|
-
export
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
928
|
+
export function buildDefaultCommandKeyboard(language?: string): B24Keyboard {
|
|
929
|
+
const labels = commandKeyboardLabels(language);
|
|
930
|
+
|
|
931
|
+
return [
|
|
932
|
+
{ TEXT: labels.help, COMMAND: 'help', BG_COLOR_TOKEN: 'primary', DISPLAY: 'LINE' },
|
|
933
|
+
{ TEXT: labels.status, COMMAND: 'status', DISPLAY: 'LINE' },
|
|
934
|
+
{ TEXT: labels.commands, COMMAND: 'commands', DISPLAY: 'LINE' },
|
|
935
|
+
{ TYPE: 'NEWLINE' },
|
|
936
|
+
{ TEXT: labels.newSession, COMMAND: 'new', DISPLAY: 'LINE' },
|
|
937
|
+
{ TEXT: labels.models, COMMAND: 'models', DISPLAY: 'LINE' },
|
|
938
|
+
];
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
export const DEFAULT_COMMAND_KEYBOARD: B24Keyboard = buildDefaultCommandKeyboard();
|
|
934
942
|
|
|
935
943
|
// ─── Keyboard / Button conversion ────────────────────────────────────────────
|
|
936
944
|
|
|
@@ -1160,7 +1168,9 @@ async function sendInitialWelcomeToWebhookOwner(params: {
|
|
|
1160
1168
|
};
|
|
1161
1169
|
const isPairing = config.dmPolicy === 'pairing';
|
|
1162
1170
|
const text = onboardingMessage(language, config.botName ?? 'OpenClaw', config.dmPolicy);
|
|
1163
|
-
const options = isPairing
|
|
1171
|
+
const options = isPairing
|
|
1172
|
+
? undefined
|
|
1173
|
+
: { keyboard: buildDefaultCommandKeyboard(language) };
|
|
1164
1174
|
|
|
1165
1175
|
try {
|
|
1166
1176
|
await sendService.sendText(sendCtx, text, options);
|
|
@@ -1978,6 +1988,8 @@ export const bitrix24Plugin = {
|
|
|
1978
1988
|
sendCtx,
|
|
1979
1989
|
config,
|
|
1980
1990
|
});
|
|
1991
|
+
let replyDelivered = false;
|
|
1992
|
+
let dispatchFailed = false;
|
|
1981
1993
|
|
|
1982
1994
|
// Download media files if present
|
|
1983
1995
|
let mediaFields: Record<string, unknown> = {};
|
|
@@ -2101,7 +2113,7 @@ export const bitrix24Plugin = {
|
|
|
2101
2113
|
});
|
|
2102
2114
|
|
|
2103
2115
|
try {
|
|
2104
|
-
await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
2116
|
+
const dispatchResult = await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
2105
2117
|
ctx: inboundCtx,
|
|
2106
2118
|
cfg,
|
|
2107
2119
|
dispatcherOptions: {
|
|
@@ -2128,6 +2140,7 @@ export const bitrix24Plugin = {
|
|
|
2128
2140
|
}
|
|
2129
2141
|
}
|
|
2130
2142
|
|
|
2143
|
+
replyDelivered = true;
|
|
2131
2144
|
await sendService.sendText(sendCtx, text, keyboard ? { keyboard } : undefined);
|
|
2132
2145
|
}
|
|
2133
2146
|
},
|
|
@@ -2139,11 +2152,28 @@ export const bitrix24Plugin = {
|
|
|
2139
2152
|
},
|
|
2140
2153
|
},
|
|
2141
2154
|
});
|
|
2155
|
+
|
|
2156
|
+
if (!replyDelivered && dispatchResult?.queuedFinal === false) {
|
|
2157
|
+
await sendService.sendText(
|
|
2158
|
+
sendCtx,
|
|
2159
|
+
replyGenerationFailed(msgCtx.language),
|
|
2160
|
+
{ convertMarkdown: false },
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2142
2163
|
} catch (err) {
|
|
2164
|
+
dispatchFailed = true;
|
|
2143
2165
|
logger.error('Error dispatching message to agent', { senderId: msgCtx.senderId, chatId: msgCtx.chatId, error: err });
|
|
2144
2166
|
} finally {
|
|
2145
2167
|
replyStatusHeartbeat.stop();
|
|
2146
2168
|
}
|
|
2169
|
+
|
|
2170
|
+
if (!replyDelivered && dispatchFailed) {
|
|
2171
|
+
await sendService.sendText(
|
|
2172
|
+
sendCtx,
|
|
2173
|
+
replyGenerationFailed(msgCtx.language),
|
|
2174
|
+
{ convertMarkdown: false },
|
|
2175
|
+
);
|
|
2176
|
+
}
|
|
2147
2177
|
} finally {
|
|
2148
2178
|
await mediaService.cleanupDownloadedMedia(downloadedMedia.map((mediaItem) => mediaItem.path));
|
|
2149
2179
|
}
|
|
@@ -2714,12 +2744,24 @@ export const bitrix24Plugin = {
|
|
|
2714
2744
|
|
|
2715
2745
|
await directTextCoalescer.flush(ctx.accountId, conversation.dialogId);
|
|
2716
2746
|
|
|
2747
|
+
const defaultCommandKeyboard = buildDefaultCommandKeyboard(cmdCtx.language);
|
|
2748
|
+
|
|
2717
2749
|
if (commandName === 'help' || commandName === 'commands') {
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2750
|
+
const helpText = buildCommandsHelpText(cmdCtx.language, { concise: commandName === 'help' });
|
|
2751
|
+
|
|
2752
|
+
if (isDm) {
|
|
2753
|
+
await sendService.sendText(
|
|
2754
|
+
sendCtx,
|
|
2755
|
+
helpText,
|
|
2756
|
+
{ keyboard: defaultCommandKeyboard, convertMarkdown: false },
|
|
2757
|
+
);
|
|
2758
|
+
} else {
|
|
2759
|
+
await sendService.answerCommandText(
|
|
2760
|
+
commandSendCtx,
|
|
2761
|
+
helpText,
|
|
2762
|
+
{ keyboard: defaultCommandKeyboard, convertMarkdown: false },
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2723
2765
|
return;
|
|
2724
2766
|
}
|
|
2725
2767
|
|
|
@@ -2763,17 +2805,19 @@ export const bitrix24Plugin = {
|
|
|
2763
2805
|
config,
|
|
2764
2806
|
});
|
|
2765
2807
|
let commandReplyDelivered = false;
|
|
2808
|
+
let commandDispatchFailed = false;
|
|
2766
2809
|
|
|
2767
2810
|
try {
|
|
2768
2811
|
await replyStatusHeartbeat.start();
|
|
2769
|
-
await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
2812
|
+
const dispatchResult = await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
2770
2813
|
ctx: inboundCtx,
|
|
2771
2814
|
cfg,
|
|
2772
2815
|
dispatcherOptions: {
|
|
2773
2816
|
deliver: async (payload) => {
|
|
2774
2817
|
await replyStatusHeartbeat.stopAndWait();
|
|
2775
2818
|
if (payload.text) {
|
|
2776
|
-
const keyboard = extractKeyboardFromPayload(payload)
|
|
2819
|
+
const keyboard = extractKeyboardFromPayload(payload)
|
|
2820
|
+
?? defaultCommandKeyboard;
|
|
2777
2821
|
const formattedPayload = normalizeCommandReplyPayload({
|
|
2778
2822
|
commandName,
|
|
2779
2823
|
commandParams,
|
|
@@ -2781,14 +2825,23 @@ export const bitrix24Plugin = {
|
|
|
2781
2825
|
language: cmdCtx.language,
|
|
2782
2826
|
});
|
|
2783
2827
|
if (!commandReplyDelivered) {
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2828
|
+
if (isDm) {
|
|
2829
|
+
commandReplyDelivered = true;
|
|
2830
|
+
await sendService.sendText(sendCtx, formattedPayload.text, {
|
|
2831
|
+
keyboard,
|
|
2832
|
+
convertMarkdown: formattedPayload.convertMarkdown,
|
|
2833
|
+
});
|
|
2834
|
+
} else {
|
|
2835
|
+
commandReplyDelivered = true;
|
|
2836
|
+
await sendService.answerCommandText(commandSendCtx, formattedPayload.text, {
|
|
2837
|
+
keyboard,
|
|
2838
|
+
convertMarkdown: formattedPayload.convertMarkdown,
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2789
2841
|
return;
|
|
2790
2842
|
}
|
|
2791
2843
|
|
|
2844
|
+
commandReplyDelivered = true;
|
|
2792
2845
|
await sendService.sendText(sendCtx, formattedPayload.text, {
|
|
2793
2846
|
keyboard,
|
|
2794
2847
|
convertMarkdown: formattedPayload.convertMarkdown,
|
|
@@ -2803,11 +2856,42 @@ export const bitrix24Plugin = {
|
|
|
2803
2856
|
},
|
|
2804
2857
|
},
|
|
2805
2858
|
});
|
|
2859
|
+
|
|
2860
|
+
if (!commandReplyDelivered && dispatchResult?.queuedFinal === false) {
|
|
2861
|
+
const fallbackText = replyGenerationFailed(cmdCtx.language);
|
|
2862
|
+
if (isDm) {
|
|
2863
|
+
await sendService.sendText(sendCtx, fallbackText, {
|
|
2864
|
+
keyboard: defaultCommandKeyboard,
|
|
2865
|
+
convertMarkdown: false,
|
|
2866
|
+
});
|
|
2867
|
+
} else {
|
|
2868
|
+
await sendService.answerCommandText(commandSendCtx, fallbackText, {
|
|
2869
|
+
keyboard: defaultCommandKeyboard,
|
|
2870
|
+
convertMarkdown: false,
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2806
2874
|
} catch (err) {
|
|
2875
|
+
commandDispatchFailed = true;
|
|
2807
2876
|
logger.error('Error dispatching command to agent', { commandName, senderId, dialogId, error: err });
|
|
2808
2877
|
} finally {
|
|
2809
2878
|
replyStatusHeartbeat.stop();
|
|
2810
2879
|
}
|
|
2880
|
+
|
|
2881
|
+
if (!commandReplyDelivered && commandDispatchFailed) {
|
|
2882
|
+
const fallbackText = replyGenerationFailed(cmdCtx.language);
|
|
2883
|
+
if (isDm) {
|
|
2884
|
+
await sendService.sendText(sendCtx, fallbackText, {
|
|
2885
|
+
keyboard: defaultCommandKeyboard,
|
|
2886
|
+
convertMarkdown: false,
|
|
2887
|
+
});
|
|
2888
|
+
} else {
|
|
2889
|
+
await sendService.answerCommandText(commandSendCtx, fallbackText, {
|
|
2890
|
+
keyboard: defaultCommandKeyboard,
|
|
2891
|
+
convertMarkdown: false,
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2811
2895
|
},
|
|
2812
2896
|
|
|
2813
2897
|
onJoinChat: async (joinCtx: FetchJoinChatContext) => {
|
|
@@ -2898,7 +2982,7 @@ export const bitrix24Plugin = {
|
|
|
2898
2982
|
await sendService.sendText(
|
|
2899
2983
|
sendCtx,
|
|
2900
2984
|
text,
|
|
2901
|
-
isPairing ? undefined : { keyboard:
|
|
2985
|
+
isPairing ? undefined : { keyboard: buildDefaultCommandKeyboard(language) },
|
|
2902
2986
|
);
|
|
2903
2987
|
welcomedDialogs.add(dialogId);
|
|
2904
2988
|
logger.info('Welcome message sent', { dialogId });
|
package/src/i18n.ts
CHANGED
|
@@ -215,3 +215,73 @@ export function onboardingMessage(
|
|
|
215
215
|
? pairingWelcomeMessage(lang, botName)
|
|
216
216
|
: welcomeMessage(lang, botName);
|
|
217
217
|
}
|
|
218
|
+
|
|
219
|
+
interface CommandKeyboardLabels {
|
|
220
|
+
help: string;
|
|
221
|
+
status: string;
|
|
222
|
+
commands: string;
|
|
223
|
+
newSession: string;
|
|
224
|
+
models: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const I18N_COMMAND_KEYBOARD_LABELS: Record<string, CommandKeyboardLabels> = {
|
|
228
|
+
en: {
|
|
229
|
+
help: 'Help',
|
|
230
|
+
status: 'Status',
|
|
231
|
+
commands: 'Commands',
|
|
232
|
+
newSession: 'New session',
|
|
233
|
+
models: 'Models',
|
|
234
|
+
},
|
|
235
|
+
ru: {
|
|
236
|
+
help: 'Справка',
|
|
237
|
+
status: 'Статус',
|
|
238
|
+
commands: 'Команды',
|
|
239
|
+
newSession: 'Новая сессия',
|
|
240
|
+
models: 'Модели',
|
|
241
|
+
},
|
|
242
|
+
de: {
|
|
243
|
+
help: 'Hilfe',
|
|
244
|
+
status: 'Status',
|
|
245
|
+
commands: 'Befehle',
|
|
246
|
+
newSession: 'Neue Sitzung',
|
|
247
|
+
models: 'Modelle',
|
|
248
|
+
},
|
|
249
|
+
es: {
|
|
250
|
+
help: 'Ayuda',
|
|
251
|
+
status: 'Estado',
|
|
252
|
+
commands: 'Comandos',
|
|
253
|
+
newSession: 'Nueva sesion',
|
|
254
|
+
models: 'Modelos',
|
|
255
|
+
},
|
|
256
|
+
fr: {
|
|
257
|
+
help: 'Aide',
|
|
258
|
+
status: 'Statut',
|
|
259
|
+
commands: 'Commandes',
|
|
260
|
+
newSession: 'Nouvelle session',
|
|
261
|
+
models: 'Modeles',
|
|
262
|
+
},
|
|
263
|
+
pt: {
|
|
264
|
+
help: 'Ajuda',
|
|
265
|
+
status: 'Status',
|
|
266
|
+
commands: 'Comandos',
|
|
267
|
+
newSession: 'Nova sessao',
|
|
268
|
+
models: 'Modelos',
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export function commandKeyboardLabels(lang: string | undefined): CommandKeyboardLabels {
|
|
273
|
+
return resolve(I18N_COMMAND_KEYBOARD_LABELS, lang);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const I18N_REPLY_GENERATION_FAILED: Record<string, string> = {
|
|
277
|
+
en: 'I could not prepare a final reply. Please try again. If your request needs web search or external tools, check that they are configured on the server.',
|
|
278
|
+
ru: 'Не удалось подготовить итоговый ответ. Попробуйте повторить запрос. Если вопрос требует веб-поиска или внешних инструментов, проверьте, что они настроены на сервере.',
|
|
279
|
+
de: 'Ich konnte keine abschliessende Antwort vorbereiten. Bitte versuchen Sie es erneut. Wenn Ihre Anfrage Websuche oder externe Tools braucht, pruefen Sie deren Server-Konfiguration.',
|
|
280
|
+
es: 'No pude preparar una respuesta final. Intente de nuevo. Si su solicitud necesita busqueda web o herramientas externas, verifique que esten configuradas en el servidor.',
|
|
281
|
+
fr: 'Je n ai pas pu preparer de reponse finale. Reessayez. Si votre demande a besoin de recherche web ou d outils externes, verifiez leur configuration sur le serveur.',
|
|
282
|
+
pt: 'Nao consegui preparar uma resposta final. Tente novamente. Se sua solicitacao precisa de busca na web ou ferramentas externas, verifique se elas estao configuradas no servidor.',
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export function replyGenerationFailed(lang: string | undefined): string {
|
|
286
|
+
return resolve(I18N_REPLY_GENERATION_FAILED, lang);
|
|
287
|
+
}
|
package/src/message-utils.ts
CHANGED
|
@@ -214,19 +214,26 @@ function isInlineAccentToken(value: string): boolean {
|
|
|
214
214
|
return false;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
// In Bitrix chat UI inline [CODE] is visually heavy, so inline backticks
|
|
218
|
+
// should default to a compact docs accent unless the token clearly looks
|
|
219
|
+
// like an executable/code snippet.
|
|
220
|
+
if (looksLikeInlineCodeSnippet(trimmed)) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
217
224
|
if (isWrappedInlineAccentToken(trimmed)) {
|
|
218
225
|
return true;
|
|
219
226
|
}
|
|
220
227
|
|
|
221
|
-
if (
|
|
222
|
-
return
|
|
228
|
+
if (isCompactInlineAccentAssignment(trimmed)) {
|
|
229
|
+
return true;
|
|
223
230
|
}
|
|
224
231
|
|
|
225
|
-
return
|
|
232
|
+
return isInlineAccentReference(trimmed);
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
function looksLikeInlineCodeSnippet(value: string): boolean {
|
|
229
|
-
if (
|
|
236
|
+
if (/`|;\s*$/.test(value)) {
|
|
230
237
|
return true;
|
|
231
238
|
}
|
|
232
239
|
|
|
@@ -234,13 +241,41 @@ function looksLikeInlineCodeSnippet(value: string): boolean {
|
|
|
234
241
|
return true;
|
|
235
242
|
}
|
|
236
243
|
|
|
237
|
-
|
|
244
|
+
if (/\b(?:const|let|var|function|class|return|await|async|import|export|from|yield|switch|case|if|else|for|while|try|catch|finally|throw|new|delete|typeof|void)\b/.test(value)) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (/\s(?:[+\-*/%<>]=?|===|!==|==|!=|<=|>=|\|\||&&|\?\?)\s/.test(value)) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (looksLikeStructuredInlineSnippet(value)) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return /^(?:curl|wget|npm|pnpm|yarn|bun|node|php|python(?:3)?|ssh|git|docker(?:-compose)?|kubectl|composer)\b.*(?:\||>|<|\$|\s--?[a-z])/i.test(value);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function looksLikeStructuredInlineSnippet(value: string): boolean {
|
|
260
|
+
if (/^(?:\{\}|\[\]|\(\))$/.test(value)) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (/^\{[\s\S]+\}$/.test(value)) {
|
|
265
|
+
return /[:,[\]{}]/.test(value.slice(1, -1));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (/^\[[\s\S]+\]$/.test(value)) {
|
|
269
|
+
return /[:,[\]{}]/.test(value.slice(1, -1));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return false;
|
|
238
273
|
}
|
|
239
274
|
|
|
240
275
|
function isWrappedInlineAccentToken(value: string): boolean {
|
|
241
276
|
const match = value.match(/^([("'`])(.+)([)"'`])$/);
|
|
242
277
|
if (!match) {
|
|
243
|
-
return
|
|
278
|
+
return /^(?:\{\}|\[\]|\(\))$/.test(value);
|
|
244
279
|
}
|
|
245
280
|
|
|
246
281
|
const open = match[1];
|
|
@@ -262,7 +297,59 @@ function isWrappedInlineAccentToken(value: string): boolean {
|
|
|
262
297
|
return false;
|
|
263
298
|
}
|
|
264
299
|
|
|
265
|
-
return
|
|
300
|
+
return isInlineAccentReference(inner);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function isCompactInlineAccentAssignment(value: string): boolean {
|
|
304
|
+
const match = value.match(/^([\p{L}_][\p{L}\p{N}._[\]-]{0,47})\s*=\s*(.+)$/u);
|
|
305
|
+
if (!match) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const left = match[1].trim();
|
|
310
|
+
const right = match[2].trim();
|
|
311
|
+
|
|
312
|
+
if (!right || /\s{2,}/.test(left)) {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (/^(?:const|let|var|return|await|yield|import|export|function|class|new|delete|typeof|void)$/i.test(left)) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (looksLikeInlineCodeSnippet(left)) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return isInlineAccentReference(right);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function isInlineAccentReference(value: string): boolean {
|
|
328
|
+
return isInlineAccentScalar(value) || isInlineAccentCandidate(value) || isWrappedInlineAccentToken(value);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isInlineAccentScalar(value: string): boolean {
|
|
332
|
+
if (!value || value.length > 48) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (/^(?:true|false|null|none|yes|no|on|off|\d+(?:\.\d+)?)$/i.test(value)) {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (/^#(?:[\p{L}\p{N}_-]+|[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/u.test(value)) {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (/^https?:\/\/[^\s]+$/i.test(value)) {
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (/^0x[0-9A-Fa-f]+$/.test(value)) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return false;
|
|
266
353
|
}
|
|
267
354
|
|
|
268
355
|
function isLowercaseNaturalLanguageWord(value: string): boolean {
|
package/src/send-service.ts
CHANGED
|
@@ -109,7 +109,7 @@ export class SendService {
|
|
|
109
109
|
ctx.messageId,
|
|
110
110
|
ctx.commandDialogId ?? ctx.dialogId,
|
|
111
111
|
chunks[0],
|
|
112
|
-
|
|
112
|
+
options?.keyboard
|
|
113
113
|
? { keyboard: options.keyboard }
|
|
114
114
|
: undefined,
|
|
115
115
|
);
|
|
@@ -119,18 +119,13 @@ export class SendService {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
for (let i = 1; i < chunks.length; i++) {
|
|
122
|
-
const isLast = i === chunks.length - 1;
|
|
123
|
-
const msgOptions = isLast && options?.keyboard
|
|
124
|
-
? { keyboard: options.keyboard }
|
|
125
|
-
: undefined;
|
|
126
|
-
|
|
127
122
|
try {
|
|
128
123
|
lastMessageId = await this.api.sendMessage(
|
|
129
124
|
ctx.webhookUrl,
|
|
130
125
|
ctx.bot,
|
|
131
126
|
ctx.dialogId,
|
|
132
127
|
chunks[i],
|
|
133
|
-
|
|
128
|
+
undefined,
|
|
134
129
|
);
|
|
135
130
|
} catch (error) {
|
|
136
131
|
this.logger.error('Failed to send command follow-up message', { error: serializeError(error) });
|