@openclaw/line 2026.5.14-beta.1 → 2026.5.16-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 +2 -2
- package/dist/{basic-cards-7DTG6E-A.js → basic-cards-BISytiSa.js} +1 -1
- package/dist/{card-command-Dtj3Rx7T.js → card-command-B1iy-vnt.js} +2 -2
- package/dist/{channel-Bf5i9X7p.js → channel-CdkYokiy.js} +9 -11
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-DDWuHIBF.js +4 -0
- package/dist/index.js +1 -1
- package/dist/{markdown-to-line-CrXM4Bcw.js → markdown-to-line-DsVRfm5Z.js} +3 -2
- package/dist/{monitor-kPDKy2FH.js → monitor-ClwWeWOU.js} +21 -20
- package/dist/monitor.runtime-D_r2vk-6.js +2 -0
- package/dist/{outbound.runtime-BQf7o-dT.js → outbound.runtime-BYwkszx0.js} +1 -1
- package/dist/probe.runtime-tp_IgA31.js +2 -0
- package/dist/{reply-payload-transform-C3Dx5kxs.js → reply-payload-transform-D2yrTDIV.js} +127 -2
- package/dist/runtime-api.js +12 -11
- package/dist/setup-api.js +1 -1
- package/dist/{setup-surface-BGjkWr_B.js → setup-surface-DAaSncHr.js} +35 -34
- package/package.json +4 -4
- package/dist/channel.runtime-Bj6SGPEZ.js +0 -4
- package/dist/monitor.runtime-C0J3cWLH.js +0 -2
- package/dist/probe.runtime-XkNsM6LR.js +0 -2
- package/dist/quick-reply-fallback-HskUUWsn.js +0 -55
- package/dist/send-receipt-zpAfxQ58.js +0 -73
- /package/dist/{probe-Cla0gZ0b.js → probe-CiPafR4R.js} +0 -0
- /package/dist/{schedule-cards-BwQChTdM.js → schedule-cards-D-yZMHDE.js} +0 -0
package/dist/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-
|
|
2
|
-
import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-
|
|
1
|
+
import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-CdkYokiy.js";
|
|
2
|
+
import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-DAaSncHr.js";
|
|
3
3
|
//#region extensions/line/src/channel.setup.ts
|
|
4
4
|
const lineSetupPlugin = {
|
|
5
5
|
id: "line",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as createReceiptCard } from "./schedule-cards-
|
|
2
|
-
import { a as createListCard, i as createInfoCard, r as createImageCard, t as createActionCard } from "./basic-cards-
|
|
1
|
+
import { r as createReceiptCard } from "./schedule-cards-D-yZMHDE.js";
|
|
2
|
+
import { a as createListCard, i as createInfoCard, r as createImageCard, t as createActionCard } from "./basic-cards-BISytiSa.js";
|
|
3
3
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
4
|
//#region extensions/line/src/card-command.ts
|
|
5
5
|
const CARD_USAGE = `Usage: /card <type> "title" "body" [options]
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-UXAvdBo_.js";
|
|
2
|
-
import { n as lineSetupAdapter, r as hasLineCredentials, t as lineSetupWizard } from "./setup-surface-
|
|
3
|
-
import {
|
|
4
|
-
import { n as parseLineDirectives, o as LineChannelConfigSchema, t as hasLineDirectives } from "./reply-payload-transform-C3Dx5kxs.js";
|
|
5
|
-
import { n as resolveLineOutboundMedia, t as createLineSendReceipt } from "./send-receipt-zpAfxQ58.js";
|
|
2
|
+
import { n as lineSetupAdapter, r as hasLineCredentials, t as lineSetupWizard } from "./setup-surface-DAaSncHr.js";
|
|
3
|
+
import { c as resolveLineOutboundMedia, f as LineChannelConfigSchema, m as resolveExactLineGroupConfigKey, n as parseLineDirectives, o as createLineSendReceipt, s as buildLineQuickReplyFallbackText, t as hasLineDirectives, u as getLineRuntime } from "./reply-payload-transform-D2yrTDIV.js";
|
|
6
4
|
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
7
5
|
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
8
6
|
import { createRestrictSendersChannelSecurity, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
@@ -107,8 +105,8 @@ const lineChannelPluginCommon = {
|
|
|
107
105
|
};
|
|
108
106
|
//#endregion
|
|
109
107
|
//#region extensions/line/src/gateway.ts
|
|
110
|
-
const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-
|
|
111
|
-
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-
|
|
108
|
+
const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-tp_IgA31.js"));
|
|
109
|
+
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-D_r2vk-6.js"));
|
|
112
110
|
const lineGatewayAdapter = {
|
|
113
111
|
startAccount: async (ctx) => {
|
|
114
112
|
const account = ctx.account;
|
|
@@ -212,7 +210,7 @@ function resolveLineGroupRequireMention(params) {
|
|
|
212
210
|
}
|
|
213
211
|
//#endregion
|
|
214
212
|
//#region extensions/line/src/outbound.ts
|
|
215
|
-
const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-
|
|
213
|
+
const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-BYwkszx0.js"));
|
|
216
214
|
function isLineUserTarget(target) {
|
|
217
215
|
const normalized = target.trim().replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
|
|
218
216
|
return /^U/i.test(normalized);
|
|
@@ -502,13 +500,13 @@ const lineMessageAdapter = defineChannelMessageAdapter({
|
|
|
502
500
|
}
|
|
503
501
|
},
|
|
504
502
|
receive: {
|
|
505
|
-
defaultAckPolicy: "
|
|
506
|
-
supportedAckPolicies: ["after_receive_record"
|
|
503
|
+
defaultAckPolicy: "after_receive_record",
|
|
504
|
+
supportedAckPolicies: ["after_receive_record"]
|
|
507
505
|
}
|
|
508
506
|
});
|
|
509
507
|
//#endregion
|
|
510
508
|
//#region extensions/line/src/status.ts
|
|
511
|
-
const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-
|
|
509
|
+
const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-tp_IgA31.js"));
|
|
512
510
|
const collectLineStatusIssues = createDependentCredentialStatusIssueCollector({
|
|
513
511
|
channel: "line",
|
|
514
512
|
dependencySourceKey: "tokenSource",
|
|
@@ -533,7 +531,7 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
|
|
|
533
531
|
});
|
|
534
532
|
//#endregion
|
|
535
533
|
//#region extensions/line/src/channel.ts
|
|
536
|
-
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-
|
|
534
|
+
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-DDWuHIBF.js"));
|
|
537
535
|
const lineSecurityAdapter = createRestrictSendersChannelSecurity({
|
|
538
536
|
channelKey: "line",
|
|
539
537
|
resolveDmPolicy: (account) => account.config.dmPolicy,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as linePlugin } from "./channel-
|
|
1
|
+
import { t as linePlugin } from "./channel-CdkYokiy.js";
|
|
2
2
|
export { linePlugin };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ let lineCardCommandPromise = null;
|
|
|
4
4
|
async function loadLineCardCommand(api) {
|
|
5
5
|
lineCardCommandPromise ??= (async () => {
|
|
6
6
|
let registered = null;
|
|
7
|
-
const { registerLineCardCommand } = await import("./card-command-
|
|
7
|
+
const { registerLineCardCommand } = await import("./card-command-B1iy-vnt.js");
|
|
8
8
|
registerLineCardCommand({
|
|
9
9
|
...api,
|
|
10
10
|
registerCommand(command) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as resolveLineAccount } from "./accounts-UXAvdBo_.js";
|
|
2
|
-
import {
|
|
3
|
-
import { r as createReceiptCard } from "./schedule-cards-
|
|
2
|
+
import { l as validateLineMediaUrl, o as createLineSendReceipt } from "./reply-payload-transform-D2yrTDIV.js";
|
|
3
|
+
import { r as createReceiptCard } from "./schedule-cards-D-yZMHDE.js";
|
|
4
4
|
import { messagingApi } from "@line/bot-sdk";
|
|
5
5
|
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
6
6
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
@@ -246,6 +246,7 @@ function normalizeTarget(to) {
|
|
|
246
246
|
if (!trimmed) throw new Error("Recipient is required for LINE sends");
|
|
247
247
|
const normalized = trimmed.replace(/^line:group:/i, "").replace(/^line:room:/i, "").replace(/^line:user:/i, "").replace(/^line:/i, "");
|
|
248
248
|
if (!normalized) throw new Error("Recipient is required for LINE sends");
|
|
249
|
+
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.slice(0, 4)}…`);
|
|
249
250
|
return normalized;
|
|
250
251
|
}
|
|
251
252
|
function isLineUserChatId(chatId) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-UXAvdBo_.js";
|
|
2
|
-
import {
|
|
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 { h as resolveLineGroupConfigEntry, s as buildLineQuickReplyFallbackText, u as getLineRuntime } from "./reply-payload-transform-D2yrTDIV.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-DsVRfm5Z.js";
|
|
4
4
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
5
5
|
import { createMessageReceiveContext, hasFinalChannelTurnDispatch } from "openclaw/plugin-sdk/channel-message";
|
|
6
6
|
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
@@ -13,7 +13,7 @@ import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runt
|
|
|
13
13
|
import { chunkMarkdownText } from "openclaw/plugin-sdk/reply-runtime";
|
|
14
14
|
import { isRequestBodyLimitError, normalizePluginHttpPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
|
|
15
15
|
import { beginWebhookRequestPipelineOrReject, createWebhookInFlightLimiter, isRequestBodyLimitError as isRequestBodyLimitError$1, readRequestBodyWithLimit, requestBodyErrorToText as requestBodyErrorToText$1 } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
16
|
-
import { DEFAULT_GROUP_HISTORY_LIMIT,
|
|
16
|
+
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
17
17
|
import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
18
18
|
import { buildMentionRegexes, formatInboundEnvelope, formatLocationText, matchesMentionPatterns, resolveInboundSessionEnvelopeContext, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
19
19
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
@@ -427,11 +427,10 @@ async function buildLineMessageContext(params) {
|
|
|
427
427
|
});
|
|
428
428
|
}
|
|
429
429
|
const historyKey = isGroup ? peerId : void 0;
|
|
430
|
-
const inboundHistory = historyKey && groupHistories && (historyLimit ?? 0) > 0 ? (groupHistories
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
})) : void 0;
|
|
430
|
+
const inboundHistory = historyKey && groupHistories && (historyLimit ?? 0) > 0 ? createChannelHistoryWindow({ historyMap: groupHistories }).buildInboundHistory({
|
|
431
|
+
historyKey,
|
|
432
|
+
limit: historyLimit ?? 0
|
|
433
|
+
}) : void 0;
|
|
435
434
|
const finalized = await finalizeLineInboundContext({
|
|
436
435
|
cfg,
|
|
437
436
|
account,
|
|
@@ -823,8 +822,7 @@ async function handleMessageEvent(event, context) {
|
|
|
823
822
|
logVerbose(`line: skipping group message (requireMention, not mentioned)`);
|
|
824
823
|
const historyKey = groupId ?? roomId;
|
|
825
824
|
const senderId = sourceInfo.userId ?? "unknown";
|
|
826
|
-
if (historyKey && context.groupHistories)
|
|
827
|
-
historyMap: context.groupHistories,
|
|
825
|
+
if (historyKey && context.groupHistories) createChannelHistoryWindow({ historyMap: context.groupHistories }).record({
|
|
828
826
|
historyKey,
|
|
829
827
|
limit: context.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT,
|
|
830
828
|
entry: {
|
|
@@ -863,8 +861,7 @@ async function handleMessageEvent(event, context) {
|
|
|
863
861
|
await processMessage(messageContext);
|
|
864
862
|
if (isGroup && context.groupHistories) {
|
|
865
863
|
const historyKey = groupId ?? roomId;
|
|
866
|
-
if (historyKey && context.groupHistories.has(historyKey))
|
|
867
|
-
historyMap: context.groupHistories,
|
|
864
|
+
if (historyKey && context.groupHistories.has(historyKey)) createChannelHistoryWindow({ historyMap: context.groupHistories }).clear({
|
|
868
865
|
historyKey,
|
|
869
866
|
limit: context.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT
|
|
870
867
|
});
|
|
@@ -1072,6 +1069,9 @@ async function readLineWebhookRequestBody(req, maxBytes = LINE_WEBHOOK_MAX_BODY_
|
|
|
1072
1069
|
timeoutMs
|
|
1073
1070
|
});
|
|
1074
1071
|
}
|
|
1072
|
+
function logLineWebhookDispatchError(runtime, err) {
|
|
1073
|
+
runtime?.error?.(danger(`line webhook dispatch failed: ${String(err)}`));
|
|
1074
|
+
}
|
|
1075
1075
|
function createLineNodeWebhookHandler(params) {
|
|
1076
1076
|
const maxBodyBytes = params.maxBodyBytes ?? LINE_WEBHOOK_MAX_BODY_BYTES;
|
|
1077
1077
|
const readBody = params.readBody ?? readLineWebhookRequestBody;
|
|
@@ -1125,19 +1125,18 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1125
1125
|
id: `${Date.now()}:line:webhook`,
|
|
1126
1126
|
channel: "line",
|
|
1127
1127
|
message: body,
|
|
1128
|
-
ackPolicy:
|
|
1128
|
+
ackPolicy: "after_receive_record",
|
|
1129
1129
|
onAck: () => {
|
|
1130
1130
|
res.statusCode = 200;
|
|
1131
1131
|
res.setHeader("Content-Type", "application/json");
|
|
1132
1132
|
res.end(JSON.stringify({ status: "ok" }));
|
|
1133
1133
|
}
|
|
1134
1134
|
});
|
|
1135
|
+
if (receiveContext.shouldAckAfter("receive_record")) await receiveContext.ack();
|
|
1135
1136
|
if (body.events && body.events.length > 0) {
|
|
1136
1137
|
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
1137
|
-
|
|
1138
|
+
Promise.resolve().then(() => params.bot.handleWebhook(body)).catch((err) => logLineWebhookDispatchError(params.runtime, err));
|
|
1138
1139
|
}
|
|
1139
|
-
const ackStage = body.events?.length ? "agent_dispatch" : "receive_record";
|
|
1140
|
-
if (receiveContext.shouldAckAfter(ackStage)) await receiveContext.ack();
|
|
1141
1140
|
} catch (err) {
|
|
1142
1141
|
await receiveContext?.nack(err);
|
|
1143
1142
|
if (isRequestBodyLimitError$1(err, "PAYLOAD_TOO_LARGE")) {
|
|
@@ -1414,13 +1413,15 @@ async function monitorLineProvider(opts) {
|
|
|
1414
1413
|
return;
|
|
1415
1414
|
}
|
|
1416
1415
|
requestLifecycle.release();
|
|
1417
|
-
if (body.events && body.events.length > 0) {
|
|
1418
|
-
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
1419
|
-
await match.target.bot.handleWebhook(body);
|
|
1420
|
-
}
|
|
1421
1416
|
res.statusCode = 200;
|
|
1422
1417
|
res.setHeader("Content-Type", "application/json");
|
|
1423
1418
|
res.end(JSON.stringify({ status: "ok" }));
|
|
1419
|
+
if (body.events && body.events.length > 0) {
|
|
1420
|
+
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
1421
|
+
Promise.resolve().then(() => match.target.bot.handleWebhook(body)).catch((err) => {
|
|
1422
|
+
match.target.runtime.error?.(danger(`line webhook dispatch failed: ${String(err)}`));
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1424
1425
|
} catch (err) {
|
|
1425
1426
|
if (isRequestBodyLimitError(err, "PAYLOAD_TOO_LARGE")) {
|
|
1426
1427
|
res.statusCode = 413;
|
|
@@ -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-DsVRfm5Z.js";
|
|
2
2
|
export { buildTemplateMessageFromPayload, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
|
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
import { n as createEventCard, t as createAgendaCard } from "./schedule-cards-
|
|
1
|
+
import { n as createEventCard, t as createAgendaCard } from "./schedule-cards-D-yZMHDE.js";
|
|
2
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
3
|
+
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
2
4
|
import { buildChannelConfigSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
3
5
|
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
|
|
4
6
|
import { z } from "zod";
|
|
7
|
+
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
8
|
+
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
|
|
9
|
+
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
5
10
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
|
+
//#region extensions/line/src/group-keys.ts
|
|
12
|
+
function resolveLineGroupLookupIds(groupId) {
|
|
13
|
+
const normalized = groupId?.trim();
|
|
14
|
+
if (!normalized) return [];
|
|
15
|
+
if (normalized.startsWith("group:") || normalized.startsWith("room:")) {
|
|
16
|
+
const rawId = normalized.split(":").slice(1).join(":");
|
|
17
|
+
return rawId ? [rawId, normalized] : [normalized];
|
|
18
|
+
}
|
|
19
|
+
return [
|
|
20
|
+
normalized,
|
|
21
|
+
`group:${normalized}`,
|
|
22
|
+
`room:${normalized}`
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
function resolveLineGroupConfigEntry(groups, params) {
|
|
26
|
+
if (!groups) return;
|
|
27
|
+
for (const candidate of resolveLineGroupLookupIds(params.groupId)) {
|
|
28
|
+
const hit = groups[candidate];
|
|
29
|
+
if (hit) return hit;
|
|
30
|
+
}
|
|
31
|
+
for (const candidate of resolveLineGroupLookupIds(params.roomId)) {
|
|
32
|
+
const hit = groups[candidate];
|
|
33
|
+
if (hit) return hit;
|
|
34
|
+
}
|
|
35
|
+
return groups["*"];
|
|
36
|
+
}
|
|
37
|
+
function resolveLineGroupsConfig(cfg, accountId) {
|
|
38
|
+
const lineConfig = cfg.channels?.line;
|
|
39
|
+
if (!lineConfig) return;
|
|
40
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
41
|
+
return resolveAccountEntry(lineConfig.accounts, normalizedAccountId)?.groups ?? lineConfig.groups;
|
|
42
|
+
}
|
|
43
|
+
function resolveExactLineGroupConfigKey(params) {
|
|
44
|
+
const groups = resolveLineGroupsConfig(params.cfg, params.accountId);
|
|
45
|
+
if (!groups) return;
|
|
46
|
+
return resolveLineGroupLookupIds(params.groupId).find((candidate) => Object.hasOwn(groups, candidate));
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
6
49
|
//#region extensions/line/src/config-schema.ts
|
|
7
50
|
const DmPolicySchema = z.enum([
|
|
8
51
|
"open",
|
|
@@ -71,6 +114,88 @@ const LineConfigSchema = LineCommonConfigSchemaBase.extend({
|
|
|
71
114
|
});
|
|
72
115
|
const LineChannelConfigSchema = buildChannelConfigSchema(LineConfigSchema);
|
|
73
116
|
//#endregion
|
|
117
|
+
//#region extensions/line/src/runtime.ts
|
|
118
|
+
const { setRuntime: setLineRuntime, clearRuntime: clearLineRuntime, getRuntime: getLineRuntime } = createPluginRuntimeStore({
|
|
119
|
+
pluginId: "line",
|
|
120
|
+
errorMessage: "LINE runtime not initialized - plugin not registered"
|
|
121
|
+
});
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region extensions/line/src/outbound-media.ts
|
|
124
|
+
const LINE_OUTBOUND_MEDIA_SSRF_POLICY = { allowPrivateNetwork: false };
|
|
125
|
+
async function validateLineMediaUrl(url) {
|
|
126
|
+
let parsed;
|
|
127
|
+
try {
|
|
128
|
+
parsed = new URL(url);
|
|
129
|
+
} catch {
|
|
130
|
+
throw new Error(`LINE outbound media URL must be a valid URL: ${url}`);
|
|
131
|
+
}
|
|
132
|
+
if (parsed.protocol !== "https:") throw new Error(`LINE outbound media URL must use HTTPS: ${url}`);
|
|
133
|
+
if (url.length > 2e3) throw new Error(`LINE outbound media URL must be 2000 chars or less (got ${url.length})`);
|
|
134
|
+
await resolvePinnedHostnameWithPolicy(parsed.hostname, { policy: LINE_OUTBOUND_MEDIA_SSRF_POLICY });
|
|
135
|
+
}
|
|
136
|
+
function isHttpsUrl(url) {
|
|
137
|
+
try {
|
|
138
|
+
return new URL(url).protocol === "https:";
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function detectLineMediaKindFromUrl(url) {
|
|
144
|
+
try {
|
|
145
|
+
const pathname = normalizeLowercaseStringOrEmpty(new URL(url).pathname);
|
|
146
|
+
if (/\.(png|jpe?g|gif|webp|bmp|heic|heif|avif)$/i.test(pathname)) return "image";
|
|
147
|
+
if (/\.(mp4|mov|m4v|webm)$/i.test(pathname)) return "video";
|
|
148
|
+
if (/\.(mp3|m4a|aac|wav|ogg|oga)$/i.test(pathname)) return "audio";
|
|
149
|
+
} catch {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
|
|
154
|
+
const trimmedUrl = mediaUrl.trim();
|
|
155
|
+
if (isHttpsUrl(trimmedUrl)) {
|
|
156
|
+
await validateLineMediaUrl(trimmedUrl);
|
|
157
|
+
const previewImageUrl = opts.previewImageUrl?.trim();
|
|
158
|
+
if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
|
|
159
|
+
return {
|
|
160
|
+
mediaUrl: trimmedUrl,
|
|
161
|
+
mediaKind: opts.mediaKind ?? (typeof opts.durationMs === "number" ? "audio" : void 0) ?? (opts.trackingId?.trim() ? "video" : void 0) ?? detectLineMediaKindFromUrl(trimmedUrl) ?? "image",
|
|
162
|
+
...previewImageUrl ? { previewImageUrl } : {},
|
|
163
|
+
...typeof opts.durationMs === "number" ? { durationMs: opts.durationMs } : {},
|
|
164
|
+
...opts.trackingId ? { trackingId: opts.trackingId } : {}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
if (new URL(trimmedUrl).protocol !== "https:") throw new Error(`LINE outbound media URL must use HTTPS: ${trimmedUrl}`);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
if (e instanceof Error && e.message.startsWith("LINE outbound")) throw e;
|
|
171
|
+
}
|
|
172
|
+
throw new Error("LINE outbound media currently requires a public HTTPS URL");
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region extensions/line/src/quick-reply-fallback.ts
|
|
176
|
+
function buildLineQuickReplyFallbackText(labels) {
|
|
177
|
+
const normalized = (labels ?? []).map((label) => label.trim()).filter(Boolean).slice(0, 13);
|
|
178
|
+
if (normalized.length === 0) return "Choose an option.";
|
|
179
|
+
return `Options:\n${normalized.map((label) => `- ${label}`).join("\n")}`;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region extensions/line/src/send-receipt.ts
|
|
183
|
+
function createLineSendReceipt(params) {
|
|
184
|
+
const messageId = params.messageId.trim();
|
|
185
|
+
const chatId = params.chatId.trim();
|
|
186
|
+
return createMessageReceiptFromOutboundResults({
|
|
187
|
+
results: messageId ? [{
|
|
188
|
+
channel: "line",
|
|
189
|
+
messageId,
|
|
190
|
+
chatId,
|
|
191
|
+
conversationId: chatId,
|
|
192
|
+
meta: { messageCount: params.messageCount ?? 1 }
|
|
193
|
+
}] : [],
|
|
194
|
+
...chatId ? { threadId: chatId } : {},
|
|
195
|
+
kind: params.kind ?? "unknown"
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
74
199
|
//#region extensions/line/src/flex-templates/media-control-cards.ts
|
|
75
200
|
/**
|
|
76
201
|
* Create a media player card for Sonos, Spotify, Apple Music, etc.
|
|
@@ -727,4 +852,4 @@ function hasLineDirectives(text) {
|
|
|
727
852
|
return /\[\[(quick_replies|location|confirm|buttons|media_player|event|agenda|device|appletv_remote):/i.test(text);
|
|
728
853
|
}
|
|
729
854
|
//#endregion
|
|
730
|
-
export { createMediaPlayerCard as a, createDeviceControlCard as i, parseLineDirectives as n,
|
|
855
|
+
export { resolveLineGroupsConfig as _, createMediaPlayerCard as a, resolveLineOutboundMedia as c, setLineRuntime as d, LineChannelConfigSchema as f, resolveLineGroupLookupIds as g, resolveLineGroupConfigEntry as h, createDeviceControlCard as i, validateLineMediaUrl as l, resolveExactLineGroupConfigKey as m, parseLineDirectives as n, createLineSendReceipt as o, LineConfigSchema as p, createAppleTvRemoteCard as r, buildLineQuickReplyFallbackText as s, hasLineDirectives as t, getLineRuntime as u };
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { i as resolveLineAccount, n as normalizeAccountId, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-UXAvdBo_.js";
|
|
2
|
-
import { a as resolveLineGroupConfigEntry, i as resolveExactLineGroupConfigKey,
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { t as probeLineBot } from "./probe-Cla0gZ0b.js";
|
|
2
|
+
import { _ as resolveLineGroupsConfig, a as createMediaPlayerCard, d as setLineRuntime, f as LineChannelConfigSchema, g as resolveLineGroupLookupIds, h as resolveLineGroupConfigEntry, i as createDeviceControlCard, m as resolveExactLineGroupConfigKey, n as parseLineDirectives, p as LineConfigSchema, r as createAppleTvRemoteCard, t as hasLineDirectives } from "./reply-payload-transform-D2yrTDIV.js";
|
|
3
|
+
import { n as createEventCard, r as createReceiptCard, t as createAgendaCard } from "./schedule-cards-D-yZMHDE.js";
|
|
4
|
+
import { a as createListCard, i as createInfoCard, n as createCarousel, o as createNotificationBubble, r as createImageCard, t as createActionCard } from "./basic-cards-BISytiSa.js";
|
|
5
|
+
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-DsVRfm5Z.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-ClwWeWOU.js";
|
|
7
|
+
import { t as probeLineBot } from "./probe-CiPafR4R.js";
|
|
9
8
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
|
|
10
9
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
11
10
|
import { createMessageReceiveContext } from "openclaw/plugin-sdk/channel-message";
|
|
@@ -27,6 +26,9 @@ function parseWebhookBody(rawBody) {
|
|
|
27
26
|
if (!rawBody) return null;
|
|
28
27
|
return parseLineWebhookBody(rawBody);
|
|
29
28
|
}
|
|
29
|
+
function logLineWebhookDispatchError(runtime, err) {
|
|
30
|
+
runtime?.error?.(danger(`line webhook dispatch failed: ${String(err)}`));
|
|
31
|
+
}
|
|
30
32
|
function createLineWebhookMiddleware(options) {
|
|
31
33
|
const { channelSecret, onEvents, runtime } = options;
|
|
32
34
|
return async (req, res, _next) => {
|
|
@@ -60,17 +62,16 @@ function createLineWebhookMiddleware(options) {
|
|
|
60
62
|
id: `${Date.now()}:line:webhook`,
|
|
61
63
|
channel: "line",
|
|
62
64
|
message: body,
|
|
63
|
-
ackPolicy:
|
|
65
|
+
ackPolicy: "after_receive_record",
|
|
64
66
|
onAck: () => {
|
|
65
67
|
res.status(200).json({ status: "ok" });
|
|
66
68
|
}
|
|
67
69
|
});
|
|
70
|
+
if (receiveContext.shouldAckAfter("receive_record")) await receiveContext.ack();
|
|
68
71
|
if (body.events && body.events.length > 0) {
|
|
69
72
|
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
70
|
-
|
|
73
|
+
Promise.resolve().then(() => onEvents(body)).catch((err) => logLineWebhookDispatchError(runtime, err));
|
|
71
74
|
}
|
|
72
|
-
const ackStage = body.events?.length ? "agent_dispatch" : "receive_record";
|
|
73
|
-
if (receiveContext.shouldAckAfter(ackStage)) await receiveContext.ack();
|
|
74
75
|
} catch (err) {
|
|
75
76
|
await receiveContext?.nack(err);
|
|
76
77
|
runtime?.error?.(danger(`line webhook error: ${String(err)}`));
|
package/dist/setup-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-
|
|
1
|
+
import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-DAaSncHr.js";
|
|
2
2
|
export { lineSetupAdapter, lineSetupWizard };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as resolveLineAccount, n as normalizeAccountId, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-UXAvdBo_.js";
|
|
2
2
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
-
import { DEFAULT_ACCOUNT_ID, createAllowFromSection, createSetupInputPresenceValidator, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
3
|
+
import { DEFAULT_ACCOUNT_ID, createAllowFromSection, createSetupInputPresenceValidator, createSetupTranslator, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
4
4
|
//#region extensions/line/src/account-helpers.ts
|
|
5
5
|
function hasLineCredentials(account) {
|
|
6
6
|
return Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim());
|
|
@@ -106,22 +106,23 @@ const lineSetupAdapter = {
|
|
|
106
106
|
};
|
|
107
107
|
//#endregion
|
|
108
108
|
//#region extensions/line/src/setup-surface.ts
|
|
109
|
+
const t = createSetupTranslator();
|
|
109
110
|
const channel = "line";
|
|
110
111
|
const LINE_SETUP_HELP_LINES = [
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
|
|
112
|
+
t("wizard.line.helpOpenConsole"),
|
|
113
|
+
t("wizard.line.helpCopyCredentials"),
|
|
114
|
+
t("wizard.line.helpEnableWebhook"),
|
|
115
|
+
t("wizard.line.helpWebhookUrl"),
|
|
116
|
+
t("wizard.channels.docs", { link: formatDocsLink("/channels/line", "channels/line") })
|
|
116
117
|
];
|
|
117
118
|
const LINE_ALLOW_FROM_HELP_LINES = [
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
119
|
+
t("wizard.line.allowlistIntro"),
|
|
120
|
+
t("wizard.line.idsCaseSensitive"),
|
|
121
|
+
t("wizard.line.examples"),
|
|
121
122
|
"- U1234567890abcdef1234567890abcdef",
|
|
122
123
|
"- line:user:U1234567890abcdef1234567890abcdef",
|
|
123
|
-
"
|
|
124
|
-
|
|
124
|
+
t("wizard.line.multipleEntries"),
|
|
125
|
+
t("wizard.channels.docs", { link: formatDocsLink("/channels/line", "channels/line") })
|
|
125
126
|
];
|
|
126
127
|
const lineDmPolicy = {
|
|
127
128
|
label: "LINE",
|
|
@@ -157,10 +158,10 @@ const lineSetupWizard = {
|
|
|
157
158
|
channel,
|
|
158
159
|
status: createStandardChannelSetupStatus({
|
|
159
160
|
channelLabel: "LINE",
|
|
160
|
-
configuredLabel: "
|
|
161
|
-
unconfiguredLabel: "
|
|
162
|
-
configuredHint: "
|
|
163
|
-
unconfiguredHint: "
|
|
161
|
+
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
162
|
+
unconfiguredLabel: t("wizard.channels.statusNeedsTokenSecret"),
|
|
163
|
+
configuredHint: t("wizard.channels.statusConfigured"),
|
|
164
|
+
unconfiguredHint: t("wizard.channels.statusNeedsTokenSecret"),
|
|
164
165
|
configuredScore: 1,
|
|
165
166
|
unconfiguredScore: 0,
|
|
166
167
|
includeStatusLine: true,
|
|
@@ -168,20 +169,20 @@ const lineSetupWizard = {
|
|
|
168
169
|
resolveExtraStatusLines: ({ cfg }) => [`Accounts: ${listLineAccountIds(cfg).length || 0}`]
|
|
169
170
|
}),
|
|
170
171
|
introNote: {
|
|
171
|
-
title: "
|
|
172
|
+
title: t("wizard.line.messagingApiTitle"),
|
|
172
173
|
lines: LINE_SETUP_HELP_LINES,
|
|
173
174
|
shouldShow: ({ cfg, accountId }) => !isLineConfigured(cfg, accountId ?? resolveDefaultLineAccountId(cfg))
|
|
174
175
|
},
|
|
175
176
|
credentials: [{
|
|
176
177
|
inputKey: "token",
|
|
177
178
|
providerHint: channel,
|
|
178
|
-
credentialLabel: "
|
|
179
|
+
credentialLabel: t("wizard.line.channelAccessToken"),
|
|
179
180
|
preferredEnvVar: "LINE_CHANNEL_ACCESS_TOKEN",
|
|
180
|
-
helpTitle: "
|
|
181
|
+
helpTitle: t("wizard.line.messagingApiTitle"),
|
|
181
182
|
helpLines: LINE_SETUP_HELP_LINES,
|
|
182
|
-
envPrompt: "
|
|
183
|
-
keepPrompt: "
|
|
184
|
-
inputPrompt: "
|
|
183
|
+
envPrompt: t("wizard.line.tokenEnvPrompt"),
|
|
184
|
+
keepPrompt: t("wizard.line.tokenKeepPrompt"),
|
|
185
|
+
inputPrompt: t("wizard.line.tokenInputPrompt"),
|
|
185
186
|
allowEnv: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID,
|
|
186
187
|
inspect: ({ cfg, accountId }) => {
|
|
187
188
|
const resolved = resolveLineAccount({
|
|
@@ -212,13 +213,13 @@ const lineSetupWizard = {
|
|
|
212
213
|
}, {
|
|
213
214
|
inputKey: "password",
|
|
214
215
|
providerHint: "line-secret",
|
|
215
|
-
credentialLabel: "
|
|
216
|
+
credentialLabel: t("wizard.line.channelSecret"),
|
|
216
217
|
preferredEnvVar: "LINE_CHANNEL_SECRET",
|
|
217
|
-
helpTitle: "
|
|
218
|
+
helpTitle: t("wizard.line.messagingApiTitle"),
|
|
218
219
|
helpLines: LINE_SETUP_HELP_LINES,
|
|
219
|
-
envPrompt: "
|
|
220
|
-
keepPrompt: "
|
|
221
|
-
inputPrompt: "
|
|
220
|
+
envPrompt: t("wizard.line.secretEnvPrompt"),
|
|
221
|
+
keepPrompt: t("wizard.line.secretKeepPrompt"),
|
|
222
|
+
inputPrompt: t("wizard.line.secretInputPrompt"),
|
|
222
223
|
allowEnv: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID,
|
|
223
224
|
inspect: ({ cfg, accountId }) => {
|
|
224
225
|
const resolved = resolveLineAccount({
|
|
@@ -248,11 +249,11 @@ const lineSetupWizard = {
|
|
|
248
249
|
})
|
|
249
250
|
}],
|
|
250
251
|
allowFrom: createAllowFromSection({
|
|
251
|
-
helpTitle: "
|
|
252
|
+
helpTitle: t("wizard.line.allowlistTitle"),
|
|
252
253
|
helpLines: LINE_ALLOW_FROM_HELP_LINES,
|
|
253
|
-
message: "
|
|
254
|
+
message: t("wizard.line.allowFromPrompt"),
|
|
254
255
|
placeholder: "U1234567890abcdef1234567890abcdef",
|
|
255
|
-
invalidWithoutCredentialNote: "
|
|
256
|
+
invalidWithoutCredentialNote: t("wizard.line.allowFromInvalid"),
|
|
256
257
|
parseInputs: splitSetupEntries,
|
|
257
258
|
parseId: parseLineAllowFromId,
|
|
258
259
|
apply: ({ cfg, accountId, allowFrom }) => patchLineAccountConfig({
|
|
@@ -267,12 +268,12 @@ const lineSetupWizard = {
|
|
|
267
268
|
}),
|
|
268
269
|
dmPolicy: lineDmPolicy,
|
|
269
270
|
completionNote: {
|
|
270
|
-
title: "
|
|
271
|
+
title: t("wizard.line.webhookTitle"),
|
|
271
272
|
lines: [
|
|
272
|
-
"
|
|
273
|
-
"
|
|
274
|
-
"
|
|
275
|
-
|
|
273
|
+
t("wizard.line.completionEnableWebhook"),
|
|
274
|
+
t("wizard.line.completionDefaultWebhook"),
|
|
275
|
+
t("wizard.line.completionWebhookPath"),
|
|
276
|
+
t("wizard.channels.docs", { link: formatDocsLink("/channels/line", "channels/line") })
|
|
276
277
|
]
|
|
277
278
|
},
|
|
278
279
|
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/line",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.16-beta.1",
|
|
4
4
|
"description": "OpenClaw LINE channel plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"openclaw": "workspace:*"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"openclaw": ">=2026.5.
|
|
19
|
+
"openclaw": ">=2026.5.16-beta.1"
|
|
20
20
|
},
|
|
21
21
|
"peerDependenciesMeta": {
|
|
22
22
|
"openclaw": {
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"minHostVersion": ">=2026.4.10"
|
|
47
47
|
},
|
|
48
48
|
"compat": {
|
|
49
|
-
"pluginApi": ">=2026.5.
|
|
49
|
+
"pluginApi": ">=2026.5.16-beta.1"
|
|
50
50
|
},
|
|
51
51
|
"build": {
|
|
52
|
-
"openclawVersion": "2026.5.
|
|
52
|
+
"openclawVersion": "2026.5.16-beta.1"
|
|
53
53
|
},
|
|
54
54
|
"release": {
|
|
55
55
|
"publishToClawHub": true,
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
2
|
-
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
3
|
-
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
4
|
-
//#region extensions/line/src/group-keys.ts
|
|
5
|
-
function resolveLineGroupLookupIds(groupId) {
|
|
6
|
-
const normalized = groupId?.trim();
|
|
7
|
-
if (!normalized) return [];
|
|
8
|
-
if (normalized.startsWith("group:") || normalized.startsWith("room:")) {
|
|
9
|
-
const rawId = normalized.split(":").slice(1).join(":");
|
|
10
|
-
return rawId ? [rawId, normalized] : [normalized];
|
|
11
|
-
}
|
|
12
|
-
return [
|
|
13
|
-
normalized,
|
|
14
|
-
`group:${normalized}`,
|
|
15
|
-
`room:${normalized}`
|
|
16
|
-
];
|
|
17
|
-
}
|
|
18
|
-
function resolveLineGroupConfigEntry(groups, params) {
|
|
19
|
-
if (!groups) return;
|
|
20
|
-
for (const candidate of resolveLineGroupLookupIds(params.groupId)) {
|
|
21
|
-
const hit = groups[candidate];
|
|
22
|
-
if (hit) return hit;
|
|
23
|
-
}
|
|
24
|
-
for (const candidate of resolveLineGroupLookupIds(params.roomId)) {
|
|
25
|
-
const hit = groups[candidate];
|
|
26
|
-
if (hit) return hit;
|
|
27
|
-
}
|
|
28
|
-
return groups["*"];
|
|
29
|
-
}
|
|
30
|
-
function resolveLineGroupsConfig(cfg, accountId) {
|
|
31
|
-
const lineConfig = cfg.channels?.line;
|
|
32
|
-
if (!lineConfig) return;
|
|
33
|
-
const normalizedAccountId = normalizeAccountId(accountId);
|
|
34
|
-
return resolveAccountEntry(lineConfig.accounts, normalizedAccountId)?.groups ?? lineConfig.groups;
|
|
35
|
-
}
|
|
36
|
-
function resolveExactLineGroupConfigKey(params) {
|
|
37
|
-
const groups = resolveLineGroupsConfig(params.cfg, params.accountId);
|
|
38
|
-
if (!groups) return;
|
|
39
|
-
return resolveLineGroupLookupIds(params.groupId).find((candidate) => Object.hasOwn(groups, candidate));
|
|
40
|
-
}
|
|
41
|
-
//#endregion
|
|
42
|
-
//#region extensions/line/src/runtime.ts
|
|
43
|
-
const { setRuntime: setLineRuntime, clearRuntime: clearLineRuntime, getRuntime: getLineRuntime } = createPluginRuntimeStore({
|
|
44
|
-
pluginId: "line",
|
|
45
|
-
errorMessage: "LINE runtime not initialized - plugin not registered"
|
|
46
|
-
});
|
|
47
|
-
//#endregion
|
|
48
|
-
//#region extensions/line/src/quick-reply-fallback.ts
|
|
49
|
-
function buildLineQuickReplyFallbackText(labels) {
|
|
50
|
-
const normalized = (labels ?? []).map((label) => label.trim()).filter(Boolean).slice(0, 13);
|
|
51
|
-
if (normalized.length === 0) return "Choose an option.";
|
|
52
|
-
return `Options:\n${normalized.map((label) => `- ${label}`).join("\n")}`;
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
export { resolveLineGroupConfigEntry as a, resolveExactLineGroupConfigKey as i, getLineRuntime as n, resolveLineGroupLookupIds as o, setLineRuntime as r, resolveLineGroupsConfig as s, buildLineQuickReplyFallbackText as t };
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
|
|
2
|
-
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
3
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
-
//#region extensions/line/src/outbound-media.ts
|
|
5
|
-
const LINE_OUTBOUND_MEDIA_SSRF_POLICY = { allowPrivateNetwork: false };
|
|
6
|
-
async function validateLineMediaUrl(url) {
|
|
7
|
-
let parsed;
|
|
8
|
-
try {
|
|
9
|
-
parsed = new URL(url);
|
|
10
|
-
} catch {
|
|
11
|
-
throw new Error(`LINE outbound media URL must be a valid URL: ${url}`);
|
|
12
|
-
}
|
|
13
|
-
if (parsed.protocol !== "https:") throw new Error(`LINE outbound media URL must use HTTPS: ${url}`);
|
|
14
|
-
if (url.length > 2e3) throw new Error(`LINE outbound media URL must be 2000 chars or less (got ${url.length})`);
|
|
15
|
-
await resolvePinnedHostnameWithPolicy(parsed.hostname, { policy: LINE_OUTBOUND_MEDIA_SSRF_POLICY });
|
|
16
|
-
}
|
|
17
|
-
function isHttpsUrl(url) {
|
|
18
|
-
try {
|
|
19
|
-
return new URL(url).protocol === "https:";
|
|
20
|
-
} catch {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function detectLineMediaKindFromUrl(url) {
|
|
25
|
-
try {
|
|
26
|
-
const pathname = normalizeLowercaseStringOrEmpty(new URL(url).pathname);
|
|
27
|
-
if (/\.(png|jpe?g|gif|webp|bmp|heic|heif|avif)$/i.test(pathname)) return "image";
|
|
28
|
-
if (/\.(mp4|mov|m4v|webm)$/i.test(pathname)) return "video";
|
|
29
|
-
if (/\.(mp3|m4a|aac|wav|ogg|oga)$/i.test(pathname)) return "audio";
|
|
30
|
-
} catch {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
|
|
35
|
-
const trimmedUrl = mediaUrl.trim();
|
|
36
|
-
if (isHttpsUrl(trimmedUrl)) {
|
|
37
|
-
await validateLineMediaUrl(trimmedUrl);
|
|
38
|
-
const previewImageUrl = opts.previewImageUrl?.trim();
|
|
39
|
-
if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
|
|
40
|
-
return {
|
|
41
|
-
mediaUrl: trimmedUrl,
|
|
42
|
-
mediaKind: opts.mediaKind ?? (typeof opts.durationMs === "number" ? "audio" : void 0) ?? (opts.trackingId?.trim() ? "video" : void 0) ?? detectLineMediaKindFromUrl(trimmedUrl) ?? "image",
|
|
43
|
-
...previewImageUrl ? { previewImageUrl } : {},
|
|
44
|
-
...typeof opts.durationMs === "number" ? { durationMs: opts.durationMs } : {},
|
|
45
|
-
...opts.trackingId ? { trackingId: opts.trackingId } : {}
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
if (new URL(trimmedUrl).protocol !== "https:") throw new Error(`LINE outbound media URL must use HTTPS: ${trimmedUrl}`);
|
|
50
|
-
} catch (e) {
|
|
51
|
-
if (e instanceof Error && e.message.startsWith("LINE outbound")) throw e;
|
|
52
|
-
}
|
|
53
|
-
throw new Error("LINE outbound media currently requires a public HTTPS URL");
|
|
54
|
-
}
|
|
55
|
-
//#endregion
|
|
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 };
|
|
File without changes
|
|
File without changes
|