@openclaw/line 2026.5.7-beta.1 → 2026.5.9-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/{channel-CnhV8Dzi.js → channel-DkbpfXGE.js} +66 -16
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-De0BoY61.js +4 -0
- package/dist/{markdown-to-line-CCWK6PJh.js → markdown-to-line-DASNAjBq.js} +23 -3
- package/dist/{monitor-B4_RxHc0.js → monitor-vLNFYADv.js} +40 -15
- package/dist/monitor.runtime-C1Toz0OI.js +2 -0
- package/dist/{outbound.runtime-B-luYwEB.js → outbound.runtime-zBxAf-it.js} +1 -1
- package/dist/runtime-api.js +18 -5
- package/dist/{outbound-media-BfhBmWq5.js → send-receipt-DSg7S42s.js} +19 -1
- package/package.json +4 -4
- package/dist/channel.runtime-bXoj8_Sl.js +0 -4
- package/dist/monitor.runtime-DyxYlrM9.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-DkbpfXGE.js";
|
|
2
2
|
import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-DCH7CE-y.js";
|
|
3
3
|
//#region extensions/line/src/channel.setup.ts
|
|
4
4
|
const lineSetupPlugin = {
|
|
@@ -2,7 +2,7 @@ import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLin
|
|
|
2
2
|
import { n as lineSetupAdapter, r as hasLineCredentials, t as lineSetupWizard } from "./setup-surface-DCH7CE-y.js";
|
|
3
3
|
import { i as resolveExactLineGroupConfigKey, n as getLineRuntime, t as buildLineQuickReplyFallbackText } from "./quick-reply-fallback-HskUUWsn.js";
|
|
4
4
|
import { n as parseLineDirectives, o as LineChannelConfigSchema, t as hasLineDirectives } from "./reply-payload-transform-Bmf8GnSU.js";
|
|
5
|
-
import {
|
|
5
|
+
import { n as resolveLineOutboundMedia, t as createLineSendReceipt } from "./send-receipt-DSg7S42s.js";
|
|
6
6
|
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
7
7
|
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
8
8
|
import { createRestrictSendersChannelSecurity, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
@@ -12,6 +12,7 @@ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/
|
|
|
12
12
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
|
|
13
13
|
import { describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
14
14
|
import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
15
|
+
import { defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
|
|
15
16
|
import { createAttachedChannelResultAdapter, createEmptyChannelResult } from "openclaw/plugin-sdk/channel-send-result";
|
|
16
17
|
import { resolveOutboundMediaUrls } from "openclaw/plugin-sdk/reply-payload";
|
|
17
18
|
import { buildTokenChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, createDependentCredentialStatusIssueCollector } from "openclaw/plugin-sdk/status-helpers";
|
|
@@ -107,7 +108,7 @@ const lineChannelPluginCommon = {
|
|
|
107
108
|
//#endregion
|
|
108
109
|
//#region extensions/line/src/gateway.ts
|
|
109
110
|
const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-N91OWW1J.js"));
|
|
110
|
-
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-
|
|
111
|
+
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-C1Toz0OI.js"));
|
|
111
112
|
const lineGatewayAdapter = {
|
|
112
113
|
startAccount: async (ctx) => {
|
|
113
114
|
const account = ctx.account;
|
|
@@ -211,7 +212,7 @@ function resolveLineGroupRequireMention(params) {
|
|
|
211
212
|
}
|
|
212
213
|
//#endregion
|
|
213
214
|
//#region extensions/line/src/outbound.ts
|
|
214
|
-
const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-
|
|
215
|
+
const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-zBxAf-it.js"));
|
|
215
216
|
function isLineUserTarget(target) {
|
|
216
217
|
const normalized = target.trim().replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
|
|
217
218
|
return /^U/i.test(normalized);
|
|
@@ -265,17 +266,11 @@ const lineOutboundAdapter = {
|
|
|
265
266
|
const quickReply = hasQuickReplies ? (lineRuntime?.createQuickReplyItems ?? outboundRuntime.createQuickReplyItems)(quickReplies) : void 0;
|
|
266
267
|
const sendMessageBatch = async (messages) => {
|
|
267
268
|
if (messages.length === 0) return;
|
|
268
|
-
for (let i = 0; i < messages.length; i += 5) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
});
|
|
274
|
-
lastResult = {
|
|
275
|
-
messageId: result.messageId,
|
|
276
|
-
chatId: result.chatId
|
|
277
|
-
};
|
|
278
|
-
}
|
|
269
|
+
for (let i = 0; i < messages.length; i += 5) lastResult = await sendBatch(to, messages.slice(i, i + 5), {
|
|
270
|
+
verbose: false,
|
|
271
|
+
cfg,
|
|
272
|
+
accountId: accountId ?? void 0
|
|
273
|
+
});
|
|
279
274
|
};
|
|
280
275
|
const processed = payload.text ? outboundRuntime.processLineMessage(payload.text) : {
|
|
281
276
|
text: "",
|
|
@@ -437,7 +432,12 @@ const lineOutboundAdapter = {
|
|
|
437
432
|
});
|
|
438
433
|
else result = {
|
|
439
434
|
messageId: "processed",
|
|
440
|
-
chatId: to
|
|
435
|
+
chatId: to,
|
|
436
|
+
receipt: createLineSendReceipt({
|
|
437
|
+
messageId: "processed",
|
|
438
|
+
chatId: to,
|
|
439
|
+
kind: "card"
|
|
440
|
+
})
|
|
441
441
|
};
|
|
442
442
|
for (const flexMsg of processed.flexMessages) {
|
|
443
443
|
const flexContents = flexMsg.contents;
|
|
@@ -457,6 +457,55 @@ const lineOutboundAdapter = {
|
|
|
457
457
|
})
|
|
458
458
|
})
|
|
459
459
|
};
|
|
460
|
+
function toLineMessageSendResult(result, kind) {
|
|
461
|
+
const source = result;
|
|
462
|
+
const receipt = result.receipt ?? (result.messageId ? createLineSendReceipt({
|
|
463
|
+
messageId: result.messageId,
|
|
464
|
+
chatId: source.chatId ?? "",
|
|
465
|
+
kind
|
|
466
|
+
}) : void 0);
|
|
467
|
+
if (!receipt) throw new Error("LINE message adapter send did not return a receipt");
|
|
468
|
+
return {
|
|
469
|
+
messageId: result.messageId || receipt.primaryPlatformMessageId,
|
|
470
|
+
receipt
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
const lineMessageAdapter = defineChannelMessageAdapter({
|
|
474
|
+
id: "line",
|
|
475
|
+
durableFinal: { capabilities: {
|
|
476
|
+
text: true,
|
|
477
|
+
media: true,
|
|
478
|
+
messageSendingHooks: true
|
|
479
|
+
} },
|
|
480
|
+
send: {
|
|
481
|
+
text: async ({ cfg, to, text, accountId }) => {
|
|
482
|
+
return toLineMessageSendResult(await lineOutboundAdapter.sendPayload({
|
|
483
|
+
cfg,
|
|
484
|
+
to,
|
|
485
|
+
text,
|
|
486
|
+
accountId,
|
|
487
|
+
payload: { text }
|
|
488
|
+
}), "text");
|
|
489
|
+
},
|
|
490
|
+
media: async ({ cfg, to, text, mediaUrl, accountId }) => {
|
|
491
|
+
return toLineMessageSendResult(await lineOutboundAdapter.sendPayload({
|
|
492
|
+
cfg,
|
|
493
|
+
to,
|
|
494
|
+
text,
|
|
495
|
+
mediaUrl,
|
|
496
|
+
accountId,
|
|
497
|
+
payload: {
|
|
498
|
+
text,
|
|
499
|
+
mediaUrl
|
|
500
|
+
}
|
|
501
|
+
}), "media");
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
receive: {
|
|
505
|
+
defaultAckPolicy: "after_agent_dispatch",
|
|
506
|
+
supportedAckPolicies: ["after_receive_record", "after_agent_dispatch"]
|
|
507
|
+
}
|
|
508
|
+
});
|
|
460
509
|
//#endregion
|
|
461
510
|
//#region extensions/line/src/status.ts
|
|
462
511
|
const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-N91OWW1J.js"));
|
|
@@ -484,7 +533,7 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
|
|
|
484
533
|
});
|
|
485
534
|
//#endregion
|
|
486
535
|
//#region extensions/line/src/channel.ts
|
|
487
|
-
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-
|
|
536
|
+
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-De0BoY61.js"));
|
|
488
537
|
const lineSecurityAdapter = createRestrictSendersChannelSecurity({
|
|
489
538
|
channelKey: "line",
|
|
490
539
|
resolveDmPolicy: (account) => account.config.dmPolicy,
|
|
@@ -530,6 +579,7 @@ const linePlugin = createChatChannelPlugin({
|
|
|
530
579
|
setup: lineSetupAdapter,
|
|
531
580
|
status: lineStatusAdapter,
|
|
532
581
|
gateway: lineGatewayAdapter,
|
|
582
|
+
message: lineMessageAdapter,
|
|
533
583
|
bindings: lineBindingsAdapter,
|
|
534
584
|
conversationBindings: { defaultTopLevelPlacement: "current" },
|
|
535
585
|
agentPrompt: { messageToolHints: () => [
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as linePlugin } from "./channel-
|
|
1
|
+
import { t as linePlugin } from "./channel-DkbpfXGE.js";
|
|
2
2
|
export { linePlugin };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as resolveLineAccount } from "./accounts-UXAvdBo_.js";
|
|
2
|
-
import {
|
|
2
|
+
import { r as validateLineMediaUrl, t as createLineSendReceipt } from "./send-receipt-DSg7S42s.js";
|
|
3
3
|
import { r as createReceiptCard } from "./schedule-cards-BwQChTdM.js";
|
|
4
4
|
import { stripMarkdown, stripMarkdown as stripMarkdown$1 } from "openclaw/plugin-sdk/text-runtime";
|
|
5
5
|
import { messagingApi } from "@line/bot-sdk";
|
|
@@ -319,6 +319,14 @@ function recordLineOutboundActivity(accountId) {
|
|
|
319
319
|
direction: "outbound"
|
|
320
320
|
});
|
|
321
321
|
}
|
|
322
|
+
function resolveLineReceiptKind(messages) {
|
|
323
|
+
const types = new Set(messages.map((message) => message.type));
|
|
324
|
+
if (types.has("audio")) return "voice";
|
|
325
|
+
if (types.has("image") || types.has("video")) return "media";
|
|
326
|
+
if (types.has("flex") || types.has("template") || types.has("location")) return "card";
|
|
327
|
+
if (types.has("text")) return "text";
|
|
328
|
+
return "unknown";
|
|
329
|
+
}
|
|
322
330
|
async function pushLineMessages(to, messages, opts, behavior = {}) {
|
|
323
331
|
if (messages.length === 0) throw new Error("Message must be non-empty for LINE sends");
|
|
324
332
|
const { account, client, chatId } = createLinePushContext(to, opts);
|
|
@@ -335,7 +343,13 @@ async function pushLineMessages(to, messages, opts, behavior = {}) {
|
|
|
335
343
|
if (opts.verbose) logVerbose(behavior.verboseMessage?.(chatId, messages.length) ?? `line: pushed ${messages.length} messages to ${chatId}`);
|
|
336
344
|
return {
|
|
337
345
|
messageId: "push",
|
|
338
|
-
chatId
|
|
346
|
+
chatId,
|
|
347
|
+
receipt: createLineSendReceipt({
|
|
348
|
+
messageId: "push",
|
|
349
|
+
chatId,
|
|
350
|
+
kind: resolveLineReceiptKind(messages),
|
|
351
|
+
messageCount: messages.length
|
|
352
|
+
})
|
|
339
353
|
};
|
|
340
354
|
}
|
|
341
355
|
async function replyLineMessages(replyToken, messages, opts, behavior = {}) {
|
|
@@ -380,7 +394,13 @@ async function sendMessageLine(to, text, opts) {
|
|
|
380
394
|
await replyLineMessages(opts.replyToken, messages, opts, { verboseMessage: () => `line: replied to ${chatId}` });
|
|
381
395
|
return {
|
|
382
396
|
messageId: "reply",
|
|
383
|
-
chatId
|
|
397
|
+
chatId,
|
|
398
|
+
receipt: createLineSendReceipt({
|
|
399
|
+
messageId: "reply",
|
|
400
|
+
chatId,
|
|
401
|
+
kind: resolveLineReceiptKind(messages),
|
|
402
|
+
messageCount: messages.length
|
|
403
|
+
})
|
|
384
404
|
};
|
|
385
405
|
}
|
|
386
406
|
return pushLineMessages(chatId, messages, opts, { verboseMessage: (resolvedChatId) => `line: pushed message to ${resolvedChatId}` });
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-UXAvdBo_.js";
|
|
2
2
|
import { a as resolveLineGroupConfigEntry, n as getLineRuntime, t as buildLineQuickReplyFallbackText } from "./quick-reply-fallback-HskUUWsn.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-
|
|
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-DASNAjBq.js";
|
|
4
4
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
5
|
+
import { createMessageReceiveContext, hasFinalChannelTurnDispatch } from "openclaw/plugin-sdk/channel-message";
|
|
5
6
|
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
6
7
|
import { lowercasePreservingWhitespace, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
|
7
8
|
import { firstDefined, isSenderIdAllowed, mergeDmAllowFromSources } from "openclaw/plugin-sdk/allow-from";
|
|
@@ -9,8 +10,6 @@ import { messagingApi } from "@line/bot-sdk";
|
|
|
9
10
|
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
|
|
10
11
|
import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, waitForAbortSignal } from "openclaw/plugin-sdk/runtime-env";
|
|
11
12
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
12
|
-
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
|
13
|
-
import { hasFinalInboundReplyDispatch } from "openclaw/plugin-sdk/inbound-reply-dispatch";
|
|
14
13
|
import { chunkMarkdownText } from "openclaw/plugin-sdk/reply-runtime";
|
|
15
14
|
import { isRequestBodyLimitError, normalizePluginHttpPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
|
|
16
15
|
import { beginWebhookRequestPipelineOrReject, createWebhookInFlightLimiter, isRequestBodyLimitError as isRequestBodyLimitError$1, readRequestBodyWithLimit, requestBodyErrorToText as requestBodyErrorToText$1 } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
@@ -1049,6 +1048,20 @@ function createLineBot(opts) {
|
|
|
1049
1048
|
};
|
|
1050
1049
|
}
|
|
1051
1050
|
//#endregion
|
|
1051
|
+
//#region extensions/line/src/monitor-durable.ts
|
|
1052
|
+
function hasLineChannelData(payload) {
|
|
1053
|
+
const lineData = payload.channelData?.line;
|
|
1054
|
+
return Boolean(lineData && Object.keys(lineData).length > 0);
|
|
1055
|
+
}
|
|
1056
|
+
function resolveLineDurableReplyOptions(params) {
|
|
1057
|
+
if (params.infoKind !== "final") return false;
|
|
1058
|
+
if (params.replyToken && !params.replyTokenUsed) return false;
|
|
1059
|
+
if (hasLineChannelData(params.payload)) return false;
|
|
1060
|
+
const reply = resolveSendableOutboundReplyParts(params.payload);
|
|
1061
|
+
if (reply.hasMedia || !reply.hasText) return false;
|
|
1062
|
+
return { to: params.to };
|
|
1063
|
+
}
|
|
1064
|
+
//#endregion
|
|
1052
1065
|
//#region extensions/line/src/reply-chunks.ts
|
|
1053
1066
|
async function sendLineReplyChunks(params) {
|
|
1054
1067
|
const hasQuickReplies = Boolean(params.quickReplies?.length);
|
|
@@ -1149,6 +1162,7 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1149
1162
|
res.end(JSON.stringify({ error: "Method Not Allowed" }));
|
|
1150
1163
|
return;
|
|
1151
1164
|
}
|
|
1165
|
+
let receiveContext;
|
|
1152
1166
|
try {
|
|
1153
1167
|
const signatureHeader = req.headers["x-line-signature"];
|
|
1154
1168
|
const signature = typeof signatureHeader === "string" ? signatureHeader.trim() : Array.isArray(signatureHeader) ? (signatureHeader[0] ?? "").trim() : "";
|
|
@@ -1175,14 +1189,25 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1175
1189
|
return;
|
|
1176
1190
|
}
|
|
1177
1191
|
params.onRequestAuthenticated?.();
|
|
1192
|
+
receiveContext = createMessageReceiveContext({
|
|
1193
|
+
id: `${Date.now()}:line:webhook`,
|
|
1194
|
+
channel: "line",
|
|
1195
|
+
message: body,
|
|
1196
|
+
ackPolicy: body.events?.length ? "after_agent_dispatch" : "after_receive_record",
|
|
1197
|
+
onAck: () => {
|
|
1198
|
+
res.statusCode = 200;
|
|
1199
|
+
res.setHeader("Content-Type", "application/json");
|
|
1200
|
+
res.end(JSON.stringify({ status: "ok" }));
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1178
1203
|
if (body.events && body.events.length > 0) {
|
|
1179
1204
|
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
1180
1205
|
await params.bot.handleWebhook(body);
|
|
1181
1206
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
res.end(JSON.stringify({ status: "ok" }));
|
|
1207
|
+
const ackStage = body.events?.length ? "agent_dispatch" : "receive_record";
|
|
1208
|
+
if (receiveContext.shouldAckAfter(ackStage)) await receiveContext.ack();
|
|
1185
1209
|
} catch (err) {
|
|
1210
|
+
await receiveContext?.nack(err);
|
|
1186
1211
|
if (isRequestBodyLimitError$1(err, "PAYLOAD_TOO_LARGE")) {
|
|
1187
1212
|
res.statusCode = 413;
|
|
1188
1213
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -1287,12 +1312,6 @@ async function monitorLineProvider(opts) {
|
|
|
1287
1312
|
try {
|
|
1288
1313
|
const textLimit = 5e3;
|
|
1289
1314
|
let replyTokenUsed = false;
|
|
1290
|
-
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
|
1291
|
-
cfg: config,
|
|
1292
|
-
agentId: route.agentId,
|
|
1293
|
-
channel: "line",
|
|
1294
|
-
accountId: route.accountId
|
|
1295
|
-
});
|
|
1296
1315
|
const core = getLineRuntime();
|
|
1297
1316
|
const turnResult = await core.channel.turn.run({
|
|
1298
1317
|
channel: "line",
|
|
@@ -1314,9 +1333,15 @@ async function monitorLineProvider(opts) {
|
|
|
1314
1333
|
recordInboundSession: core.channel.session.recordInboundSession,
|
|
1315
1334
|
dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
1316
1335
|
record: ctx.turn.record,
|
|
1317
|
-
|
|
1318
|
-
replyOptions: { onModelSelected },
|
|
1336
|
+
replyPipeline: {},
|
|
1319
1337
|
delivery: {
|
|
1338
|
+
durable: (payload, info) => resolveLineDurableReplyOptions({
|
|
1339
|
+
payload,
|
|
1340
|
+
infoKind: info.kind,
|
|
1341
|
+
to: ctxPayload.From,
|
|
1342
|
+
replyToken,
|
|
1343
|
+
replyTokenUsed
|
|
1344
|
+
}),
|
|
1320
1345
|
deliver: async (payload) => {
|
|
1321
1346
|
const lineData = payload.channelData?.line ?? {};
|
|
1322
1347
|
if (ctx.userId && !ctx.isGroup) showLoadingAnimation(ctx.userId, {
|
|
@@ -1365,7 +1390,7 @@ async function monitorLineProvider(opts) {
|
|
|
1365
1390
|
})
|
|
1366
1391
|
}
|
|
1367
1392
|
});
|
|
1368
|
-
if (!
|
|
1393
|
+
if (!hasFinalChannelTurnDispatch(turnResult.dispatched ? turnResult.dispatchResult : void 0)) logVerbose(`line: no response generated for message from ${ctxPayload.From}`);
|
|
1369
1394
|
} catch (err) {
|
|
1370
1395
|
runtime.error?.(danger(`line: auto-reply failed: ${String(err)}`));
|
|
1371
1396
|
if (replyToken) try {
|
|
@@ -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-DASNAjBq.js";
|
|
2
2
|
export { buildTemplateMessageFromPayload, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
|
package/dist/runtime-api.js
CHANGED
|
@@ -3,17 +3,18 @@ import { a as resolveLineGroupConfigEntry, i as resolveExactLineGroupConfigKey,
|
|
|
3
3
|
import { a as createMediaPlayerCard, i as createDeviceControlCard, n as parseLineDirectives, o as LineChannelConfigSchema, r as createAppleTvRemoteCard, s as LineConfigSchema, t as hasLineDirectives } from "./reply-payload-transform-Bmf8GnSU.js";
|
|
4
4
|
import { n as createEventCard, r as createReceiptCard, t as createAgendaCard } from "./schedule-cards-BwQChTdM.js";
|
|
5
5
|
import { a as createListCard, i as createInfoCard, n as createCarousel, o as createNotificationBubble, r as createImageCard, t as createActionCard } from "./basic-cards-7DTG6E-A.js";
|
|
6
|
-
import { A as buildTemplateMessageFromPayload, B as createYesNoConfirm, C as pushMessagesLine, D as sendMessageLine, E as replyMessageLine, F as createImageCarousel, G as toFlexMessage, H as messageAction, 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, U as postbackAction, V as datetimePickerAction, W as uriAction, _ 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-
|
|
7
|
-
import { a as validateLineSignature, c as isSenderAllowed, i as parseLineWebhookBody, l as normalizeAllowFrom, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider, u as normalizeDmAllowFromWithStore } from "./monitor-
|
|
6
|
+
import { A as buildTemplateMessageFromPayload, B as createYesNoConfirm, C as pushMessagesLine, D as sendMessageLine, E as replyMessageLine, F as createImageCarousel, G as toFlexMessage, H as messageAction, 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, U as postbackAction, V as datetimePickerAction, W as uriAction, _ 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-DASNAjBq.js";
|
|
7
|
+
import { a as validateLineSignature, c as isSenderAllowed, i as parseLineWebhookBody, l as normalizeAllowFrom, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider, u as normalizeDmAllowFromWithStore } from "./monitor-vLNFYADv.js";
|
|
8
8
|
import { t as probeLineBot } from "./probe-D9BPzb2n.js";
|
|
9
9
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
|
|
10
10
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
11
|
-
import {
|
|
11
|
+
import { createMessageReceiveContext } from "openclaw/plugin-sdk/channel-message";
|
|
12
12
|
import { DEFAULT_ACCOUNT_ID, formatDocsLink, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
13
13
|
import { buildComputedAccountStatusSnapshot, buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/status-helpers";
|
|
14
14
|
import { messagingApi } from "@line/bot-sdk";
|
|
15
15
|
import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
16
16
|
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/agent-media-payload";
|
|
17
|
+
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
17
18
|
import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media";
|
|
18
19
|
//#region extensions/line/src/webhook.ts
|
|
19
20
|
const LINE_WEBHOOK_MAX_RAW_BODY_BYTES = 64 * 1024;
|
|
@@ -29,6 +30,7 @@ function parseWebhookBody(rawBody) {
|
|
|
29
30
|
function createLineWebhookMiddleware(options) {
|
|
30
31
|
const { channelSecret, onEvents, runtime } = options;
|
|
31
32
|
return async (req, res, _next) => {
|
|
33
|
+
let receiveContext;
|
|
32
34
|
try {
|
|
33
35
|
const signature = req.headers["x-line-signature"];
|
|
34
36
|
if (!signature || typeof signature !== "string") {
|
|
@@ -54,12 +56,23 @@ function createLineWebhookMiddleware(options) {
|
|
|
54
56
|
res.status(400).json({ error: "Invalid webhook payload" });
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
59
|
+
receiveContext = createMessageReceiveContext({
|
|
60
|
+
id: `${Date.now()}:line:webhook`,
|
|
61
|
+
channel: "line",
|
|
62
|
+
message: body,
|
|
63
|
+
ackPolicy: body.events?.length ? "after_agent_dispatch" : "after_receive_record",
|
|
64
|
+
onAck: () => {
|
|
65
|
+
res.status(200).json({ status: "ok" });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
57
68
|
if (body.events && body.events.length > 0) {
|
|
58
69
|
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
59
70
|
await onEvents(body);
|
|
60
71
|
}
|
|
61
|
-
|
|
72
|
+
const ackStage = body.events?.length ? "agent_dispatch" : "receive_record";
|
|
73
|
+
if (receiveContext.shouldAckAfter(ackStage)) await receiveContext.ack();
|
|
62
74
|
} catch (err) {
|
|
75
|
+
await receiveContext?.nack(err);
|
|
63
76
|
runtime?.error?.(danger(`line webhook error: ${String(err)}`));
|
|
64
77
|
if (!res.headersSent) res.status(500).json({ error: "Internal server error" });
|
|
65
78
|
}
|
|
@@ -117,7 +130,7 @@ async function createRichMenu(menu, opts) {
|
|
|
117
130
|
async function uploadRichMenuImage(richMenuId, imagePath, opts) {
|
|
118
131
|
const blobClient = getBlobClient(opts);
|
|
119
132
|
const media = await loadWebMediaRaw(imagePath, { localRoots: opts.mediaLocalRoots ?? getAgentScopedMediaLocalRoots(opts.cfg) });
|
|
120
|
-
const contentType = media.contentType === "image/png" || media.contentType === "image/jpeg" ? media.contentType :
|
|
133
|
+
const contentType = media.contentType === "image/png" || media.contentType === "image/jpeg" ? media.contentType : mimeTypeFromFilePath(imagePath) === "image/png" ? "image/png" : "image/jpeg";
|
|
121
134
|
const imageBytes = new ArrayBuffer(media.buffer.byteLength);
|
|
122
135
|
new Uint8Array(imageBytes).set(media.buffer);
|
|
123
136
|
await blobClient.setRichMenuImage(richMenuId, new Blob([imageBytes], { type: contentType }));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
|
|
1
2
|
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
2
3
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
3
4
|
//#region extensions/line/src/outbound-media.ts
|
|
@@ -52,4 +53,21 @@ async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
|
|
|
52
53
|
throw new Error("LINE outbound media currently requires a public HTTPS URL");
|
|
53
54
|
}
|
|
54
55
|
//#endregion
|
|
55
|
-
|
|
56
|
+
//#region extensions/line/src/send-receipt.ts
|
|
57
|
+
function createLineSendReceipt(params) {
|
|
58
|
+
const messageId = params.messageId.trim();
|
|
59
|
+
const chatId = params.chatId.trim();
|
|
60
|
+
return createMessageReceiptFromOutboundResults({
|
|
61
|
+
results: messageId ? [{
|
|
62
|
+
channel: "line",
|
|
63
|
+
messageId,
|
|
64
|
+
chatId,
|
|
65
|
+
conversationId: chatId,
|
|
66
|
+
meta: { messageCount: params.messageCount ?? 1 }
|
|
67
|
+
}] : [],
|
|
68
|
+
...chatId ? { threadId: chatId } : {},
|
|
69
|
+
kind: params.kind ?? "unknown"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { resolveLineOutboundMedia as n, validateLineMediaUrl as r, createLineSendReceipt as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/line",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.9-beta.1",
|
|
4
4
|
"description": "OpenClaw LINE channel plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"openclaw": "workspace:*"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.5.
|
|
18
|
+
"openclaw": ">=2026.5.9-beta.1"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"minHostVersion": ">=2026.4.10"
|
|
46
46
|
},
|
|
47
47
|
"compat": {
|
|
48
|
-
"pluginApi": ">=2026.5.
|
|
48
|
+
"pluginApi": ">=2026.5.9-beta.1"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.5.
|
|
51
|
+
"openclawVersion": "2026.5.9-beta.1"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|