@barryfan2045/dsh-matrix 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +286 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/assets/avatars/instance-01.png +0 -0
- package/assets/avatars/instance-02.png +0 -0
- package/assets/avatars/instance-03.png +0 -0
- package/assets/avatars/instance-04.png +0 -0
- package/assets/avatars/instance-05.png +0 -0
- package/assets/avatars/instance-06.png +0 -0
- package/assets/avatars/instance-07.png +0 -0
- package/assets/avatars/instance-08.png +0 -0
- package/assets/avatars/instance-09.png +0 -0
- package/assets/avatars/instance-10.png +0 -0
- package/assets/avatars/instance-11.png +0 -0
- package/assets/avatars/instance-12.png +0 -0
- package/assets/avatars/management.png +0 -0
- package/cordis.patch.yml +13 -0
- package/dist/auth/admin-commands.d.ts +47 -0
- package/dist/auth/admin-commands.d.ts.map +1 -0
- package/dist/auth/admin-commands.js +112 -0
- package/dist/auth/admin-commands.js.map +1 -0
- package/dist/auth/challenge.d.ts +93 -0
- package/dist/auth/challenge.d.ts.map +1 -0
- package/dist/auth/challenge.js +104 -0
- package/dist/auth/challenge.js.map +1 -0
- package/dist/auth/store.d.ts +55 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +124 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/bridge/agent-commands.d.ts +39 -0
- package/dist/bridge/agent-commands.d.ts.map +1 -0
- package/dist/bridge/agent-commands.js +243 -0
- package/dist/bridge/agent-commands.js.map +1 -0
- package/dist/bridge/attachments-ledger.d.ts +54 -0
- package/dist/bridge/attachments-ledger.d.ts.map +1 -0
- package/dist/bridge/attachments-ledger.js +68 -0
- package/dist/bridge/attachments-ledger.js.map +1 -0
- package/dist/bridge/commands.d.ts +28 -0
- package/dist/bridge/commands.d.ts.map +1 -0
- package/dist/bridge/commands.js +18 -0
- package/dist/bridge/commands.js.map +1 -0
- package/dist/bridge/crypto-health.d.ts +37 -0
- package/dist/bridge/crypto-health.d.ts.map +1 -0
- package/dist/bridge/crypto-health.js +38 -0
- package/dist/bridge/crypto-health.js.map +1 -0
- package/dist/bridge/group-gate.d.ts +38 -0
- package/dist/bridge/group-gate.d.ts.map +1 -0
- package/dist/bridge/group-gate.js +49 -0
- package/dist/bridge/group-gate.js.map +1 -0
- package/dist/bridge/instance-lock.d.ts +10 -0
- package/dist/bridge/instance-lock.d.ts.map +1 -0
- package/dist/bridge/instance-lock.js +82 -0
- package/dist/bridge/instance-lock.js.map +1 -0
- package/dist/bridge/interactions.d.ts +62 -0
- package/dist/bridge/interactions.d.ts.map +1 -0
- package/dist/bridge/interactions.js +237 -0
- package/dist/bridge/interactions.js.map +1 -0
- package/dist/bridge/management-room.d.ts +13 -0
- package/dist/bridge/management-room.d.ts.map +1 -0
- package/dist/bridge/management-room.js +27 -0
- package/dist/bridge/management-room.js.map +1 -0
- package/dist/bridge/multi-project.d.ts +34 -0
- package/dist/bridge/multi-project.d.ts.map +1 -0
- package/dist/bridge/multi-project.js +44 -0
- package/dist/bridge/multi-project.js.map +1 -0
- package/dist/bridge/pipeline.d.ts +151 -0
- package/dist/bridge/pipeline.d.ts.map +1 -0
- package/dist/bridge/pipeline.js +502 -0
- package/dist/bridge/pipeline.js.map +1 -0
- package/dist/bridge/pmctl.d.ts +50 -0
- package/dist/bridge/pmctl.d.ts.map +1 -0
- package/dist/bridge/pmctl.js +272 -0
- package/dist/bridge/pmctl.js.map +1 -0
- package/dist/bridge/power-levels.d.ts +57 -0
- package/dist/bridge/power-levels.d.ts.map +1 -0
- package/dist/bridge/power-levels.js +92 -0
- package/dist/bridge/power-levels.js.map +1 -0
- package/dist/bridge/power-sync.d.ts +64 -0
- package/dist/bridge/power-sync.d.ts.map +1 -0
- package/dist/bridge/power-sync.js +121 -0
- package/dist/bridge/power-sync.js.map +1 -0
- package/dist/bridge/projects.d.ts +97 -0
- package/dist/bridge/projects.d.ts.map +1 -0
- package/dist/bridge/projects.js +288 -0
- package/dist/bridge/projects.js.map +1 -0
- package/dist/bridge/quote-cache.d.ts +38 -0
- package/dist/bridge/quote-cache.d.ts.map +1 -0
- package/dist/bridge/quote-cache.js +56 -0
- package/dist/bridge/quote-cache.js.map +1 -0
- package/dist/bridge/reply.d.ts +31 -0
- package/dist/bridge/reply.d.ts.map +1 -0
- package/dist/bridge/reply.js +122 -0
- package/dist/bridge/reply.js.map +1 -0
- package/dist/bridge/space-identity.d.ts +43 -0
- package/dist/bridge/space-identity.d.ts.map +1 -0
- package/dist/bridge/space-identity.js +92 -0
- package/dist/bridge/space-identity.js.map +1 -0
- package/dist/bridge/space-sync.d.ts +94 -0
- package/dist/bridge/space-sync.d.ts.map +1 -0
- package/dist/bridge/space-sync.js +137 -0
- package/dist/bridge/space-sync.js.map +1 -0
- package/dist/bridge/turn-commands.d.ts +28 -0
- package/dist/bridge/turn-commands.d.ts.map +1 -0
- package/dist/bridge/turn-commands.js +97 -0
- package/dist/bridge/turn-commands.js.map +1 -0
- package/dist/cli/config-store.d.ts +32 -0
- package/dist/cli/config-store.d.ts.map +1 -0
- package/dist/cli/config-store.js +118 -0
- package/dist/cli/config-store.js.map +1 -0
- package/dist/cli/crypto-store.d.ts +42 -0
- package/dist/cli/crypto-store.d.ts.map +1 -0
- package/dist/cli/crypto-store.js +70 -0
- package/dist/cli/crypto-store.js.map +1 -0
- package/dist/cli/device-prune.d.ts +105 -0
- package/dist/cli/device-prune.d.ts.map +1 -0
- package/dist/cli/device-prune.js +163 -0
- package/dist/cli/device-prune.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +584 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/log-filter.d.ts +32 -0
- package/dist/cli/log-filter.d.ts.map +1 -0
- package/dist/cli/log-filter.js +40 -0
- package/dist/cli/log-filter.js.map +1 -0
- package/dist/cli/pnpm-builds.d.ts +32 -0
- package/dist/cli/pnpm-builds.d.ts.map +1 -0
- package/dist/cli/pnpm-builds.js +73 -0
- package/dist/cli/pnpm-builds.js.map +1 -0
- package/dist/cli/profile-patch.d.ts +26 -0
- package/dist/cli/profile-patch.d.ts.map +1 -0
- package/dist/cli/profile-patch.js +56 -0
- package/dist/cli/profile-patch.js.map +1 -0
- package/dist/cli/setup.d.ts +39 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +234 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/systemd-hint.d.ts +11 -0
- package/dist/cli/systemd-hint.d.ts.map +1 -0
- package/dist/cli/systemd-hint.js +46 -0
- package/dist/cli/systemd-hint.js.map +1 -0
- package/dist/cli/systemd.d.ts +32 -0
- package/dist/cli/systemd.d.ts.map +1 -0
- package/dist/cli/systemd.js +58 -0
- package/dist/cli/systemd.js.map +1 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/dsh/backend.d.ts +110 -0
- package/dist/dsh/backend.d.ts.map +1 -0
- package/dist/dsh/backend.js +481 -0
- package/dist/dsh/backend.js.map +1 -0
- package/dist/dsh/interactions.d.ts +50 -0
- package/dist/dsh/interactions.d.ts.map +1 -0
- package/dist/dsh/interactions.js +141 -0
- package/dist/dsh/interactions.js.map +1 -0
- package/dist/dsh/stream.d.ts +32 -0
- package/dist/dsh/stream.d.ts.map +1 -0
- package/dist/dsh/stream.js +109 -0
- package/dist/dsh/stream.js.map +1 -0
- package/dist/formatting.d.ts +14 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +63 -0
- package/dist/formatting.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +28 -0
- package/dist/logger.js.map +1 -0
- package/dist/matrix/matrix-events.d.ts +49 -0
- package/dist/matrix/matrix-events.d.ts.map +1 -0
- package/dist/matrix/matrix-events.js +89 -0
- package/dist/matrix/matrix-events.js.map +1 -0
- package/dist/matrix/transport.d.ts +192 -0
- package/dist/matrix/transport.d.ts.map +1 -0
- package/dist/matrix/transport.js +510 -0
- package/dist/matrix/transport.js.map +1 -0
- package/dist/ports/attachments.d.ts +51 -0
- package/dist/ports/attachments.d.ts.map +1 -0
- package/dist/ports/attachments.js +27 -0
- package/dist/ports/attachments.js.map +1 -0
- package/dist/ports/backend.d.ts +159 -0
- package/dist/ports/backend.d.ts.map +1 -0
- package/dist/ports/backend.js +25 -0
- package/dist/ports/backend.js.map +1 -0
- package/dist/ports/interactions.d.ts +65 -0
- package/dist/ports/interactions.d.ts.map +1 -0
- package/dist/ports/interactions.js +15 -0
- package/dist/ports/interactions.js.map +1 -0
- package/dist/ports/streaming.d.ts +45 -0
- package/dist/ports/streaming.d.ts.map +1 -0
- package/dist/ports/streaming.js +23 -0
- package/dist/ports/streaming.js.map +1 -0
- package/dist/ports/transport.d.ts +68 -0
- package/dist/ports/transport.d.ts.map +1 -0
- package/dist/ports/transport.js +2 -0
- package/dist/ports/transport.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matrix transport: implements the Transport port over matrix-bot-sdk.
|
|
3
|
+
* Ported essentials from pi-courier src/transports/matrix.ts + matrix-utils.ts
|
|
4
|
+
* (MIT), with the spike-verified fixes (docs §7, §9):
|
|
5
|
+
* - login with password → cache user_id (getUserId() is undefined pre-sync)
|
|
6
|
+
* - skip history replayed by the initial sync via an origin_server_ts cut
|
|
7
|
+
* - skip edits (m.new_content), notices, empty bodies, our own messages
|
|
8
|
+
*
|
|
9
|
+
* The SDK surface is behind a factory so seam tests inject a fake client.
|
|
10
|
+
*/
|
|
11
|
+
import { MatrixClient, AutojoinRoomsMixin, SimpleFsStorageProvider } from 'matrix-bot-sdk';
|
|
12
|
+
import { MediaTooLargeError } from "../ports/attachments.js";
|
|
13
|
+
import { translateRoomEvent } from "./matrix-events.js";
|
|
14
|
+
import { BOT_POWER_LEVEL } from "../bridge/power-levels.js";
|
|
15
|
+
import { createCryptoHealth } from "../bridge/crypto-health.js";
|
|
16
|
+
/** T8 defaults: room-member facts are cached, >2 members means a group. */
|
|
17
|
+
export const MEMBER_COUNT_TTL_MS = 60_000;
|
|
18
|
+
/** Localpart of an MXID (@user:server → user). */
|
|
19
|
+
function extractLocalpart(userId) {
|
|
20
|
+
return userId.replace(/^@/, '').replace(/:.*$/, '');
|
|
21
|
+
}
|
|
22
|
+
function escapeRegExp(s) {
|
|
23
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
24
|
+
}
|
|
25
|
+
/** Mention check (pi-courier matrix-utils port): full MXID text or @localpart word. */
|
|
26
|
+
function wasBotMentioned(text, botUserId) {
|
|
27
|
+
if (botUserId && text.includes(botUserId))
|
|
28
|
+
return true;
|
|
29
|
+
const localpart = extractLocalpart(botUserId);
|
|
30
|
+
if (!localpart)
|
|
31
|
+
return false;
|
|
32
|
+
return new RegExp(`@${escapeRegExp(localpart)}\\b`, 'i').test(text);
|
|
33
|
+
}
|
|
34
|
+
/** Strip bot mention tokens, symmetric with wasBotMentioned. */
|
|
35
|
+
function stripBotMention(text, botUserId) {
|
|
36
|
+
if (!botUserId)
|
|
37
|
+
return text;
|
|
38
|
+
const localpart = extractLocalpart(botUserId);
|
|
39
|
+
let out = text.replace(new RegExp(escapeRegExp(botUserId), 'g'), '');
|
|
40
|
+
if (localpart)
|
|
41
|
+
out = out.replace(new RegExp(`@${escapeRegExp(localpart)}\\b`, 'gi'), '');
|
|
42
|
+
return out.trim();
|
|
43
|
+
}
|
|
44
|
+
export class MatrixTransport {
|
|
45
|
+
opts;
|
|
46
|
+
handlers = [];
|
|
47
|
+
joinHandlers = [];
|
|
48
|
+
client;
|
|
49
|
+
botUserId = '';
|
|
50
|
+
accessToken;
|
|
51
|
+
connectedAt = 0;
|
|
52
|
+
loggerRef;
|
|
53
|
+
now;
|
|
54
|
+
/** roomId → member count snapshot with fetch timestamp (T8). */
|
|
55
|
+
memberCache = new Map();
|
|
56
|
+
/** T13:解密健康观测(失败到阈值告警一次,成功则重置);start() 时按 logger 建。 */
|
|
57
|
+
cryptoHealth;
|
|
58
|
+
/**
|
|
59
|
+
* T6: mxc → 加密附件块 + 事件声明的 mimetype(translate 层填充)。
|
|
60
|
+
* downloadMedia 用它决定是否走 decryptMedia;有界 FIFO,防无界增长。
|
|
61
|
+
*/
|
|
62
|
+
mediaHints = new Map();
|
|
63
|
+
mediaHintOrder = [];
|
|
64
|
+
constructor(opts) {
|
|
65
|
+
this.opts = opts;
|
|
66
|
+
this.accessToken = opts.accessToken ?? '';
|
|
67
|
+
this.now = opts.now ?? Date.now;
|
|
68
|
+
}
|
|
69
|
+
/** translate 层登记一条媒体事件的下载提示(E2EE 块 / 声明类型)。 */
|
|
70
|
+
noteMediaHint(mxc, hint) {
|
|
71
|
+
if (!this.mediaHints.has(mxc)) {
|
|
72
|
+
this.mediaHintOrder.push(mxc);
|
|
73
|
+
if (this.mediaHintOrder.length > 200) {
|
|
74
|
+
const drop = this.mediaHintOrder.splice(0, this.mediaHintOrder.length - 200);
|
|
75
|
+
for (const k of drop)
|
|
76
|
+
this.mediaHints.delete(k);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.mediaHints.set(mxc, hint);
|
|
80
|
+
}
|
|
81
|
+
onIncoming(handler) {
|
|
82
|
+
this.handlers.push(handler);
|
|
83
|
+
return () => {
|
|
84
|
+
const i = this.handlers.indexOf(handler);
|
|
85
|
+
if (i >= 0)
|
|
86
|
+
this.handlers.splice(i, 1);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** T8: notified when the bot joins a room, with the group verdict. */
|
|
90
|
+
onRoomJoin(handler) {
|
|
91
|
+
this.joinHandlers.push(handler);
|
|
92
|
+
return () => {
|
|
93
|
+
const i = this.joinHandlers.indexOf(handler);
|
|
94
|
+
if (i >= 0)
|
|
95
|
+
this.joinHandlers.splice(i, 1);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async start(logger) {
|
|
99
|
+
this.loggerRef = logger;
|
|
100
|
+
this.cryptoHealth = this.opts.cryptoHealth ?? createCryptoHealth({ logger });
|
|
101
|
+
// T14:**crypto 存储必须在登录之前就位**,因为登录要带上它的 deviceId。
|
|
102
|
+
// 为什么:密码登录不带 device_id 时,Synapse 每次都会**新建一个设备**;而
|
|
103
|
+
// OlmMachine 仍然以存储里那个旧 deviceId 自居。于是两边分叉 ——
|
|
104
|
+
// · 上传的 device keys 挂在旧设备上 → 发送方把房间密钥加密给旧设备;
|
|
105
|
+
// · /sync 拿的却是新设备的 access_token → to-device 的密钥永远送不到。
|
|
106
|
+
// 症状就是那句 "Can't find the room key to decrypt the event, withheld code: None",
|
|
107
|
+
// 而且全程无声无息(桥照常启动、照常打 E2EE 已启用)。
|
|
108
|
+
const providers = await this.buildProviders(logger);
|
|
109
|
+
const storedDeviceId = await readStoredDeviceId(providers);
|
|
110
|
+
if (!this.accessToken) {
|
|
111
|
+
const { username, password, homeserver } = this.opts;
|
|
112
|
+
if (!username || !password)
|
|
113
|
+
throw new Error('dsh-matrix matrix: 无 accessToken 时需要 username+password');
|
|
114
|
+
const doFetch = this.opts.fetchImpl ?? fetch;
|
|
115
|
+
const res = await doFetch(`${homeserver.replace(/\/$/, '')}/_matrix/client/v3/login`, {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: { 'content-type': 'application/json' },
|
|
118
|
+
body: JSON.stringify({
|
|
119
|
+
type: 'm.login.password',
|
|
120
|
+
identifier: { type: 'm.id.user', user: username },
|
|
121
|
+
password,
|
|
122
|
+
// 复用存储里的设备身份:重启不再造新设备,E2EE 身份也就不会每次漂移
|
|
123
|
+
...(storedDeviceId ? { device_id: storedDeviceId } : {}),
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
const login = (await res.json());
|
|
127
|
+
if (!login.access_token)
|
|
128
|
+
throw new Error(`dsh-matrix matrix login 失败: ${JSON.stringify(login)}`);
|
|
129
|
+
this.accessToken = login.access_token;
|
|
130
|
+
this.botUserId = login.user_id ?? '';
|
|
131
|
+
logger.info(`matrix 登录成功: ${this.botUserId}${storedDeviceId ? `(复用设备 ${storedDeviceId})` : ''}`);
|
|
132
|
+
// 服务端理论上会照办;真没照办就必须当场可见,否则又是"静默解不开"
|
|
133
|
+
if (storedDeviceId && login.device_id && login.device_id !== storedDeviceId) {
|
|
134
|
+
logger.warn(`matrix E2EE 设备身份不一致:存储是 ${storedDeviceId},登录拿到 ${login.device_id}。` +
|
|
135
|
+
'加密房间会收不到消息,请跑 `dsh-matrix reset-crypto` 复位存储。');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const factory = this.opts.clientFactory ?? defaultClientFactory;
|
|
139
|
+
const client = factory(this.opts.homeserver, this.accessToken, providers);
|
|
140
|
+
this.client = client;
|
|
141
|
+
client.on('room.message', (roomId, event) => {
|
|
142
|
+
// T8: translation is async (member-count lookup); dispatch on settle.
|
|
143
|
+
void this.translate(roomId, event)
|
|
144
|
+
.then((translated) => {
|
|
145
|
+
if (!translated)
|
|
146
|
+
return;
|
|
147
|
+
for (const h of [...this.handlers]) {
|
|
148
|
+
try {
|
|
149
|
+
void Promise.resolve(h(translated)).catch((error) => logger.warn('incoming handler:', error));
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
logger.warn('incoming handler:', error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
.catch((error) => logger.warn('event translate:', error));
|
|
157
|
+
});
|
|
158
|
+
client.on('room.join', (roomId, event) => {
|
|
159
|
+
// Initial sync replays old joins: only act on fresh ones (same 2s slack
|
|
160
|
+
// as room.message). Group-room join hints are bridge policy, not ours.
|
|
161
|
+
const ts = typeof event?.origin_server_ts === 'number' ? event.origin_server_ts : this.now();
|
|
162
|
+
if (ts < this.connectedAt)
|
|
163
|
+
return;
|
|
164
|
+
void this.resolveIsGroup(roomId)
|
|
165
|
+
.then((isGroup) => {
|
|
166
|
+
for (const h of [...this.joinHandlers]) {
|
|
167
|
+
try {
|
|
168
|
+
h(roomId, isGroup);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger.warn('join handler:', error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
.catch((error) => logger.warn('room join:', error));
|
|
176
|
+
});
|
|
177
|
+
// T13:crypto 健康。SDK 对解密失败/成功各有一个一等公民事件,直接数,不猜。
|
|
178
|
+
client.on('room.failed_decryption', (roomId, event) => {
|
|
179
|
+
this.cryptoHealth.onFailedDecryption(roomId, typeof event?.event_id === 'string' ? event.event_id : undefined);
|
|
180
|
+
});
|
|
181
|
+
client.on('room.decrypted_event', (roomId, event) => {
|
|
182
|
+
if (event?.type === 'm.room.message')
|
|
183
|
+
this.cryptoHealth.onDecrypted(roomId);
|
|
184
|
+
});
|
|
185
|
+
// 成员变动会让"是不是群"的判定作废:有人在房里加入/退出时立刻删掉缓存。
|
|
186
|
+
// 否则一个刚变成 3 人的房间会在 TTL(60s)内继续被当成私聊 —— 群闸门被整个
|
|
187
|
+
// 绕过,未被 @ 的群消息会直接进模型(实测踩到:bot 进群时 2 人,第三人加入后
|
|
188
|
+
// 群消息被当私聊回答)。只认 m.room.member,避免把每条消息都变成一次 HTTP。
|
|
189
|
+
client.on('room.event', (roomId, event) => {
|
|
190
|
+
if (event?.type === 'm.room.member')
|
|
191
|
+
this.memberCache.delete(roomId);
|
|
192
|
+
});
|
|
193
|
+
// 初始同步会重放历史事件:只接受启动(留 2s 时钟容差)之后的新消息
|
|
194
|
+
this.connectedAt = this.now() - 2000;
|
|
195
|
+
await client.start();
|
|
196
|
+
// accessToken 直连时 login 响应缺席,问一次 client 拿 bot MXID(best effort)
|
|
197
|
+
if (!this.botUserId && typeof client.getUserId === 'function') {
|
|
198
|
+
try {
|
|
199
|
+
this.botUserId = await client.getUserId();
|
|
200
|
+
}
|
|
201
|
+
catch { /* pre-sync: mention detection degrades to body fallback */ }
|
|
202
|
+
}
|
|
203
|
+
// T14 兜底体检:accessToken 直连也走同一条分叉(存储设备 ≠ token 设备)。
|
|
204
|
+
// 这里只报警不擅自复位 —— 复位会换设备身份,必须由人显式决定(T13 §4)。
|
|
205
|
+
await this.warnOnDeviceMismatch(client, storedDeviceId, logger);
|
|
206
|
+
}
|
|
207
|
+
/** 持久化 sync 状态(重启不再全量重放)+ 可选 E2EE(失败降级明文)。 */
|
|
208
|
+
async buildProviders(logger) {
|
|
209
|
+
const providers = {};
|
|
210
|
+
if (this.opts.storePath) {
|
|
211
|
+
providers.storage = new SimpleFsStorageProvider(this.opts.storePath);
|
|
212
|
+
}
|
|
213
|
+
if (this.opts.encryption !== false && this.opts.cryptoPath) {
|
|
214
|
+
try {
|
|
215
|
+
const { RustSdkCryptoStorageProvider } = await import('matrix-bot-sdk');
|
|
216
|
+
const { StoreType } = await import('@matrix-org/matrix-sdk-crypto-nodejs');
|
|
217
|
+
const mkdirSync = (await import('node:fs')).mkdirSync;
|
|
218
|
+
mkdirSync(this.opts.cryptoPath, { recursive: true });
|
|
219
|
+
providers.crypto = new RustSdkCryptoStorageProvider(this.opts.cryptoPath, 0 /* StoreType.Sqlite */);
|
|
220
|
+
logger.info(`E2EE 已启用(crypto: ${this.opts.cryptoPath})`);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
logger.warn('E2EE 不可用,降级为明文:', error instanceof Error ? error.message : error);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return providers;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* T14:校验"crypto 存储的设备"与"access_token 所属设备"是否同一个。
|
|
230
|
+
* 不一致 = 加密房必然收不到消息(见 start() 的长注释),所以必须吼一声。
|
|
231
|
+
* best-effort:任何失败都只是放弃体检,绝不影响启动。
|
|
232
|
+
*/
|
|
233
|
+
async warnOnDeviceMismatch(client, storedDeviceId, logger) {
|
|
234
|
+
if (!storedDeviceId || typeof client.getWhoAmI !== 'function')
|
|
235
|
+
return;
|
|
236
|
+
try {
|
|
237
|
+
const who = await client.getWhoAmI();
|
|
238
|
+
const tokenDevice = typeof who?.device_id === 'string' ? who.device_id : '';
|
|
239
|
+
if (!tokenDevice || tokenDevice === storedDeviceId)
|
|
240
|
+
return;
|
|
241
|
+
logger.warn(`matrix E2EE 设备身份分叉:crypto 存储是 ${storedDeviceId},access_token 属于 ${tokenDevice}。` +
|
|
242
|
+
'加密房间的消息会因为拿不到房间密钥而收不到;请跑 `dsh-matrix reset-crypto` 复位存储,' +
|
|
243
|
+
'或改用不带 accessToken 的密码登录(登录会自动复用存储里的设备)。');
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
/* 体检失败不影响启动 */
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* T8: group fact = more than two joined members (bot + one other is a DM).
|
|
251
|
+
* Cached per room for memberTtlMs so the hot path never hits the API;
|
|
252
|
+
* any failure defaults to DM (skips the group gate, never the reverse).
|
|
253
|
+
*/
|
|
254
|
+
async resolveIsGroup(roomId) {
|
|
255
|
+
const client = this.client;
|
|
256
|
+
if (!client?.getJoinedRoomMembers)
|
|
257
|
+
return false;
|
|
258
|
+
const now = this.now();
|
|
259
|
+
const ttl = this.opts.memberTtlMs ?? MEMBER_COUNT_TTL_MS;
|
|
260
|
+
const hit = this.memberCache.get(roomId);
|
|
261
|
+
if (hit && now - hit.at < ttl)
|
|
262
|
+
return hit.count > 2;
|
|
263
|
+
try {
|
|
264
|
+
const members = await client.getJoinedRoomMembers(roomId);
|
|
265
|
+
const count = Array.isArray(members) ? members.length : 2;
|
|
266
|
+
this.memberCache.set(roomId, { count, at: now });
|
|
267
|
+
return count > 2;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
return false; // 不缓存失败:下一条消息重试
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Unified translation: T6 pure event semantics (text/media/unsupported +
|
|
275
|
+
* self/replay/edit guards) THEN T8 group facts (isGroup via member count,
|
|
276
|
+
* mention detect+strip, replyTo). null = skip silently by design.
|
|
277
|
+
*/
|
|
278
|
+
async translate(roomId, event) {
|
|
279
|
+
const { message, warn } = translateRoomEvent({
|
|
280
|
+
botUserId: this.botUserId,
|
|
281
|
+
connectedAt: this.connectedAt,
|
|
282
|
+
now: this.now,
|
|
283
|
+
noteMediaHint: (mxc, hint) => this.noteMediaHint(mxc, hint),
|
|
284
|
+
}, roomId, event);
|
|
285
|
+
if (warn)
|
|
286
|
+
this.loggerRef?.warn(`matrix: ${warn}`);
|
|
287
|
+
if (!message)
|
|
288
|
+
return null;
|
|
289
|
+
// ── T8 group facts ─────────────────────────────────────────────
|
|
290
|
+
const isGroup = await this.resolveIsGroup(roomId);
|
|
291
|
+
let mentionsBot = false;
|
|
292
|
+
let text = message.text;
|
|
293
|
+
if (isGroup && text) {
|
|
294
|
+
const content = (event.content ?? {});
|
|
295
|
+
const ids = content['m.mentions']?.user_ids;
|
|
296
|
+
mentionsBot = this.botUserId !== '' && Array.isArray(ids) && ids.includes(this.botUserId);
|
|
297
|
+
if (!mentionsBot)
|
|
298
|
+
mentionsBot = wasBotMentioned(text, this.botUserId);
|
|
299
|
+
if (mentionsBot && this.botUserId) {
|
|
300
|
+
text = stripBotMention(text, this.botUserId);
|
|
301
|
+
if (!text)
|
|
302
|
+
return null; // 光@不说事:无事发生
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const relates = (event.content?.['m.relates_to'] ?? {});
|
|
306
|
+
const replyEventId = relates['m.in_reply_to']?.event_id;
|
|
307
|
+
return {
|
|
308
|
+
...message,
|
|
309
|
+
text,
|
|
310
|
+
isDm: !isGroup,
|
|
311
|
+
isGroup,
|
|
312
|
+
mentionsBot,
|
|
313
|
+
...(typeof replyEventId === 'string' && replyEventId ? { replyTo: { eventId: replyEventId } } : {}),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
async send(roomId, text) {
|
|
317
|
+
return this.requireClient().sendMessage(roomId, { msgtype: 'm.text', body: text });
|
|
318
|
+
}
|
|
319
|
+
async notify(roomId, text) {
|
|
320
|
+
return this.requireClient().sendMessage(roomId, { msgtype: 'm.notice', body: text });
|
|
321
|
+
}
|
|
322
|
+
async edit(roomId, editId, text) {
|
|
323
|
+
// Matrix rich edits: an m.replace relation carrying full new content.
|
|
324
|
+
await this.requireClient().sendMessage(roomId, {
|
|
325
|
+
msgtype: 'm.text',
|
|
326
|
+
body: `* ${text.slice(0, 80)}`,
|
|
327
|
+
'm.new_content': { msgtype: 'm.text', body: text },
|
|
328
|
+
'm.relates_to': { rel_type: 'm.replace', event_id: editId },
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* T6: download one mxc payload (E2EE-decrypted when the event carried an
|
|
333
|
+
* encrypted file block, see noteMediaHint). Throws a room-ready message on
|
|
334
|
+
* transport failure, MediaTooLargeError when the REAL bytes exceed the cap.
|
|
335
|
+
*/
|
|
336
|
+
async downloadMedia(mxc, maxSizeBytes) {
|
|
337
|
+
const client = this.requireClient();
|
|
338
|
+
if (!client.downloadContent)
|
|
339
|
+
throw new Error('当前 Matrix 客户端不支持内容下载');
|
|
340
|
+
const hint = this.mediaHints.get(mxc);
|
|
341
|
+
// 先拒"需要解密但没有 crypto":省一次必然无用的下载。
|
|
342
|
+
if (hint?.file && !client.crypto) {
|
|
343
|
+
throw new Error('加密附件需要启用 E2EE(本部署未开启 crypto)');
|
|
344
|
+
}
|
|
345
|
+
let data;
|
|
346
|
+
let contentType;
|
|
347
|
+
try {
|
|
348
|
+
const res = await client.downloadContent(mxc, true);
|
|
349
|
+
data = hint?.file ? await client.crypto.decryptMedia(hint.file) : res.data;
|
|
350
|
+
contentType = res.contentType;
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
if (error instanceof MediaTooLargeError)
|
|
354
|
+
throw error;
|
|
355
|
+
throw new Error(`附件下载失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
356
|
+
}
|
|
357
|
+
if (data.length > maxSizeBytes)
|
|
358
|
+
throw new MediaTooLargeError(data.length, maxSizeBytes);
|
|
359
|
+
// E2EE 下载 contentType 恒为 application/octet-stream:事件声明的
|
|
360
|
+
// info.mimetype 是唯一可信来源(明文路径以真实响应头为准)。
|
|
361
|
+
const mimeType = hint?.file && hint.mimeType ? hint.mimeType : contentType;
|
|
362
|
+
return {
|
|
363
|
+
bytes: new Uint8Array(data),
|
|
364
|
+
mimeType,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async stop() {
|
|
368
|
+
const client = this.client;
|
|
369
|
+
this.client = undefined;
|
|
370
|
+
if (!client)
|
|
371
|
+
return;
|
|
372
|
+
try {
|
|
373
|
+
client.stop();
|
|
374
|
+
}
|
|
375
|
+
catch { /* idempotent */ }
|
|
376
|
+
}
|
|
377
|
+
// ── T9 房间操作(移植 pi-courier matrix-rooms.ts 的最小子集) ──────────────
|
|
378
|
+
/** 建私有项目/管理房间:名字 + 邀请(preset=private_chat 让受邀者自动可见)。 */
|
|
379
|
+
async createRoom(opts) {
|
|
380
|
+
return this.requireClient().createRoom({
|
|
381
|
+
name: opts.name,
|
|
382
|
+
invite: [opts.inviteUserId],
|
|
383
|
+
preset: 'private_chat',
|
|
384
|
+
visibility: 'private',
|
|
385
|
+
...this.botPowerOverride(),
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 桥建自己的房间时,把自己的 PL 放到 101(用户仍是 100)。
|
|
390
|
+
* 不这样,Matrix 不允许桥改动"与自己同级"的人 —— `/revoke` 就降不动那些 100 的
|
|
391
|
+
* trusted 用户(M_FORBIDDEN: … equal to your own)。bot MXID 未知时不写。
|
|
392
|
+
*/
|
|
393
|
+
botPowerOverride() {
|
|
394
|
+
if (!this.botUserId)
|
|
395
|
+
return {};
|
|
396
|
+
return { power_level_content_override: { users: { [this.botUserId]: BOT_POWER_LEVEL } } };
|
|
397
|
+
}
|
|
398
|
+
/** 设置房间显示名(m.room.name 状态事件,state_key 空串)。 */
|
|
399
|
+
async setRoomName(roomId, name) {
|
|
400
|
+
await this.requireClient().sendStateEvent(roomId, 'm.room.name', '', { name });
|
|
401
|
+
}
|
|
402
|
+
/** 设置用户权限等级(矩阵 SDK 参数序是 user,room,level)。 */
|
|
403
|
+
async setUserPowerLevel(roomId, userId, level) {
|
|
404
|
+
await this.requireClient().setUserPowerLevel(userId, roomId, level);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* T12(US37):读某人在该房间的权限等级。
|
|
408
|
+
*
|
|
409
|
+
* 读 m.room.power_levels 状态事件:先看 users[userId],再回落 users_default,
|
|
410
|
+
* 最后回落 0(没有该状态事件也按 0 算)。提权前必须先读,否则没法区分"这个人
|
|
411
|
+
* 是我们提上来的"还是"管理员自己给的"。
|
|
412
|
+
*/
|
|
413
|
+
async getUserPowerLevel(roomId, userId) {
|
|
414
|
+
const client = this.requireClient();
|
|
415
|
+
let content;
|
|
416
|
+
try {
|
|
417
|
+
content = await client.getRoomStateEvent(roomId, 'm.room.power_levels', '');
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
content = undefined; // 房间还没有 PL 状态事件:按默认 0 算
|
|
421
|
+
}
|
|
422
|
+
const users = (content?.users ?? {});
|
|
423
|
+
const own = users[userId];
|
|
424
|
+
if (typeof own === 'number')
|
|
425
|
+
return own;
|
|
426
|
+
const fallback = content?.users_default;
|
|
427
|
+
return typeof fallback === 'number' ? fallback : 0;
|
|
428
|
+
}
|
|
429
|
+
/** 独立邀请(SDK createRoom 已带 invite 时不必调,留给管理房补拉人等场景)。 */
|
|
430
|
+
async inviteUser(roomId, userId) {
|
|
431
|
+
await this.requireClient().inviteUser(userId, roomId);
|
|
432
|
+
}
|
|
433
|
+
// ── T12 空间与头像(US38) ────────────────────────────────────────────────
|
|
434
|
+
/** 建 space:私有 + `creation_content: {type:'m.space'}`(Matrix 的 space 标记)。 */
|
|
435
|
+
async createSpace(opts) {
|
|
436
|
+
return this.requireClient().createRoom({
|
|
437
|
+
name: opts.name,
|
|
438
|
+
...(opts.inviteUserId ? { invite: [opts.inviteUserId] } : {}),
|
|
439
|
+
preset: 'private_chat',
|
|
440
|
+
visibility: 'private',
|
|
441
|
+
creation_content: { type: 'm.space' },
|
|
442
|
+
...this.botPowerOverride(),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/** 读房间头像;没设过(或读不到)返回 undefined —— 调用方据此决定"补挂"。 */
|
|
446
|
+
async getRoomAvatar(roomId) {
|
|
447
|
+
const client = this.requireClient();
|
|
448
|
+
let content;
|
|
449
|
+
try {
|
|
450
|
+
content = await client.getRoomStateEvent(roomId, 'm.room.avatar', '');
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
if (!content)
|
|
456
|
+
return undefined;
|
|
457
|
+
const url = typeof content.url === 'string' ? content.url : undefined;
|
|
458
|
+
const info = typeof content.info === 'object' && content.info !== null
|
|
459
|
+
? content.info
|
|
460
|
+
: undefined;
|
|
461
|
+
return { ...(url ? { url } : {}), ...(info ? { info } : {}) };
|
|
462
|
+
}
|
|
463
|
+
/** 设房间头像:先传图拿 mxc,再发 m.room.avatar(带 info 让客户端有尺寸可用)。 */
|
|
464
|
+
async setRoomAvatar(roomId, png, info) {
|
|
465
|
+
const client = this.requireClient();
|
|
466
|
+
const mxc = await client.uploadContent(png, 'image/png', 'avatar.png');
|
|
467
|
+
await client.sendStateEvent(roomId, 'm.room.avatar', '', { url: mxc, ...(info ? { info } : {}) });
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* 把房间挂进 space:在 **space** 里发 `m.space.child`,state_key 是房间 ID。
|
|
471
|
+
* `via` 缺省从房间 ID 的 server 部分取(加入该 space 的客户端据此找房间)。
|
|
472
|
+
*/
|
|
473
|
+
async addRoomToSpace(spaceId, roomId, via) {
|
|
474
|
+
const client = this.requireClient();
|
|
475
|
+
const server = via ?? roomId.split(':')[1] ?? '';
|
|
476
|
+
await client.sendStateEvent(spaceId, 'm.space.child', roomId, { via: server ? [server] : [] });
|
|
477
|
+
}
|
|
478
|
+
/** 桥账号 MXID(密码登录时从 login 响应缓存;未知 → undefined)。 */
|
|
479
|
+
getBotUserId() {
|
|
480
|
+
return this.botUserId || undefined;
|
|
481
|
+
}
|
|
482
|
+
requireClient() {
|
|
483
|
+
if (!this.client)
|
|
484
|
+
throw new Error('dsh-matrix matrix transport 未启动');
|
|
485
|
+
return this.client;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function defaultClientFactory(homeserver, accessToken, providers) {
|
|
489
|
+
if (!providers.storage)
|
|
490
|
+
throw new Error('dsh-matrix: 生产 MatrixClient 需要 storePath(避免重启全量重放)');
|
|
491
|
+
const client = new MatrixClient(homeserver, accessToken, providers.storage, providers.crypto);
|
|
492
|
+
AutojoinRoomsMixin.setupOnClient(client);
|
|
493
|
+
return client;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* T14:crypto 存储里记着的设备身份(没有则 undefined = 全新库,由 SDK 自己认领)。
|
|
497
|
+
* 读失败按"不知道"处理:登录退回不带 device_id 的旧行为,不会因此起不来。
|
|
498
|
+
*/
|
|
499
|
+
async function readStoredDeviceId(providers) {
|
|
500
|
+
if (!providers.crypto?.getDeviceId)
|
|
501
|
+
return undefined;
|
|
502
|
+
try {
|
|
503
|
+
const stored = await providers.crypto.getDeviceId();
|
|
504
|
+
return typeof stored === 'string' && stored ? stored : undefined;
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
return undefined;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/matrix/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T6 attachment vocabulary (spec US25-29, 实现决策 9).
|
|
3
|
+
*
|
|
4
|
+
* Kept transport-flavored on purpose: the Matrix msgtype/mxc naming is the
|
|
5
|
+
* agreed port shape (the only real transport is Matrix), while the bridge
|
|
6
|
+
* core only ever sees these plain data types - never an SDK object.
|
|
7
|
+
*/
|
|
8
|
+
/** Matrix msgtypes whose content carries a downloadable media payload. */
|
|
9
|
+
export type MediaMsgtype = 'm.image' | 'm.file' | 'm.audio' | 'm.video' | 'm.sticker';
|
|
10
|
+
export declare function isMediaMsgtype(value: unknown): value is MediaMsgtype;
|
|
11
|
+
/** One media payload referenced by an inbound message (not yet downloaded). */
|
|
12
|
+
export interface MediaIncoming {
|
|
13
|
+
/** mxc:// content URI - the download address (encrypted `file.url` wins). */
|
|
14
|
+
readonly mxc: string;
|
|
15
|
+
readonly msgtype: MediaMsgtype;
|
|
16
|
+
/** Original event body - Element puts the filename here for media. */
|
|
17
|
+
readonly filename?: string;
|
|
18
|
+
/** Declared info.size: a pre-check hint only, never trusted (servers lie). */
|
|
19
|
+
readonly size?: number;
|
|
20
|
+
/** Raw event body kept for diagnostics (usually identical to filename). */
|
|
21
|
+
readonly body: string;
|
|
22
|
+
}
|
|
23
|
+
/** Result of one Transport.downloadMedia call (post-decryption bytes). */
|
|
24
|
+
export interface MediaDownload {
|
|
25
|
+
readonly bytes: Uint8Array;
|
|
26
|
+
/** Content type reported by the homeserver (info.mimetype is not authoritative). */
|
|
27
|
+
readonly mimeType: string;
|
|
28
|
+
readonly filename?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One ledger attachment, already classified for the prompt path:
|
|
32
|
+
* 'image' → into the model as a native image block;
|
|
33
|
+
* 'file' → persisted verbatim, the model sees the saved path.
|
|
34
|
+
*/
|
|
35
|
+
export interface PreparedAttachment {
|
|
36
|
+
readonly kind: 'image' | 'file';
|
|
37
|
+
readonly bytes: Uint8Array;
|
|
38
|
+
readonly mimeType: string;
|
|
39
|
+
readonly filename?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Thrown by Transport.downloadMedia when the REAL downloaded bytes exceed the
|
|
43
|
+
* caller's cap. A distinct error so the bridge answers with the size refusal
|
|
44
|
+
* receipt instead of a generic download failure.
|
|
45
|
+
*/
|
|
46
|
+
export declare class MediaTooLargeError extends Error {
|
|
47
|
+
readonly bytes: number;
|
|
48
|
+
readonly maxBytes: number;
|
|
49
|
+
constructor(bytes: number, maxBytes: number);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../src/ports/attachments.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAA;AAIrF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAC9B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;aACf,KAAK,EAAE,MAAM;aAAkB,QAAQ,EAAE,MAAM;gBAA/C,KAAK,EAAE,MAAM,EAAkB,QAAQ,EAAE,MAAM;CAI5E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T6 attachment vocabulary (spec US25-29, 实现决策 9).
|
|
3
|
+
*
|
|
4
|
+
* Kept transport-flavored on purpose: the Matrix msgtype/mxc naming is the
|
|
5
|
+
* agreed port shape (the only real transport is Matrix), while the bridge
|
|
6
|
+
* core only ever sees these plain data types - never an SDK object.
|
|
7
|
+
*/
|
|
8
|
+
const MEDIA_MSGTYPES = ['m.image', 'm.file', 'm.audio', 'm.video', 'm.sticker'];
|
|
9
|
+
export function isMediaMsgtype(value) {
|
|
10
|
+
return MEDIA_MSGTYPES.includes(value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thrown by Transport.downloadMedia when the REAL downloaded bytes exceed the
|
|
14
|
+
* caller's cap. A distinct error so the bridge answers with the size refusal
|
|
15
|
+
* receipt instead of a generic download failure.
|
|
16
|
+
*/
|
|
17
|
+
export class MediaTooLargeError extends Error {
|
|
18
|
+
bytes;
|
|
19
|
+
maxBytes;
|
|
20
|
+
constructor(bytes, maxBytes) {
|
|
21
|
+
super(`下载内容 ${bytes} 字节超过上限 ${maxBytes} 字节`);
|
|
22
|
+
this.bytes = bytes;
|
|
23
|
+
this.maxBytes = maxBytes;
|
|
24
|
+
this.name = 'MediaTooLargeError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/ports/attachments.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,MAAM,cAAc,GAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,CAAC,CAAA;AAElG,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,cAAc,CAAC,QAAQ,CAAC,KAAe,CAAC,CAAA;AACjD,CAAC;AAmCD;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACf;IAA+B;IAA3D,YAA4B,KAAa,EAAkB,QAAgB;QACzE,KAAK,CAAC,QAAQ,KAAK,WAAW,QAAQ,KAAK,CAAC,CAAA;QADlB,UAAK,GAAL,KAAK,CAAQ;QAAkB,aAAQ,GAAR,QAAQ,CAAQ;QAEzE,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAA;IAClC,CAAC;CACF"}
|