@openclaw/matrix 2026.3.13 → 2026.5.9-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/account-config-D2W-V1eQ.js +96 -0
  2. package/dist/account-selection-BWwIruri.js +158 -0
  3. package/dist/accounts-Bm90Rzvp.js +130 -0
  4. package/dist/active-client-uhlxdhEy.js +20 -0
  5. package/dist/allowlist-sTzpCn5d.js +68 -0
  6. package/dist/api.js +12 -0
  7. package/dist/approval-handler.runtime-DWTQfd4m.js +370 -0
  8. package/dist/approval-ids-DoC2z7tR.js +7 -0
  9. package/dist/approval-reaction-auth-DbcA1gGd.js +27 -0
  10. package/dist/approval-reactions-o2_tuH8D.js +162 -0
  11. package/dist/async-lock-uQfhfQIY.js +19 -0
  12. package/dist/auth-presence.js +26 -0
  13. package/dist/backup-health-Cabu_WQC.js +60 -0
  14. package/dist/channel-DJNir3Rb.js +1116 -0
  15. package/dist/channel-plugin-api.js +2 -0
  16. package/dist/channel.runtime-BQu0hTih.js +246 -0
  17. package/dist/cli-BmfTmg7x.js +1340 -0
  18. package/dist/cli-metadata-B-PCEzrA.js +22 -0
  19. package/dist/cli-metadata.js +2 -0
  20. package/dist/client-DkcXnm0X.js +25 -0
  21. package/dist/client-_hckQNGW.js +31 -0
  22. package/dist/client-bootstrap-Rb8oHvhH.js +114 -0
  23. package/dist/config--5-S2Akv.js +452 -0
  24. package/dist/config-paths-nsVaysCu.js +19 -0
  25. package/dist/config-schema-nPLpEgHl.js +200 -0
  26. package/dist/config-secret-input.runtime-DiKFehsE.js +2 -0
  27. package/dist/config-update-wZX-HLMn.js +143 -0
  28. package/dist/contract-api.js +9 -0
  29. package/dist/create-client-DCnqDaqd.js +64 -0
  30. package/dist/credentials-DV6fWXhC.js +56 -0
  31. package/dist/credentials-read-cmHgousK.js +112 -0
  32. package/dist/credentials-write.runtime-zniTq-Gr.js +17 -0
  33. package/dist/crypto-node.runtime-pihzdpY7.js +12 -0
  34. package/dist/crypto-runtime-ZI0zAtn3.js +1214 -0
  35. package/dist/deps-C6WqKY7m.js +235 -0
  36. package/dist/device-health-UVYpbA_W.js +16 -0
  37. package/dist/direct-management-DMMMgtTB.js +249 -0
  38. package/dist/direct-room-XkutHjES.js +76 -0
  39. package/dist/directory-live-DmOtMhyr.js +150 -0
  40. package/dist/doctor-C4__7c-U.js +153 -0
  41. package/dist/doctor-contract-D4-64QuJ.js +246 -0
  42. package/dist/doctor-contract-api.js +2 -0
  43. package/dist/draft-stream-BE2QevQQ.js +144 -0
  44. package/dist/encryption-guidance-BPi3A_m3.js +15 -0
  45. package/dist/env-auth-BJqGI8M6.js +63 -0
  46. package/dist/env-vars-C7uQCTKn.js +63 -0
  47. package/dist/errors-CTcpEDq-.js +17 -0
  48. package/dist/exec-approval-resolver-Bza9Dhlm.js +15 -0
  49. package/dist/exec-approvals-Crnh543m.js +196 -0
  50. package/dist/helper-api.js +4 -0
  51. package/dist/http-client-C7AeVJay.js +319 -0
  52. package/dist/index.js +46 -0
  53. package/dist/legacy-crypto-inspector-poDWldgy.js +41 -0
  54. package/dist/legacy-crypto-restore-Biw-w2ng.js +85 -0
  55. package/dist/logger-CnZRVrux.js +78 -0
  56. package/dist/logging-DZHSPP5N.js +99 -0
  57. package/dist/matrix-migration.runtime-WY6ffcrf.js +525 -0
  58. package/dist/media-text-DU6nWZuj.js +146 -0
  59. package/dist/messages-BpihMh82.js +140 -0
  60. package/dist/migration-snapshot-backup-DaCHTp8C.js +69 -0
  61. package/dist/migration-snapshot.runtime-CKHE3xF9.js +2 -0
  62. package/dist/monitor-C_81r_Ck.js +4125 -0
  63. package/dist/plugin-entry.handlers.runtime.js +51 -0
  64. package/dist/probe.runtime-BvAzYAIe.js +3 -0
  65. package/dist/profile-BlHu0wDX.js +111 -0
  66. package/dist/profile-update-DjeBNgIV.js +69 -0
  67. package/dist/reaction-common-ejrL19w-.js +71 -0
  68. package/dist/reaction-events-CiARZfjk.js +121 -0
  69. package/dist/record-shared-CHWJCTWf.js +2 -0
  70. package/dist/recovery-key-store-BTJ6jz5v.js +294 -0
  71. package/dist/resolve-targets-YtJnw1Tb.js +140 -0
  72. package/dist/resolver.runtime-D9piiGEl.js +5 -0
  73. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  74. package/dist/route-D6rg-iXN.js +161 -0
  75. package/dist/runtime-C6X4h_SJ.js +6 -0
  76. package/dist/runtime-Dog86njy.js +8 -0
  77. package/dist/runtime-api-BXWBFIqm.js +25 -0
  78. package/dist/runtime-api.js +25 -0
  79. package/dist/runtime-heavy-api.js +3 -0
  80. package/dist/runtime-setter-api.js +2 -0
  81. package/dist/sdk-B2vZA27-.js +1416 -0
  82. package/dist/secret-contract-DcrJWCQI.js +120 -0
  83. package/dist/secret-contract-api.js +2 -0
  84. package/dist/send-Bo0DU1ca.js +1200 -0
  85. package/dist/session-store-metadata-DI5SCofx.js +77 -0
  86. package/dist/setup-bootstrap-ImenBsMt.js +62 -0
  87. package/dist/setup-core-CfZy05oW.js +116 -0
  88. package/dist/setup-dm-policy-2-r1FrQh.js +194 -0
  89. package/dist/setup-entry.js +19 -0
  90. package/dist/setup-plugin-api.js +44 -0
  91. package/dist/setup-surface-CqT_o61M.js +540 -0
  92. package/dist/shared-CpMoYKm1.js +195 -0
  93. package/dist/startup-abort-56edvmbM.js +32 -0
  94. package/dist/startup-verification-Demyp0bP.js +132 -0
  95. package/dist/storage-paths-BJLdnCjV.js +52 -0
  96. package/dist/storage-tC3ujLiW.js +281 -0
  97. package/dist/subagent-hooks-DQbyqq9V.js +149 -0
  98. package/dist/subagent-hooks-api.js +23 -0
  99. package/dist/sync-state-C_beeevA.js +12 -0
  100. package/dist/target-ids-80nQ2gql.js +77 -0
  101. package/dist/test-api.js +4 -0
  102. package/dist/thread-binding-api-Cq_E-E1K.js +17 -0
  103. package/dist/thread-binding-api.js +2 -0
  104. package/dist/thread-bindings-B9mesxXk.js +352 -0
  105. package/dist/thread-bindings-runtime.js +2 -0
  106. package/dist/thread-bindings-shared-DK-d-oYX.js +97 -0
  107. package/dist/timeout-abort-signal-CtaIaP1v.js +2 -0
  108. package/dist/tool-actions.runtime-BIH49vRr.js +532 -0
  109. package/dist/url-validation-DiK9j7jz.js +36 -0
  110. package/dist/verification-CZ2rDeHL.js +345 -0
  111. package/openclaw.plugin.json +788 -1
  112. package/package.json +82 -16
  113. package/CHANGELOG.md +0 -104
  114. package/index.ts +0 -22
  115. package/src/actions.ts +0 -195
  116. package/src/channel.directory.test.ts +0 -135
  117. package/src/channel.ts +0 -461
  118. package/src/config-schema.test.ts +0 -26
  119. package/src/config-schema.ts +0 -62
  120. package/src/directory-live.test.ts +0 -85
  121. package/src/directory-live.ts +0 -209
  122. package/src/group-mentions.ts +0 -52
  123. package/src/matrix/accounts.test.ts +0 -131
  124. package/src/matrix/accounts.ts +0 -114
  125. package/src/matrix/actions/client.ts +0 -47
  126. package/src/matrix/actions/limits.test.ts +0 -15
  127. package/src/matrix/actions/limits.ts +0 -6
  128. package/src/matrix/actions/messages.ts +0 -126
  129. package/src/matrix/actions/pins.test.ts +0 -74
  130. package/src/matrix/actions/pins.ts +0 -84
  131. package/src/matrix/actions/reactions.test.ts +0 -109
  132. package/src/matrix/actions/reactions.ts +0 -102
  133. package/src/matrix/actions/room.ts +0 -85
  134. package/src/matrix/actions/summary.ts +0 -75
  135. package/src/matrix/actions/types.ts +0 -85
  136. package/src/matrix/actions.ts +0 -15
  137. package/src/matrix/active-client.ts +0 -32
  138. package/src/matrix/client/config.ts +0 -245
  139. package/src/matrix/client/create-client.ts +0 -125
  140. package/src/matrix/client/logging.ts +0 -46
  141. package/src/matrix/client/runtime.ts +0 -4
  142. package/src/matrix/client/shared.test.ts +0 -85
  143. package/src/matrix/client/shared.ts +0 -210
  144. package/src/matrix/client/startup.test.ts +0 -49
  145. package/src/matrix/client/startup.ts +0 -29
  146. package/src/matrix/client/storage.ts +0 -131
  147. package/src/matrix/client/types.ts +0 -34
  148. package/src/matrix/client-bootstrap.ts +0 -47
  149. package/src/matrix/client.test.ts +0 -56
  150. package/src/matrix/client.ts +0 -14
  151. package/src/matrix/credentials.ts +0 -125
  152. package/src/matrix/deps.test.ts +0 -74
  153. package/src/matrix/deps.ts +0 -126
  154. package/src/matrix/format.test.ts +0 -33
  155. package/src/matrix/format.ts +0 -22
  156. package/src/matrix/index.ts +0 -11
  157. package/src/matrix/monitor/access-policy.ts +0 -126
  158. package/src/matrix/monitor/allowlist.test.ts +0 -45
  159. package/src/matrix/monitor/allowlist.ts +0 -94
  160. package/src/matrix/monitor/auto-join.ts +0 -72
  161. package/src/matrix/monitor/direct.test.ts +0 -396
  162. package/src/matrix/monitor/direct.ts +0 -152
  163. package/src/matrix/monitor/events.test.ts +0 -186
  164. package/src/matrix/monitor/events.ts +0 -168
  165. package/src/matrix/monitor/handler.body-for-agent.test.ts +0 -196
  166. package/src/matrix/monitor/handler.ts +0 -768
  167. package/src/matrix/monitor/inbound-body.test.ts +0 -73
  168. package/src/matrix/monitor/inbound-body.ts +0 -28
  169. package/src/matrix/monitor/index.test.ts +0 -18
  170. package/src/matrix/monitor/index.ts +0 -414
  171. package/src/matrix/monitor/location.ts +0 -100
  172. package/src/matrix/monitor/media.test.ts +0 -86
  173. package/src/matrix/monitor/media.ts +0 -118
  174. package/src/matrix/monitor/mentions.test.ts +0 -154
  175. package/src/matrix/monitor/mentions.ts +0 -62
  176. package/src/matrix/monitor/replies.test.ts +0 -184
  177. package/src/matrix/monitor/replies.ts +0 -124
  178. package/src/matrix/monitor/room-info.ts +0 -55
  179. package/src/matrix/monitor/rooms.test.ts +0 -124
  180. package/src/matrix/monitor/rooms.ts +0 -47
  181. package/src/matrix/monitor/threads.ts +0 -68
  182. package/src/matrix/monitor/types.ts +0 -39
  183. package/src/matrix/poll-types.test.ts +0 -21
  184. package/src/matrix/poll-types.ts +0 -167
  185. package/src/matrix/probe.ts +0 -69
  186. package/src/matrix/sdk-runtime.ts +0 -18
  187. package/src/matrix/send/client.ts +0 -99
  188. package/src/matrix/send/formatting.ts +0 -93
  189. package/src/matrix/send/media.ts +0 -230
  190. package/src/matrix/send/targets.test.ts +0 -98
  191. package/src/matrix/send/targets.ts +0 -150
  192. package/src/matrix/send/types.ts +0 -110
  193. package/src/matrix/send-queue.test.ts +0 -145
  194. package/src/matrix/send-queue.ts +0 -28
  195. package/src/matrix/send.test.ts +0 -319
  196. package/src/matrix/send.ts +0 -267
  197. package/src/onboarding.ts +0 -462
  198. package/src/outbound.test.ts +0 -159
  199. package/src/outbound.ts +0 -58
  200. package/src/resolve-targets.test.ts +0 -68
  201. package/src/resolve-targets.ts +0 -125
  202. package/src/runtime.ts +0 -6
  203. package/src/secret-input.ts +0 -13
  204. package/src/test-mocks.ts +0 -53
  205. package/src/tool-actions.ts +0 -164
  206. package/src/types.ts +0 -118
@@ -0,0 +1,1200 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
+ import { r as normalizeMatrixResolvableTarget, t as isMatrixQualifiedUserId } from "./target-ids-80nQ2gql.js";
3
+ import { a as resolveMatrixAccountConfig } from "./account-config-D2W-V1eQ.js";
4
+ import { t as getMatrixRuntime } from "./runtime-Dog86njy.js";
5
+ import { r as buildMatrixReactionContent } from "./reaction-common-ejrL19w-.js";
6
+ import { c as MSC4357_LIVE_KEY, l as MsgType, n as inspectMatrixDirectRooms, o as EventType, r as persistMatrixDirectRoomMapping, u as RelationType } from "./direct-management-DMMMgtTB.js";
7
+ import { a as isStrictDirectRoom } from "./direct-room-XkutHjES.js";
8
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
9
+ import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
10
+ import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
11
+ import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
12
+ import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
13
+ import MarkdownIt from "markdown-it";
14
+ import { isAutoLinkedFileRef } from "openclaw/plugin-sdk/text-autolink-runtime";
15
+ import { parseBuffer } from "music-metadata";
16
+ //#region extensions/matrix/src/matrix/poll-types.ts
17
+ /**
18
+ * Matrix Poll Types (MSC3381)
19
+ *
20
+ * Defines types for Matrix poll events:
21
+ * - m.poll.start - Creates a new poll
22
+ * - m.poll.response - Records a vote
23
+ * - m.poll.end - Closes a poll
24
+ */
25
+ const M_POLL_START = "m.poll.start";
26
+ const M_POLL_RESPONSE = "m.poll.response";
27
+ const M_POLL_END = "m.poll.end";
28
+ const ORG_POLL_START = "org.matrix.msc3381.poll.start";
29
+ const ORG_POLL_RESPONSE = "org.matrix.msc3381.poll.response";
30
+ const ORG_POLL_END = "org.matrix.msc3381.poll.end";
31
+ const POLL_EVENT_TYPES = [
32
+ M_POLL_START,
33
+ M_POLL_RESPONSE,
34
+ M_POLL_END,
35
+ ORG_POLL_START,
36
+ ORG_POLL_RESPONSE,
37
+ ORG_POLL_END
38
+ ];
39
+ const POLL_START_TYPES = [M_POLL_START, ORG_POLL_START];
40
+ const POLL_RESPONSE_TYPES = [M_POLL_RESPONSE, ORG_POLL_RESPONSE];
41
+ const POLL_END_TYPES = [M_POLL_END, ORG_POLL_END];
42
+ function isPollStartType(eventType) {
43
+ return POLL_START_TYPES.includes(eventType);
44
+ }
45
+ function isPollResponseType(eventType) {
46
+ return POLL_RESPONSE_TYPES.includes(eventType);
47
+ }
48
+ function isPollEndType(eventType) {
49
+ return POLL_END_TYPES.includes(eventType);
50
+ }
51
+ function isPollEventType(eventType) {
52
+ return POLL_EVENT_TYPES.includes(eventType);
53
+ }
54
+ function getTextContent(text) {
55
+ if (!text) return "";
56
+ return text["m.text"] ?? text["org.matrix.msc1767.text"] ?? text.body ?? "";
57
+ }
58
+ function parsePollStart(content) {
59
+ const poll = content["m.poll.start"] ?? content[ORG_POLL_START] ?? content["m.poll"];
60
+ if (!poll) return null;
61
+ const question = getTextContent(poll.question).trim();
62
+ if (!question) return null;
63
+ const answers = poll.answers.map((answer) => ({
64
+ id: answer.id,
65
+ text: getTextContent(answer).trim()
66
+ })).filter((answer) => answer.id.trim().length > 0 && answer.text.length > 0);
67
+ if (answers.length === 0) return null;
68
+ const maxSelectionsRaw = poll.max_selections;
69
+ const maxSelections = typeof maxSelectionsRaw === "number" && Number.isFinite(maxSelectionsRaw) ? Math.floor(maxSelectionsRaw) : 1;
70
+ return {
71
+ question,
72
+ answers,
73
+ kind: poll.kind ?? "m.poll.disclosed",
74
+ maxSelections: Math.min(Math.max(maxSelections, 1), answers.length)
75
+ };
76
+ }
77
+ function parsePollStartContent(content) {
78
+ const parsed = parsePollStart(content);
79
+ if (!parsed) return null;
80
+ return {
81
+ eventId: "",
82
+ roomId: "",
83
+ sender: "",
84
+ senderName: "",
85
+ question: parsed.question,
86
+ answers: parsed.answers.map((answer) => answer.text),
87
+ kind: parsed.kind,
88
+ maxSelections: parsed.maxSelections
89
+ };
90
+ }
91
+ function formatPollAsText(summary) {
92
+ return [
93
+ "[Poll]",
94
+ summary.question,
95
+ "",
96
+ ...summary.answers.map((answer, idx) => `${idx + 1}. ${answer}`)
97
+ ].join("\n");
98
+ }
99
+ function resolvePollReferenceEventId(content) {
100
+ if (!content || typeof content !== "object") return null;
101
+ const relates = content["m.relates_to"];
102
+ if (!relates || typeof relates.event_id !== "string") return null;
103
+ const eventId = relates.event_id.trim();
104
+ return eventId.length > 0 ? eventId : null;
105
+ }
106
+ function parsePollResponseAnswerIds(content) {
107
+ if (!content || typeof content !== "object") return null;
108
+ const response = content[M_POLL_RESPONSE] ?? content[ORG_POLL_RESPONSE];
109
+ if (!response || !Array.isArray(response.answers)) return null;
110
+ return response.answers.filter((answer) => typeof answer === "string");
111
+ }
112
+ function buildPollResultsSummary(params) {
113
+ const parsed = parsePollStart(params.content);
114
+ if (!parsed) return null;
115
+ let pollClosedAt = Number.POSITIVE_INFINITY;
116
+ for (const event of params.relationEvents) {
117
+ if (event.unsigned?.redacted_because) continue;
118
+ if (!isPollEndType(typeof event.type === "string" ? event.type : "")) continue;
119
+ if (event.sender !== params.sender) continue;
120
+ const ts = typeof event.origin_server_ts === "number" && Number.isFinite(event.origin_server_ts) ? event.origin_server_ts : Number.POSITIVE_INFINITY;
121
+ if (ts < pollClosedAt) pollClosedAt = ts;
122
+ }
123
+ const answerIds = new Set(parsed.answers.map((answer) => answer.id));
124
+ const latestVoteBySender = /* @__PURE__ */ new Map();
125
+ const orderedRelationEvents = [...params.relationEvents].toSorted((left, right) => {
126
+ const leftTs = typeof left.origin_server_ts === "number" && Number.isFinite(left.origin_server_ts) ? left.origin_server_ts : Number.POSITIVE_INFINITY;
127
+ const rightTs = typeof right.origin_server_ts === "number" && Number.isFinite(right.origin_server_ts) ? right.origin_server_ts : Number.POSITIVE_INFINITY;
128
+ if (leftTs !== rightTs) return leftTs - rightTs;
129
+ return (left.event_id ?? "").localeCompare(right.event_id ?? "");
130
+ });
131
+ for (const event of orderedRelationEvents) {
132
+ if (event.unsigned?.redacted_because) continue;
133
+ if (!isPollResponseType(typeof event.type === "string" ? event.type : "")) continue;
134
+ const senderId = normalizeOptionalString(event.sender) ?? "";
135
+ if (!senderId) continue;
136
+ const eventTs = typeof event.origin_server_ts === "number" && Number.isFinite(event.origin_server_ts) ? event.origin_server_ts : Number.POSITIVE_INFINITY;
137
+ if (eventTs > pollClosedAt) continue;
138
+ const rawAnswers = parsePollResponseAnswerIds(event.content) ?? [];
139
+ const normalizedAnswers = Array.from(new Set(rawAnswers.map((answerId) => normalizeOptionalString(answerId) ?? "").filter((answerId) => answerIds.has(answerId)).slice(0, parsed.maxSelections)));
140
+ latestVoteBySender.set(senderId, {
141
+ ts: eventTs,
142
+ eventId: typeof event.event_id === "string" ? event.event_id : "",
143
+ answerIds: normalizedAnswers
144
+ });
145
+ }
146
+ const voteCounts = new Map(parsed.answers.map((answer) => [answer.id, 0]));
147
+ let totalVotes = 0;
148
+ for (const latestVote of latestVoteBySender.values()) {
149
+ if (latestVote.answerIds.length === 0) continue;
150
+ totalVotes += 1;
151
+ for (const answerId of latestVote.answerIds) voteCounts.set(answerId, (voteCounts.get(answerId) ?? 0) + 1);
152
+ }
153
+ return {
154
+ eventId: params.pollEventId,
155
+ roomId: params.roomId,
156
+ sender: params.sender,
157
+ senderName: params.senderName,
158
+ question: parsed.question,
159
+ answers: parsed.answers.map((answer) => answer.text),
160
+ kind: parsed.kind,
161
+ maxSelections: parsed.maxSelections,
162
+ entries: parsed.answers.map((answer) => ({
163
+ id: answer.id,
164
+ text: answer.text,
165
+ votes: voteCounts.get(answer.id) ?? 0
166
+ })),
167
+ totalVotes,
168
+ closed: Number.isFinite(pollClosedAt)
169
+ };
170
+ }
171
+ function formatPollResultsAsText(summary) {
172
+ const lines = [
173
+ summary.closed ? "[Poll closed]" : "[Poll]",
174
+ summary.question,
175
+ ""
176
+ ];
177
+ const revealResults = summary.kind === "m.poll.disclosed" || summary.closed;
178
+ for (const [index, entry] of summary.entries.entries()) {
179
+ if (!revealResults) {
180
+ lines.push(`${index + 1}. ${entry.text}`);
181
+ continue;
182
+ }
183
+ lines.push(`${index + 1}. ${entry.text} (${entry.votes} vote${entry.votes === 1 ? "" : "s"})`);
184
+ }
185
+ lines.push("");
186
+ if (!revealResults) lines.push("Responses are hidden until the poll closes.");
187
+ else lines.push(`Total voters: ${summary.totalVotes}`);
188
+ return lines.join("\n");
189
+ }
190
+ function buildTextContent$1(body) {
191
+ return {
192
+ "m.text": body,
193
+ "org.matrix.msc1767.text": body
194
+ };
195
+ }
196
+ function buildPollFallbackText(question, answers) {
197
+ if (answers.length === 0) return question;
198
+ return `${question}\n${answers.map((answer, idx) => `${idx + 1}. ${answer}`).join("\n")}`;
199
+ }
200
+ function buildPollStartContent(poll) {
201
+ const normalized = normalizePollInput(poll);
202
+ const answers = normalized.options.map((option, idx) => ({
203
+ id: `answer${idx + 1}`,
204
+ ...buildTextContent$1(option)
205
+ }));
206
+ const isMultiple = normalized.maxSelections > 1;
207
+ const fallbackText = buildPollFallbackText(normalized.question, answers.map((answer) => getTextContent(answer)));
208
+ return {
209
+ [M_POLL_START]: {
210
+ question: buildTextContent$1(normalized.question),
211
+ kind: isMultiple ? "m.poll.undisclosed" : "m.poll.disclosed",
212
+ max_selections: normalized.maxSelections,
213
+ answers
214
+ },
215
+ "m.text": fallbackText,
216
+ "org.matrix.msc1767.text": fallbackText
217
+ };
218
+ }
219
+ function buildPollResponseContent(pollEventId, answerIds) {
220
+ return {
221
+ [M_POLL_RESPONSE]: { answers: answerIds },
222
+ [ORG_POLL_RESPONSE]: { answers: answerIds },
223
+ "m.relates_to": {
224
+ rel_type: "m.reference",
225
+ event_id: pollEventId
226
+ }
227
+ };
228
+ }
229
+ //#endregion
230
+ //#region extensions/matrix/src/matrix/send/client.ts
231
+ let matrixSendClientRuntimePromise = null;
232
+ async function loadMatrixSendClientRuntime() {
233
+ matrixSendClientRuntimePromise ??= import("./client-bootstrap-Rb8oHvhH.js").then((n) => n.t);
234
+ return await matrixSendClientRuntimePromise;
235
+ }
236
+ function resolveMediaMaxBytes(accountId, cfg) {
237
+ if (!cfg) throw new Error("Matrix media limits requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
238
+ const matrixCfg = resolveMatrixAccountConfig({
239
+ cfg: requireRuntimeConfig(cfg, "Matrix media limits"),
240
+ accountId
241
+ });
242
+ const mediaMaxMb = typeof matrixCfg.mediaMaxMb === "number" ? matrixCfg.mediaMaxMb : void 0;
243
+ if (typeof mediaMaxMb === "number") return mediaMaxMb * 1024 * 1024;
244
+ }
245
+ async function withResolvedMatrixSendClient(opts, run) {
246
+ return await withResolvedMatrixClient({
247
+ ...opts,
248
+ readiness: "started"
249
+ }, run, "persist");
250
+ }
251
+ async function withResolvedMatrixControlClient(opts, run) {
252
+ return await withResolvedMatrixClient({
253
+ ...opts,
254
+ readiness: "none"
255
+ }, run);
256
+ }
257
+ async function withResolvedMatrixClient(opts, run, shutdownBehavior) {
258
+ if (opts.client) return await run(opts.client);
259
+ const { withResolvedRuntimeMatrixClient } = await loadMatrixSendClientRuntime();
260
+ return await withResolvedRuntimeMatrixClient(opts, run, shutdownBehavior);
261
+ }
262
+ //#endregion
263
+ //#region extensions/matrix/src/matrix/format.ts
264
+ const md = new MarkdownIt({
265
+ html: false,
266
+ linkify: true,
267
+ breaks: true,
268
+ typographer: false
269
+ });
270
+ md.enable("strikethrough");
271
+ const { escapeHtml } = md.utils;
272
+ const ESCAPED_MENTION_SENTINEL = "";
273
+ const MENTION_PATTERN = /@[A-Za-z0-9._=+\-/:[\]]+/g;
274
+ const MATRIX_MENTION_USER_ID_PATTERN = /^@[A-Za-z0-9._=+\-/]+:(?:[A-Za-z0-9.-]+|\[[0-9A-Fa-f:.]+\])(?::\d+)?$/;
275
+ const TRIMMABLE_MENTION_SUFFIX = /[),.!?:;\]]/;
276
+ function shouldSuppressAutoLink(tokens, idx) {
277
+ const token = tokens[idx];
278
+ if (token?.type !== "link_open" || token.info !== "auto") return false;
279
+ const href = token.attrGet("href") ?? "";
280
+ const label = tokens[idx + 1]?.type === "text" ? tokens[idx + 1]?.content ?? "" : "";
281
+ return Boolean(href && label && isAutoLinkedFileRef(href, label));
282
+ }
283
+ md.renderer.rules.image = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
284
+ md.renderer.rules.html_block = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
285
+ md.renderer.rules.html_inline = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
286
+ md.renderer.rules.link_open = (tokens, idx, _options, _env, self) => shouldSuppressAutoLink(tokens, idx) ? "" : self.renderToken(tokens, idx, _options);
287
+ md.renderer.rules.link_close = (tokens, idx, _options, _env, self) => {
288
+ const openIdx = idx - 2;
289
+ if (openIdx >= 0 && shouldSuppressAutoLink(tokens, openIdx)) return "";
290
+ return self.renderToken(tokens, idx, _options);
291
+ };
292
+ function maskEscapedMentions(markdown) {
293
+ let masked = "";
294
+ let idx = 0;
295
+ let codeFenceLength = 0;
296
+ while (idx < markdown.length) {
297
+ if (markdown[idx] === "`" && !isMarkdownEscaped(markdown, idx)) {
298
+ let runLength = 1;
299
+ while (markdown[idx + runLength] === "`") runLength += 1;
300
+ if (codeFenceLength === 0) codeFenceLength = runLength;
301
+ else if (runLength === codeFenceLength) codeFenceLength = 0;
302
+ masked += markdown.slice(idx, idx + runLength);
303
+ idx += runLength;
304
+ continue;
305
+ }
306
+ if (codeFenceLength === 0 && markdown[idx] === "\\" && markdown[idx + 1] === "@") {
307
+ masked += ESCAPED_MENTION_SENTINEL;
308
+ idx += 2;
309
+ continue;
310
+ }
311
+ masked += markdown[idx] ?? "";
312
+ idx += 1;
313
+ }
314
+ return masked;
315
+ }
316
+ function isMarkdownEscaped(markdown, idx) {
317
+ let slashCount = 0;
318
+ let cursor = idx - 1;
319
+ while (cursor >= 0 && markdown[cursor] === "\\") {
320
+ slashCount += 1;
321
+ cursor -= 1;
322
+ }
323
+ return slashCount % 2 === 1;
324
+ }
325
+ function restoreEscapedMentions(text) {
326
+ return text.replaceAll(ESCAPED_MENTION_SENTINEL, "@");
327
+ }
328
+ function restoreEscapedMentionsInCode(text) {
329
+ return text.replaceAll(ESCAPED_MENTION_SENTINEL, "\\@");
330
+ }
331
+ function restoreEscapedMentionsInBlockTokens(tokens) {
332
+ for (const token of tokens) if ((token.type === "fence" || token.type === "code_block") && token.content) token.content = restoreEscapedMentionsInCode(token.content);
333
+ }
334
+ function isMentionStartBoundary(charBefore) {
335
+ return !charBefore || !/[A-Za-z0-9_]/.test(charBefore);
336
+ }
337
+ function trimMentionSuffix(raw, end) {
338
+ while (raw.length > 1 && TRIMMABLE_MENTION_SUFFIX.test(raw.at(-1) ?? "")) {
339
+ if (raw.at(-1) === "]" && /\[[0-9A-Fa-f:.]+\](?::\d+)?$/i.test(raw)) break;
340
+ raw = raw.slice(0, -1);
341
+ end -= 1;
342
+ }
343
+ if (!raw.startsWith("@") || raw === "@") return null;
344
+ return {
345
+ raw,
346
+ end
347
+ };
348
+ }
349
+ function isMatrixMentionUserId(raw) {
350
+ return isMatrixQualifiedUserId(raw) && MATRIX_MENTION_USER_ID_PATTERN.test(raw);
351
+ }
352
+ function buildMentionCandidate(raw, start) {
353
+ const normalized = trimMentionSuffix(raw, start + raw.length);
354
+ if (!normalized) return null;
355
+ const kind = normalizeLowercaseStringOrEmpty(normalized.raw) === "@room" ? "room" : "user";
356
+ const base = {
357
+ raw: normalized.raw,
358
+ start,
359
+ end: normalized.end,
360
+ kind
361
+ };
362
+ if (kind === "room") return base;
363
+ const userCandidate = isMatrixMentionUserId(normalized.raw) ? {
364
+ ...base,
365
+ userId: normalized.raw
366
+ } : null;
367
+ if (!userCandidate) return null;
368
+ return userCandidate;
369
+ }
370
+ function collectMentionCandidates(text) {
371
+ const mentions = [];
372
+ for (const match of text.matchAll(MENTION_PATTERN)) {
373
+ const raw = match[0];
374
+ const start = match.index ?? -1;
375
+ if (start < 0 || !raw) continue;
376
+ if (!isMentionStartBoundary(text[start - 1])) continue;
377
+ const candidate = buildMentionCandidate(raw, start);
378
+ if (!candidate) continue;
379
+ mentions.push(candidate);
380
+ }
381
+ return mentions;
382
+ }
383
+ function createToken(sample, type, tag, nesting) {
384
+ const TokenCtor = sample.constructor;
385
+ return new TokenCtor(type, tag, nesting);
386
+ }
387
+ function createTextToken(sample, content) {
388
+ const token = createToken(sample, "text", "", 0);
389
+ token.content = content;
390
+ return token;
391
+ }
392
+ function createMentionLinkTokens(params) {
393
+ const open = createToken(params.sample, "link_open", "a", 1);
394
+ open.attrSet("href", params.href);
395
+ return [
396
+ open,
397
+ createTextToken(params.sample, params.label),
398
+ createToken(params.sample, "link_close", "a", -1)
399
+ ];
400
+ }
401
+ function resolveMentionUserId(match) {
402
+ if (match.kind !== "user") return null;
403
+ return match.userId ?? null;
404
+ }
405
+ async function resolveMatrixSelfUserId(client) {
406
+ const getUserId = client.getUserId;
407
+ if (typeof getUserId !== "function") return null;
408
+ return await Promise.resolve(getUserId.call(client)).catch(() => null);
409
+ }
410
+ function mutateInlineTokensWithMentions(params) {
411
+ const nextChildren = [];
412
+ let roomMentioned = false;
413
+ let insideLinkDepth = 0;
414
+ for (const child of params.children) {
415
+ if (child.type === "link_open") {
416
+ insideLinkDepth += 1;
417
+ nextChildren.push(child);
418
+ continue;
419
+ }
420
+ if (child.type === "link_close") {
421
+ insideLinkDepth = Math.max(0, insideLinkDepth - 1);
422
+ nextChildren.push(child);
423
+ continue;
424
+ }
425
+ if (child.type !== "text" || !child.content) {
426
+ nextChildren.push(child);
427
+ continue;
428
+ }
429
+ const visibleContent = restoreEscapedMentions(child.content);
430
+ if (insideLinkDepth > 0) {
431
+ nextChildren.push(createTextToken(child, visibleContent));
432
+ continue;
433
+ }
434
+ const matches = collectMentionCandidates(child.content);
435
+ if (matches.length === 0) {
436
+ nextChildren.push(createTextToken(child, visibleContent));
437
+ continue;
438
+ }
439
+ let cursor = 0;
440
+ for (const match of matches) {
441
+ if (match.start > cursor) nextChildren.push(createTextToken(child, restoreEscapedMentions(child.content.slice(cursor, match.start))));
442
+ cursor = match.end;
443
+ if (match.kind === "room") {
444
+ roomMentioned = true;
445
+ nextChildren.push(createTextToken(child, match.raw));
446
+ continue;
447
+ }
448
+ const resolvedUserId = resolveMentionUserId(match);
449
+ if (!resolvedUserId || resolvedUserId === params.selfUserId) {
450
+ nextChildren.push(createTextToken(child, match.raw));
451
+ continue;
452
+ }
453
+ if (!params.seenUserIds.has(resolvedUserId)) {
454
+ params.seenUserIds.add(resolvedUserId);
455
+ params.userIds.push(resolvedUserId);
456
+ }
457
+ nextChildren.push(...createMentionLinkTokens({
458
+ sample: child,
459
+ href: `https://matrix.to/#/${encodeURIComponent(resolvedUserId)}`,
460
+ label: match.raw
461
+ }));
462
+ }
463
+ if (cursor < child.content.length) nextChildren.push(createTextToken(child, restoreEscapedMentions(child.content.slice(cursor))));
464
+ }
465
+ return {
466
+ children: nextChildren,
467
+ roomMentioned
468
+ };
469
+ }
470
+ function compactLooseListTokens(tokens) {
471
+ const listItemStack = [];
472
+ for (const [index, token] of tokens.entries()) {
473
+ if (token.type === "list_item_open") {
474
+ listItemStack.push({
475
+ level: token.level,
476
+ immediateParagraphOpenIndexes: [],
477
+ immediateParagraphCloseIndexes: []
478
+ });
479
+ continue;
480
+ }
481
+ if (token.type === "list_item_close") {
482
+ const item = listItemStack.pop();
483
+ if (item && item.immediateParagraphOpenIndexes.length === 1 && item.immediateParagraphCloseIndexes.length === 1) {
484
+ tokens[item.immediateParagraphOpenIndexes[0]].hidden = true;
485
+ tokens[item.immediateParagraphCloseIndexes[0]].hidden = true;
486
+ }
487
+ continue;
488
+ }
489
+ const currentItem = listItemStack.at(-1);
490
+ if (!currentItem || token.level !== currentItem.level + 1) continue;
491
+ if (token.type === "paragraph_open") currentItem.immediateParagraphOpenIndexes.push(index);
492
+ else if (token.type === "paragraph_close") currentItem.immediateParagraphCloseIndexes.push(index);
493
+ }
494
+ }
495
+ function markdownToMatrixHtml(markdown) {
496
+ const tokens = md.parse(markdown ?? "", {});
497
+ compactLooseListTokens(tokens);
498
+ return md.renderer.render(tokens, md.options, {}).trimEnd();
499
+ }
500
+ async function resolveMarkdownMentionState(params) {
501
+ const markdown = maskEscapedMentions(params.markdown ?? "");
502
+ const tokens = md.parse(markdown, {});
503
+ restoreEscapedMentionsInBlockTokens(tokens);
504
+ const selfUserId = await resolveMatrixSelfUserId(params.client);
505
+ const userIds = [];
506
+ const seenUserIds = /* @__PURE__ */ new Set();
507
+ let roomMentioned = false;
508
+ for (const token of tokens) {
509
+ if (!token.children?.length) continue;
510
+ const mutated = mutateInlineTokensWithMentions({
511
+ children: token.children,
512
+ userIds,
513
+ seenUserIds,
514
+ selfUserId
515
+ });
516
+ token.children = mutated.children;
517
+ roomMentioned ||= mutated.roomMentioned;
518
+ }
519
+ const mentions = {};
520
+ if (userIds.length > 0) mentions.user_ids = userIds;
521
+ if (roomMentioned) mentions.room = true;
522
+ return {
523
+ tokens,
524
+ mentions
525
+ };
526
+ }
527
+ async function resolveMatrixMentionsInMarkdown(params) {
528
+ return (await resolveMarkdownMentionState(params)).mentions;
529
+ }
530
+ async function renderMarkdownToMatrixHtmlWithMentions(params) {
531
+ const state = await resolveMarkdownMentionState(params);
532
+ compactLooseListTokens(state.tokens);
533
+ return {
534
+ html: md.renderer.render(state.tokens, md.options, {}).trimEnd() || void 0,
535
+ mentions: state.mentions
536
+ };
537
+ }
538
+ //#endregion
539
+ //#region extensions/matrix/src/matrix/send/formatting.ts
540
+ const getCore$2 = () => getMatrixRuntime();
541
+ async function renderMatrixFormattedContent(params) {
542
+ const markdown = params.markdown ?? "";
543
+ if (params.includeMentions === false) return { html: markdownToMatrixHtml(markdown).trimEnd() || void 0 };
544
+ const { html, mentions } = await renderMarkdownToMatrixHtmlWithMentions({
545
+ markdown,
546
+ client: params.client
547
+ });
548
+ return {
549
+ html,
550
+ mentions
551
+ };
552
+ }
553
+ function buildTextContent(body, relation, opts = {}) {
554
+ const msgtype = opts.msgtype ?? MsgType.Text;
555
+ return relation ? {
556
+ msgtype,
557
+ body,
558
+ "m.relates_to": relation
559
+ } : {
560
+ msgtype,
561
+ body
562
+ };
563
+ }
564
+ async function enrichMatrixFormattedContent(params) {
565
+ const { html, mentions } = await renderMatrixFormattedContent({
566
+ client: params.client,
567
+ markdown: params.markdown,
568
+ includeMentions: params.includeMentions
569
+ });
570
+ if (mentions) params.content["m.mentions"] = mentions;
571
+ else delete params.content["m.mentions"];
572
+ if (!html) {
573
+ delete params.content.format;
574
+ delete params.content.formatted_body;
575
+ return;
576
+ }
577
+ params.content.format = "org.matrix.custom.html";
578
+ params.content.formatted_body = html;
579
+ }
580
+ async function resolveMatrixMentionsForBody(params) {
581
+ return await resolveMatrixMentionsInMarkdown({
582
+ markdown: params.body ?? "",
583
+ client: params.client
584
+ });
585
+ }
586
+ function normalizeMentionUserIds(value) {
587
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
588
+ }
589
+ function extractMatrixMentions(content) {
590
+ const rawMentions = content?.["m.mentions"];
591
+ if (!rawMentions || typeof rawMentions !== "object") return {};
592
+ const mentions = rawMentions;
593
+ const normalized = {};
594
+ const userIds = normalizeMentionUserIds(mentions.user_ids);
595
+ if (userIds.length > 0) normalized.user_ids = userIds;
596
+ if (mentions.room === true) normalized.room = true;
597
+ return normalized;
598
+ }
599
+ function diffMatrixMentions(current, previous) {
600
+ const previousUserIds = new Set(previous.user_ids ?? []);
601
+ const newUserIds = (current.user_ids ?? []).filter((userId) => !previousUserIds.has(userId));
602
+ const delta = {};
603
+ if (newUserIds.length > 0) delta.user_ids = newUserIds;
604
+ if (current.room && !previous.room) delta.room = true;
605
+ return delta;
606
+ }
607
+ function buildReplyRelation(replyToId) {
608
+ const trimmed = replyToId?.trim();
609
+ if (!trimmed) return;
610
+ return { "m.in_reply_to": { event_id: trimmed } };
611
+ }
612
+ function buildThreadRelation(threadId, replyToId) {
613
+ const trimmed = threadId.trim();
614
+ return {
615
+ rel_type: RelationType.Thread,
616
+ event_id: trimmed,
617
+ is_falling_back: true,
618
+ "m.in_reply_to": { event_id: replyToId?.trim() || trimmed }
619
+ };
620
+ }
621
+ function resolveMatrixMsgType(contentType, _fileName) {
622
+ switch (getCore$2().media.mediaKindFromMime(contentType ?? "")) {
623
+ case "image": return MsgType.Image;
624
+ case "audio": return MsgType.Audio;
625
+ case "video": return MsgType.Video;
626
+ default: return MsgType.File;
627
+ }
628
+ }
629
+ function resolveMatrixVoiceDecision(opts) {
630
+ if (!opts.wantsVoice) return { useVoice: false };
631
+ if (isMatrixVoiceCompatibleAudio(opts)) return { useVoice: true };
632
+ return { useVoice: false };
633
+ }
634
+ function isMatrixVoiceCompatibleAudio(opts) {
635
+ return getCore$2().media.isVoiceCompatibleAudio({
636
+ contentType: opts.contentType,
637
+ fileName: opts.fileName
638
+ });
639
+ }
640
+ //#endregion
641
+ //#region extensions/matrix/src/matrix/send/media.ts
642
+ const getCore$1 = () => getMatrixRuntime();
643
+ function buildMatrixMediaInfo(params) {
644
+ const base = {};
645
+ if (Number.isFinite(params.size)) base.size = params.size;
646
+ if (params.mimetype) base.mimetype = params.mimetype;
647
+ if (params.imageInfo) {
648
+ const dimensional = {
649
+ ...base,
650
+ ...params.imageInfo
651
+ };
652
+ if (typeof params.durationMs === "number") return {
653
+ ...dimensional,
654
+ duration: params.durationMs
655
+ };
656
+ return dimensional;
657
+ }
658
+ if (typeof params.durationMs === "number") return {
659
+ ...base,
660
+ duration: params.durationMs
661
+ };
662
+ if (Object.keys(base).length === 0) return;
663
+ return base;
664
+ }
665
+ function buildMediaContent(params) {
666
+ const info = buildMatrixMediaInfo({
667
+ size: params.size,
668
+ mimetype: params.mimetype,
669
+ durationMs: params.durationMs,
670
+ imageInfo: params.imageInfo
671
+ });
672
+ const base = {
673
+ msgtype: params.msgtype,
674
+ body: params.body,
675
+ filename: params.filename,
676
+ info: info ?? void 0
677
+ };
678
+ if (!params.file && params.url) base.url = params.url;
679
+ if (params.file) base.file = params.file;
680
+ if (params.isVoice) {
681
+ base["org.matrix.msc3245.voice"] = {};
682
+ if (typeof params.durationMs === "number") base["org.matrix.msc1767.audio"] = { duration: params.durationMs };
683
+ }
684
+ if (params.relation) base["m.relates_to"] = params.relation;
685
+ return base;
686
+ }
687
+ const THUMBNAIL_MAX_SIDE = 800;
688
+ const THUMBNAIL_QUALITY = 80;
689
+ async function prepareImageInfo(params) {
690
+ const meta = await getCore$1().media.getImageMetadata(params.buffer).catch(() => null);
691
+ if (!meta) return;
692
+ const imageInfo = {
693
+ w: meta.width,
694
+ h: meta.height
695
+ };
696
+ if (Math.max(meta.width, meta.height) > THUMBNAIL_MAX_SIDE) try {
697
+ const thumbBuffer = await getCore$1().media.resizeToJpeg({
698
+ buffer: params.buffer,
699
+ maxSide: THUMBNAIL_MAX_SIDE,
700
+ quality: THUMBNAIL_QUALITY,
701
+ withoutEnlargement: true
702
+ });
703
+ const thumbMeta = await getCore$1().media.getImageMetadata(thumbBuffer).catch(() => null);
704
+ const result = await uploadMediaWithEncryption(params.client, thumbBuffer, {
705
+ contentType: "image/jpeg",
706
+ filename: "thumbnail.jpg",
707
+ encrypted: params.encrypted === true
708
+ });
709
+ if (result.file) imageInfo.thumbnail_file = result.file;
710
+ else imageInfo.thumbnail_url = result.url;
711
+ if (thumbMeta) imageInfo.thumbnail_info = {
712
+ w: thumbMeta.width,
713
+ h: thumbMeta.height,
714
+ mimetype: "image/jpeg",
715
+ size: thumbBuffer.byteLength
716
+ };
717
+ } catch {}
718
+ return imageInfo;
719
+ }
720
+ async function resolveMediaDurationMs(params) {
721
+ if (params.kind !== "audio" && params.kind !== "video") return;
722
+ try {
723
+ const fileInfo = params.contentType || params.fileName ? {
724
+ mimeType: params.contentType,
725
+ size: params.buffer.byteLength,
726
+ path: params.fileName
727
+ } : void 0;
728
+ const durationSeconds = (await parseBuffer(params.buffer, fileInfo, {
729
+ duration: true,
730
+ skipCovers: true
731
+ })).format.duration;
732
+ if (typeof durationSeconds === "number" && Number.isFinite(durationSeconds)) return Math.max(0, Math.round(durationSeconds * 1e3));
733
+ } catch {}
734
+ }
735
+ async function uploadFile(client, file, params) {
736
+ return await client.uploadContent(file, params.contentType, params.filename);
737
+ }
738
+ async function uploadMediaWithEncryption(client, buffer, params) {
739
+ if (params.encrypted && client.crypto) {
740
+ const encrypted = await client.crypto.encryptMedia(buffer);
741
+ const mxc = await client.uploadContent(encrypted.buffer, params.contentType, params.filename);
742
+ return {
743
+ url: mxc,
744
+ file: {
745
+ url: mxc,
746
+ ...encrypted.file
747
+ }
748
+ };
749
+ }
750
+ return { url: await uploadFile(client, buffer, params) };
751
+ }
752
+ /**
753
+ * Upload media with optional encryption for E2EE rooms.
754
+ */
755
+ async function uploadMediaMaybeEncrypted(client, roomId, buffer, params) {
756
+ const isEncrypted = Boolean(client.crypto && await client.crypto.isRoomEncrypted(roomId));
757
+ return await uploadMediaWithEncryption(client, buffer, {
758
+ ...params,
759
+ encrypted: isEncrypted
760
+ });
761
+ }
762
+ //#endregion
763
+ //#region extensions/matrix/src/matrix/send/targets.ts
764
+ function normalizeTarget(raw) {
765
+ const trimmed = raw.trim();
766
+ if (!trimmed) throw new Error("Matrix target is required (room:<id> or #alias)");
767
+ return trimmed;
768
+ }
769
+ function normalizeThreadId(raw) {
770
+ return normalizeOptionalStringifiedId(raw) ?? null;
771
+ }
772
+ const MAX_DIRECT_ROOM_CACHE_SIZE = 1024;
773
+ const directRoomCacheByClient = /* @__PURE__ */ new WeakMap();
774
+ function resolveDirectRoomCache(client) {
775
+ const existing = directRoomCacheByClient.get(client);
776
+ if (existing) return existing;
777
+ const created = /* @__PURE__ */ new Map();
778
+ directRoomCacheByClient.set(client, created);
779
+ return created;
780
+ }
781
+ function setDirectRoomCached(client, key, value) {
782
+ const directRoomCache = resolveDirectRoomCache(client);
783
+ directRoomCache.set(key, value);
784
+ if (directRoomCache.size > MAX_DIRECT_ROOM_CACHE_SIZE) {
785
+ const oldest = directRoomCache.keys().next().value;
786
+ if (oldest !== void 0) directRoomCache.delete(oldest);
787
+ }
788
+ }
789
+ async function resolveDirectRoomId(client, userId) {
790
+ const trimmed = userId.trim();
791
+ if (!isMatrixQualifiedUserId(trimmed)) throw new Error(`Matrix user IDs must be fully qualified (got "${trimmed}")`);
792
+ const selfUserId = (await client.getUserId().catch(() => null))?.trim() || null;
793
+ const directRoomCache = resolveDirectRoomCache(client);
794
+ const cached = directRoomCache.get(trimmed);
795
+ if (cached && await isStrictDirectRoom({
796
+ client,
797
+ roomId: cached,
798
+ remoteUserId: trimmed,
799
+ selfUserId
800
+ })) return cached;
801
+ if (cached) directRoomCache.delete(trimmed);
802
+ const inspection = await inspectMatrixDirectRooms({
803
+ client,
804
+ remoteUserId: trimmed
805
+ });
806
+ if (inspection.activeRoomId) {
807
+ setDirectRoomCached(client, trimmed, inspection.activeRoomId);
808
+ if (inspection.mappedRoomIds[0] !== inspection.activeRoomId) await persistMatrixDirectRoomMapping({
809
+ client,
810
+ remoteUserId: trimmed,
811
+ roomId: inspection.activeRoomId
812
+ }).catch(() => {});
813
+ return inspection.activeRoomId;
814
+ }
815
+ throw new Error(`No direct room found for ${trimmed} (m.direct missing)`);
816
+ }
817
+ async function resolveMatrixRoomId(client, raw) {
818
+ const target = normalizeMatrixResolvableTarget(normalizeTarget(raw));
819
+ if (normalizeLowercaseStringOrEmpty(target).startsWith("user:")) return await resolveDirectRoomId(client, target.slice(5));
820
+ if (isMatrixQualifiedUserId(target)) return await resolveDirectRoomId(client, target);
821
+ if (target.startsWith("#")) {
822
+ const resolved = await client.resolveRoom(target);
823
+ if (!resolved) throw new Error(`Matrix alias ${target} could not be resolved`);
824
+ return resolved;
825
+ }
826
+ return target;
827
+ }
828
+ //#endregion
829
+ //#region extensions/matrix/src/matrix/send.ts
830
+ var send_exports = /* @__PURE__ */ __exportAll({
831
+ chunkMatrixText: () => chunkMatrixText,
832
+ editMessageMatrix: () => editMessageMatrix,
833
+ prepareMatrixSingleText: () => prepareMatrixSingleText,
834
+ reactMatrixMessage: () => reactMatrixMessage,
835
+ resolveMatrixRoomId: () => resolveMatrixRoomId,
836
+ sendMessageMatrix: () => sendMessageMatrix,
837
+ sendPollMatrix: () => sendPollMatrix,
838
+ sendReadReceiptMatrix: () => sendReadReceiptMatrix,
839
+ sendSingleTextMessageMatrix: () => sendSingleTextMessageMatrix,
840
+ sendTypingMatrix: () => sendTypingMatrix
841
+ });
842
+ const MATRIX_TEXT_LIMIT = 4e3;
843
+ const getCore = () => getMatrixRuntime();
844
+ function createMatrixSendReceipt(params) {
845
+ return createMessageReceiptFromOutboundResults({
846
+ kind: params.kind,
847
+ ...params.replyToId ? { replyToId: params.replyToId } : {},
848
+ ...params.threadId ? { threadId: params.threadId } : {},
849
+ results: params.platformMessageIds.map((messageId) => ({
850
+ channel: "matrix",
851
+ messageId,
852
+ roomId: params.roomId
853
+ }))
854
+ });
855
+ }
856
+ function isMatrixClient(value) {
857
+ return typeof value.sendEvent === "function";
858
+ }
859
+ function normalizeMatrixClientResolveOpts(opts) {
860
+ if (!opts) return {};
861
+ if (isMatrixClient(opts)) return { client: opts };
862
+ return {
863
+ client: opts.client,
864
+ cfg: opts.cfg,
865
+ timeoutMs: opts.timeoutMs,
866
+ accountId: opts.accountId
867
+ };
868
+ }
869
+ function resolvePreviousEditContent(previousEvent) {
870
+ if (!previousEvent || typeof previousEvent !== "object") return;
871
+ const eventRecord = previousEvent;
872
+ if (!eventRecord.content || typeof eventRecord.content !== "object") return;
873
+ const content = eventRecord.content;
874
+ const newContent = content["m.new_content"];
875
+ return newContent && typeof newContent === "object" ? newContent : content;
876
+ }
877
+ function hasMatrixMentionsMetadata(content) {
878
+ return Boolean(content && Object.hasOwn(content, "m.mentions"));
879
+ }
880
+ function withMatrixExtraContentFields(content, extraContent) {
881
+ if (!extraContent) return content;
882
+ return {
883
+ ...content,
884
+ ...extraContent
885
+ };
886
+ }
887
+ async function resolvePreviousEditMentions(params) {
888
+ if (hasMatrixMentionsMetadata(params.content)) return extractMatrixMentions(params.content);
889
+ const body = typeof params.content?.body === "string" ? params.content.body : "";
890
+ if (!body) return {};
891
+ return await resolveMatrixMentionsForBody({
892
+ client: params.client,
893
+ body
894
+ });
895
+ }
896
+ function prepareMatrixSingleText(text, opts) {
897
+ const trimmedText = text.trim();
898
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix text preparation");
899
+ const tableMode = opts.tableMode ?? getCore().channel.text.resolveMarkdownTableMode({
900
+ cfg,
901
+ channel: "matrix",
902
+ accountId: opts.accountId
903
+ });
904
+ const convertedText = getCore().channel.text.convertMarkdownTables(trimmedText, tableMode);
905
+ const singleEventLimit = Math.min(getCore().channel.text.resolveTextChunkLimit(cfg, "matrix", opts.accountId), MATRIX_TEXT_LIMIT);
906
+ return {
907
+ trimmedText,
908
+ convertedText,
909
+ singleEventLimit,
910
+ fitsInSingleEvent: convertedText.length <= singleEventLimit
911
+ };
912
+ }
913
+ function chunkMatrixText(text, opts) {
914
+ const preparedText = prepareMatrixSingleText(text, opts);
915
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix text chunking");
916
+ const chunkMode = getCore().channel.text.resolveChunkMode(cfg, "matrix", opts.accountId);
917
+ return {
918
+ ...preparedText,
919
+ chunks: getCore().channel.text.chunkMarkdownTextWithMode(preparedText.convertedText, preparedText.singleEventLimit, chunkMode)
920
+ };
921
+ }
922
+ async function sendMessageMatrix(to, message, opts) {
923
+ const trimmedMessage = message?.trim() ?? "";
924
+ if (!trimmedMessage && !opts.mediaUrl) throw new Error("Matrix send requires text or media");
925
+ return await withResolvedMatrixSendClient({
926
+ client: opts.client,
927
+ cfg: opts.cfg,
928
+ timeoutMs: opts.timeoutMs,
929
+ accountId: opts.accountId
930
+ }, async (client) => {
931
+ const roomId = await resolveMatrixRoomId(client, to);
932
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix send");
933
+ const { chunks } = chunkMatrixText(trimmedMessage, {
934
+ cfg,
935
+ accountId: opts.accountId
936
+ });
937
+ const threadId = normalizeThreadId(opts.threadId);
938
+ const relation = threadId ? buildThreadRelation(threadId, opts.replyToId) : buildReplyRelation(opts.replyToId);
939
+ let pendingExtraContent = opts.extraContent;
940
+ const sendContent = async (content) => {
941
+ const contentWithExtra = withMatrixExtraContentFields(content, pendingExtraContent);
942
+ pendingExtraContent = void 0;
943
+ return await client.sendMessage(roomId, contentWithExtra);
944
+ };
945
+ const platformMessageIds = [];
946
+ let lastMessageId = "";
947
+ let receiptKind = "text";
948
+ if (opts.mediaUrl) {
949
+ const maxBytes = resolveMediaMaxBytes(opts.accountId, cfg);
950
+ const media = await loadOutboundMediaFromUrl(opts.mediaUrl, {
951
+ maxBytes,
952
+ mediaAccess: opts.mediaAccess,
953
+ mediaLocalRoots: opts.mediaLocalRoots,
954
+ mediaReadFile: opts.mediaReadFile
955
+ });
956
+ const uploaded = await uploadMediaMaybeEncrypted(client, roomId, media.buffer, {
957
+ contentType: media.contentType,
958
+ filename: media.fileName
959
+ });
960
+ const durationMs = await resolveMediaDurationMs({
961
+ buffer: media.buffer,
962
+ contentType: media.contentType,
963
+ fileName: media.fileName,
964
+ kind: media.kind ?? "unknown"
965
+ });
966
+ const baseMsgType = resolveMatrixMsgType(media.contentType, media.fileName);
967
+ const { useVoice } = resolveMatrixVoiceDecision({
968
+ wantsVoice: opts.audioAsVoice === true,
969
+ contentType: media.contentType,
970
+ fileName: media.fileName
971
+ });
972
+ const msgtype = useVoice ? MsgType.Audio : baseMsgType;
973
+ receiptKind = useVoice ? "voice" : "media";
974
+ const imageInfo = msgtype === MsgType.Image ? await prepareImageInfo({
975
+ buffer: media.buffer,
976
+ client,
977
+ encrypted: Boolean(uploaded.file)
978
+ }) : void 0;
979
+ const [firstChunk, ...rest] = chunks;
980
+ const captionMarkdown = useVoice ? "" : firstChunk ?? "";
981
+ const content = buildMediaContent({
982
+ msgtype,
983
+ body: useVoice ? "Voice message" : captionMarkdown || media.fileName || "(file)",
984
+ url: uploaded.url,
985
+ file: uploaded.file,
986
+ filename: media.fileName,
987
+ mimetype: media.contentType,
988
+ size: media.buffer.byteLength,
989
+ durationMs,
990
+ relation,
991
+ isVoice: useVoice,
992
+ imageInfo
993
+ });
994
+ await enrichMatrixFormattedContent({
995
+ client,
996
+ content,
997
+ markdown: captionMarkdown
998
+ });
999
+ const eventId = await sendContent(content);
1000
+ lastMessageId = eventId ?? lastMessageId;
1001
+ if (eventId) platformMessageIds.push(eventId);
1002
+ const textChunks = useVoice ? chunks : rest;
1003
+ const followupRelation = useVoice || threadId ? relation : void 0;
1004
+ for (const chunk of textChunks) {
1005
+ const text = chunk.trim();
1006
+ if (!text) continue;
1007
+ const followup = buildTextContent(text, followupRelation);
1008
+ await enrichMatrixFormattedContent({
1009
+ client,
1010
+ content: followup,
1011
+ markdown: text
1012
+ });
1013
+ const followupEventId = await sendContent(followup);
1014
+ lastMessageId = followupEventId ?? lastMessageId;
1015
+ if (followupEventId) platformMessageIds.push(followupEventId);
1016
+ }
1017
+ } else for (const chunk of chunks.length ? chunks : [""]) {
1018
+ const text = chunk.trim();
1019
+ if (!text) continue;
1020
+ const content = buildTextContent(text, relation);
1021
+ await enrichMatrixFormattedContent({
1022
+ client,
1023
+ content,
1024
+ markdown: text
1025
+ });
1026
+ const eventId = await sendContent(content);
1027
+ lastMessageId = eventId ?? lastMessageId;
1028
+ if (eventId) platformMessageIds.push(eventId);
1029
+ }
1030
+ return {
1031
+ messageId: lastMessageId || "unknown",
1032
+ roomId,
1033
+ primaryMessageId: platformMessageIds[0] ?? (lastMessageId || "unknown"),
1034
+ receipt: createMatrixSendReceipt({
1035
+ roomId,
1036
+ platformMessageIds,
1037
+ kind: receiptKind,
1038
+ replyToId: opts.replyToId,
1039
+ threadId
1040
+ })
1041
+ };
1042
+ });
1043
+ }
1044
+ async function sendPollMatrix(to, poll, opts) {
1045
+ if (!poll.question?.trim()) throw new Error("Matrix poll requires a question");
1046
+ if (!poll.options?.length) throw new Error("Matrix poll requires options");
1047
+ return await withResolvedMatrixSendClient({
1048
+ client: opts.client,
1049
+ cfg: opts.cfg,
1050
+ timeoutMs: opts.timeoutMs,
1051
+ accountId: opts.accountId
1052
+ }, async (client) => {
1053
+ const roomId = await resolveMatrixRoomId(client, to);
1054
+ const pollContent = buildPollStartContent(poll);
1055
+ const mentions = await resolveMatrixMentionsForBody({
1056
+ client,
1057
+ body: pollContent["m.text"] ?? pollContent["org.matrix.msc1767.text"] ?? poll.question ?? ""
1058
+ });
1059
+ const threadId = normalizeThreadId(opts.threadId);
1060
+ const pollPayload = threadId ? {
1061
+ ...pollContent,
1062
+ "m.relates_to": buildThreadRelation(threadId)
1063
+ } : { ...pollContent };
1064
+ pollPayload["m.mentions"] = mentions;
1065
+ return {
1066
+ eventId: await client.sendEvent(roomId, "m.poll.start", pollPayload) ?? "unknown",
1067
+ roomId
1068
+ };
1069
+ });
1070
+ }
1071
+ async function sendTypingMatrix(roomId, typing, optsOrTimeoutMs, client) {
1072
+ const opts = typeof optsOrTimeoutMs === "number" ? {
1073
+ timeoutMs: optsOrTimeoutMs,
1074
+ ...client ? { client } : {}
1075
+ } : {
1076
+ ...normalizeMatrixClientResolveOpts(optsOrTimeoutMs),
1077
+ ...client ? { client } : {}
1078
+ };
1079
+ await withResolvedMatrixControlClient({
1080
+ client: opts.client,
1081
+ cfg: opts.cfg,
1082
+ timeoutMs: opts.timeoutMs,
1083
+ accountId: opts.accountId
1084
+ }, async (resolved) => {
1085
+ const resolvedRoom = await resolveMatrixRoomId(resolved, roomId);
1086
+ const resolvedTimeoutMs = typeof opts.timeoutMs === "number" ? opts.timeoutMs : 3e4;
1087
+ await resolved.setTyping(resolvedRoom, typing, resolvedTimeoutMs);
1088
+ });
1089
+ }
1090
+ async function sendReadReceiptMatrix(roomId, eventId, client) {
1091
+ if (!eventId?.trim()) return;
1092
+ await withResolvedMatrixControlClient({ client }, async (resolved) => {
1093
+ const resolvedRoom = await resolveMatrixRoomId(resolved, roomId);
1094
+ await resolved.sendReadReceipt(resolvedRoom, eventId.trim());
1095
+ });
1096
+ }
1097
+ async function sendSingleTextMessageMatrix(roomId, text, opts) {
1098
+ const { trimmedText, convertedText, singleEventLimit, fitsInSingleEvent } = prepareMatrixSingleText(text, {
1099
+ cfg: opts.cfg,
1100
+ accountId: opts.accountId
1101
+ });
1102
+ if (!trimmedText) throw new Error("Matrix single-message send requires text");
1103
+ if (!fitsInSingleEvent) throw new Error(`Matrix single-message text exceeds limit (${convertedText.length} > ${singleEventLimit})`);
1104
+ return await withResolvedMatrixSendClient({
1105
+ client: opts.client,
1106
+ cfg: opts.cfg,
1107
+ accountId: opts.accountId
1108
+ }, async (client) => {
1109
+ const resolvedRoom = await resolveMatrixRoomId(client, roomId);
1110
+ const normalizedThreadId = normalizeThreadId(opts.threadId);
1111
+ const content = withMatrixExtraContentFields(buildTextContent(convertedText, normalizedThreadId ? buildThreadRelation(normalizedThreadId, opts.replyToId) : buildReplyRelation(opts.replyToId), { msgtype: opts.msgtype }), opts.extraContent);
1112
+ await enrichMatrixFormattedContent({
1113
+ client,
1114
+ content,
1115
+ markdown: convertedText,
1116
+ includeMentions: opts.includeMentions
1117
+ });
1118
+ if (opts.live) content[MSC4357_LIVE_KEY] = {};
1119
+ const eventId = await client.sendMessage(resolvedRoom, content);
1120
+ return {
1121
+ messageId: eventId ?? "unknown",
1122
+ roomId: resolvedRoom,
1123
+ primaryMessageId: eventId ?? "unknown",
1124
+ receipt: createMatrixSendReceipt({
1125
+ roomId: resolvedRoom,
1126
+ platformMessageIds: eventId ? [eventId] : [],
1127
+ kind: "text",
1128
+ replyToId: opts.replyToId,
1129
+ threadId: normalizedThreadId
1130
+ })
1131
+ };
1132
+ });
1133
+ }
1134
+ async function getPreviousMatrixEvent(client, roomId, eventId) {
1135
+ const getEvent = client.getEvent;
1136
+ if (typeof getEvent !== "function") return null;
1137
+ return await Promise.resolve(getEvent.call(client, roomId, eventId)).catch(() => null);
1138
+ }
1139
+ async function editMessageMatrix(roomId, originalEventId, newText, opts) {
1140
+ return await withResolvedMatrixSendClient({
1141
+ client: opts.client,
1142
+ cfg: opts.cfg,
1143
+ accountId: opts.accountId,
1144
+ timeoutMs: opts.timeoutMs
1145
+ }, async (client) => {
1146
+ const resolvedRoom = await resolveMatrixRoomId(client, roomId);
1147
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix message edit");
1148
+ const tableMode = getCore().channel.text.resolveMarkdownTableMode({
1149
+ cfg,
1150
+ channel: "matrix",
1151
+ accountId: opts.accountId
1152
+ });
1153
+ const convertedText = getCore().channel.text.convertMarkdownTables(newText, tableMode);
1154
+ const newContent = withMatrixExtraContentFields(buildTextContent(convertedText, void 0, { msgtype: opts.msgtype }), opts.extraContent);
1155
+ await enrichMatrixFormattedContent({
1156
+ client,
1157
+ content: newContent,
1158
+ markdown: convertedText,
1159
+ includeMentions: opts.includeMentions
1160
+ });
1161
+ const replaceMentions = opts.includeMentions === false ? void 0 : diffMatrixMentions(extractMatrixMentions(newContent), await resolvePreviousEditMentions({
1162
+ client,
1163
+ content: resolvePreviousEditContent(await getPreviousMatrixEvent(client, resolvedRoom, originalEventId))
1164
+ }));
1165
+ const replaceRelation = {
1166
+ rel_type: RelationType.Replace,
1167
+ event_id: originalEventId
1168
+ };
1169
+ const threadId = normalizeThreadId(opts.threadId);
1170
+ if (threadId) replaceRelation["m.in_reply_to"] = { event_id: threadId };
1171
+ const content = {
1172
+ ...newContent,
1173
+ body: `* ${convertedText}`,
1174
+ ...typeof newContent.formatted_body === "string" ? { formatted_body: `* ${newContent.formatted_body}` } : {},
1175
+ "m.new_content": newContent,
1176
+ "m.relates_to": replaceRelation
1177
+ };
1178
+ if (replaceMentions !== void 0) content["m.mentions"] = replaceMentions;
1179
+ if (opts.live) {
1180
+ content[MSC4357_LIVE_KEY] = {};
1181
+ content["m.new_content"][MSC4357_LIVE_KEY] = {};
1182
+ }
1183
+ return await client.sendMessage(resolvedRoom, content) ?? "";
1184
+ });
1185
+ }
1186
+ async function reactMatrixMessage(roomId, messageId, emoji, opts) {
1187
+ const clientOpts = normalizeMatrixClientResolveOpts(opts);
1188
+ await withResolvedMatrixSendClient({
1189
+ client: clientOpts.client,
1190
+ cfg: clientOpts.cfg,
1191
+ timeoutMs: clientOpts.timeoutMs,
1192
+ accountId: clientOpts.accountId ?? void 0
1193
+ }, async (resolved) => {
1194
+ const resolvedRoom = await resolveMatrixRoomId(resolved, roomId);
1195
+ const reaction = buildMatrixReactionContent(messageId, emoji);
1196
+ await resolved.sendEvent(resolvedRoom, EventType.Reaction, reaction);
1197
+ });
1198
+ }
1199
+ //#endregion
1200
+ export { parsePollStart as _, sendMessageMatrix as a, sendTypingMatrix as c, buildPollResponseContent as d, buildPollResultsSummary as f, isPollStartType as g, isPollEventType as h, reactMatrixMessage as i, send_exports as l, formatPollResultsAsText as m, editMessageMatrix as n, sendPollMatrix as o, formatPollAsText as p, prepareMatrixSingleText as r, sendSingleTextMessageMatrix as s, chunkMatrixText as t, resolveMatrixRoomId as u, parsePollStartContent as v, resolvePollReferenceEventId as y };