@openclaw/line 2026.7.2-beta.7 → 2026.8.1-beta.2
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 +2 -3
- package/dist/{card-command-ty11OJNt.js → card-command-DALdUSnc.js} +14 -22
- package/dist/{channel-D4i_K4fQ.js → channel-CAdQJRw7.js} +74 -76
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-CuKoy9xw.js +4 -0
- package/dist/{flex-templates-iKJsyCq-.js → flex-templates-BBBlNqpV.js} +108 -81
- package/dist/index.js +1 -1
- package/dist/markdown-to-line-D4I-6lm6.js +499 -0
- package/dist/{monitor-DXEodYFJ.js → monitor-QEh_ZqyR.js} +94 -43
- package/dist/monitor.runtime-72isajCp.js +2 -0
- package/dist/outbound.runtime-CWQMIE7N.js +3 -0
- package/dist/{reply-payload-transform-D5-dHB8b.js → reply-payload-transform-BOPGHVPo.js} +4 -3
- package/dist/runtime-api.js +6 -5
- package/dist/send-CFFr0-rZ.js +366 -0
- package/dist/setup-api.js +1 -1
- package/dist/{setup-surface-DRQ7n0En.js → setup-surface-W0Dbgu58.js} +2 -1
- package/openclaw.plugin.json +7 -0
- package/package.json +9 -5
- package/dist/channel.runtime-BdhK8Xw7.js +0 -4
- package/dist/markdown-to-line-DMDQTMhx.js +0 -799
- package/dist/monitor.runtime-CZquR7jI.js +0 -2
- package/dist/outbound.runtime-BS0IhU_M.js +0 -2
- package/dist/send-receipt-CY2ey8XH.js +0 -107
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-BqEVsDoB.js";
|
|
2
2
|
import { a as resolveLineGroupConfigEntry, n as getLineRuntime, t as buildLineQuickReplyFallbackText } from "./quick-reply-fallback-D86FwAfr.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { v as buildLineMediaMessage, y as hasLineSpecificMediaOptions } from "./flex-templates-BBBlNqpV.js";
|
|
4
|
+
import { i as processLineMessage, o as buildTemplateMessageFromPayload } from "./markdown-to-line-D4I-6lm6.js";
|
|
5
|
+
import { _ as replyMessageLine, a as createQuickReplyItems, c as getUserDisplayName, i as createLocationMessage, m as pushMessagesLine, n as createFlexMessage, p as pushMessageLine, y as showLoadingAnimation } from "./send-CFFr0-rZ.js";
|
|
5
6
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
6
7
|
import { normalizeNullableString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
8
|
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
|
+
import { buildChannelInboundEventContext, buildMentionRegexes, formatInboundEnvelope, formatInboundMediaUnavailableText, formatLocationText, hasFinalInboundReplyDispatch, isChannelPartialDeliveryError, matchesMentionPatterns, resolveInboundSessionEnvelopeContext, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
8
10
|
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
9
11
|
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
10
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
11
12
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
13
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
14
|
import { firstDefined } from "openclaw/plugin-sdk/allow-from";
|
|
13
15
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
14
16
|
import { MediaFetchError } from "openclaw/plugin-sdk/media-runtime";
|
|
@@ -16,12 +18,13 @@ import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
|
16
18
|
import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
17
19
|
import { fetchWithRuntimeDispatcherOrMockedGlobal } from "openclaw/plugin-sdk/runtime-fetch";
|
|
18
20
|
import { HTTPFetchError } from "@line/bot-sdk";
|
|
19
|
-
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
21
|
+
import { collectErrorGraphCandidates, extractErrorCode, formatErrorMessage, readErrorName, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
20
22
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
21
|
-
import {
|
|
23
|
+
import { channelReadyPatch, channelStoppedPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
22
24
|
import { chunkMarkdownText } from "openclaw/plugin-sdk/reply-runtime";
|
|
23
|
-
import { isRequestBodyLimitError, normalizePluginHttpPath, normalizeWebhookPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
|
|
25
|
+
import { canonicalizeWebhookRouteKey, isRequestBodyLimitError, normalizePluginHttpPath, normalizeWebhookPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
|
|
24
26
|
import { beginWebhookRequestPipelineOrReject, createWebhookInFlightLimiter, isRequestBodyLimitError as isRequestBodyLimitError$1, readRequestBodyWithLimit, requestBodyErrorToText as requestBodyErrorToText$1, runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
27
|
+
import { classifyTransientNetworkErrorCode } from "openclaw/plugin-sdk/retry-runtime";
|
|
25
28
|
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
26
29
|
import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
27
30
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
@@ -147,8 +150,15 @@ function toLineDeliveryError(error) {
|
|
|
147
150
|
return error instanceof Error ? error : new Error("LINE message send failed", { cause: error });
|
|
148
151
|
}
|
|
149
152
|
function getLineHttpError(error) {
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
return collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]).find((candidate) => candidate instanceof HTTPFetchError);
|
|
154
|
+
}
|
|
155
|
+
function canFallbackAfterLineReplyFailure(error) {
|
|
156
|
+
const httpError = getLineHttpError(error);
|
|
157
|
+
if (httpError) return httpError.status >= 400 && httpError.status < 500 && httpError.status !== 408;
|
|
158
|
+
const candidates = collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]);
|
|
159
|
+
if (candidates.some((candidate) => readErrorName(candidate) === "AbortError" || classifyTransientNetworkErrorCode(extractErrorCode(candidate)) === "ambiguous")) return false;
|
|
160
|
+
if (candidates.some((candidate) => classifyTransientNetworkErrorCode(extractErrorCode(candidate)) === "pre-connect")) return true;
|
|
161
|
+
return !candidates.some((candidate) => candidate instanceof TypeError && candidate.message === "fetch failed");
|
|
152
162
|
}
|
|
153
163
|
function markLineVisibleDeliveryError(error) {
|
|
154
164
|
const deliveryError = toLineDeliveryError(error);
|
|
@@ -176,6 +186,7 @@ async function deliverLineAutoReply(params) {
|
|
|
176
186
|
visibleReplySent = true;
|
|
177
187
|
return result;
|
|
178
188
|
} catch (error) {
|
|
189
|
+
if (isChannelPartialDeliveryError(error)) visibleReplySent = true;
|
|
179
190
|
if (visibleReplySent) throw markLineVisibleDeliveryError(error);
|
|
180
191
|
throw error;
|
|
181
192
|
}
|
|
@@ -192,7 +203,7 @@ async function deliverLineAutoReply(params) {
|
|
|
192
203
|
accountId
|
|
193
204
|
}));
|
|
194
205
|
} catch (error) {
|
|
195
|
-
if (typeof error === "object" && error !== null) failedPushSegments.set(error, {
|
|
206
|
+
if (!isChannelPartialDeliveryError(error) && typeof error === "object" && error !== null) failedPushSegments.set(error, {
|
|
196
207
|
allowFailedBatchTextRecovery,
|
|
197
208
|
failedBatch: batch,
|
|
198
209
|
unattemptedTail: [...messages.slice(i + batch.length), ...externalTail]
|
|
@@ -212,6 +223,7 @@ async function deliverLineAutoReply(params) {
|
|
|
212
223
|
accountId
|
|
213
224
|
});
|
|
214
225
|
} catch (err) {
|
|
226
|
+
if (isChannelPartialDeliveryError(err) || !canFallbackAfterLineReplyFailure(err)) throw err;
|
|
215
227
|
deps.onReplyError?.(err);
|
|
216
228
|
await pushLineMessages(replyBatch, getLineHttpError(err)?.status === 400, remaining.slice(replyBatch.length));
|
|
217
229
|
} finally {
|
|
@@ -223,19 +235,26 @@ async function deliverLineAutoReply(params) {
|
|
|
223
235
|
};
|
|
224
236
|
const richMessages = [];
|
|
225
237
|
const hasQuickReplies = Boolean(lineData.quickReplies?.length);
|
|
226
|
-
if (lineData.flexMessage) richMessages.push(deps.createFlexMessage(
|
|
238
|
+
if (lineData.flexMessage) richMessages.push(deps.createFlexMessage(lineData.flexMessage.altText, lineData.flexMessage.contents));
|
|
227
239
|
if (lineData.templateMessage) {
|
|
228
240
|
const templateMsg = deps.buildTemplateMessageFromPayload(lineData.templateMessage);
|
|
229
241
|
if (templateMsg) richMessages.push(templateMsg);
|
|
230
242
|
}
|
|
231
|
-
if (lineData.location)
|
|
243
|
+
if (lineData.location) {
|
|
244
|
+
const locationMessage = deps.createLocationMessage(lineData.location);
|
|
245
|
+
if (locationMessage) richMessages.push(locationMessage);
|
|
246
|
+
}
|
|
232
247
|
const visibleText = payload.text ? sanitizeAssistantVisibleText(payload.text) : "";
|
|
233
248
|
const processed = visibleText ? deps.processLineMessage(visibleText) : {
|
|
234
249
|
text: "",
|
|
235
250
|
flexMessages: []
|
|
236
251
|
};
|
|
237
|
-
for (const flexMsg of processed.flexMessages) richMessages.push(deps.createFlexMessage(
|
|
238
|
-
const
|
|
252
|
+
if (!processed.segments) for (const flexMsg of processed.flexMessages) richMessages.push(deps.createFlexMessage(flexMsg.altText, flexMsg.contents));
|
|
253
|
+
const orderedMessages = processed.segments?.flatMap((segment) => segment.type === "flex" ? [deps.createFlexMessage(segment.message.altText, segment.message.contents)] : deps.chunkMarkdownText(segment.text, textLimit).map((text) => ({
|
|
254
|
+
type: "text",
|
|
255
|
+
text
|
|
256
|
+
})));
|
|
257
|
+
const chunks = orderedMessages ? orderedMessages.flatMap((message) => message.type === "text" ? [message.text] : []) : processed.text ? deps.chunkMarkdownText(processed.text, textLimit) : [];
|
|
239
258
|
const mediaUrls = resolveSendableOutboundReplyParts(payload).mediaUrls;
|
|
240
259
|
const mediaOpts = {
|
|
241
260
|
mediaKind: hasLineSpecificMediaOptions(lineData) ? lineData.mediaKind : "image",
|
|
@@ -264,14 +283,14 @@ async function deliverLineAutoReply(params) {
|
|
|
264
283
|
text: buildLineQuickReplyFallbackText(lineData.quickReplies)
|
|
265
284
|
});
|
|
266
285
|
if (hasQuickReplies) {
|
|
267
|
-
const targetMessages = textMessages.length > 0 ? textMessages : richMediaMessages;
|
|
286
|
+
const targetMessages = orderedMessages?.length ? orderedMessages : textMessages.length > 0 ? textMessages : richMediaMessages;
|
|
268
287
|
const lastIndex = targetMessages.length - 1;
|
|
269
288
|
targetMessages[lastIndex] = {
|
|
270
289
|
...expectDefined(targetMessages[lastIndex], "last LINE auto-reply message"),
|
|
271
290
|
quickReply: deps.createQuickReplyItems(lineData.quickReplies)
|
|
272
291
|
};
|
|
273
292
|
}
|
|
274
|
-
const messages = hasQuickReplies ? [...richMediaMessages, ...textMessages] : [...textMessages, ...richMediaMessages];
|
|
293
|
+
const messages = hasQuickReplies ? [...richMediaMessages, ...orderedMessages ?? textMessages] : [...orderedMessages ?? textMessages, ...richMediaMessages];
|
|
275
294
|
try {
|
|
276
295
|
await sendLineMessages(messages, true);
|
|
277
296
|
} catch (err) {
|
|
@@ -463,7 +482,8 @@ async function finalizeLineInboundContext(params) {
|
|
|
463
482
|
previousTimestamp,
|
|
464
483
|
envelope: envelopeOptions
|
|
465
484
|
});
|
|
466
|
-
const ctxPayload = buildChannelInboundEventContext({
|
|
485
|
+
const ctxPayload = (params.buildContext ?? buildChannelInboundEventContext)({
|
|
486
|
+
channelIngress: params.channelIngress,
|
|
467
487
|
channel: "line",
|
|
468
488
|
accountId: params.route.accountId,
|
|
469
489
|
messageId: params.messageSid,
|
|
@@ -593,6 +613,13 @@ async function buildLineMessageContext(params) {
|
|
|
593
613
|
timestamp,
|
|
594
614
|
messageSid: messageId,
|
|
595
615
|
commandAuthorized,
|
|
616
|
+
channelIngress: params.resolveChannelIngress ? await params.resolveChannelIngress({
|
|
617
|
+
agentId: route.agentId,
|
|
618
|
+
sessionKey: route.sessionKey,
|
|
619
|
+
messageId,
|
|
620
|
+
inboundEventKind: "user_request"
|
|
621
|
+
}) : params.channelIngress,
|
|
622
|
+
buildContext: params.buildContext,
|
|
596
623
|
media: mediaFacts,
|
|
597
624
|
locationContext,
|
|
598
625
|
verboseLog: {
|
|
@@ -649,6 +676,13 @@ async function buildLinePostbackContext(params) {
|
|
|
649
676
|
timestamp,
|
|
650
677
|
messageSid,
|
|
651
678
|
commandAuthorized,
|
|
679
|
+
channelIngress: params.resolveChannelIngress ? await params.resolveChannelIngress({
|
|
680
|
+
agentId: route.agentId,
|
|
681
|
+
sessionKey: route.sessionKey,
|
|
682
|
+
messageId: messageSid,
|
|
683
|
+
inboundEventKind: "user_request"
|
|
684
|
+
}) : params.channelIngress,
|
|
685
|
+
buildContext: params.buildContext,
|
|
652
686
|
media: [],
|
|
653
687
|
verboseLog: { kind: "postback" }
|
|
654
688
|
});
|
|
@@ -757,6 +791,7 @@ async function sendLinePairingReply(params) {
|
|
|
757
791
|
return;
|
|
758
792
|
} catch (err) {
|
|
759
793
|
logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
|
|
794
|
+
if (isChannelPartialDeliveryError(err)) return;
|
|
760
795
|
}
|
|
761
796
|
try {
|
|
762
797
|
await pushMessageLine(`line:${senderId}`, text, {
|
|
@@ -814,7 +849,7 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
814
849
|
hasAnyMention: hasAnyLineMention(event.message)
|
|
815
850
|
};
|
|
816
851
|
})();
|
|
817
|
-
const
|
|
852
|
+
const resolveAccess = async (contextBinding) => await resolveStableChannelMessageIngress({
|
|
818
853
|
channelId: "line",
|
|
819
854
|
accountId: account.accountId,
|
|
820
855
|
identity: {
|
|
@@ -830,6 +865,7 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
830
865
|
kind: isGroup ? "group" : "direct",
|
|
831
866
|
id: (groupId ?? roomId ?? senderId) || "unknown"
|
|
832
867
|
},
|
|
868
|
+
...contextBinding ? { contextBinding } : {},
|
|
833
869
|
...isGroup && groupConfig?.enabled === false ? { route: {
|
|
834
870
|
id: "line:group-config",
|
|
835
871
|
enabled: false
|
|
@@ -857,13 +893,17 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
857
893
|
groupOwnerAllowFrom: "none"
|
|
858
894
|
}
|
|
859
895
|
});
|
|
896
|
+
const access = await resolveAccess();
|
|
860
897
|
warnMissingProviderGroupPolicyFallbackOnce({
|
|
861
898
|
providerMissingFallbackApplied,
|
|
862
899
|
providerKey: "line",
|
|
863
900
|
accountId: account.accountId,
|
|
864
901
|
log: (message) => logVerbose(message)
|
|
865
902
|
});
|
|
866
|
-
if (access.senderAccess.decision === "allow" && (access.ingress.admission === "dispatch" || access.ingress.admission === "observe" || access.ingress.admission === "skip")) return
|
|
903
|
+
if (access.senderAccess.decision === "allow" && (access.ingress.admission === "dispatch" || access.ingress.admission === "observe" || access.ingress.admission === "skip")) return {
|
|
904
|
+
access,
|
|
905
|
+
resolveBoundAccess: resolveAccess
|
|
906
|
+
};
|
|
867
907
|
if (access.senderAccess.decision === "allow") {
|
|
868
908
|
logVerbose(`Blocked line event (${access.ingress.reasonCode})`);
|
|
869
909
|
return null;
|
|
@@ -934,7 +974,7 @@ async function handleMessageEvent(event, context) {
|
|
|
934
974
|
const decision = await shouldProcessLineEvent(event, context);
|
|
935
975
|
if (!decision) return;
|
|
936
976
|
const { isGroup, groupId, roomId } = getLineSourceInfo(event.source);
|
|
937
|
-
if (isGroup && decision.activationAccess.shouldSkip) {
|
|
977
|
+
if (isGroup && decision.access.activationAccess.shouldSkip) {
|
|
938
978
|
const rawText = message.type === "text" ? message.text : "";
|
|
939
979
|
const sourceInfo = getLineSourceInfo(event.source);
|
|
940
980
|
logVerbose(`line: skipping group message (requireMention, not mentioned)`);
|
|
@@ -976,8 +1016,10 @@ async function handleMessageEvent(event, context) {
|
|
|
976
1016
|
mediaUnavailable,
|
|
977
1017
|
cfg,
|
|
978
1018
|
account,
|
|
979
|
-
commandAuthorized: decision.commandAccess.authorized,
|
|
980
|
-
|
|
1019
|
+
commandAuthorized: decision.access.commandAccess.authorized,
|
|
1020
|
+
resolveChannelIngress: decision.resolveBoundAccess,
|
|
1021
|
+
inboundHistory: historyReservation.inboundHistory,
|
|
1022
|
+
buildContext: context.buildContext
|
|
981
1023
|
});
|
|
982
1024
|
if (!messageContext) {
|
|
983
1025
|
logVerbose("line: skipping empty message");
|
|
@@ -1014,7 +1056,9 @@ async function handlePostbackEvent(event, context) {
|
|
|
1014
1056
|
event,
|
|
1015
1057
|
cfg: context.cfg,
|
|
1016
1058
|
account: context.account,
|
|
1017
|
-
commandAuthorized: decision.commandAccess.authorized
|
|
1059
|
+
commandAuthorized: decision.access.commandAccess.authorized,
|
|
1060
|
+
resolveChannelIngress: decision.resolveBoundAccess,
|
|
1061
|
+
buildContext: context.buildContext
|
|
1018
1062
|
});
|
|
1019
1063
|
if (!postbackContext) return;
|
|
1020
1064
|
await context.processMessage(postbackContext, context.turnAdoptionLifecycle ? { turnAdoptionLifecycle: context.turnAdoptionLifecycle } : {});
|
|
@@ -1027,7 +1071,7 @@ async function handleLineWebhookEvents(events, context) {
|
|
|
1027
1071
|
context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`));
|
|
1028
1072
|
firstError ??= err;
|
|
1029
1073
|
}
|
|
1030
|
-
if (firstError) throw
|
|
1074
|
+
if (firstError) throw toErrorObject(firstError, "Non-Error thrown");
|
|
1031
1075
|
}
|
|
1032
1076
|
async function handleLineWebhookEvent(event, context) {
|
|
1033
1077
|
switch (event.type) {
|
|
@@ -1052,13 +1096,6 @@ async function handleLineWebhookEvent(event, context) {
|
|
|
1052
1096
|
default: logVerbose(`line: unhandled event type: ${event.type}`);
|
|
1053
1097
|
}
|
|
1054
1098
|
}
|
|
1055
|
-
function toLintErrorObject(value, fallbackMessage) {
|
|
1056
|
-
if (value instanceof Error) return value;
|
|
1057
|
-
if (typeof value === "string") return new Error(value);
|
|
1058
|
-
const error = new Error(fallbackMessage, { cause: value });
|
|
1059
|
-
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
|
|
1060
|
-
return error;
|
|
1061
|
-
}
|
|
1062
1099
|
//#endregion
|
|
1063
1100
|
//#region extensions/line/src/webhook-spool.ts
|
|
1064
1101
|
const LINE_WEBHOOK_SPOOL_VERSION = 1;
|
|
@@ -1112,9 +1149,6 @@ function parseStoredEvent(rawEvent) {
|
|
|
1112
1149
|
}
|
|
1113
1150
|
return event;
|
|
1114
1151
|
}
|
|
1115
|
-
function errorText(error) {
|
|
1116
|
-
return error instanceof Error ? error.message : String(error);
|
|
1117
|
-
}
|
|
1118
1152
|
function isLineAuthenticationFailure(error) {
|
|
1119
1153
|
if (!error || typeof error !== "object") return false;
|
|
1120
1154
|
const status = error.status;
|
|
@@ -1184,7 +1218,7 @@ function createLineWebhookSpool(options) {
|
|
|
1184
1218
|
handedOff = true;
|
|
1185
1219
|
if (!acceptsDeferredClaims) {
|
|
1186
1220
|
Promise.resolve().then(() => boundLifecycle.onAbandoned()).catch((error) => {
|
|
1187
|
-
options.runtime.error?.(danger(`line: failed to abandon a late webhook delivery: ${
|
|
1221
|
+
options.runtime.error?.(danger(`line: failed to abandon a late webhook delivery: ${formatErrorMessage(error)}`));
|
|
1188
1222
|
});
|
|
1189
1223
|
return;
|
|
1190
1224
|
}
|
|
@@ -1238,14 +1272,14 @@ function createLineWebhookSpool(options) {
|
|
|
1238
1272
|
};
|
|
1239
1273
|
if (isLineAuthenticationFailure(error)) return {
|
|
1240
1274
|
reason: "authentication-failed",
|
|
1241
|
-
message:
|
|
1275
|
+
message: formatErrorMessage(error)
|
|
1242
1276
|
};
|
|
1243
1277
|
return null;
|
|
1244
1278
|
},
|
|
1245
1279
|
onLog: (message) => options.runtime.error?.(danger(`line: ${message}`))
|
|
1246
1280
|
},
|
|
1247
1281
|
createStoppedError: () => /* @__PURE__ */ new Error("LINE webhook spool is stopped."),
|
|
1248
|
-
onError: (error) => options.runtime.error?.(danger(`line: webhook spool drain failed: ${
|
|
1282
|
+
onError: (error) => options.runtime.error?.(danger(`line: webhook spool drain failed: ${formatErrorMessage(error)}`))
|
|
1249
1283
|
});
|
|
1250
1284
|
let stopTask;
|
|
1251
1285
|
return {
|
|
@@ -1279,6 +1313,7 @@ function createLineWebhookSpool(options) {
|
|
|
1279
1313
|
}
|
|
1280
1314
|
//#endregion
|
|
1281
1315
|
//#region extensions/line/src/bot.ts
|
|
1316
|
+
const DEFAULT_MEDIA_MAX_MB = 10;
|
|
1282
1317
|
function createLineBot(opts) {
|
|
1283
1318
|
const runtime = opts.runtime ?? createNonExitingRuntime();
|
|
1284
1319
|
const cfg = opts.config ?? getRuntimeConfig();
|
|
@@ -1286,7 +1321,7 @@ function createLineBot(opts) {
|
|
|
1286
1321
|
cfg,
|
|
1287
1322
|
accountId: opts.accountId
|
|
1288
1323
|
});
|
|
1289
|
-
const mediaMaxBytes = (opts.mediaMaxMb
|
|
1324
|
+
const mediaMaxBytes = ([opts.mediaMaxMb, account.config.mediaMaxMb].find((value) => typeof value === "number" && value > 0) ?? DEFAULT_MEDIA_MAX_MB) * 1024 * 1024;
|
|
1290
1325
|
const processMessage = opts.onMessage ?? (async () => {
|
|
1291
1326
|
logVerbose("line: no message handler configured");
|
|
1292
1327
|
});
|
|
@@ -1298,6 +1333,7 @@ function createLineBot(opts) {
|
|
|
1298
1333
|
cfg,
|
|
1299
1334
|
account,
|
|
1300
1335
|
runtime,
|
|
1336
|
+
buildContext: opts.buildContext,
|
|
1301
1337
|
mediaMaxBytes,
|
|
1302
1338
|
processMessage,
|
|
1303
1339
|
...control.turnAdoptionLifecycle ? { turnAdoptionLifecycle: control.turnAdoptionLifecycle } : {},
|
|
@@ -1433,6 +1469,14 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1433
1469
|
const lineWebhookInFlightLimiter = createWebhookInFlightLimiter();
|
|
1434
1470
|
const LINE_WEBHOOK_PREAUTH_MAX_BODY_BYTES = 64 * 1024;
|
|
1435
1471
|
const LINE_WEBHOOK_PREAUTH_BODY_TIMEOUT_MS = 5e3;
|
|
1472
|
+
async function registerLineWebhookTarget(params, bot) {
|
|
1473
|
+
try {
|
|
1474
|
+
return registerWebhookTargetWithPluginRoute(params);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
await Promise.allSettled([bot.stop()]);
|
|
1477
|
+
throw error;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1436
1480
|
const lineWebhookTargets = /* @__PURE__ */ new Map();
|
|
1437
1481
|
function startLineLoadingKeepalive(params) {
|
|
1438
1482
|
const intervalMs = params.intervalMs ?? 18e3;
|
|
@@ -1455,7 +1499,7 @@ function startLineLoadingKeepalive(params) {
|
|
|
1455
1499
|
};
|
|
1456
1500
|
}
|
|
1457
1501
|
async function monitorLineProvider(opts) {
|
|
1458
|
-
const { channelAccessToken, channelSecret, accountId, config, runtime, abortSignal, webhookPath } = opts;
|
|
1502
|
+
const { channelAccessToken, channelSecret, accountId, config, runtime, buildContext, abortSignal, webhookPath, statusSink } = opts;
|
|
1459
1503
|
const resolvedAccountId = accountId ?? resolveDefaultLineAccountId(config);
|
|
1460
1504
|
const token = channelAccessToken.trim();
|
|
1461
1505
|
const secret = channelSecret.trim();
|
|
@@ -1466,6 +1510,7 @@ async function monitorLineProvider(opts) {
|
|
|
1466
1510
|
channelSecret: secret,
|
|
1467
1511
|
accountId,
|
|
1468
1512
|
runtime,
|
|
1513
|
+
buildContext,
|
|
1469
1514
|
config,
|
|
1470
1515
|
onMessage: async (ctx, deliveryControl) => {
|
|
1471
1516
|
if (!ctx) return;
|
|
@@ -1575,12 +1620,13 @@ async function monitorLineProvider(opts) {
|
|
|
1575
1620
|
}
|
|
1576
1621
|
});
|
|
1577
1622
|
const normalizedPath = normalizeWebhookPath(normalizePluginHttpPath(webhookPath, "/line/webhook") ?? "/line/webhook");
|
|
1623
|
+
const webhookRouteKey = canonicalizeWebhookRouteKey(normalizedPath);
|
|
1578
1624
|
const createScopedLineWebhookHandler = (target) => createLineNodeWebhookHandler({
|
|
1579
1625
|
channelSecret: target.channelSecret,
|
|
1580
1626
|
bot: target.bot,
|
|
1581
1627
|
runtime: target.runtime
|
|
1582
1628
|
});
|
|
1583
|
-
const { unregister: unregisterHttp } =
|
|
1629
|
+
const { unregister: unregisterHttp } = await registerLineWebhookTarget({
|
|
1584
1630
|
targetsByPath: lineWebhookTargets,
|
|
1585
1631
|
target: {
|
|
1586
1632
|
accountId: resolvedAccountId,
|
|
@@ -1592,10 +1638,12 @@ async function monitorLineProvider(opts) {
|
|
|
1592
1638
|
route: {
|
|
1593
1639
|
auth: "plugin",
|
|
1594
1640
|
pluginId: "line",
|
|
1641
|
+
source: "line-webhook",
|
|
1595
1642
|
accountId: resolvedAccountId,
|
|
1596
1643
|
log: (msg) => logVerbose(msg),
|
|
1644
|
+
throwOnFailure: true,
|
|
1597
1645
|
handler: async (req, res) => {
|
|
1598
|
-
const targets = lineWebhookTargets.get(
|
|
1646
|
+
const targets = lineWebhookTargets.get(webhookRouteKey) ?? [];
|
|
1599
1647
|
const firstTarget = targets[0];
|
|
1600
1648
|
if (req.method !== "POST") {
|
|
1601
1649
|
if (!firstTarget) {
|
|
@@ -1610,7 +1658,7 @@ async function monitorLineProvider(opts) {
|
|
|
1610
1658
|
req,
|
|
1611
1659
|
res,
|
|
1612
1660
|
inFlightLimiter: lineWebhookInFlightLimiter,
|
|
1613
|
-
inFlightKey: `line:${
|
|
1661
|
+
inFlightKey: `line:${webhookRouteKey}`
|
|
1614
1662
|
});
|
|
1615
1663
|
if (!requestLifecycle.ok) return;
|
|
1616
1664
|
try {
|
|
@@ -1678,8 +1726,9 @@ async function monitorLineProvider(opts) {
|
|
|
1678
1726
|
}
|
|
1679
1727
|
}
|
|
1680
1728
|
}
|
|
1681
|
-
});
|
|
1729
|
+
}, bot);
|
|
1682
1730
|
logVerbose(`line: registered webhook handler at ${normalizedPath}`);
|
|
1731
|
+
statusSink?.(channelReadyPatch());
|
|
1683
1732
|
let stopped = false;
|
|
1684
1733
|
let stopPromise;
|
|
1685
1734
|
const stopHandler = () => {
|
|
@@ -1688,7 +1737,9 @@ async function monitorLineProvider(opts) {
|
|
|
1688
1737
|
stopped = true;
|
|
1689
1738
|
logVerbose(`line: stopping provider for account ${resolvedAccountId}`);
|
|
1690
1739
|
unregisterHttp();
|
|
1691
|
-
stopPromise = bot.stop()
|
|
1740
|
+
stopPromise = bot.stop().finally(() => {
|
|
1741
|
+
statusSink?.(channelStoppedPatch());
|
|
1742
|
+
});
|
|
1692
1743
|
return stopPromise;
|
|
1693
1744
|
};
|
|
1694
1745
|
const stopOnAbort = () => void stopHandler();
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { i as processLineMessage, o as buildTemplateMessageFromPayload } from "./markdown-to-line-D4I-6lm6.js";
|
|
2
|
+
import { a as createQuickReplyItems, f as pushLocationMessage, g as pushTextMessageWithQuickReplies, h as pushTemplateMessage, i as createLocationMessage, m as pushMessagesLine, n as createFlexMessage, p as pushMessageLine, u as pushFlexMessage, v as sendMessageLine } from "./send-CFFr0-rZ.js";
|
|
3
|
+
export { buildTemplateMessageFromPayload, createFlexMessage, createLocationMessage, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-
|
|
1
|
+
import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-BBBlNqpV.js";
|
|
2
2
|
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import { DmPolicySchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
4
4
|
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
|
|
@@ -15,6 +15,7 @@ const ThreadBindingsSchema = z.object({
|
|
|
15
15
|
}).strict();
|
|
16
16
|
const LineCommonConfigSchemaBase = z.object({
|
|
17
17
|
enabled: z.boolean().optional(),
|
|
18
|
+
configWrites: z.boolean().optional(),
|
|
18
19
|
channelAccessToken: z.string().optional(),
|
|
19
20
|
channelSecret: z.string().optional(),
|
|
20
21
|
tokenFile: z.string().optional(),
|
|
@@ -100,9 +101,9 @@ function parseLineDirectives(payload) {
|
|
|
100
101
|
const lonStr = expectDefined(parts[3], "location longitude field");
|
|
101
102
|
const latitude = parseStrictFiniteNumber(latStr);
|
|
102
103
|
const longitude = parseStrictFiniteNumber(lonStr);
|
|
103
|
-
if (latitude !== void 0 && longitude !== void 0) lineData.location = {
|
|
104
|
+
if (address && latitude !== void 0 && longitude !== void 0) lineData.location = {
|
|
104
105
|
title: title || "Location",
|
|
105
|
-
address
|
|
106
|
+
address,
|
|
106
107
|
latitude,
|
|
107
108
|
longitude
|
|
108
109
|
};
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { i as resolveLineAccount, n as normalizeAccountId, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BqEVsDoB.js";
|
|
2
2
|
import { a as resolveLineGroupConfigEntry, i as resolveExactLineGroupConfigKey, o as resolveLineGroupLookupIds, r as setLineRuntime, s as resolveLineGroupsConfig } from "./quick-reply-fallback-D86FwAfr.js";
|
|
3
|
-
import { i as LineConfigSchema, n as parseLineDirectives, r as LineChannelConfigSchema, t as hasLineDirectives } from "./reply-payload-transform-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
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-
|
|
3
|
+
import { i as LineConfigSchema, n as parseLineDirectives, r as LineChannelConfigSchema, t as hasLineDirectives } from "./reply-payload-transform-BOPGHVPo.js";
|
|
4
|
+
import { a as createEventCard, c as createImageCard, d as datetimePickerAction, f as messageAction, g as uriAction, h as postbackAction, i as createAgendaCard, l as createInfoCard, n as createDeviceControlCard, o as createReceiptCard, r as createMediaPlayerCard, s as createActionCard, t as createAppleTvRemoteCard, u as createListCard } from "./flex-templates-BBBlNqpV.js";
|
|
5
|
+
import { a as stripMarkdown, c as createCarouselColumn, d as toFlexMessage, i as processLineMessage, l as createConfirmTemplate, n as convertTableToFlexBubble, o as buildTemplateMessageFromPayload, r as hasMarkdownToConvert, s as createButtonTemplate, t as convertCodeBlockToFlexBubble, u as createTemplateCarousel } from "./markdown-to-line-D4I-6lm6.js";
|
|
6
|
+
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-QEh_ZqyR.js";
|
|
7
7
|
import { t as probeLineBot } from "./probe-CM03wo6r.js";
|
|
8
|
+
import { _ as replyMessageLine, a as createQuickReplyItems, b as resolveLineChannelAccessToken, c as getUserDisplayName, d as pushImageMessage, f as pushLocationMessage, g as pushTextMessageWithQuickReplies, h as pushTemplateMessage, i as createLocationMessage, l as getUserProfile, m as pushMessagesLine, n as createFlexMessage, o as createTextMessageWithQuickReplies, p as pushMessageLine, r as createImageMessage, s as createVideoMessage, t as createAudioMessage, u as pushFlexMessage, v as sendMessageLine, y as showLoadingAnimation } from "./send-CFFr0-rZ.js";
|
|
8
9
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
|
|
9
10
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
10
11
|
import { DEFAULT_ACCOUNT_ID, formatDocsLink, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
@@ -258,4 +259,4 @@ function createDefaultMenuConfig() {
|
|
|
258
259
|
};
|
|
259
260
|
}
|
|
260
261
|
//#endregion
|
|
261
|
-
export { DEFAULT_ACCOUNT_ID, LineChannelConfigSchema, LineConfigSchema, buildChannelConfigSchema, buildComputedAccountStatusSnapshot, buildTemplateMessageFromPayload, buildTokenChannelStatusSummary, cancelDefaultRichMenu, clearAccountEntryFields, convertCodeBlockToFlexBubble, convertTableToFlexBubble, createActionCard, createAgendaCard, createAppleTvRemoteCard, createAudioMessage,
|
|
262
|
+
export { DEFAULT_ACCOUNT_ID, LineChannelConfigSchema, LineConfigSchema, buildChannelConfigSchema, buildComputedAccountStatusSnapshot, buildTemplateMessageFromPayload, buildTokenChannelStatusSummary, cancelDefaultRichMenu, clearAccountEntryFields, convertCodeBlockToFlexBubble, convertTableToFlexBubble, createActionCard, createAgendaCard, createAppleTvRemoteCard, createAudioMessage, createButtonTemplate, createCarouselColumn, createConfirmTemplate, createDefaultMenuConfig, createDeviceControlCard, createEventCard, createFlexMessage, createGridLayout, createImageCard, createImageMessage, createInfoCard, createLineNodeWebhookHandler, createLineWebhookMiddleware, createListCard, createLocationMessage, createMediaPlayerCard, createQuickReplyItems, createReceiptCard, createRichMenu, createRichMenuAlias, createTemplateCarousel, createTextMessageWithQuickReplies, createVideoMessage, datetimePickerAction, deleteRichMenu, deleteRichMenuAlias, downloadLineMedia, 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 };
|