@openclaw/line 2026.8.1-beta.3 → 2026.8.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.
Files changed (153) hide show
  1. package/dist/account-helpers-BSUyZwLm.js +12 -0
  2. package/dist/api.js +2 -2
  3. package/dist/{card-command-CyDSm-OU.js → card-command-BywtX77U.js} +66 -29
  4. package/dist/{channel-DmoKZXmG.js → channel-DpMgRaxl.js} +52 -349
  5. package/dist/channel-plugin-api.js +1 -1
  6. package/dist/channel.runtime-Bty5U_1N.js +4 -0
  7. package/dist/doctor-contract-api.js +141 -0
  8. package/dist/index.js +1 -1
  9. package/dist/{markdown-to-line-rEaras_0.js → markdown-to-line-CG9Jy22s.js} +29 -25
  10. package/dist/{monitor-BYXS_B6r.js → monitor-Ch6TIatG.js} +261 -166
  11. package/dist/monitor.runtime-Czc3NxNV.js +2 -0
  12. package/dist/outbound.runtime-CyleVzFR.js +3 -0
  13. package/dist/probe.runtime-DTxBWvKz.js +2 -0
  14. package/dist/rich-messages-t_zRL24p.js +760 -0
  15. package/dist/runtime-api.js +10 -11
  16. package/dist/{send-BJCeiiOB.js → send-B-K2B6au.js} +138 -104
  17. package/dist/{send-receipt-DKZzgHMO.js → send-retry-CUvrarfE.js} +137 -413
  18. package/dist/setup-api.js +1 -1
  19. package/dist/{setup-surface-W0Dbgu58.js → setup-surface-DSJ92oJ5.js} +24 -17
  20. package/dist/webhook-spool-contract-CNe2h1I2.js +58 -0
  21. package/node_modules/typebox/build/format/date.d.mts +1 -1
  22. package/node_modules/typebox/build/format/date.mjs +1 -1
  23. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  24. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  25. package/node_modules/typebox/build/format/email.d.mts +1 -1
  26. package/node_modules/typebox/build/format/email.mjs +2 -2
  27. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  28. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  29. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  30. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  31. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  32. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  33. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  34. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  35. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  36. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  37. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  38. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  39. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
  40. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  41. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  42. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  43. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  44. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  45. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  46. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  47. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  48. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  49. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  50. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  51. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  52. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  53. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  54. package/node_modules/typebox/build/format/iri.mjs +26 -8
  55. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  56. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  57. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  58. package/node_modules/typebox/build/format/regex.mjs +2 -6
  59. package/node_modules/typebox/build/format/time.d.mts +2 -2
  60. package/node_modules/typebox/build/format/time.mjs +19 -14
  61. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  62. package/node_modules/typebox/build/format/uri.mjs +3 -130
  63. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  64. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  65. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  66. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  67. package/node_modules/typebox/build/format/url.d.mts +3 -3
  68. package/node_modules/typebox/build/format/url.mjs +4 -5
  69. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  70. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  71. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  72. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  73. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  74. package/node_modules/typebox/build/guard/string.mjs +7 -7
  75. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  76. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  77. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  78. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  79. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  80. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  81. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  82. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  83. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  84. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  85. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  86. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  87. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  88. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  89. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  90. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  91. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  92. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  93. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  94. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  95. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  96. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  97. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  98. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  99. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  100. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  101. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  102. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  103. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  104. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  105. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  106. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  107. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  108. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  109. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  110. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  111. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  112. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  113. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  114. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  115. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  116. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  117. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  118. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  119. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  120. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  121. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  122. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  123. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  124. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  125. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  126. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  127. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  128. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  129. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  130. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  131. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  132. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  133. package/node_modules/typebox/package.json +27 -27
  134. package/node_modules/typebox/readme.md +79 -82
  135. package/openclaw.plugin.json +26 -2
  136. package/package.json +6 -6
  137. package/dist/channel.runtime-qrUV6wPE.js +0 -4
  138. package/dist/config-schema-Be3azjB_.js +0 -47
  139. package/dist/monitor.runtime-B2z_5ugU.js +0 -2
  140. package/dist/outbound.runtime-DDTeqaBW.js +0 -3
  141. package/dist/probe.runtime-D13ZtXsd.js +0 -2
  142. package/dist/quick-reply-fallback-D86FwAfr.js +0 -56
  143. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  144. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  145. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  146. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  147. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  148. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  149. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  150. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  151. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  152. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
  153. package/dist/{probe-CM03wo6r.js → probe-4gc_YQ2L.js} +1 -1
@@ -1,20 +1,19 @@
1
1
  import { i as resolveLineAccount, n as normalizeAccountId, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BqEVsDoB.js";
2
- import { a as resolveLineGroupConfigEntry, i as resolveExactLineGroupConfigKey, o as resolveLineGroupLookupIds, r as setLineRuntime, s as resolveLineGroupsConfig } from "./quick-reply-fallback-D86FwAfr.js";
3
- import { n as LineConfigSchema, t as LineChannelConfigSchema } from "./config-schema-Be3azjB_.js";
4
- import { _ as uriAction, a as createAgendaCard, c as createActionCard, d as createListCard, f as datetimePickerAction, g as postbackAction, i as createMediaPlayerCard, l as createImageCard, n as createAppleTvRemoteCard, o as createEventCard, p as messageAction, r as createDeviceControlCard, s as createReceiptCard, u as createInfoCard } from "./send-receipt-DKZzgHMO.js";
5
- import { a as stripMarkdown, c as createCarouselColumn, d as toFlexMessage, i as processLineMessage, l as createConfirmTemplate, n as convertTableToFlexBubble, o as buildTemplateMessageFromPayload, r as hasMarkdownToConvert, s as createButtonTemplate, t as convertCodeBlockToFlexBubble, u as createTemplateCarousel } from "./markdown-to-line-rEaras_0.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-BYXS_B6r.js";
7
- import { t as probeLineBot } from "./probe-CM03wo6r.js";
8
- import { _ as replyMessageLine, a as createQuickReplyItems, b as resolveLineChannelAccessToken, c as getUserDisplayName, d as pushImageMessage, f as pushLocationMessage, g as pushTextMessageWithQuickReplies, h as pushTemplateMessage, i as createLocationMessage, l as getUserProfile, m as pushMessagesLine, n as createFlexMessage, o as createTextMessageWithQuickReplies, p as pushMessageLine, r as createImageMessage, s as createVideoMessage, t as createAudioMessage, u as pushFlexMessage, v as sendMessageLine, y as showLoadingAnimation } from "./send-BJCeiiOB.js";
9
- import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
2
+ import { _ as LineChannelConfigSchema, c as createDeviceControlCard, d as resolveExactLineGroupConfigKey, f as resolveLineGroupConfigEntry, g as setLineRuntime, l as createMediaPlayerCard, m as resolveLineGroupsConfig, p as resolveLineGroupLookupIds, s as createAppleTvRemoteCard, v as LineConfigSchema } from "./rich-messages-t_zRL24p.js";
3
+ import { S as createVideoMessage, a as createAgendaCard, b as createAudioMessage, c as createActionCard, d as createListCard, f as datetimePickerAction, g as postbackAction, l as createImageCard, o as createEventCard, p as messageAction, s as createReceiptCard, u as createInfoCard, v as uriAction, x as createImageMessage } from "./send-retry-CUvrarfE.js";
4
+ import { a as validateLineSignature, c as normalizeAllowFrom, i as parseLineWebhookBody, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider } from "./monitor-Ch6TIatG.js";
5
+ import { t as probeLineBot } from "./probe-4gc_YQ2L.js";
6
+ import { a as stripMarkdown, c as createButtonTemplate, d as createTemplateCarousel, i as processLineMessage, l as createCarouselColumn, n as convertTableToFlexBubble, o as toFlexMessage, r as hasMarkdownToConvert, s as buildTemplateMessageFromPayload, t as convertCodeBlockToFlexBubble, u as createConfirmTemplate } from "./markdown-to-line-CG9Jy22s.js";
7
+ import { _ as showLoadingAnimation, c as pushFlexMessage, d as pushMessageLine, f as pushMessagesLine, g as sendMessageLine, h as replyMessageLine, i as createTextMessageWithQuickReplies, l as pushImageMessage, m as pushTextMessageWithQuickReplies, n as createLocationMessage, o as getUserDisplayName, p as pushTemplateMessage, r as createQuickReplyItems, s as getUserProfile, t as createFlexMessage, u as pushLocationMessage, y as resolveLineChannelAccessToken } from "./send-B-K2B6au.js";
10
8
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
9
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
10
+ import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
11
+ import { messagingApi } from "@line/bot-sdk";
11
12
  import { DEFAULT_ACCOUNT_ID, formatDocsLink, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
12
13
  import { buildComputedAccountStatusSnapshot, buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/status-helpers";
14
+ import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
13
15
  import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
14
- import { messagingApi } from "@line/bot-sdk";
15
- import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
16
16
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
17
- import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
18
17
  import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media";
19
18
  //#region extensions/line/src/webhook.ts
20
19
  const LINE_WEBHOOK_MAX_RAW_BODY_BYTES = 64 * 1024;
@@ -1,17 +1,17 @@
1
1
  import { i as resolveLineAccount } from "./accounts-BqEVsDoB.js";
2
- import { h as normalizeLineMessageActions, p as messageAction, t as createLineSendReceipt, x as validateLineMediaUrl } from "./send-receipt-DKZzgHMO.js";
2
+ import { h as normalizeLineMessageActions, i as createLineSendReceipt, p as messageAction, r as runLinePushWithRetries, y as buildLineMediaMessage } from "./send-retry-CUvrarfE.js";
3
3
  import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
4
4
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
5
+ import { HTTPFetchError, messagingApi } from "@line/bot-sdk";
5
6
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
6
7
  import { fetchWithRuntimeDispatcherOrMockedGlobal } from "openclaw/plugin-sdk/runtime-fetch";
7
- import { HTTPFetchError, messagingApi } from "@line/bot-sdk";
8
- import { collectErrorGraphCandidates, extractErrorCode } from "openclaw/plugin-sdk/error-runtime";
9
8
  import { randomUUID } from "node:crypto";
10
9
  import lineBotSdkPackage from "@line/bot-sdk/package.json" with { type: "json" };
11
10
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
12
11
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
13
12
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
14
- import { classifyTransientNetworkErrorCode, createChannelApiRetryRunner } from "openclaw/plugin-sdk/retry-runtime";
13
+ import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
14
+ import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
15
15
  //#region extensions/line/src/channel-access-token.ts
16
16
  function resolveLineChannelAccessToken(explicit, params) {
17
17
  if (explicit?.trim()) return explicit.trim();
@@ -19,35 +19,69 @@ function resolveLineChannelAccessToken(explicit, params) {
19
19
  return params.channelAccessToken.trim();
20
20
  }
21
21
  //#endregion
22
- //#region extensions/line/src/send-retry.ts
23
- function isRetryableLinePushError(error) {
24
- const candidates = collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]);
25
- const httpError = candidates.find((candidate) => candidate instanceof HTTPFetchError);
26
- if (httpError) return httpError.status >= 500;
27
- return candidates.some((candidate) => classifyTransientNetworkErrorCode(extractErrorCode(candidate)) !== void 0);
28
- }
29
- /**
30
- * Pushes are non-idempotent without a retry key, so the generic message-matching
31
- * fallback stays off and only the classification above may replay a request.
32
- */
33
- const runLinePushWithRetries = createChannelApiRetryRunner({
34
- shouldRetry: isRetryableLinePushError,
35
- strictShouldRetry: true,
36
- verbose: true
37
- });
22
+ //#region extensions/line/src/outbound-message-log.ts
23
+ const RECENT_SENT_LIMIT = 500;
24
+ const recentSentByAccount = /* @__PURE__ */ new Map();
25
+ function recentSentFor(accountId) {
26
+ const existing = recentSentByAccount.get(accountId);
27
+ if (existing) return existing;
28
+ const created = createDedupeCache({
29
+ ttlMs: 0,
30
+ maxSize: RECENT_SENT_LIMIT
31
+ });
32
+ recentSentByAccount.set(accountId, created);
33
+ return created;
34
+ }
35
+ function recordLineSentMessages(accountId, messageIds) {
36
+ if (messageIds.length === 0) return;
37
+ const recentSent = recentSentFor(accountId);
38
+ for (const messageId of messageIds) recentSent.check(messageId);
39
+ }
40
+ function quotesLineBotMessage(accountId, quotedMessageId) {
41
+ return recentSentByAccount.get(accountId)?.peek(quotedMessageId) ?? false;
42
+ }
38
43
  //#endregion
39
44
  //#region extensions/line/src/send.ts
40
- const userProfileCache = /* @__PURE__ */ new Map();
45
+ const profileCache = {
46
+ values: /* @__PURE__ */ new Map(),
47
+ pending: /* @__PURE__ */ new Map()
48
+ };
49
+ const groupNameCache = {
50
+ values: /* @__PURE__ */ new Map(),
51
+ pending: /* @__PURE__ */ new Map()
52
+ };
41
53
  const PROFILE_CACHE_TTL_MS = 300 * 1e3;
42
54
  const PROFILE_CACHE_MAX_ENTRIES = 1e3;
43
55
  const LINE_FLEX_ALT_TEXT_LIMIT = 1500;
44
56
  const LINE_LOCATION_LABEL_LIMIT = 100;
45
- function cacheUserProfile(userId, profile) {
46
- userProfileCache.delete(userId);
47
- userProfileCache.set(userId, profile);
48
- if (userProfileCache.size <= PROFILE_CACHE_MAX_ENTRIES) return;
49
- for (const [key, cached] of userProfileCache) if (profile.fetchedAt - cached.fetchedAt >= PROFILE_CACHE_TTL_MS) userProfileCache.delete(key);
50
- pruneMapToMaxSize(userProfileCache, PROFILE_CACHE_MAX_ENTRIES);
57
+ const LINE_PROVIDER_RESPONSE_MAX_BYTES = 16 * 1024;
58
+ function rememberLineIdentity(cache, key, value) {
59
+ const entry = {
60
+ value,
61
+ fetchedAt: Date.now()
62
+ };
63
+ cache.values.delete(key);
64
+ cache.values.set(key, entry);
65
+ if (cache.values.size <= PROFILE_CACHE_MAX_ENTRIES) return;
66
+ for (const [cachedKey, cached] of cache.values) if (entry.fetchedAt - cached.fetchedAt >= PROFILE_CACHE_TTL_MS) cache.values.delete(cachedKey);
67
+ pruneMapToMaxSize(cache.values, PROFILE_CACHE_MAX_ENTRIES);
68
+ }
69
+ async function loadLineIdentity(cache, key, load) {
70
+ const cached = cache.values.get(key);
71
+ if (cached && Date.now() - cached.fetchedAt < PROFILE_CACHE_TTL_MS) return cached.value;
72
+ const pending = cache.pending.get(key);
73
+ if (pending) return await pending;
74
+ const lookup = load().then((value) => {
75
+ rememberLineIdentity(cache, key, value);
76
+ return value;
77
+ });
78
+ cache.pending.set(key, lookup);
79
+ pruneMapToMaxSize(cache.pending, PROFILE_CACHE_MAX_ENTRIES);
80
+ try {
81
+ return await lookup;
82
+ } finally {
83
+ if (cache.pending.get(key) === lookup) cache.pending.delete(key);
84
+ }
51
85
  }
52
86
  function resolveLineProviderMessageIds(response, operation) {
53
87
  const sentMessages = Array.isArray(response?.sentMessages) ? response.sentMessages : [];
@@ -74,9 +108,6 @@ function normalizeTarget(to) {
74
108
  if (normalized.length >= 33 && !/^[CUR]/.test(normalized)) throw new Error(`Recipient is not a valid LINE id (case-sensitive; expected leading capital C/U/R): ${truncateUtf16Safe(normalized, 4)}…`);
75
109
  return normalized;
76
110
  }
77
- function isLineUserChatId(chatId) {
78
- return /^U/i.test(chatId);
79
- }
80
111
  function resolveLineMessagingAccount(opts) {
81
112
  const account = resolveLineAccount({
82
113
  cfg: requireRuntimeConfig(opts.cfg, "LINE send"),
@@ -114,15 +145,17 @@ async function sendLineProviderMessages(operation, token, request, retryKey) {
114
145
  body: JSON.stringify(request)
115
146
  });
116
147
  const acceptedRetryConflict = retryKey !== void 0 && response.status === 409;
117
- if (!response.ok && !acceptedRetryConflict) throw new HTTPFetchError(`${response.status} - ${response.statusText}`, {
118
- status: response.status,
119
- statusText: response.statusText,
120
- headers: response.headers,
121
- body: await response.text()
122
- });
148
+ if (!response.ok && !acceptedRetryConflict) {
149
+ const body = await readResponseTextLimited(response, LINE_PROVIDER_RESPONSE_MAX_BYTES).catch(() => "");
150
+ throw new HTTPFetchError(`${response.status} - ${response.statusText}`, {
151
+ status: response.status,
152
+ statusText: response.statusText,
153
+ headers: response.headers,
154
+ body
155
+ });
156
+ }
123
157
  try {
124
- const text = await response.text();
125
- return text ? JSON.parse(text) : null;
158
+ return await readProviderJsonResponse(response, `LINE ${operation} response`, { maxBytes: LINE_PROVIDER_RESPONSE_MAX_BYTES });
126
159
  } catch (error) {
127
160
  throw createChannelPartialDeliveryError(error, {
128
161
  messageIds: [],
@@ -136,28 +169,6 @@ function createTextMessage(text) {
136
169
  text
137
170
  };
138
171
  }
139
- function createImageMessage(originalContentUrl, previewImageUrl) {
140
- return {
141
- type: "image",
142
- originalContentUrl,
143
- previewImageUrl: previewImageUrl ?? originalContentUrl
144
- };
145
- }
146
- function createVideoMessage(originalContentUrl, previewImageUrl, trackingId) {
147
- return {
148
- type: "video",
149
- originalContentUrl,
150
- previewImageUrl,
151
- ...trackingId ? { trackingId } : {}
152
- };
153
- }
154
- function createAudioMessage(originalContentUrl, durationMs) {
155
- return {
156
- type: "audio",
157
- originalContentUrl,
158
- duration: durationMs
159
- };
160
- }
161
172
  function isValidLineLocation(location) {
162
173
  return location.title.trim().length > 0 && location.address.trim().length > 0;
163
174
  }
@@ -183,6 +194,7 @@ function logLineHttpError(err, context) {
183
194
  if (typeof body === "string") logVerbose(`line: ${context} failed (${status ? `${status} ${statusText ?? ""}`.trim() : "unknown status"}): ${body}`);
184
195
  }
185
196
  function recordLineOutboundActivity(accountId, delivery) {
197
+ recordLineSentMessages(accountId, delivery.messageIds);
186
198
  try {
187
199
  recordChannelActivity({
188
200
  channel: "line",
@@ -253,29 +265,12 @@ async function sendMessageLine(to, text, opts) {
253
265
  const chatId = normalizeTarget(to);
254
266
  const messages = [];
255
267
  const mediaUrl = opts.mediaUrl?.trim();
256
- if (mediaUrl) {
257
- await validateLineMediaUrl(mediaUrl);
258
- switch (opts.mediaKind) {
259
- case "video": {
260
- const previewImageUrl = opts.previewImageUrl?.trim();
261
- if (!previewImageUrl) throw new Error("LINE video messages require previewImageUrl to reference an image URL");
262
- await validateLineMediaUrl(previewImageUrl);
263
- const trackingId = isLineUserChatId(chatId) ? opts.trackingId : void 0;
264
- messages.push(createVideoMessage(mediaUrl, previewImageUrl, trackingId));
265
- break;
266
- }
267
- case "audio":
268
- messages.push(createAudioMessage(mediaUrl, opts.durationMs ?? 6e4));
269
- break;
270
- default:
271
- {
272
- const previewImageUrl = opts.previewImageUrl?.trim() || mediaUrl;
273
- await validateLineMediaUrl(previewImageUrl);
274
- messages.push(createImageMessage(mediaUrl, previewImageUrl));
275
- }
276
- break;
277
- }
278
- }
268
+ if (mediaUrl) messages.push(await buildLineMediaMessage(mediaUrl, {
269
+ mediaKind: opts.mediaKind,
270
+ previewImageUrl: opts.previewImageUrl,
271
+ durationMs: opts.durationMs,
272
+ trackingId: opts.trackingId
273
+ }, chatId));
279
274
  if (text?.trim()) messages.push(createTextMessage(text.trim()));
280
275
  if (messages.length === 0) throw new Error("Message must be non-empty for LINE sends");
281
276
  if (opts.replyToken) {
@@ -322,9 +317,10 @@ function createFlexMessage(altText, contents) {
322
317
  };
323
318
  }
324
319
  async function pushImageMessage(to, originalContentUrl, previewImageUrl, opts) {
325
- await validateLineMediaUrl(originalContentUrl);
326
- if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
327
- return pushLineMessages(to, [createImageMessage(originalContentUrl, previewImageUrl)], opts, { verboseMessage: (chatId) => `line: pushed image to ${chatId}` });
320
+ return pushLineMessages(to, [await buildLineMediaMessage(originalContentUrl, {
321
+ mediaKind: "image",
322
+ previewImageUrl
323
+ }, to)], opts, { verboseMessage: (chatId) => `line: pushed image to ${chatId}` });
328
324
  }
329
325
  async function pushLocationMessage(to, location, opts) {
330
326
  return pushLineMessages(to, [createLocationMessage(location)], opts, { verboseMessage: (chatId) => `line: pushed location to ${chatId}` });
@@ -366,26 +362,43 @@ async function showLoadingAnimation(chatId, opts) {
366
362
  logVerbose(`line: loading animation failed (non-fatal): ${String(err)}`);
367
363
  }
368
364
  }
365
+ function lineProfileCacheKey(accountId, userId, scope) {
366
+ const conversation = scope.groupId ? ["group", scope.groupId] : scope.roomId ? ["room", scope.roomId] : ["direct"];
367
+ return JSON.stringify([
368
+ accountId,
369
+ "profile",
370
+ ...conversation,
371
+ userId
372
+ ]);
373
+ }
374
+ function fetchLineMemberProfile(client, userId, scope) {
375
+ if (scope.groupId) return client.getGroupMemberProfile(scope.groupId, userId);
376
+ if (scope.roomId) return client.getRoomMemberProfile(scope.roomId, userId);
377
+ return client.getProfile(userId);
378
+ }
369
379
  async function getUserProfile(userId, opts) {
370
- if (opts.useCache ?? true) {
371
- const cached = userProfileCache.get(userId);
372
- if (cached && Date.now() - cached.fetchedAt < PROFILE_CACHE_TTL_MS) return {
373
- displayName: cached.displayName,
374
- pictureUrl: cached.pictureUrl
375
- };
376
- }
377
- const { client } = createLineMessagingClient(opts);
380
+ const useCache = opts.useCache ?? true;
378
381
  try {
379
- const profile = await client.getProfile(userId);
380
- const result = {
381
- displayName: profile.displayName,
382
- pictureUrl: profile.pictureUrl
382
+ const { account, token } = resolveLineMessagingAccount(opts);
383
+ const cacheKey = lineProfileCacheKey(account.accountId, userId, opts);
384
+ const load = async () => {
385
+ try {
386
+ const profile = await fetchLineMemberProfile(new messagingApi.MessagingApiClient({ channelAccessToken: token }), userId, opts);
387
+ return {
388
+ displayName: profile.displayName,
389
+ pictureUrl: profile.pictureUrl
390
+ };
391
+ } catch (err) {
392
+ logVerbose(`line: failed to fetch profile for ${userId}: ${String(err)}`);
393
+ return null;
394
+ }
383
395
  };
384
- cacheUserProfile(userId, {
385
- ...result,
386
- fetchedAt: Date.now()
387
- });
388
- return result;
396
+ if (!useCache) {
397
+ const profile = await load();
398
+ rememberLineIdentity(profileCache, cacheKey, profile);
399
+ return profile;
400
+ }
401
+ return await loadLineIdentity(profileCache, cacheKey, load);
389
402
  } catch (err) {
390
403
  logVerbose(`line: failed to fetch profile for ${userId}: ${String(err)}`);
391
404
  return null;
@@ -394,5 +407,26 @@ async function getUserProfile(userId, opts) {
394
407
  async function getUserDisplayName(userId, opts) {
395
408
  return (await getUserProfile(userId, opts))?.displayName ?? userId;
396
409
  }
410
+ async function getLineGroupName(groupId, opts) {
411
+ try {
412
+ const { account, token } = resolveLineMessagingAccount(opts);
413
+ const cacheKey = JSON.stringify([
414
+ account.accountId,
415
+ "group",
416
+ groupId
417
+ ]);
418
+ return await loadLineIdentity(groupNameCache, cacheKey, async () => {
419
+ try {
420
+ return (await new messagingApi.MessagingApiClient({ channelAccessToken: token }).getGroupSummary(groupId)).groupName.trim() || void 0;
421
+ } catch (err) {
422
+ logVerbose(`line: failed to fetch group summary for ${groupId}: ${String(err)}`);
423
+ return;
424
+ }
425
+ });
426
+ } catch (err) {
427
+ logVerbose(`line: failed to fetch group summary for ${groupId}: ${String(err)}`);
428
+ return;
429
+ }
430
+ }
397
431
  //#endregion
398
- export { replyMessageLine as _, createQuickReplyItems as a, resolveLineChannelAccessToken as b, getUserDisplayName as c, pushImageMessage as d, pushLocationMessage as f, pushTextMessageWithQuickReplies as g, pushTemplateMessage as h, createLocationMessage as i, getUserProfile as l, pushMessagesLine as m, createFlexMessage as n, createTextMessageWithQuickReplies as o, pushMessageLine as p, createImageMessage as r, createVideoMessage as s, createAudioMessage as t, pushFlexMessage as u, sendMessageLine as v, showLoadingAnimation as y };
432
+ export { showLoadingAnimation as _, getLineGroupName as a, pushFlexMessage as c, pushMessageLine as d, pushMessagesLine as f, sendMessageLine as g, replyMessageLine as h, createTextMessageWithQuickReplies as i, pushImageMessage as l, pushTextMessageWithQuickReplies as m, createLocationMessage as n, getUserDisplayName as o, pushTemplateMessage as p, createQuickReplyItems as r, getUserProfile as s, createFlexMessage as t, pushLocationMessage as u, quotesLineBotMessage as v, resolveLineChannelAccessToken as y };