@openclaw/line 2026.8.1-beta.3 → 2026.8.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/account-helpers-BSUyZwLm.js +12 -0
- package/dist/{accounts-BqEVsDoB.js → accounts-BBEFMYbY.js} +2 -1
- package/dist/api.js +2 -2
- package/dist/card-command-DS6A5V6o.js +252 -0
- package/dist/{channel-DmoKZXmG.js → channel-CLCy4jAa.js} +97 -357
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-Ih7sq1GN.js +4 -0
- package/dist/contract-api.js +1 -1
- package/dist/doctor-contract-api.js +141 -0
- package/dist/index.js +5 -17
- package/dist/{markdown-to-line-rEaras_0.js → markdown-to-line--jytmFan.js} +36 -29
- package/dist/{monitor-BYXS_B6r.js → monitor-CSpwQCBI.js} +292 -194
- package/dist/monitor.runtime-CV8EjkIT.js +2 -0
- package/dist/outbound.runtime-DLqcz_BQ.js +3 -0
- package/dist/probe.runtime-DTxBWvKz.js +2 -0
- package/dist/rich-messages-CIP7xn5E.js +761 -0
- package/dist/runtime-api.js +12 -13
- package/dist/{send-receipt-DKZzgHMO.js → send-retry-CUvrarfE.js} +137 -413
- package/dist/{send-BJCeiiOB.js → send-wVUV00WS.js} +150 -109
- package/dist/setup-api.js +1 -1
- package/dist/{setup-surface-W0Dbgu58.js → setup-surface-CDlgkTKi.js} +47 -39
- package/dist/webhook-spool-contract-1rY8OJw8.js +59 -0
- package/node_modules/typebox/build/format/date.d.mts +1 -1
- package/node_modules/typebox/build/format/date.mjs +1 -1
- package/node_modules/typebox/build/format/date_time.d.mts +2 -2
- package/node_modules/typebox/build/format/date_time.mjs +3 -3
- package/node_modules/typebox/build/format/email.d.mts +1 -1
- package/node_modules/typebox/build/format/email.mjs +2 -2
- package/node_modules/typebox/build/format/hostname.mjs +2 -10
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_email.mjs +3 -3
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
- package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
- package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
- package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +41 -0
- package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
- package/node_modules/typebox/build/format/idna/index.mjs +2 -0
- package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
- package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
- package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
- package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
- package/node_modules/typebox/build/format/ipv4.mjs +2 -32
- package/node_modules/typebox/build/format/ipv6.mjs +2 -61
- package/node_modules/typebox/build/format/iri.d.mts +2 -2
- package/node_modules/typebox/build/format/iri.mjs +26 -8
- package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
- package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
- package/node_modules/typebox/build/format/regex.d.mts +1 -2
- package/node_modules/typebox/build/format/regex.mjs +2 -6
- package/node_modules/typebox/build/format/time.d.mts +2 -2
- package/node_modules/typebox/build/format/time.mjs +19 -14
- package/node_modules/typebox/build/format/uri.d.mts +1 -1
- package/node_modules/typebox/build/format/uri.mjs +3 -130
- package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
- package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
- package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
- package/node_modules/typebox/build/format/uri_template.mjs +4 -5
- package/node_modules/typebox/build/format/url.d.mts +3 -3
- package/node_modules/typebox/build/format/url.mjs +4 -5
- package/node_modules/typebox/build/guard/emit.d.mts +3 -1
- package/node_modules/typebox/build/guard/emit.mjs +12 -5
- package/node_modules/typebox/build/guard/guard.d.mts +8 -2
- package/node_modules/typebox/build/guard/guard.mjs +25 -4
- package/node_modules/typebox/build/guard/string.d.mts +2 -2
- package/node_modules/typebox/build/guard/string.mjs +7 -7
- package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
- package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
- package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
- package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
- package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
- package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
- package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
- package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
- package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
- package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
- package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
- package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
- package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
- package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
- package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
- package/node_modules/typebox/build/schema/types/index.mjs +0 -2
- package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
- package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
- package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
- package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
- package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
- package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
- package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
- package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
- package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
- package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
- package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
- package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
- package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
- package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
- package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
- package/node_modules/typebox/build/type/script/parser.mjs +27 -23
- package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
- package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
- package/node_modules/typebox/package.json +27 -27
- package/node_modules/typebox/readme.md +79 -82
- package/openclaw.plugin.json +26 -2
- package/package.json +6 -6
- package/dist/card-command-CyDSm-OU.js +0 -224
- package/dist/channel.runtime-qrUV6wPE.js +0 -4
- package/dist/config-schema-Be3azjB_.js +0 -47
- package/dist/monitor.runtime-B2z_5ugU.js +0 -2
- package/dist/outbound.runtime-DDTeqaBW.js +0 -3
- package/dist/probe.runtime-D13ZtXsd.js +0 -2
- package/dist/quick-reply-fallback-D86FwAfr.js +0 -56
- package/node_modules/typebox/build/format/_idna.d.mts +0 -2
- package/node_modules/typebox/build/format/_idna.mjs +0 -246
- package/node_modules/typebox/build/format/_puny.d.mts +0 -1
- package/node_modules/typebox/build/format/_puny.mjs +0 -77
- package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
- package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
- package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
- package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
- package/dist/{probe-CM03wo6r.js → probe-4gc_YQ2L.js} +1 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
1
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
2
|
+
import { collectErrorGraphCandidates, extractErrorCode } from "openclaw/plugin-sdk/error-runtime";
|
|
3
|
+
import { getFileExtension, mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
3
4
|
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
4
|
-
import { isRecord
|
|
5
|
+
import { isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
5
6
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
7
|
+
import { HTTPFetchError } from "@line/bot-sdk";
|
|
8
|
+
import { classifyTransientNetworkErrorCode, createChannelApiRetryRunner } from "openclaw/plugin-sdk/retry-runtime";
|
|
6
9
|
//#region extensions/line/src/outbound-media.ts
|
|
7
10
|
const LINE_OUTBOUND_MEDIA_SSRF_POLICY = { allowPrivateNetwork: false };
|
|
8
11
|
async function validateLineMediaUrl(url) {
|
|
@@ -23,15 +26,39 @@ function isHttpsUrl(url) {
|
|
|
23
26
|
return false;
|
|
24
27
|
}
|
|
25
28
|
}
|
|
29
|
+
const LINE_MEDIA_KIND_BY_MIME = {
|
|
30
|
+
"image/jpeg": "image",
|
|
31
|
+
"image/png": "image",
|
|
32
|
+
"video/mp4": "video",
|
|
33
|
+
"audio/mpeg": "audio",
|
|
34
|
+
"audio/x-m4a": "audio"
|
|
35
|
+
};
|
|
26
36
|
function detectLineMediaKindFromUrl(url) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
const mimeType = mimeTypeFromFilePath(url);
|
|
38
|
+
if (mimeType === void 0) return getFileExtension(url) === void 0 ? void 0 : "unsupported";
|
|
39
|
+
return LINE_MEDIA_KIND_BY_MIME[mimeType] ?? "unsupported";
|
|
40
|
+
}
|
|
41
|
+
function resolveLineMediaKind(url, opts) {
|
|
42
|
+
if (opts.mediaKind !== void 0) return {
|
|
43
|
+
mediaKind: opts.mediaKind,
|
|
44
|
+
kindSource: "declared"
|
|
45
|
+
};
|
|
46
|
+
if (typeof opts.durationMs === "number") return {
|
|
47
|
+
mediaKind: "audio",
|
|
48
|
+
kindSource: "metadata"
|
|
49
|
+
};
|
|
50
|
+
if (opts.trackingId?.trim()) return {
|
|
51
|
+
mediaKind: "video",
|
|
52
|
+
kindSource: "metadata"
|
|
53
|
+
};
|
|
54
|
+
const detected = detectLineMediaKindFromUrl(url);
|
|
55
|
+
return detected === void 0 ? {
|
|
56
|
+
mediaKind: "image",
|
|
57
|
+
kindSource: "fallback"
|
|
58
|
+
} : {
|
|
59
|
+
mediaKind: detected,
|
|
60
|
+
kindSource: "url"
|
|
61
|
+
};
|
|
35
62
|
}
|
|
36
63
|
async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
|
|
37
64
|
const trimmedUrl = mediaUrl.trim();
|
|
@@ -41,7 +68,7 @@ async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
|
|
|
41
68
|
if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
|
|
42
69
|
return {
|
|
43
70
|
mediaUrl: trimmedUrl,
|
|
44
|
-
|
|
71
|
+
...resolveLineMediaKind(trimmedUrl, opts),
|
|
45
72
|
...previewImageUrl ? { previewImageUrl } : {},
|
|
46
73
|
...typeof opts.durationMs === "number" ? { durationMs: opts.durationMs } : {},
|
|
47
74
|
...opts.trackingId ? { trackingId: opts.trackingId } : {}
|
|
@@ -58,31 +85,45 @@ function isLineUserTarget(target) {
|
|
|
58
85
|
const normalized = target.trim().replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
|
|
59
86
|
return /^U/i.test(normalized);
|
|
60
87
|
}
|
|
61
|
-
function
|
|
62
|
-
return
|
|
88
|
+
function createImageMessage(originalContentUrl, previewImageUrl) {
|
|
89
|
+
return {
|
|
90
|
+
type: "image",
|
|
91
|
+
originalContentUrl,
|
|
92
|
+
previewImageUrl: previewImageUrl ?? originalContentUrl
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function createVideoMessage(originalContentUrl, previewImageUrl, trackingId) {
|
|
96
|
+
return {
|
|
97
|
+
type: "video",
|
|
98
|
+
originalContentUrl,
|
|
99
|
+
previewImageUrl,
|
|
100
|
+
...trackingId ? { trackingId } : {}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function createAudioMessage(originalContentUrl, durationMs) {
|
|
104
|
+
return {
|
|
105
|
+
type: "audio",
|
|
106
|
+
originalContentUrl,
|
|
107
|
+
duration: durationMs
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function lineMediaUrlFallback(mediaUrl) {
|
|
111
|
+
return {
|
|
112
|
+
type: "text",
|
|
113
|
+
text: mediaUrl
|
|
114
|
+
};
|
|
63
115
|
}
|
|
64
116
|
function buildLineMediaMessageObject(resolved, opts) {
|
|
65
117
|
switch (resolved.mediaKind) {
|
|
118
|
+
case "unsupported": return lineMediaUrlFallback(resolved.mediaUrl);
|
|
66
119
|
case "video": {
|
|
67
120
|
const previewImageUrl = resolved.previewImageUrl?.trim();
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
originalContentUrl: resolved.mediaUrl,
|
|
72
|
-
previewImageUrl,
|
|
73
|
-
...opts?.allowTrackingId && resolved.trackingId ? { trackingId: resolved.trackingId } : {}
|
|
74
|
-
};
|
|
121
|
+
if (previewImageUrl) return createVideoMessage(resolved.mediaUrl, previewImageUrl, opts?.allowTrackingId ? resolved.trackingId : void 0);
|
|
122
|
+
if (resolved.kindSource !== "url") throw new Error("LINE video messages require previewImageUrl to reference an image URL");
|
|
123
|
+
return lineMediaUrlFallback(resolved.mediaUrl);
|
|
75
124
|
}
|
|
76
|
-
case "audio": return
|
|
77
|
-
|
|
78
|
-
originalContentUrl: resolved.mediaUrl,
|
|
79
|
-
duration: resolved.durationMs ?? 6e4
|
|
80
|
-
};
|
|
81
|
-
default: return {
|
|
82
|
-
type: "image",
|
|
83
|
-
originalContentUrl: resolved.mediaUrl,
|
|
84
|
-
previewImageUrl: resolved.previewImageUrl ?? resolved.mediaUrl
|
|
85
|
-
};
|
|
125
|
+
case "audio": return createAudioMessage(resolved.mediaUrl, resolved.durationMs ?? 6e4);
|
|
126
|
+
default: return createImageMessage(resolved.mediaUrl, resolved.previewImageUrl);
|
|
86
127
|
}
|
|
87
128
|
}
|
|
88
129
|
async function buildLineMediaMessage(mediaUrl, opts, target) {
|
|
@@ -139,7 +180,7 @@ const actionTypes = /* @__PURE__ */ new Set([
|
|
|
139
180
|
"uri"
|
|
140
181
|
]);
|
|
141
182
|
function isLineAction(value) {
|
|
142
|
-
return isRecord
|
|
183
|
+
return isRecord(value) && typeof value.type === "string" && actionTypes.has(value.type);
|
|
143
184
|
}
|
|
144
185
|
function isUnavailableAction(action) {
|
|
145
186
|
return action[unavailableActionMarker] === true;
|
|
@@ -150,7 +191,7 @@ function normalizeNestedActions(value, labelLimit, warnings) {
|
|
|
150
191
|
for (const item of value) normalized.push(normalizeNestedActions(item, labelLimit, warnings));
|
|
151
192
|
return normalized;
|
|
152
193
|
}
|
|
153
|
-
if (!isRecord
|
|
194
|
+
if (!isRecord(value)) return value;
|
|
154
195
|
const normalized = { ...value };
|
|
155
196
|
for (const [key, nested] of Object.entries(value)) if ((key === "action" || key === "defaultAction") && isLineAction(nested)) {
|
|
156
197
|
const action = normalizeLineAction(nested, labelLimit);
|
|
@@ -163,10 +204,10 @@ function normalizeNestedActions(value, labelLimit, warnings) {
|
|
|
163
204
|
return normalized;
|
|
164
205
|
}
|
|
165
206
|
function normalizeFlexBubbleActions(value) {
|
|
166
|
-
if (!isRecord
|
|
207
|
+
if (!isRecord(value) || value.type !== "bubble") return normalizeNestedActions(value, 40);
|
|
167
208
|
const warnings = [];
|
|
168
209
|
const normalized = normalizeNestedActions(value, 40, warnings);
|
|
169
|
-
if (!isRecord
|
|
210
|
+
if (!isRecord(normalized) || warnings.length === 0) return normalized;
|
|
170
211
|
const warning = {
|
|
171
212
|
type: "text",
|
|
172
213
|
text: [...new Set(warnings)].join("\n"),
|
|
@@ -176,7 +217,7 @@ function normalizeFlexBubbleActions(value) {
|
|
|
176
217
|
margin: "md"
|
|
177
218
|
};
|
|
178
219
|
const body = normalized.body;
|
|
179
|
-
if (isRecord
|
|
220
|
+
if (isRecord(body) && Array.isArray(body.contents)) normalized.body = {
|
|
180
221
|
...body,
|
|
181
222
|
contents: [...body.contents, warning]
|
|
182
223
|
};
|
|
@@ -188,7 +229,7 @@ function normalizeFlexBubbleActions(value) {
|
|
|
188
229
|
return normalized;
|
|
189
230
|
}
|
|
190
231
|
function normalizeFlexContainerActions(value) {
|
|
191
|
-
if (!isRecord
|
|
232
|
+
if (!isRecord(value)) return value;
|
|
192
233
|
if (value.type === "bubble") return normalizeFlexBubbleActions(value);
|
|
193
234
|
if (value.type === "carousel" && Array.isArray(value.contents)) return {
|
|
194
235
|
...value,
|
|
@@ -444,7 +485,7 @@ function createInfoCard(title, body, footer) {
|
|
|
444
485
|
flex: 1,
|
|
445
486
|
margin: "lg"
|
|
446
487
|
}]
|
|
447
|
-
}, {
|
|
488
|
+
}, ...body ? [{
|
|
448
489
|
type: "box",
|
|
449
490
|
layout: "vertical",
|
|
450
491
|
contents: [{
|
|
@@ -459,7 +500,7 @@ function createInfoCard(title, body, footer) {
|
|
|
459
500
|
paddingAll: "lg",
|
|
460
501
|
backgroundColor: "#F8F9FA",
|
|
461
502
|
cornerRadius: "lg"
|
|
462
|
-
}],
|
|
503
|
+
}] : []],
|
|
463
504
|
paddingAll: "xl",
|
|
464
505
|
backgroundColor: "#FFFFFF"
|
|
465
506
|
}
|
|
@@ -604,14 +645,14 @@ function createActionCard(title, body, actions, options) {
|
|
|
604
645
|
weight: "bold",
|
|
605
646
|
size: "xl",
|
|
606
647
|
wrap: true
|
|
607
|
-
}, {
|
|
648
|
+
}, ...body ? [{
|
|
608
649
|
type: "text",
|
|
609
650
|
text: body,
|
|
610
651
|
size: "md",
|
|
611
652
|
wrap: true,
|
|
612
653
|
margin: "md",
|
|
613
654
|
color: "#666666"
|
|
614
|
-
}],
|
|
655
|
+
}] : []],
|
|
615
656
|
paddingAll: "lg"
|
|
616
657
|
},
|
|
617
658
|
footer: {
|
|
@@ -702,7 +743,7 @@ function createReceiptCard(params) {
|
|
|
702
743
|
weight: item.highlight ? "bold" : "regular",
|
|
703
744
|
flex: 3,
|
|
704
745
|
wrap: true
|
|
705
|
-
}, {
|
|
746
|
+
}, ...item.value ? [{
|
|
706
747
|
type: "text",
|
|
707
748
|
text: item.value,
|
|
708
749
|
size: "sm",
|
|
@@ -711,7 +752,7 @@ function createReceiptCard(params) {
|
|
|
711
752
|
flex: 2,
|
|
712
753
|
align: "end",
|
|
713
754
|
wrap: true
|
|
714
|
-
}],
|
|
755
|
+
}] : []],
|
|
715
756
|
paddingAll: "md",
|
|
716
757
|
backgroundColor: index % 2 === 0 ? "#FFFFFF" : "#FAFAFA"
|
|
717
758
|
}));
|
|
@@ -976,376 +1017,6 @@ function createAgendaCard(params) {
|
|
|
976
1017
|
});
|
|
977
1018
|
}
|
|
978
1019
|
//#endregion
|
|
979
|
-
//#region extensions/line/src/flex-templates/media-control-cards.ts
|
|
980
|
-
/**
|
|
981
|
-
* Create a media player card for Sonos, Spotify, Apple Music, etc.
|
|
982
|
-
*
|
|
983
|
-
* Editorial design: Album art hero with gradient overlay for text,
|
|
984
|
-
* prominent now-playing indicator, refined playback controls.
|
|
985
|
-
*/
|
|
986
|
-
function createMediaPlayerCard(params) {
|
|
987
|
-
const { title, subtitle, source, imageUrl, isPlaying, progress, controls, extraActions } = params;
|
|
988
|
-
const trackInfo = [{
|
|
989
|
-
type: "text",
|
|
990
|
-
text: title,
|
|
991
|
-
weight: "bold",
|
|
992
|
-
size: "xl",
|
|
993
|
-
color: "#111111",
|
|
994
|
-
wrap: true
|
|
995
|
-
}];
|
|
996
|
-
if (subtitle) trackInfo.push({
|
|
997
|
-
type: "text",
|
|
998
|
-
text: subtitle,
|
|
999
|
-
size: "md",
|
|
1000
|
-
color: "#666666",
|
|
1001
|
-
wrap: true,
|
|
1002
|
-
margin: "sm"
|
|
1003
|
-
});
|
|
1004
|
-
const statusItems = [];
|
|
1005
|
-
if (isPlaying !== void 0) statusItems.push({
|
|
1006
|
-
type: "box",
|
|
1007
|
-
layout: "horizontal",
|
|
1008
|
-
contents: [{
|
|
1009
|
-
type: "box",
|
|
1010
|
-
layout: "vertical",
|
|
1011
|
-
contents: [],
|
|
1012
|
-
width: "8px",
|
|
1013
|
-
height: "8px",
|
|
1014
|
-
backgroundColor: isPlaying ? "#06C755" : "#CCCCCC",
|
|
1015
|
-
cornerRadius: "4px"
|
|
1016
|
-
}, {
|
|
1017
|
-
type: "text",
|
|
1018
|
-
text: isPlaying ? "Now Playing" : "Paused",
|
|
1019
|
-
size: "xs",
|
|
1020
|
-
color: isPlaying ? "#06C755" : "#888888",
|
|
1021
|
-
weight: "bold",
|
|
1022
|
-
margin: "sm"
|
|
1023
|
-
}],
|
|
1024
|
-
alignItems: "center"
|
|
1025
|
-
});
|
|
1026
|
-
if (source) statusItems.push({
|
|
1027
|
-
type: "text",
|
|
1028
|
-
text: source,
|
|
1029
|
-
size: "xs",
|
|
1030
|
-
color: "#AAAAAA",
|
|
1031
|
-
margin: statusItems.length > 0 ? "lg" : void 0
|
|
1032
|
-
});
|
|
1033
|
-
if (progress) statusItems.push({
|
|
1034
|
-
type: "text",
|
|
1035
|
-
text: progress,
|
|
1036
|
-
size: "xs",
|
|
1037
|
-
color: "#888888",
|
|
1038
|
-
align: "end",
|
|
1039
|
-
flex: 1
|
|
1040
|
-
});
|
|
1041
|
-
const bodyContents = [{
|
|
1042
|
-
type: "box",
|
|
1043
|
-
layout: "vertical",
|
|
1044
|
-
contents: trackInfo
|
|
1045
|
-
}];
|
|
1046
|
-
if (statusItems.length > 0) bodyContents.push({
|
|
1047
|
-
type: "box",
|
|
1048
|
-
layout: "horizontal",
|
|
1049
|
-
contents: statusItems,
|
|
1050
|
-
margin: "lg",
|
|
1051
|
-
alignItems: "center"
|
|
1052
|
-
});
|
|
1053
|
-
const bubble = {
|
|
1054
|
-
type: "bubble",
|
|
1055
|
-
size: "mega",
|
|
1056
|
-
body: {
|
|
1057
|
-
type: "box",
|
|
1058
|
-
layout: "vertical",
|
|
1059
|
-
contents: bodyContents,
|
|
1060
|
-
paddingAll: "xl",
|
|
1061
|
-
backgroundColor: "#FFFFFF"
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
if (imageUrl) bubble.hero = {
|
|
1065
|
-
type: "image",
|
|
1066
|
-
url: imageUrl,
|
|
1067
|
-
size: "full",
|
|
1068
|
-
aspectRatio: "1:1",
|
|
1069
|
-
aspectMode: "cover"
|
|
1070
|
-
};
|
|
1071
|
-
if (controls || extraActions?.length) {
|
|
1072
|
-
const footerContents = [];
|
|
1073
|
-
if (controls) {
|
|
1074
|
-
const controlButtons = [];
|
|
1075
|
-
if (controls.previous) controlButtons.push({
|
|
1076
|
-
type: "button",
|
|
1077
|
-
action: postbackAction("⏮", controls.previous.data),
|
|
1078
|
-
style: "secondary",
|
|
1079
|
-
flex: 1,
|
|
1080
|
-
height: "sm"
|
|
1081
|
-
});
|
|
1082
|
-
if (controls.play) controlButtons.push({
|
|
1083
|
-
type: "button",
|
|
1084
|
-
action: postbackAction("▶", controls.play.data),
|
|
1085
|
-
style: isPlaying ? "secondary" : "primary",
|
|
1086
|
-
flex: 1,
|
|
1087
|
-
height: "sm",
|
|
1088
|
-
margin: controls.previous ? "md" : void 0
|
|
1089
|
-
});
|
|
1090
|
-
if (controls.pause) controlButtons.push({
|
|
1091
|
-
type: "button",
|
|
1092
|
-
action: postbackAction("⏸", controls.pause.data),
|
|
1093
|
-
style: isPlaying ? "primary" : "secondary",
|
|
1094
|
-
flex: 1,
|
|
1095
|
-
height: "sm",
|
|
1096
|
-
margin: controlButtons.length > 0 ? "md" : void 0
|
|
1097
|
-
});
|
|
1098
|
-
if (controls.next) controlButtons.push({
|
|
1099
|
-
type: "button",
|
|
1100
|
-
action: postbackAction("⏭", controls.next.data),
|
|
1101
|
-
style: "secondary",
|
|
1102
|
-
flex: 1,
|
|
1103
|
-
height: "sm",
|
|
1104
|
-
margin: controlButtons.length > 0 ? "md" : void 0
|
|
1105
|
-
});
|
|
1106
|
-
if (controlButtons.length > 0) footerContents.push({
|
|
1107
|
-
type: "box",
|
|
1108
|
-
layout: "horizontal",
|
|
1109
|
-
contents: controlButtons
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
if (extraActions?.length) footerContents.push({
|
|
1113
|
-
type: "box",
|
|
1114
|
-
layout: "horizontal",
|
|
1115
|
-
contents: extraActions.slice(0, 2).map((action, index) => ({
|
|
1116
|
-
type: "button",
|
|
1117
|
-
action: postbackAction(truncateLineActionLabel(action.label, 15), action.data),
|
|
1118
|
-
style: "secondary",
|
|
1119
|
-
flex: 1,
|
|
1120
|
-
height: "sm",
|
|
1121
|
-
margin: index > 0 ? "md" : void 0
|
|
1122
|
-
})),
|
|
1123
|
-
margin: "md"
|
|
1124
|
-
});
|
|
1125
|
-
if (footerContents.length > 0) bubble.footer = {
|
|
1126
|
-
type: "box",
|
|
1127
|
-
layout: "vertical",
|
|
1128
|
-
contents: footerContents,
|
|
1129
|
-
paddingAll: "lg",
|
|
1130
|
-
backgroundColor: "#FAFAFA"
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
return bubble;
|
|
1134
|
-
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Create an Apple TV remote card with a D-pad and control rows.
|
|
1137
|
-
*/
|
|
1138
|
-
function createAppleTvRemoteCard(params) {
|
|
1139
|
-
const { deviceName, status, actionData } = params;
|
|
1140
|
-
const headerContents = [{
|
|
1141
|
-
type: "text",
|
|
1142
|
-
text: deviceName,
|
|
1143
|
-
weight: "bold",
|
|
1144
|
-
size: "xl",
|
|
1145
|
-
color: "#111111",
|
|
1146
|
-
wrap: true
|
|
1147
|
-
}];
|
|
1148
|
-
if (status) headerContents.push({
|
|
1149
|
-
type: "text",
|
|
1150
|
-
text: status,
|
|
1151
|
-
size: "sm",
|
|
1152
|
-
color: "#666666",
|
|
1153
|
-
wrap: true,
|
|
1154
|
-
margin: "sm"
|
|
1155
|
-
});
|
|
1156
|
-
const makeButton = (label, data, style = "secondary") => ({
|
|
1157
|
-
type: "button",
|
|
1158
|
-
action: postbackAction(label, data),
|
|
1159
|
-
style,
|
|
1160
|
-
height: "sm",
|
|
1161
|
-
flex: 1
|
|
1162
|
-
});
|
|
1163
|
-
const dpadRows = [
|
|
1164
|
-
{
|
|
1165
|
-
type: "box",
|
|
1166
|
-
layout: "horizontal",
|
|
1167
|
-
contents: [
|
|
1168
|
-
{ type: "filler" },
|
|
1169
|
-
makeButton("↑", actionData.up),
|
|
1170
|
-
{ type: "filler" }
|
|
1171
|
-
]
|
|
1172
|
-
},
|
|
1173
|
-
{
|
|
1174
|
-
type: "box",
|
|
1175
|
-
layout: "horizontal",
|
|
1176
|
-
contents: [
|
|
1177
|
-
makeButton("←", actionData.left),
|
|
1178
|
-
makeButton("OK", actionData.select, "primary"),
|
|
1179
|
-
makeButton("→", actionData.right)
|
|
1180
|
-
],
|
|
1181
|
-
margin: "md"
|
|
1182
|
-
},
|
|
1183
|
-
{
|
|
1184
|
-
type: "box",
|
|
1185
|
-
layout: "horizontal",
|
|
1186
|
-
contents: [
|
|
1187
|
-
{ type: "filler" },
|
|
1188
|
-
makeButton("↓", actionData.down),
|
|
1189
|
-
{ type: "filler" }
|
|
1190
|
-
],
|
|
1191
|
-
margin: "md"
|
|
1192
|
-
}
|
|
1193
|
-
];
|
|
1194
|
-
const menuRow = {
|
|
1195
|
-
type: "box",
|
|
1196
|
-
layout: "horizontal",
|
|
1197
|
-
contents: [makeButton("Menu", actionData.menu), makeButton("Home", actionData.home)],
|
|
1198
|
-
margin: "lg"
|
|
1199
|
-
};
|
|
1200
|
-
const playbackRow = {
|
|
1201
|
-
type: "box",
|
|
1202
|
-
layout: "horizontal",
|
|
1203
|
-
contents: [makeButton("Play", actionData.play), makeButton("Pause", actionData.pause)],
|
|
1204
|
-
margin: "md"
|
|
1205
|
-
};
|
|
1206
|
-
const volumeRow = {
|
|
1207
|
-
type: "box",
|
|
1208
|
-
layout: "horizontal",
|
|
1209
|
-
contents: [
|
|
1210
|
-
makeButton("Vol +", actionData.volumeUp),
|
|
1211
|
-
makeButton("Mute", actionData.mute),
|
|
1212
|
-
makeButton("Vol -", actionData.volumeDown)
|
|
1213
|
-
],
|
|
1214
|
-
margin: "md"
|
|
1215
|
-
};
|
|
1216
|
-
return {
|
|
1217
|
-
type: "bubble",
|
|
1218
|
-
size: "mega",
|
|
1219
|
-
body: {
|
|
1220
|
-
type: "box",
|
|
1221
|
-
layout: "vertical",
|
|
1222
|
-
contents: [
|
|
1223
|
-
{
|
|
1224
|
-
type: "box",
|
|
1225
|
-
layout: "vertical",
|
|
1226
|
-
contents: headerContents
|
|
1227
|
-
},
|
|
1228
|
-
{
|
|
1229
|
-
type: "separator",
|
|
1230
|
-
margin: "lg",
|
|
1231
|
-
color: "#EEEEEE"
|
|
1232
|
-
},
|
|
1233
|
-
...dpadRows,
|
|
1234
|
-
menuRow,
|
|
1235
|
-
playbackRow,
|
|
1236
|
-
volumeRow
|
|
1237
|
-
],
|
|
1238
|
-
paddingAll: "xl",
|
|
1239
|
-
backgroundColor: "#FFFFFF"
|
|
1240
|
-
}
|
|
1241
|
-
};
|
|
1242
|
-
}
|
|
1243
|
-
/**
|
|
1244
|
-
* Create a device control card for Apple TV, smart home devices, etc.
|
|
1245
|
-
*
|
|
1246
|
-
* Editorial design: Device-focused header with status indicator,
|
|
1247
|
-
* clean control grid with clear visual hierarchy.
|
|
1248
|
-
*/
|
|
1249
|
-
function createDeviceControlCard(params) {
|
|
1250
|
-
const { deviceName, deviceType, status, isOnline, imageUrl, controls } = params;
|
|
1251
|
-
const headerContents = [{
|
|
1252
|
-
type: "box",
|
|
1253
|
-
layout: "horizontal",
|
|
1254
|
-
contents: [{
|
|
1255
|
-
type: "box",
|
|
1256
|
-
layout: "vertical",
|
|
1257
|
-
contents: [],
|
|
1258
|
-
width: "10px",
|
|
1259
|
-
height: "10px",
|
|
1260
|
-
backgroundColor: isOnline !== false ? "#06C755" : "#FF5555",
|
|
1261
|
-
cornerRadius: "5px"
|
|
1262
|
-
}, {
|
|
1263
|
-
type: "text",
|
|
1264
|
-
text: deviceName,
|
|
1265
|
-
weight: "bold",
|
|
1266
|
-
size: "xl",
|
|
1267
|
-
color: "#111111",
|
|
1268
|
-
wrap: true,
|
|
1269
|
-
flex: 1,
|
|
1270
|
-
margin: "md"
|
|
1271
|
-
}],
|
|
1272
|
-
alignItems: "center"
|
|
1273
|
-
}];
|
|
1274
|
-
if (deviceType) headerContents.push({
|
|
1275
|
-
type: "text",
|
|
1276
|
-
text: deviceType,
|
|
1277
|
-
size: "sm",
|
|
1278
|
-
color: "#888888",
|
|
1279
|
-
margin: "sm"
|
|
1280
|
-
});
|
|
1281
|
-
if (status) headerContents.push({
|
|
1282
|
-
type: "box",
|
|
1283
|
-
layout: "vertical",
|
|
1284
|
-
contents: [{
|
|
1285
|
-
type: "text",
|
|
1286
|
-
text: status,
|
|
1287
|
-
size: "sm",
|
|
1288
|
-
color: "#444444",
|
|
1289
|
-
wrap: true
|
|
1290
|
-
}],
|
|
1291
|
-
margin: "lg",
|
|
1292
|
-
paddingAll: "md",
|
|
1293
|
-
backgroundColor: "#F8F9FA",
|
|
1294
|
-
cornerRadius: "md"
|
|
1295
|
-
});
|
|
1296
|
-
const bubble = {
|
|
1297
|
-
type: "bubble",
|
|
1298
|
-
size: "mega",
|
|
1299
|
-
body: {
|
|
1300
|
-
type: "box",
|
|
1301
|
-
layout: "vertical",
|
|
1302
|
-
contents: headerContents,
|
|
1303
|
-
paddingAll: "xl",
|
|
1304
|
-
backgroundColor: "#FFFFFF"
|
|
1305
|
-
}
|
|
1306
|
-
};
|
|
1307
|
-
if (imageUrl) bubble.hero = {
|
|
1308
|
-
type: "image",
|
|
1309
|
-
url: imageUrl,
|
|
1310
|
-
size: "full",
|
|
1311
|
-
aspectRatio: "16:9",
|
|
1312
|
-
aspectMode: "cover"
|
|
1313
|
-
};
|
|
1314
|
-
if (controls.length > 0) {
|
|
1315
|
-
const rows = [];
|
|
1316
|
-
const limitedControls = controls.slice(0, 6);
|
|
1317
|
-
for (let i = 0; i < limitedControls.length; i += 2) {
|
|
1318
|
-
const rowButtons = [];
|
|
1319
|
-
for (const [offset, ctrl] of limitedControls.slice(i, i + 2).entries()) {
|
|
1320
|
-
const buttonLabel = ctrl.icon ? `${ctrl.icon} ${ctrl.label}` : ctrl.label;
|
|
1321
|
-
rowButtons.push({
|
|
1322
|
-
type: "button",
|
|
1323
|
-
action: postbackAction(truncateLineActionLabel(buttonLabel, 18), ctrl.data),
|
|
1324
|
-
style: ctrl.style ?? "secondary",
|
|
1325
|
-
flex: 1,
|
|
1326
|
-
height: "sm",
|
|
1327
|
-
margin: offset > 0 ? "md" : void 0
|
|
1328
|
-
});
|
|
1329
|
-
}
|
|
1330
|
-
if (rowButtons.length === 1) rowButtons.push({ type: "filler" });
|
|
1331
|
-
rows.push({
|
|
1332
|
-
type: "box",
|
|
1333
|
-
layout: "horizontal",
|
|
1334
|
-
contents: rowButtons,
|
|
1335
|
-
margin: i > 0 ? "md" : void 0
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
bubble.footer = {
|
|
1339
|
-
type: "box",
|
|
1340
|
-
layout: "vertical",
|
|
1341
|
-
contents: rows,
|
|
1342
|
-
paddingAll: "lg",
|
|
1343
|
-
backgroundColor: "#FAFAFA"
|
|
1344
|
-
};
|
|
1345
|
-
}
|
|
1346
|
-
return bubble;
|
|
1347
|
-
}
|
|
1348
|
-
//#endregion
|
|
1349
1020
|
//#region extensions/line/src/send-receipt.ts
|
|
1350
1021
|
function createLineSendReceipt(params) {
|
|
1351
1022
|
const messageIds = (params.messageIds ?? [params.messageId]).map((messageId) => messageId.trim()).filter(Boolean);
|
|
@@ -1363,4 +1034,57 @@ function createLineSendReceipt(params) {
|
|
|
1363
1034
|
});
|
|
1364
1035
|
}
|
|
1365
1036
|
//#endregion
|
|
1366
|
-
|
|
1037
|
+
//#region extensions/line/src/send-retry.ts
|
|
1038
|
+
/** The LINE HTTP response carried by an error graph, when the request reached LINE. */
|
|
1039
|
+
function findLineHttpError(error) {
|
|
1040
|
+
return collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]).find((candidate) => candidate instanceof HTTPFetchError);
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* LINE answered this attempt with a client error, so it rejected the request and
|
|
1044
|
+
* sent nothing.
|
|
1045
|
+
*
|
|
1046
|
+
* A 429 proves this attempt was refused but remains retryable by durable
|
|
1047
|
+
* delivery. A 408 stays ambiguous because the request may have reached LINE.
|
|
1048
|
+
*/
|
|
1049
|
+
function resolveAttemptNonDispatchRetryable(error) {
|
|
1050
|
+
const status = findLineHttpError(error)?.status;
|
|
1051
|
+
if (status === 429) return true;
|
|
1052
|
+
if (status === 409) return;
|
|
1053
|
+
return status !== void 0 && status >= 400 && status < 500 && status !== 408 ? false : void 0;
|
|
1054
|
+
}
|
|
1055
|
+
const pushErrorsWithAmbiguousAttempt = /* @__PURE__ */ new WeakSet();
|
|
1056
|
+
/** Retryability when LINE refused every attempt, or undefined when delivery is ambiguous. */
|
|
1057
|
+
function resolveLineNonDispatchRetryable(error) {
|
|
1058
|
+
if (collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]).some((candidate) => typeof candidate === "object" && candidate !== null && pushErrorsWithAmbiguousAttempt.has(candidate))) return;
|
|
1059
|
+
return resolveAttemptNonDispatchRetryable(error);
|
|
1060
|
+
}
|
|
1061
|
+
function isRetryableLinePushError(error) {
|
|
1062
|
+
const httpError = findLineHttpError(error);
|
|
1063
|
+
if (httpError) return httpError.status >= 500;
|
|
1064
|
+
return collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]).some((candidate) => classifyTransientNetworkErrorCode(extractErrorCode(candidate)) !== void 0);
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Pushes are non-idempotent without a retry key, so the generic message-matching
|
|
1068
|
+
* fallback stays off and only the classification above may replay a request.
|
|
1069
|
+
*/
|
|
1070
|
+
const runLinePushAttempts = createChannelApiRetryRunner({
|
|
1071
|
+
shouldRetry: isRetryableLinePushError,
|
|
1072
|
+
strictShouldRetry: true,
|
|
1073
|
+
verbose: true
|
|
1074
|
+
});
|
|
1075
|
+
const runLinePushWithRetries = (fn, label) => {
|
|
1076
|
+
let sawAmbiguousAttempt = false;
|
|
1077
|
+
return runLinePushAttempts(async () => {
|
|
1078
|
+
try {
|
|
1079
|
+
return await fn();
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
sawAmbiguousAttempt ||= resolveAttemptNonDispatchRetryable(error) === void 0;
|
|
1082
|
+
throw error;
|
|
1083
|
+
}
|
|
1084
|
+
}, label).catch((error) => {
|
|
1085
|
+
if (sawAmbiguousAttempt && typeof error === "object" && error !== null) pushErrorsWithAmbiguousAttempt.add(error);
|
|
1086
|
+
throw error;
|
|
1087
|
+
});
|
|
1088
|
+
};
|
|
1089
|
+
//#endregion
|
|
1090
|
+
export { createVideoMessage as S, truncateLineActionLabel as _, createAgendaCard as a, createAudioMessage as b, createActionCard as c, createListCard as d, datetimePickerAction as f, postbackAction as g, normalizeLineMessageActions as h, createLineSendReceipt as i, createImageCard as l, normalizeLineAction as m, resolveLineNonDispatchRetryable as n, createEventCard as o, messageAction as p, runLinePushWithRetries as r, createReceiptCard as s, findLineHttpError as t, createInfoCard as u, uriAction as v, createImageMessage as x, buildLineMediaMessage as y };
|