@openclaw/line 2026.7.1-beta.6 → 2026.7.2-beta.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/dist/api.js +1 -1
- package/dist/{card-command--jtkAqp8.js → card-command-DQ-epsgt.js} +7 -3
- package/dist/{channel-C_8hLniz.js → channel-CnPxxRbJ.js} +13 -15
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-FPcRtjcT.js +4 -0
- package/dist/{flex-templates-DEIXn69v.js → flex-templates-K1lS9i8L.js} +2 -3
- package/dist/index.js +1 -1
- package/dist/{markdown-to-line-DmpZY78n.js → markdown-to-line-CixmAKiG.js} +18 -18
- package/dist/{monitor-0eXlx_Pe.js → monitor-q3UE2J1g.js} +15 -69
- package/dist/monitor.runtime-BiNrVz5G.js +2 -0
- package/dist/{outbound.runtime-BwC7Y2U9.js → outbound.runtime-CI-t7Z8P.js} +1 -1
- package/dist/{reply-payload-transform-C7r5npxP.js → reply-payload-transform-BsbwCJGH.js} +61 -35
- package/dist/runtime-api.js +5 -32
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/dist/channel.runtime-Bh_AQM0k.js +0 -4
- package/dist/monitor.runtime-BGIpxzfB.js +0 -2
package/dist/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-
|
|
1
|
+
import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-CnPxxRbJ.js";
|
|
2
2
|
import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-Bc36YSS3.js";
|
|
3
3
|
//#region extensions/line/src/channel.setup.ts
|
|
4
4
|
const lineSetupPlugin = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, m as createImageCard, o as messageAction, s as postbackAction } from "./flex-templates-
|
|
1
|
+
import { c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, m as createImageCard, o as messageAction, s as postbackAction } from "./flex-templates-K1lS9i8L.js";
|
|
2
2
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
4
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
4
5
|
//#region extensions/line/src/card-command.ts
|
|
5
6
|
const CARD_USAGE = `Usage: /card <type> "title" "body" [options]
|
|
6
7
|
|
|
@@ -92,9 +93,12 @@ function parseCardArgs(argsStrInput) {
|
|
|
92
93
|
}
|
|
93
94
|
const quotedRegex = /"([^"]*?)"/g;
|
|
94
95
|
let match;
|
|
95
|
-
while ((match = quotedRegex.exec(argsStr)) !== null) result.args.push(match[1]);
|
|
96
|
+
while ((match = quotedRegex.exec(argsStr)) !== null) result.args.push(expectDefined(match[1], "quoted card argument capture"));
|
|
96
97
|
const flagRegex = /--(\w+)\s+(?:"([^"]*?)"|(\S+))/g;
|
|
97
|
-
while ((match = flagRegex.exec(argsStr)) !== null)
|
|
98
|
+
while ((match = flagRegex.exec(argsStr)) !== null) {
|
|
99
|
+
const key = expectDefined(match[1], "card flag name capture");
|
|
100
|
+
result.flags[key] = expectDefined(match[2] ?? match[3], "card flag value capture");
|
|
101
|
+
}
|
|
98
102
|
return result;
|
|
99
103
|
}
|
|
100
104
|
function registerLineCardCommand(api) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-DJVOv1JI.js";
|
|
2
2
|
import { n as lineSetupAdapter, r as hasLineCredentials, t as lineSetupWizard } from "./setup-surface-Bc36YSS3.js";
|
|
3
|
-
import { a as resolveLineOutboundMedia, d as resolveExactLineGroupConfigKey, i as buildLineQuickReplyFallbackText, l as LineChannelConfigSchema, n as parseLineDirectives, r as createLineSendReceipt, s as getLineRuntime, t as hasLineDirectives } from "./reply-payload-transform-
|
|
3
|
+
import { a as resolveLineOutboundMedia, d as resolveExactLineGroupConfigKey, i as buildLineQuickReplyFallbackText, l as LineChannelConfigSchema, n as parseLineDirectives, r as createLineSendReceipt, s as getLineRuntime, t as hasLineDirectives } from "./reply-payload-transform-BsbwCJGH.js";
|
|
4
4
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
5
5
|
import { buildChannelOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
6
6
|
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
7
|
-
import { createRestrictSendersChannelSecurity,
|
|
7
|
+
import { buildChannelGroupsScopeTree, createRestrictSendersChannelSecurity, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
8
8
|
import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
|
|
9
9
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/account-id";
|
|
10
10
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
|
|
@@ -108,7 +108,7 @@ const lineChannelPluginCommon = {
|
|
|
108
108
|
//#endregion
|
|
109
109
|
//#region extensions/line/src/gateway.ts
|
|
110
110
|
const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-CRhVGqwl.js"));
|
|
111
|
-
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-
|
|
111
|
+
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-BiNrVz5G.js"));
|
|
112
112
|
const lineGatewayAdapter = {
|
|
113
113
|
startAccount: async (ctx) => {
|
|
114
114
|
const account = ctx.account;
|
|
@@ -198,16 +198,14 @@ const lineGatewayAdapter = {
|
|
|
198
198
|
//#endregion
|
|
199
199
|
//#region extensions/line/src/group-policy.ts
|
|
200
200
|
function resolveLineGroupRequireMention(params) {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
const tree = buildChannelGroupsScopeTree(params.cfg, "line", params.accountId);
|
|
202
|
+
const matchedKey = resolveExactLineGroupConfigKey({
|
|
203
|
+
groups: tree.scopes,
|
|
204
204
|
groupId: params.groupId
|
|
205
205
|
});
|
|
206
|
-
return
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
groupId: exactGroupId ?? params.groupId,
|
|
210
|
-
accountId: params.accountId
|
|
206
|
+
return resolveScopeRequireMention({
|
|
207
|
+
tree,
|
|
208
|
+
path: matchedKey ? [matchedKey] : []
|
|
211
209
|
});
|
|
212
210
|
}
|
|
213
211
|
//#endregion
|
|
@@ -225,7 +223,7 @@ function inferLineTargetChatType(target) {
|
|
|
225
223
|
}
|
|
226
224
|
//#endregion
|
|
227
225
|
//#region extensions/line/src/outbound.ts
|
|
228
|
-
const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-
|
|
226
|
+
const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-CI-t7Z8P.js"));
|
|
229
227
|
function isLineUserTarget(target) {
|
|
230
228
|
const normalized = target.trim().replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
|
|
231
229
|
return /^U/i.test(normalized);
|
|
@@ -367,12 +365,12 @@ const lineOutboundAdapter = {
|
|
|
367
365
|
}
|
|
368
366
|
const sendMediaAfterText = !(hasQuickReplies && chunks.length > 0);
|
|
369
367
|
if (mediaUrls.length > 0 && !shouldSendQuickRepliesInline && !sendMediaAfterText) await sendMediaMessages();
|
|
370
|
-
if (chunks.length > 0) for (
|
|
368
|
+
if (chunks.length > 0) for (const [i, chunk] of chunks.entries()) if (i === chunks.length - 1 && hasQuickReplies) await recordResult(sendQuickReplies(to, chunk, quickReplies, {
|
|
371
369
|
verbose: false,
|
|
372
370
|
cfg,
|
|
373
371
|
accountId: accountId ?? void 0
|
|
374
372
|
}));
|
|
375
|
-
else await recordResult(sendText(to,
|
|
373
|
+
else await recordResult(sendText(to, chunk, {
|
|
376
374
|
verbose: false,
|
|
377
375
|
cfg,
|
|
378
376
|
accountId: accountId ?? void 0
|
|
@@ -562,7 +560,7 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
|
|
|
562
560
|
});
|
|
563
561
|
//#endregion
|
|
564
562
|
//#region extensions/line/src/channel.ts
|
|
565
|
-
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-
|
|
563
|
+
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-FPcRtjcT.js"));
|
|
566
564
|
const lineSecurityAdapter = createRestrictSendersChannelSecurity({
|
|
567
565
|
channelKey: "line",
|
|
568
566
|
resolveDmPolicy: (account) => account.config.dmPolicy,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as linePlugin } from "./channel-
|
|
1
|
+
import { t as linePlugin } from "./channel-CnPxxRbJ.js";
|
|
2
2
|
export { linePlugin };
|
|
@@ -1081,8 +1081,7 @@ function createDeviceControlCard(params) {
|
|
|
1081
1081
|
const limitedControls = controls.slice(0, 6);
|
|
1082
1082
|
for (let i = 0; i < limitedControls.length; i += 2) {
|
|
1083
1083
|
const rowButtons = [];
|
|
1084
|
-
for (
|
|
1085
|
-
const ctrl = limitedControls[j];
|
|
1084
|
+
for (const [offset, ctrl] of limitedControls.slice(i, i + 2).entries()) {
|
|
1086
1085
|
const buttonLabel = ctrl.icon ? `${ctrl.icon} ${ctrl.label}` : ctrl.label;
|
|
1087
1086
|
rowButtons.push({
|
|
1088
1087
|
type: "button",
|
|
@@ -1094,7 +1093,7 @@ function createDeviceControlCard(params) {
|
|
|
1094
1093
|
style: ctrl.style ?? "secondary",
|
|
1095
1094
|
flex: 1,
|
|
1096
1095
|
height: "sm",
|
|
1097
|
-
margin:
|
|
1096
|
+
margin: offset > 0 ? "md" : void 0
|
|
1098
1097
|
});
|
|
1099
1098
|
}
|
|
1100
1099
|
if (rowButtons.length === 1) rowButtons.push({ type: "filler" });
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
|
4
4
|
function createLineCardCommandLoader(api) {
|
|
5
5
|
return createLazyRuntimeModule(async () => {
|
|
6
6
|
let registered = null;
|
|
7
|
-
const { registerLineCardCommand } = await import("./card-command
|
|
7
|
+
const { registerLineCardCommand } = await import("./card-command-DQ-epsgt.js");
|
|
8
8
|
registerLineCardCommand({
|
|
9
9
|
...api,
|
|
10
10
|
registerCommand(command) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { i as resolveLineAccount } from "./accounts-DJVOv1JI.js";
|
|
2
|
-
import { o as validateLineMediaUrl, r as createLineSendReceipt } from "./reply-payload-transform-
|
|
3
|
-
import { c as uriAction, d as createReceiptCard, o as messageAction, s as postbackAction, t as toFlexMessage } from "./flex-templates-
|
|
2
|
+
import { o as validateLineMediaUrl, r as createLineSendReceipt } from "./reply-payload-transform-BsbwCJGH.js";
|
|
3
|
+
import { c as uriAction, d as createReceiptCard, o as messageAction, s as postbackAction, t as toFlexMessage } from "./flex-templates-K1lS9i8L.js";
|
|
4
4
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
5
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
5
6
|
import { messagingApi } from "@line/bot-sdk";
|
|
6
7
|
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
7
8
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
@@ -60,14 +61,15 @@ function createConfirmTemplate(text, confirmAction, cancelAction, altText) {
|
|
|
60
61
|
* Create a button template with title, text, and action buttons
|
|
61
62
|
*/
|
|
62
63
|
function createButtonTemplate(title, text, actions, options) {
|
|
64
|
+
const normalizedTitle = title || void 0;
|
|
63
65
|
const textLimit = resolveTemplateTextLimit({
|
|
64
|
-
title,
|
|
66
|
+
title: normalizedTitle,
|
|
65
67
|
thumbnailImageUrl: options?.thumbnailImageUrl,
|
|
66
68
|
textOnlyLimit: 160
|
|
67
69
|
});
|
|
68
70
|
const template = {
|
|
69
71
|
type: "buttons",
|
|
70
|
-
title: truncateTemplateText(
|
|
72
|
+
...normalizedTitle ? { title: truncateTemplateText(normalizedTitle, 40) } : {},
|
|
71
73
|
text: truncateTemplateText(text, textLimit),
|
|
72
74
|
actions: actions.slice(0, 4),
|
|
73
75
|
thumbnailImageUrl: options?.thumbnailImageUrl,
|
|
@@ -78,7 +80,7 @@ function createButtonTemplate(title, text, actions, options) {
|
|
|
78
80
|
};
|
|
79
81
|
return {
|
|
80
82
|
type: "template",
|
|
81
|
-
altText: truncateOptionalTemplateText(options?.altText, 400) ?? truncateTemplateText(`${
|
|
83
|
+
altText: truncateOptionalTemplateText(options?.altText, 400) ?? truncateTemplateText(normalizedTitle ? `${normalizedTitle}: ${text}` : text, 400),
|
|
82
84
|
template
|
|
83
85
|
};
|
|
84
86
|
}
|
|
@@ -224,7 +226,7 @@ function normalizeTarget(to) {
|
|
|
224
226
|
if (!trimmed) throw new Error("Recipient is required for LINE sends");
|
|
225
227
|
const normalized = trimmed.replace(/^line:group:/i, "").replace(/^line:room:/i, "").replace(/^line:user:/i, "").replace(/^line:/i, "");
|
|
226
228
|
if (!normalized) throw new Error("Recipient is required for LINE sends");
|
|
227
|
-
if (normalized.length >= 33 && !/^[CUR]/.test(normalized)) throw new Error(`Recipient is not a valid LINE id (case-sensitive; expected leading capital C/U/R): ${normalized
|
|
229
|
+
if (normalized.length >= 33 && !/^[CUR]/.test(normalized)) throw new Error(`Recipient is not a valid LINE id (case-sensitive; expected leading capital C/U/R): ${truncateUtf16Safe(normalized, 4)}…`);
|
|
228
230
|
return normalized;
|
|
229
231
|
}
|
|
230
232
|
function isLineUserChatId(chatId) {
|
|
@@ -498,9 +500,9 @@ function extractMarkdownTables(text) {
|
|
|
498
500
|
let match;
|
|
499
501
|
const matches = [];
|
|
500
502
|
while ((match = MARKDOWN_TABLE_REGEX.exec(text)) !== null) {
|
|
501
|
-
const fullMatch = match[0];
|
|
502
|
-
const headerLine = match[1];
|
|
503
|
-
const bodyLines = match[2];
|
|
503
|
+
const fullMatch = expectDefined(match[0], "Markdown table match");
|
|
504
|
+
const headerLine = expectDefined(match[1], "Markdown table header capture");
|
|
505
|
+
const bodyLines = expectDefined(match[2], "Markdown table body capture");
|
|
504
506
|
const headers = parseTableRow(headerLine);
|
|
505
507
|
const rows = bodyLines.trim().split(/[\r\n]+/).filter((line) => line.trim()).map(parseTableRow);
|
|
506
508
|
if (headers.length > 0 && rows.length > 0) matches.push({
|
|
@@ -511,8 +513,7 @@ function extractMarkdownTables(text) {
|
|
|
511
513
|
}
|
|
512
514
|
});
|
|
513
515
|
}
|
|
514
|
-
for (
|
|
515
|
-
const { fullMatch, table } = matches[i];
|
|
516
|
+
for (const { fullMatch, table } of matches.toReversed()) {
|
|
516
517
|
tables.unshift(table);
|
|
517
518
|
textWithoutTables = textWithoutTables.replace(fullMatch, "");
|
|
518
519
|
}
|
|
@@ -627,9 +628,9 @@ function extractCodeBlocks(text) {
|
|
|
627
628
|
let match;
|
|
628
629
|
const matches = [];
|
|
629
630
|
while ((match = MARKDOWN_CODE_BLOCK_REGEX.exec(text)) !== null) {
|
|
630
|
-
const fullMatch = match[0];
|
|
631
|
+
const fullMatch = expectDefined(match[0], "Markdown code block match");
|
|
631
632
|
const language = match[1] || void 0;
|
|
632
|
-
const code = match[2];
|
|
633
|
+
const code = expectDefined(match[2], "Markdown code body capture");
|
|
633
634
|
matches.push({
|
|
634
635
|
fullMatch,
|
|
635
636
|
block: {
|
|
@@ -638,8 +639,7 @@ function extractCodeBlocks(text) {
|
|
|
638
639
|
}
|
|
639
640
|
});
|
|
640
641
|
}
|
|
641
|
-
for (
|
|
642
|
-
const { fullMatch, block } = matches[i];
|
|
642
|
+
for (const { fullMatch, block } of matches.toReversed()) {
|
|
643
643
|
codeBlocks.unshift(block);
|
|
644
644
|
textWithoutCode = textWithoutCode.replace(fullMatch, "");
|
|
645
645
|
}
|
|
@@ -692,8 +692,8 @@ function extractLinks(text) {
|
|
|
692
692
|
MARKDOWN_LINK_REGEX.lastIndex = 0;
|
|
693
693
|
let match;
|
|
694
694
|
while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) links.push({
|
|
695
|
-
text: match[1],
|
|
696
|
-
url: match[2]
|
|
695
|
+
text: expectDefined(match[1], "Markdown link text capture"),
|
|
696
|
+
url: expectDefined(match[2], "Markdown link URL capture")
|
|
697
697
|
});
|
|
698
698
|
return {
|
|
699
699
|
links,
|
|
@@ -756,7 +756,7 @@ function processLineMessage(text) {
|
|
|
756
756
|
}
|
|
757
757
|
const { textWithLinks } = extractLinks(processedText);
|
|
758
758
|
processedText = textWithLinks;
|
|
759
|
-
processedText = stripMarkdown(processedText);
|
|
759
|
+
processedText = stripMarkdown(processedText, { assistantTranscriptRoleHeaders: true });
|
|
760
760
|
return {
|
|
761
761
|
text: processedText,
|
|
762
762
|
flexMessages
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-DJVOv1JI.js";
|
|
2
|
-
import { f as resolveLineGroupConfigEntry, i as buildLineQuickReplyFallbackText, s as getLineRuntime } from "./reply-payload-transform-
|
|
3
|
-
import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, E as replyMessageLine, O as showLoadingAnimation, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, c as processLineMessage, d as createFlexMessage, f as createImageMessage, h as createTextMessageWithQuickReplies, m as createQuickReplyItems, p as createLocationMessage } from "./markdown-to-line-
|
|
2
|
+
import { f as resolveLineGroupConfigEntry, i as buildLineQuickReplyFallbackText, s as getLineRuntime } from "./reply-payload-transform-BsbwCJGH.js";
|
|
3
|
+
import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, E as replyMessageLine, O as showLoadingAnimation, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, c as processLineMessage, d as createFlexMessage, f as createImageMessage, h as createTextMessageWithQuickReplies, m as createQuickReplyItems, p as createLocationMessage } from "./markdown-to-line-CixmAKiG.js";
|
|
4
4
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
5
5
|
import { normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import { createMessageReceiveContext } from "openclaw/plugin-sdk/channel-outbound";
|
|
7
7
|
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
8
8
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
9
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
9
10
|
import { firstDefined } from "openclaw/plugin-sdk/allow-from";
|
|
10
11
|
import { messagingApi } from "@line/bot-sdk";
|
|
11
12
|
import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
@@ -25,6 +26,7 @@ import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/p
|
|
|
25
26
|
import { resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
26
27
|
import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
|
27
28
|
import crypto from "node:crypto";
|
|
29
|
+
import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
28
30
|
//#region extensions/line/src/bot-access.ts
|
|
29
31
|
function normalizeLineAllowEntry(value) {
|
|
30
32
|
const trimmed = String(value).trim();
|
|
@@ -403,7 +405,7 @@ async function finalizeLineInboundContext(params) {
|
|
|
403
405
|
sessionKey: params.route.sessionKey
|
|
404
406
|
});
|
|
405
407
|
if (shouldLogVerbose()) {
|
|
406
|
-
const preview = body
|
|
408
|
+
const preview = truncateUtf16Safe(body, 200).replace(/\n/g, "\\n");
|
|
407
409
|
const mediaInfo = params.verboseLog.kind === "inbound" && (params.verboseLog.mediaCount ?? 0) > 1 ? ` mediaCount=${params.verboseLog.mediaCount}` : "";
|
|
408
410
|
logVerbose(`${params.verboseLog.kind === "inbound" ? "line inbound" : "line postback"}: from=${ctxPayload.From} len=${body.length}${mediaInfo} preview="${preview}"`);
|
|
409
411
|
}
|
|
@@ -584,12 +586,6 @@ const LINE_WEBHOOK_REPLAY_MAX_ENTRIES = 4096;
|
|
|
584
586
|
function normalizeLineIngressEntry(value) {
|
|
585
587
|
return normalizeLineAllowEntry(value) || null;
|
|
586
588
|
}
|
|
587
|
-
var LineRetryableWebhookError = class extends Error {
|
|
588
|
-
constructor(message, options) {
|
|
589
|
-
super(message, options);
|
|
590
|
-
this.name = "LineRetryableWebhookError";
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
589
|
function createLineWebhookReplayCache() {
|
|
594
590
|
return createClaimableDedupe({
|
|
595
591
|
ttlMs: LINE_WEBHOOK_REPLAY_WINDOW_MS,
|
|
@@ -710,7 +706,7 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
710
706
|
defaultGroupPolicy: resolveDefaultGroupPolicy(cfg)
|
|
711
707
|
});
|
|
712
708
|
const groupPolicy = runtimeGroupPolicy === "disabled" ? "disabled" : groupConfig?.allowFrom !== void 0 ? "allowlist" : runtimeGroupPolicy;
|
|
713
|
-
const groupAllowFrom = normalizeStringEntries(firstDefined(groupConfig?.allowFrom, account.config.groupAllowFrom
|
|
709
|
+
const groupAllowFrom = normalizeStringEntries(firstDefined(groupConfig?.allowFrom, account.config.groupAllowFrom));
|
|
714
710
|
const mentionFacts = (() => {
|
|
715
711
|
if (!isGroup || event.type !== "message") return {
|
|
716
712
|
canDetectMention: false,
|
|
@@ -979,8 +975,7 @@ async function handleLineWebhookEvents(events, context) {
|
|
|
979
975
|
}
|
|
980
976
|
if (replayCandidate) await replayCandidate.cache.commit(replayCandidate.key);
|
|
981
977
|
} catch (err) {
|
|
982
|
-
if (replayCandidate)
|
|
983
|
-
else await replayCandidate.cache.commit(replayCandidate.key);
|
|
978
|
+
if (replayCandidate) await replayCandidate.cache.commit(replayCandidate.key);
|
|
984
979
|
context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`));
|
|
985
980
|
firstError ??= err;
|
|
986
981
|
}
|
|
@@ -1044,7 +1039,7 @@ function resolveLineDurableReplyOptions(params) {
|
|
|
1044
1039
|
//#endregion
|
|
1045
1040
|
//#region extensions/line/src/reply-chunks.ts
|
|
1046
1041
|
async function sendLineReplyChunks(params) {
|
|
1047
|
-
const
|
|
1042
|
+
const quickReplies = params.quickReplies?.length ? params.quickReplies : void 0;
|
|
1048
1043
|
let replyTokenUsed = Boolean(params.replyTokenUsed);
|
|
1049
1044
|
if (params.chunks.length === 0) return { replyTokenUsed };
|
|
1050
1045
|
if (params.replyToken && !replyTokenUsed) try {
|
|
@@ -1054,20 +1049,20 @@ async function sendLineReplyChunks(params) {
|
|
|
1054
1049
|
type: "text",
|
|
1055
1050
|
text: chunk
|
|
1056
1051
|
}));
|
|
1057
|
-
if (
|
|
1052
|
+
if (quickReplies && remaining.length === 0 && replyMessages.length > 0) {
|
|
1058
1053
|
const lastIndex = replyMessages.length - 1;
|
|
1059
|
-
replyMessages[lastIndex] = params.createTextMessageWithQuickReplies(replyBatch[lastIndex],
|
|
1054
|
+
replyMessages[lastIndex] = params.createTextMessageWithQuickReplies(expectDefined(replyBatch[lastIndex], "last non-empty LINE reply batch chunk"), quickReplies);
|
|
1060
1055
|
}
|
|
1061
1056
|
await params.replyMessageLine(params.replyToken, replyMessages, {
|
|
1062
1057
|
cfg: params.cfg,
|
|
1063
1058
|
accountId: params.accountId
|
|
1064
1059
|
});
|
|
1065
1060
|
replyTokenUsed = true;
|
|
1066
|
-
for (
|
|
1061
|
+
for (const [i, chunk] of remaining.entries()) if (i === remaining.length - 1 && quickReplies) await params.pushTextMessageWithQuickReplies(params.to, chunk, quickReplies, {
|
|
1067
1062
|
cfg: params.cfg,
|
|
1068
1063
|
accountId: params.accountId
|
|
1069
1064
|
});
|
|
1070
|
-
else await params.pushMessageLine(params.to,
|
|
1065
|
+
else await params.pushMessageLine(params.to, chunk, {
|
|
1071
1066
|
cfg: params.cfg,
|
|
1072
1067
|
accountId: params.accountId
|
|
1073
1068
|
});
|
|
@@ -1076,11 +1071,11 @@ async function sendLineReplyChunks(params) {
|
|
|
1076
1071
|
params.onReplyError?.(err);
|
|
1077
1072
|
replyTokenUsed = true;
|
|
1078
1073
|
}
|
|
1079
|
-
for (
|
|
1074
|
+
for (const [i, chunk] of params.chunks.entries()) if (i === params.chunks.length - 1 && quickReplies) await params.pushTextMessageWithQuickReplies(params.to, chunk, quickReplies, {
|
|
1080
1075
|
cfg: params.cfg,
|
|
1081
1076
|
accountId: params.accountId
|
|
1082
1077
|
});
|
|
1083
|
-
else await params.pushMessageLine(params.to,
|
|
1078
|
+
else await params.pushMessageLine(params.to, chunk, {
|
|
1084
1079
|
cfg: params.cfg,
|
|
1085
1080
|
accountId: params.accountId
|
|
1086
1081
|
});
|
|
@@ -1089,16 +1084,7 @@ async function sendLineReplyChunks(params) {
|
|
|
1089
1084
|
//#endregion
|
|
1090
1085
|
//#region extensions/line/src/signature.ts
|
|
1091
1086
|
function validateLineSignature(body, signature, channelSecret) {
|
|
1092
|
-
|
|
1093
|
-
const hashBuffer = Buffer.from(hash);
|
|
1094
|
-
const signatureBuffer = Buffer.from(signature);
|
|
1095
|
-
const maxLen = Math.max(hashBuffer.length, signatureBuffer.length);
|
|
1096
|
-
const paddedHash = Buffer.alloc(maxLen);
|
|
1097
|
-
const paddedSig = Buffer.alloc(maxLen);
|
|
1098
|
-
hashBuffer.copy(paddedHash);
|
|
1099
|
-
signatureBuffer.copy(paddedSig);
|
|
1100
|
-
const timingResult = crypto.timingSafeEqual(paddedHash, paddedSig);
|
|
1101
|
-
return hashBuffer.length === signatureBuffer.length && timingResult;
|
|
1087
|
+
return safeEqualSecret(signature, crypto.createHmac("SHA256", channelSecret).update(body).digest("base64"));
|
|
1102
1088
|
}
|
|
1103
1089
|
//#endregion
|
|
1104
1090
|
//#region extensions/line/src/webhook-utils.ts
|
|
@@ -1213,24 +1199,10 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1213
1199
|
}
|
|
1214
1200
|
//#endregion
|
|
1215
1201
|
//#region extensions/line/src/monitor.ts
|
|
1216
|
-
const runtimeState = /* @__PURE__ */ new Map();
|
|
1217
1202
|
const lineWebhookInFlightLimiter = createWebhookInFlightLimiter();
|
|
1218
1203
|
const LINE_WEBHOOK_PREAUTH_MAX_BODY_BYTES = 64 * 1024;
|
|
1219
1204
|
const LINE_WEBHOOK_PREAUTH_BODY_TIMEOUT_MS = 5e3;
|
|
1220
1205
|
const lineWebhookTargets = /* @__PURE__ */ new Map();
|
|
1221
|
-
function recordChannelRuntimeState(params) {
|
|
1222
|
-
const key = `${params.channel}:${params.accountId}`;
|
|
1223
|
-
const existing = runtimeState.get(key) ?? {
|
|
1224
|
-
running: false,
|
|
1225
|
-
lastStartAt: null,
|
|
1226
|
-
lastStopAt: null,
|
|
1227
|
-
lastError: null
|
|
1228
|
-
};
|
|
1229
|
-
runtimeState.set(key, {
|
|
1230
|
-
...existing,
|
|
1231
|
-
...params.state
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1234
1206
|
function startLineLoadingKeepalive(params) {
|
|
1235
1207
|
const intervalMs = params.intervalMs ?? 18e3;
|
|
1236
1208
|
const loadingSeconds = params.loadingSeconds ?? 20;
|
|
@@ -1267,11 +1239,6 @@ async function monitorLineProvider(opts) {
|
|
|
1267
1239
|
onMessage: async (ctx) => {
|
|
1268
1240
|
if (!ctx) return;
|
|
1269
1241
|
const { ctxPayload, replyToken, route } = ctx;
|
|
1270
|
-
recordChannelRuntimeState({
|
|
1271
|
-
channel: "line",
|
|
1272
|
-
accountId: resolvedAccountId,
|
|
1273
|
-
state: { lastInboundAt: Date.now() }
|
|
1274
|
-
});
|
|
1275
1242
|
const shouldShowLoading = Boolean(ctx.userId && !ctx.isGroup);
|
|
1276
1243
|
const displayNamePromise = ctx.userId ? getUserDisplayName(ctx.userId, {
|
|
1277
1244
|
cfg: config,
|
|
@@ -1352,11 +1319,6 @@ async function monitorLineProvider(opts) {
|
|
|
1352
1319
|
});
|
|
1353
1320
|
replyTokenUsed = deliveryResult.replyTokenUsed;
|
|
1354
1321
|
if (deliveryResult.status === "partial") throw deliveryResult.error;
|
|
1355
|
-
recordChannelRuntimeState({
|
|
1356
|
-
channel: "line",
|
|
1357
|
-
accountId: resolvedAccountId,
|
|
1358
|
-
state: { lastOutboundAt: Date.now() }
|
|
1359
|
-
});
|
|
1360
1322
|
},
|
|
1361
1323
|
onError: (err, info) => {
|
|
1362
1324
|
runtime.error?.(danger(`line ${info.kind} reply failed: ${String(err)}`));
|
|
@@ -1491,14 +1453,6 @@ async function monitorLineProvider(opts) {
|
|
|
1491
1453
|
}
|
|
1492
1454
|
}
|
|
1493
1455
|
});
|
|
1494
|
-
recordChannelRuntimeState({
|
|
1495
|
-
channel: "line",
|
|
1496
|
-
accountId: resolvedAccountId,
|
|
1497
|
-
state: {
|
|
1498
|
-
running: true,
|
|
1499
|
-
lastStartAt: Date.now()
|
|
1500
|
-
}
|
|
1501
|
-
});
|
|
1502
1456
|
logVerbose(`line: registered webhook handler at ${normalizedPath}`);
|
|
1503
1457
|
let stopped = false;
|
|
1504
1458
|
const stopHandler = () => {
|
|
@@ -1506,14 +1460,6 @@ async function monitorLineProvider(opts) {
|
|
|
1506
1460
|
stopped = true;
|
|
1507
1461
|
logVerbose(`line: stopping provider for account ${resolvedAccountId}`);
|
|
1508
1462
|
unregisterHttp();
|
|
1509
|
-
recordChannelRuntimeState({
|
|
1510
|
-
channel: "line",
|
|
1511
|
-
accountId: resolvedAccountId,
|
|
1512
|
-
state: {
|
|
1513
|
-
running: false,
|
|
1514
|
-
lastStopAt: Date.now()
|
|
1515
|
-
}
|
|
1516
|
-
});
|
|
1517
1463
|
};
|
|
1518
1464
|
if (abortSignal?.aborted) stopHandler();
|
|
1519
1465
|
else if (abortSignal) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, D as sendMessageLine, S as pushMessageLine, T as pushTextMessageWithQuickReplies, c as processLineMessage, m as createQuickReplyItems, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage } from "./markdown-to-line-
|
|
1
|
+
import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, D as sendMessageLine, S as pushMessageLine, T as pushTextMessageWithQuickReplies, c as processLineMessage, m as createQuickReplyItems, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage } from "./markdown-to-line-CixmAKiG.js";
|
|
2
2
|
export { buildTemplateMessageFromPayload, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as createMediaPlayerCard, l as createAgendaCard, n as createAppleTvRemoteCard, r as createDeviceControlCard, u as createEventCard } from "./flex-templates-
|
|
1
|
+
import { i as createMediaPlayerCard, l as createAgendaCard, n as createAppleTvRemoteCard, r as createDeviceControlCard, u as createEventCard } from "./flex-templates-K1lS9i8L.js";
|
|
2
2
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
3
3
|
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
4
4
|
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -8,6 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
9
9
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
10
10
|
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
11
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
11
12
|
import { parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
12
13
|
//#region extensions/line/src/group-keys.ts
|
|
13
14
|
function resolveLineGroupLookupIds(groupId) {
|
|
@@ -42,7 +43,7 @@ function resolveLineGroupsConfig(cfg, accountId) {
|
|
|
42
43
|
return resolveAccountEntry(lineConfig.accounts, normalizedAccountId)?.groups ?? lineConfig.groups;
|
|
43
44
|
}
|
|
44
45
|
function resolveExactLineGroupConfigKey(params) {
|
|
45
|
-
const groups =
|
|
46
|
+
const { groups } = params;
|
|
46
47
|
if (!groups) return;
|
|
47
48
|
return resolveLineGroupLookupIds(params.groupId).find((candidate) => Object.hasOwn(groups, candidate));
|
|
48
49
|
}
|
|
@@ -116,7 +117,7 @@ const LineConfigSchema = LineCommonConfigSchemaBase.extend({
|
|
|
116
117
|
const LineChannelConfigSchema = buildChannelConfigSchema(LineConfigSchema);
|
|
117
118
|
//#endregion
|
|
118
119
|
//#region extensions/line/src/runtime.ts
|
|
119
|
-
const { setRuntime: setLineRuntime,
|
|
120
|
+
const { setRuntime: setLineRuntime, getRuntime: getLineRuntime } = createPluginRuntimeStore({
|
|
120
121
|
pluginId: "line",
|
|
121
122
|
errorMessage: "LINE runtime not initialized - plugin not registered"
|
|
122
123
|
});
|
|
@@ -223,17 +224,31 @@ function parseLineDirectives(payload) {
|
|
|
223
224
|
if (extras) for (const [key, value] of Object.entries(extras)) base.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
224
225
|
return base.join("&");
|
|
225
226
|
};
|
|
227
|
+
const parseConfirmAction = (part) => {
|
|
228
|
+
const colonIndex = part.indexOf(":");
|
|
229
|
+
if (colonIndex === -1) return {
|
|
230
|
+
label: part,
|
|
231
|
+
data: normalizeLowercaseStringOrEmpty(part)
|
|
232
|
+
};
|
|
233
|
+
return {
|
|
234
|
+
label: part.slice(0, colonIndex).trim(),
|
|
235
|
+
data: part.slice(colonIndex + 1).trim()
|
|
236
|
+
};
|
|
237
|
+
};
|
|
226
238
|
const quickRepliesMatch = text.match(/\[\[quick_replies:\s*([^\]]+)\]\]/i);
|
|
227
239
|
if (quickRepliesMatch) {
|
|
228
|
-
const options = normalizeStringEntries(quickRepliesMatch[1].split(","));
|
|
240
|
+
const options = normalizeStringEntries(expectDefined(quickRepliesMatch[1], "quick replies directive body").split(","));
|
|
229
241
|
if (options.length > 0) lineData.quickReplies = [...lineData.quickReplies || [], ...options];
|
|
230
242
|
text = text.replace(quickRepliesMatch[0], "").trim();
|
|
231
243
|
}
|
|
232
244
|
const locationMatch = text.match(/\[\[location:\s*([^\]]+)\]\]/i);
|
|
233
245
|
if (locationMatch && !lineData.location) {
|
|
234
|
-
const parts = locationMatch[1].split("|").map((s) => s.trim());
|
|
246
|
+
const parts = expectDefined(locationMatch[1], "location directive body").split("|").map((s) => s.trim());
|
|
235
247
|
if (parts.length >= 4) {
|
|
236
|
-
const
|
|
248
|
+
const title = expectDefined(parts[0], "location title field");
|
|
249
|
+
const address = expectDefined(parts[1], "location address field");
|
|
250
|
+
const latStr = expectDefined(parts[2], "location latitude field");
|
|
251
|
+
const lonStr = expectDefined(parts[3], "location longitude field");
|
|
237
252
|
const latitude = parseStrictFiniteNumber(latStr);
|
|
238
253
|
const longitude = parseStrictFiniteNumber(lonStr);
|
|
239
254
|
if (latitude !== void 0 && longitude !== void 0) lineData.location = {
|
|
@@ -247,18 +262,20 @@ function parseLineDirectives(payload) {
|
|
|
247
262
|
}
|
|
248
263
|
const confirmMatch = text.match(/\[\[confirm:\s*([^\]]+)\]\]/i);
|
|
249
264
|
if (confirmMatch && !lineData.templateMessage) {
|
|
250
|
-
const parts = confirmMatch[1].split("|").map((s) => s.trim());
|
|
265
|
+
const parts = expectDefined(confirmMatch[1], "confirm directive body").split("|").map((s) => s.trim());
|
|
251
266
|
if (parts.length >= 3) {
|
|
252
|
-
const
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
|
|
267
|
+
const question = expectDefined(parts[0], "confirm question field");
|
|
268
|
+
const yesPart = expectDefined(parts[1], "confirm yes field");
|
|
269
|
+
const noPart = expectDefined(parts[2], "confirm no field");
|
|
270
|
+
const yesAction = parseConfirmAction(yesPart);
|
|
271
|
+
const noAction = parseConfirmAction(noPart);
|
|
272
|
+
if (question && yesAction.label && noAction.label) lineData.templateMessage = {
|
|
256
273
|
type: "confirm",
|
|
257
274
|
text: question,
|
|
258
|
-
confirmLabel:
|
|
259
|
-
confirmData:
|
|
260
|
-
cancelLabel:
|
|
261
|
-
cancelData:
|
|
275
|
+
confirmLabel: yesAction.label,
|
|
276
|
+
confirmData: yesAction.data,
|
|
277
|
+
cancelLabel: noAction.label,
|
|
278
|
+
cancelData: noAction.data,
|
|
262
279
|
altText: question
|
|
263
280
|
};
|
|
264
281
|
}
|
|
@@ -266,10 +283,11 @@ function parseLineDirectives(payload) {
|
|
|
266
283
|
}
|
|
267
284
|
const buttonsMatch = text.match(/\[\[buttons:\s*([^\]]+)\]\]/i);
|
|
268
285
|
if (buttonsMatch && !lineData.templateMessage) {
|
|
269
|
-
const parts = buttonsMatch[1].split("|").map((s) => s.trim());
|
|
286
|
+
const parts = expectDefined(buttonsMatch[1], "buttons directive body").split("|").map((s) => s.trim());
|
|
270
287
|
if (parts.length >= 3) {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
288
|
+
const title = expectDefined(parts[0], "buttons title field");
|
|
289
|
+
const bodyText = expectDefined(parts[1], "buttons text field");
|
|
290
|
+
const actions = expectDefined(parts[2], "buttons actions field").split(",").map((actionStr) => {
|
|
273
291
|
const trimmed = actionStr.trim();
|
|
274
292
|
const colonIndex = (() => {
|
|
275
293
|
const index = trimmed.indexOf(":");
|
|
@@ -302,22 +320,23 @@ function parseLineDirectives(payload) {
|
|
|
302
320
|
label,
|
|
303
321
|
data: data || label
|
|
304
322
|
};
|
|
305
|
-
});
|
|
306
|
-
if (actions.length > 0) lineData.templateMessage = {
|
|
323
|
+
}).filter((action) => action.label);
|
|
324
|
+
if (actions.length > 0 && bodyText) lineData.templateMessage = {
|
|
307
325
|
type: "buttons",
|
|
308
|
-
title,
|
|
326
|
+
...title ? { title } : {},
|
|
309
327
|
text: bodyText,
|
|
310
328
|
actions: actions.slice(0, 4),
|
|
311
|
-
altText: `${title}: ${bodyText}`
|
|
329
|
+
altText: title ? `${title}: ${bodyText}` : bodyText
|
|
312
330
|
};
|
|
313
331
|
}
|
|
314
332
|
text = text.replace(buttonsMatch[0], "").trim();
|
|
315
333
|
}
|
|
316
334
|
const mediaPlayerMatch = text.match(/\[\[media_player:\s*([^\]]+)\]\]/i);
|
|
317
335
|
if (mediaPlayerMatch && !lineData.flexMessage) {
|
|
318
|
-
const parts = mediaPlayerMatch[1].split("|").map((s) => s.trim());
|
|
336
|
+
const parts = expectDefined(mediaPlayerMatch[1], "media player directive body").split("|").map((s) => s.trim());
|
|
319
337
|
if (parts.length >= 1) {
|
|
320
|
-
const
|
|
338
|
+
const title = expectDefined(parts[0], "media player title field");
|
|
339
|
+
const [, artist, source, imageUrl, statusStr] = parts;
|
|
321
340
|
const isPlaying = normalizeLowercaseStringOrEmpty(statusStr) === "playing";
|
|
322
341
|
const validImageUrl = imageUrl?.startsWith("https://") ? imageUrl : void 0;
|
|
323
342
|
const deviceKey = toSlug(source || title || "media");
|
|
@@ -343,9 +362,13 @@ function parseLineDirectives(payload) {
|
|
|
343
362
|
}
|
|
344
363
|
const eventMatch = text.match(/\[\[event:\s*([^\]]+)\]\]/i);
|
|
345
364
|
if (eventMatch && !lineData.flexMessage) {
|
|
346
|
-
const parts = eventMatch[1].split("|").map((s) => s.trim());
|
|
365
|
+
const parts = expectDefined(eventMatch[1], "event directive body").split("|").map((s) => s.trim());
|
|
347
366
|
if (parts.length >= 2) {
|
|
348
|
-
const
|
|
367
|
+
const title = expectDefined(parts[0], "event title field");
|
|
368
|
+
const date = expectDefined(parts[1], "event date field");
|
|
369
|
+
const time = parts[2];
|
|
370
|
+
const location = parts[3];
|
|
371
|
+
const description = parts[4];
|
|
349
372
|
const card = createEventCard({
|
|
350
373
|
title: title || "Event",
|
|
351
374
|
date: date || "TBD",
|
|
@@ -362,9 +385,10 @@ function parseLineDirectives(payload) {
|
|
|
362
385
|
}
|
|
363
386
|
const appleTvMatch = text.match(/\[\[appletv_remote:\s*([^\]]+)\]\]/i);
|
|
364
387
|
if (appleTvMatch && !lineData.flexMessage) {
|
|
365
|
-
const parts = appleTvMatch[1].split("|").map((s) => s.trim());
|
|
388
|
+
const parts = expectDefined(appleTvMatch[1], "Apple TV directive body").split("|").map((s) => s.trim());
|
|
366
389
|
if (parts.length >= 1) {
|
|
367
|
-
const
|
|
390
|
+
const deviceName = expectDefined(parts[0], "Apple TV device name field");
|
|
391
|
+
const [, status] = parts;
|
|
368
392
|
const deviceKey = toSlug(deviceName || "apple_tv");
|
|
369
393
|
const card = createAppleTvRemoteCard({
|
|
370
394
|
deviceName: deviceName || "Apple TV",
|
|
@@ -393,10 +417,10 @@ function parseLineDirectives(payload) {
|
|
|
393
417
|
}
|
|
394
418
|
const agendaMatch = text.match(/\[\[agenda:\s*([^\]]+)\]\]/i);
|
|
395
419
|
if (agendaMatch && !lineData.flexMessage) {
|
|
396
|
-
const parts = agendaMatch[1].split("|").map((s) => s.trim());
|
|
420
|
+
const parts = expectDefined(agendaMatch[1], "agenda directive body").split("|").map((s) => s.trim());
|
|
397
421
|
if (parts.length >= 2) {
|
|
398
|
-
const
|
|
399
|
-
const events =
|
|
422
|
+
const title = expectDefined(parts[0], "agenda title field");
|
|
423
|
+
const events = expectDefined(parts[1], "agenda events field").split(",").map((eventStr) => {
|
|
400
424
|
const trimmed = eventStr.trim();
|
|
401
425
|
const colonIdx = trimmed.lastIndexOf(":");
|
|
402
426
|
if (colonIdx > 0) return {
|
|
@@ -418,13 +442,15 @@ function parseLineDirectives(payload) {
|
|
|
418
442
|
}
|
|
419
443
|
const deviceMatch = text.match(/\[\[device:\s*([^\]]+)\]\]/i);
|
|
420
444
|
if (deviceMatch && !lineData.flexMessage) {
|
|
421
|
-
const parts = deviceMatch[1].split("|").map((s) => s.trim());
|
|
445
|
+
const parts = expectDefined(deviceMatch[1], "device directive body").split("|").map((s) => s.trim());
|
|
422
446
|
if (parts.length >= 1) {
|
|
423
|
-
const
|
|
447
|
+
const deviceName = expectDefined(parts[0], "device name field");
|
|
448
|
+
const [, deviceType, status, controlsStr] = parts;
|
|
424
449
|
const deviceKey = toSlug(deviceName || "device");
|
|
425
450
|
const controls = controlsStr ? controlsStr.split(",").map((ctrlStr) => {
|
|
426
|
-
const
|
|
427
|
-
const
|
|
451
|
+
const controlParts = ctrlStr.split(":").map((s) => s.trim());
|
|
452
|
+
const label = expectDefined(controlParts[0], "device control label");
|
|
453
|
+
const action = controlParts[1] || normalizeLowercaseStringOrEmpty(label).replace(/\s+/g, "_");
|
|
428
454
|
return {
|
|
429
455
|
label,
|
|
430
456
|
data: lineActionData(action, { "line.device": deviceKey })
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as resolveLineAccount, n as normalizeAccountId, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-DJVOv1JI.js";
|
|
2
|
-
import { c as setLineRuntime, d as resolveExactLineGroupConfigKey, f as resolveLineGroupConfigEntry, l as LineChannelConfigSchema, m as resolveLineGroupsConfig, n as parseLineDirectives, p as resolveLineGroupLookupIds, t as hasLineDirectives, u as LineConfigSchema } from "./reply-payload-transform-
|
|
3
|
-
import { _ as createNotificationBubble, a as datetimePickerAction, c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, i as createMediaPlayerCard, l as createAgendaCard, m as createImageCard, n as createAppleTvRemoteCard, o as messageAction, p as createCarousel, r as createDeviceControlCard, s as postbackAction, t as toFlexMessage, u as createEventCard } from "./flex-templates-
|
|
4
|
-
import { a as validateLineSignature, c as normalizeAllowFrom, i as parseLineWebhookBody, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider } from "./monitor-
|
|
2
|
+
import { c as setLineRuntime, d as resolveExactLineGroupConfigKey, f as resolveLineGroupConfigEntry, l as LineChannelConfigSchema, m as resolveLineGroupsConfig, n as parseLineDirectives, p as resolveLineGroupLookupIds, t as hasLineDirectives, u as LineConfigSchema } from "./reply-payload-transform-BsbwCJGH.js";
|
|
3
|
+
import { _ as createNotificationBubble, a as datetimePickerAction, c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, i as createMediaPlayerCard, l as createAgendaCard, m as createImageCard, n as createAppleTvRemoteCard, o as messageAction, p as createCarousel, r as createDeviceControlCard, s as postbackAction, t as toFlexMessage, u as createEventCard } from "./flex-templates-K1lS9i8L.js";
|
|
4
|
+
import { a as validateLineSignature, c as normalizeAllowFrom, i as parseLineWebhookBody, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider } from "./monitor-q3UE2J1g.js";
|
|
5
5
|
import { t as probeLineBot } from "./probe-BslD77tJ.js";
|
|
6
|
-
import { A as buildTemplateMessageFromPayload, B as createYesNoConfirm, C as pushMessagesLine, D as sendMessageLine, E as replyMessageLine, F as createImageCarousel, I as createImageCarouselColumn, L as createLinkMenu, M as createButtonTemplate, N as createCarouselColumn, O as showLoadingAnimation, P as createConfirmTemplate, R as createProductCarousel, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, a as extractLinks, b as pushImageMessage, c as processLineMessage, d as createFlexMessage, f as createImageMessage, g as createVideoMessage, h as createTextMessageWithQuickReplies, i as extractCodeBlocks, j as createButtonMenu, k as resolveLineChannelAccessToken, l as stripMarkdown, m as createQuickReplyItems, n as convertLinksToFlexBubble, o as extractMarkdownTables, p as createLocationMessage, r as convertTableToFlexBubble, s as hasMarkdownToConvert, t as convertCodeBlockToFlexBubble, u as createAudioMessage, v as getUserProfile, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage, z as createTemplateCarousel } from "./markdown-to-line-
|
|
6
|
+
import { A as buildTemplateMessageFromPayload, B as createYesNoConfirm, C as pushMessagesLine, D as sendMessageLine, E as replyMessageLine, F as createImageCarousel, I as createImageCarouselColumn, L as createLinkMenu, M as createButtonTemplate, N as createCarouselColumn, O as showLoadingAnimation, P as createConfirmTemplate, R as createProductCarousel, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, a as extractLinks, b as pushImageMessage, c as processLineMessage, d as createFlexMessage, f as createImageMessage, g as createVideoMessage, h as createTextMessageWithQuickReplies, i as extractCodeBlocks, j as createButtonMenu, k as resolveLineChannelAccessToken, l as stripMarkdown, m as createQuickReplyItems, n as convertLinksToFlexBubble, o as extractMarkdownTables, p as createLocationMessage, r as convertTableToFlexBubble, s as hasMarkdownToConvert, t as convertCodeBlockToFlexBubble, u as createAudioMessage, v as getUserProfile, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage, z as createTemplateCarousel } from "./markdown-to-line-CixmAKiG.js";
|
|
7
7
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
|
|
8
8
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
9
9
|
import { createMessageReceiveContext } from "openclaw/plugin-sdk/channel-outbound";
|
|
@@ -92,7 +92,6 @@ function startLineWebhook(options) {
|
|
|
92
92
|
}
|
|
93
93
|
//#endregion
|
|
94
94
|
//#region extensions/line/src/rich-menu.ts
|
|
95
|
-
const USER_BATCH_SIZE = 500;
|
|
96
95
|
const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
97
96
|
function getClient(opts) {
|
|
98
97
|
const account = resolveLineAccount({
|
|
@@ -110,11 +109,6 @@ function getBlobClient(opts) {
|
|
|
110
109
|
const token = resolveLineChannelAccessToken(opts.channelAccessToken, account);
|
|
111
110
|
return new messagingApi.MessagingApiBlobClient({ channelAccessToken: token });
|
|
112
111
|
}
|
|
113
|
-
function chunkUserIds(userIds) {
|
|
114
|
-
const batches = [];
|
|
115
|
-
for (let i = 0; i < userIds.length; i += USER_BATCH_SIZE) batches.push(userIds.slice(i, i + USER_BATCH_SIZE));
|
|
116
|
-
return batches;
|
|
117
|
-
}
|
|
118
112
|
function truncateGraphemes(input, maxLength) {
|
|
119
113
|
let result = "";
|
|
120
114
|
let count = 0;
|
|
@@ -163,27 +157,6 @@ async function getDefaultRichMenuId(opts) {
|
|
|
163
157
|
return null;
|
|
164
158
|
}
|
|
165
159
|
}
|
|
166
|
-
async function linkRichMenuToUser(userId, richMenuId, opts) {
|
|
167
|
-
await getClient(opts).linkRichMenuIdToUser(userId, richMenuId);
|
|
168
|
-
if (opts.verbose) logVerbose(`line: linked rich menu ${richMenuId} to user ${userId}`);
|
|
169
|
-
}
|
|
170
|
-
async function linkRichMenuToUsers(userIds, richMenuId, opts) {
|
|
171
|
-
const client = getClient(opts);
|
|
172
|
-
for (const batch of chunkUserIds(userIds)) await client.linkRichMenuIdToUsers({
|
|
173
|
-
richMenuId,
|
|
174
|
-
userIds: batch
|
|
175
|
-
});
|
|
176
|
-
if (opts.verbose) logVerbose(`line: linked rich menu ${richMenuId} to ${userIds.length} users`);
|
|
177
|
-
}
|
|
178
|
-
async function unlinkRichMenuFromUser(userId, opts) {
|
|
179
|
-
await getClient(opts).unlinkRichMenuIdFromUser(userId);
|
|
180
|
-
if (opts.verbose) logVerbose(`line: unlinked rich menu from user ${userId}`);
|
|
181
|
-
}
|
|
182
|
-
async function unlinkRichMenuFromUsers(userIds, opts) {
|
|
183
|
-
const client = getClient(opts);
|
|
184
|
-
for (const batch of chunkUserIds(userIds)) await client.unlinkRichMenuIdFromUsers({ userIds: batch });
|
|
185
|
-
if (opts.verbose) logVerbose(`line: unlinked rich menu from ${userIds.length} users`);
|
|
186
|
-
}
|
|
187
160
|
async function getRichMenuIdOfUser(userId, opts) {
|
|
188
161
|
const client = getClient(opts);
|
|
189
162
|
try {
|
|
@@ -298,4 +271,4 @@ function createDefaultMenuConfig() {
|
|
|
298
271
|
};
|
|
299
272
|
}
|
|
300
273
|
//#endregion
|
|
301
|
-
export { DEFAULT_ACCOUNT_ID, LineChannelConfigSchema, LineConfigSchema, buildChannelConfigSchema, buildComputedAccountStatusSnapshot, buildTemplateMessageFromPayload, buildTokenChannelStatusSummary, cancelDefaultRichMenu, clearAccountEntryFields, convertCodeBlockToFlexBubble, convertLinksToFlexBubble, convertTableToFlexBubble, createActionCard, createAgendaCard, createAppleTvRemoteCard, createAudioMessage, createButtonMenu, createButtonTemplate, createCarousel, createCarouselColumn, createConfirmTemplate, createDefaultMenuConfig, createDeviceControlCard, createEventCard, createFlexMessage, createGridLayout, createImageCard, createImageCarousel, createImageCarouselColumn, createImageMessage, createInfoCard, createLineNodeWebhookHandler, createLineWebhookMiddleware, createLinkMenu, createListCard, createLocationMessage, createMediaPlayerCard, createNotificationBubble, createProductCarousel, createQuickReplyItems, createReceiptCard, createRichMenu, createRichMenuAlias, createTemplateCarousel, createTextMessageWithQuickReplies, createVideoMessage, createYesNoConfirm, datetimePickerAction, deleteRichMenu, deleteRichMenuAlias, downloadLineMedia, extractCodeBlocks, extractLinks, extractMarkdownTables, firstDefined, formatDocsLink, getDefaultRichMenuId, getRichMenu, getRichMenuIdOfUser, getRichMenuList, getUserDisplayName, getUserProfile, hasLineDirectives, hasMarkdownToConvert,
|
|
274
|
+
export { DEFAULT_ACCOUNT_ID, LineChannelConfigSchema, LineConfigSchema, buildChannelConfigSchema, buildComputedAccountStatusSnapshot, buildTemplateMessageFromPayload, buildTokenChannelStatusSummary, cancelDefaultRichMenu, clearAccountEntryFields, convertCodeBlockToFlexBubble, convertLinksToFlexBubble, convertTableToFlexBubble, createActionCard, createAgendaCard, createAppleTvRemoteCard, createAudioMessage, createButtonMenu, createButtonTemplate, createCarousel, createCarouselColumn, createConfirmTemplate, createDefaultMenuConfig, createDeviceControlCard, createEventCard, createFlexMessage, createGridLayout, createImageCard, createImageCarousel, createImageCarouselColumn, createImageMessage, createInfoCard, createLineNodeWebhookHandler, createLineWebhookMiddleware, createLinkMenu, createListCard, createLocationMessage, createMediaPlayerCard, createNotificationBubble, createProductCarousel, createQuickReplyItems, createReceiptCard, createRichMenu, createRichMenuAlias, createTemplateCarousel, createTextMessageWithQuickReplies, createVideoMessage, createYesNoConfirm, datetimePickerAction, deleteRichMenu, deleteRichMenuAlias, downloadLineMedia, extractCodeBlocks, extractLinks, extractMarkdownTables, firstDefined, formatDocsLink, getDefaultRichMenuId, getRichMenu, getRichMenuIdOfUser, getRichMenuList, getUserDisplayName, getUserProfile, hasLineDirectives, hasMarkdownToConvert, listLineAccountIds, messageAction, monitorLineProvider, normalizeAccountId, normalizeAllowFrom, parseLineDirectives, parseLineWebhookBody, postbackAction, probeLineBot, processLineMessage, pushFlexMessage, pushImageMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, readLineWebhookRequestBody, replyMessageLine, resolveDefaultLineAccountId, resolveExactLineGroupConfigKey, resolveLineAccount, resolveLineChannelAccessToken, resolveLineGroupConfigEntry, resolveLineGroupLookupIds, resolveLineGroupsConfig, sendMessageLine, setDefaultRichMenu, setLineRuntime, setSetupChannelEnabled, showLoadingAnimation, splitSetupEntries, startLineWebhook, stripMarkdown, toFlexMessage, uploadRichMenuImage, uriAction, validateLineSignature };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/line",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/line",
|
|
9
|
-
"version": "2026.7.
|
|
9
|
+
"version": "2026.7.2-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@line/bot-sdk": "11.1.0",
|
|
12
12
|
"zod": "4.4.3"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"openclaw": ">=2026.7.
|
|
15
|
+
"openclaw": ">=2026.7.2-beta.1"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/line",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.1",
|
|
4
4
|
"description": "OpenClaw LINE channel plugin for LINE Bot API chats.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"zod": "4.4.3"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"openclaw": ">=2026.7.
|
|
15
|
+
"openclaw": ">=2026.7.2-beta.1"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"openclaw": {
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"minHostVersion": ">=2026.4.10"
|
|
43
43
|
},
|
|
44
44
|
"compat": {
|
|
45
|
-
"pluginApi": ">=2026.7.
|
|
45
|
+
"pluginApi": ">=2026.7.2-beta.1"
|
|
46
46
|
},
|
|
47
47
|
"build": {
|
|
48
|
-
"openclawVersion": "2026.7.
|
|
48
|
+
"openclawVersion": "2026.7.2-beta.1"
|
|
49
49
|
},
|
|
50
50
|
"release": {
|
|
51
51
|
"publishToClawHub": true,
|