morok-bot-sdk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +602 -0
- package/README.ru.md +602 -0
- package/dist/bot.d.ts +232 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +558 -0
- package/dist/bot.js.map +1 -0
- package/dist/crypto/channel-cipher.d.ts +32 -0
- package/dist/crypto/channel-cipher.d.ts.map +1 -0
- package/dist/crypto/channel-cipher.js +77 -0
- package/dist/crypto/channel-cipher.js.map +1 -0
- package/dist/crypto/channel-key-store.d.ts +37 -0
- package/dist/crypto/channel-key-store.d.ts.map +1 -0
- package/dist/crypto/channel-key-store.js +149 -0
- package/dist/crypto/channel-key-store.js.map +1 -0
- package/dist/crypto/cross-signing.d.ts +57 -0
- package/dist/crypto/cross-signing.d.ts.map +1 -0
- package/dist/crypto/cross-signing.js +111 -0
- package/dist/crypto/cross-signing.js.map +1 -0
- package/dist/crypto/file-cipher.d.ts +36 -0
- package/dist/crypto/file-cipher.d.ts.map +1 -0
- package/dist/crypto/file-cipher.js +61 -0
- package/dist/crypto/file-cipher.js.map +1 -0
- package/dist/crypto/group-secret-cipher.d.ts +49 -0
- package/dist/crypto/group-secret-cipher.d.ts.map +1 -0
- package/dist/crypto/group-secret-cipher.js +69 -0
- package/dist/crypto/group-secret-cipher.js.map +1 -0
- package/dist/crypto/group-secret-store.d.ts +35 -0
- package/dist/crypto/group-secret-store.d.ts.map +1 -0
- package/dist/crypto/group-secret-store.js +149 -0
- package/dist/crypto/group-secret-store.js.map +1 -0
- package/dist/crypto/signal.d.ts +81 -0
- package/dist/crypto/signal.d.ts.map +1 -0
- package/dist/crypto/signal.js +125 -0
- package/dist/crypto/signal.js.map +1 -0
- package/dist/crypto/stores.d.ts +130 -0
- package/dist/crypto/stores.d.ts.map +1 -0
- package/dist/crypto/stores.js +314 -0
- package/dist/crypto/stores.js.map +1 -0
- package/dist/flow/attachments.d.ts +110 -0
- package/dist/flow/attachments.d.ts.map +1 -0
- package/dist/flow/attachments.js +409 -0
- package/dist/flow/attachments.js.map +1 -0
- package/dist/flow/conv-cache.d.ts +36 -0
- package/dist/flow/conv-cache.d.ts.map +1 -0
- package/dist/flow/conv-cache.js +84 -0
- package/dist/flow/conv-cache.js.map +1 -0
- package/dist/flow/direct.d.ts +109 -0
- package/dist/flow/direct.d.ts.map +1 -0
- package/dist/flow/direct.js +346 -0
- package/dist/flow/direct.js.map +1 -0
- package/dist/flow/groups.d.ts +146 -0
- package/dist/flow/groups.d.ts.map +1 -0
- package/dist/flow/groups.js +768 -0
- package/dist/flow/groups.js.map +1 -0
- package/dist/flow/prekeys.d.ts +45 -0
- package/dist/flow/prekeys.d.ts.map +1 -0
- package/dist/flow/prekeys.js +111 -0
- package/dist/flow/prekeys.js.map +1 -0
- package/dist/flow/receive.d.ts +125 -0
- package/dist/flow/receive.d.ts.map +1 -0
- package/dist/flow/receive.js +773 -0
- package/dist/flow/receive.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/morokbot-file.d.ts +14 -0
- package/dist/morokbot-file.d.ts.map +1 -0
- package/dist/morokbot-file.js +88 -0
- package/dist/morokbot-file.js.map +1 -0
- package/dist/ratelimit.d.ts +40 -0
- package/dist/ratelimit.d.ts.map +1 -0
- package/dist/ratelimit.js +76 -0
- package/dist/ratelimit.js.map +1 -0
- package/dist/sessions.d.ts +34 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +69 -0
- package/dist/sessions.js.map +1 -0
- package/dist/state-lock.d.ts +17 -0
- package/dist/state-lock.d.ts.map +1 -0
- package/dist/state-lock.js +66 -0
- package/dist/state-lock.js.map +1 -0
- package/dist/transport/http.d.ts +48 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +112 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/ws.d.ts +65 -0
- package/dist/transport/ws.d.ts.map +1 -0
- package/dist/transport/ws.js +219 -0
- package/dist/transport/ws.js.map +1 -0
- package/dist/types.d.ts +254 -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 +59 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { timingSafeEqual, randomUUID } from 'node:crypto';
|
|
4
|
+
function toArrayBuffer(b64) {
|
|
5
|
+
const buf = Buffer.from(b64, 'base64');
|
|
6
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
7
|
+
}
|
|
8
|
+
function fromArrayBuffer(buf) {
|
|
9
|
+
return Buffer.from(buf).toString('base64');
|
|
10
|
+
}
|
|
11
|
+
function serializeKeyPair(kp) {
|
|
12
|
+
return {
|
|
13
|
+
pub: fromArrayBuffer(kp.pubKey),
|
|
14
|
+
priv: fromArrayBuffer(kp.privKey),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function deserializeKeyPair(s) {
|
|
18
|
+
return {
|
|
19
|
+
pubKey: toArrayBuffer(s.pub),
|
|
20
|
+
privKey: toArrayBuffer(s.priv),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function constantTimeEqualBytes(a, b) {
|
|
24
|
+
if (a.byteLength !== b.byteLength)
|
|
25
|
+
return false;
|
|
26
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
27
|
+
}
|
|
28
|
+
async function writeJsonAtomic(filePath, value) {
|
|
29
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
30
|
+
const tmp = `${filePath}.tmp-${randomUUID()}`;
|
|
31
|
+
await fs.writeFile(tmp, JSON.stringify(value), { mode: 0o600 });
|
|
32
|
+
await fs.rename(tmp, filePath);
|
|
33
|
+
}
|
|
34
|
+
async function readJson(filePath) {
|
|
35
|
+
try {
|
|
36
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
37
|
+
return JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err.code === 'ENOENT')
|
|
41
|
+
return undefined;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const DIRECTION_ENUM = {
|
|
46
|
+
SENDING: 1,
|
|
47
|
+
RECEIVING: 2,
|
|
48
|
+
};
|
|
49
|
+
export class FileSignalStore {
|
|
50
|
+
stateDir;
|
|
51
|
+
Direction = DIRECTION_ENUM;
|
|
52
|
+
identityCache;
|
|
53
|
+
stateCache;
|
|
54
|
+
constructor(stateDir) {
|
|
55
|
+
this.stateDir = stateDir;
|
|
56
|
+
}
|
|
57
|
+
async ensureLayout() {
|
|
58
|
+
for (const sub of ['', 'sessions', 'prekeys', 'identity-cache', 'quarantine']) {
|
|
59
|
+
await fs.mkdir(path.join(this.stateDir, sub), { recursive: true, mode: 0o700 });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async importInitial(opts) {
|
|
63
|
+
await this.ensureLayout();
|
|
64
|
+
const existing = await readJson(this.identityFile());
|
|
65
|
+
if (existing) {
|
|
66
|
+
if (existing.userId && existing.userId !== opts.botUserId) {
|
|
67
|
+
throw new Error(`state directory ${this.stateDir} already holds bot user ${existing.userId}; ` +
|
|
68
|
+
`refusing to overwrite with bot user ${opts.botUserId}. Use a different stateDir.`);
|
|
69
|
+
}
|
|
70
|
+
if (existing.registrationId !== opts.registrationId) {
|
|
71
|
+
throw new Error(`state directory ${this.stateDir} holds registrationId ${existing.registrationId}; ` +
|
|
72
|
+
`incoming .morokbot has ${opts.registrationId}. The .morokbot likely doesn't ` +
|
|
73
|
+
`match the imported state. Use a fresh stateDir or restore the matching file.`);
|
|
74
|
+
}
|
|
75
|
+
if (!existing.userId) {
|
|
76
|
+
await writeJsonAtomic(this.identityFile(), {
|
|
77
|
+
...existing,
|
|
78
|
+
userId: opts.botUserId,
|
|
79
|
+
deviceId: opts.deviceId,
|
|
80
|
+
});
|
|
81
|
+
this.identityCache = undefined;
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const initState = {
|
|
86
|
+
lastSignedPreKeyRotationMs: Date.now(),
|
|
87
|
+
nextOneTimePreKeyId: Math.max(...opts.oneTimePreKeys.map(k => k.keyId), 0) + 1,
|
|
88
|
+
nextSignedPreKeyId: opts.signedPreKey.keyId + 1,
|
|
89
|
+
};
|
|
90
|
+
await writeJsonAtomic(this.stateFile(), initState);
|
|
91
|
+
await writeJsonAtomic(this.signedPreKeyFile(opts.signedPreKey.keyId), {
|
|
92
|
+
keyId: opts.signedPreKey.keyId,
|
|
93
|
+
pub: opts.signedPreKey.pub,
|
|
94
|
+
priv: opts.signedPreKey.priv,
|
|
95
|
+
signature: opts.signedPreKey.signature,
|
|
96
|
+
});
|
|
97
|
+
for (const otp of opts.oneTimePreKeys) {
|
|
98
|
+
await writeJsonAtomic(this.oneTimePreKeyFile(otp.keyId), {
|
|
99
|
+
keyId: otp.keyId,
|
|
100
|
+
pub: otp.pub,
|
|
101
|
+
priv: otp.priv,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const identity = {
|
|
105
|
+
identityKeyPair: opts.identityKeyPair,
|
|
106
|
+
registrationId: opts.registrationId,
|
|
107
|
+
userId: opts.botUserId,
|
|
108
|
+
deviceId: opts.deviceId,
|
|
109
|
+
accountSigningKey: opts.accountSigningKey,
|
|
110
|
+
};
|
|
111
|
+
await writeJsonAtomic(this.identityFile(), identity);
|
|
112
|
+
}
|
|
113
|
+
async fsck() {
|
|
114
|
+
await this.ensureLayout();
|
|
115
|
+
const quarantinedSessions = [];
|
|
116
|
+
const quarantinedPreKeys = [];
|
|
117
|
+
for (const entry of await fs.readdir(path.join(this.stateDir, 'sessions')).catch(() => [])) {
|
|
118
|
+
const full = path.join(this.stateDir, 'sessions', entry);
|
|
119
|
+
let bad;
|
|
120
|
+
try {
|
|
121
|
+
const j = await readJson(full);
|
|
122
|
+
bad = !j || typeof j.record !== 'string' || j.record.length === 0;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
bad = true;
|
|
126
|
+
}
|
|
127
|
+
if (bad) {
|
|
128
|
+
const dst = path.join(this.stateDir, 'quarantine', `session-${Date.now()}-${entry}`);
|
|
129
|
+
await fs.rename(full, dst).catch(() => { });
|
|
130
|
+
quarantinedSessions.push(full);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for (const entry of await fs.readdir(path.join(this.stateDir, 'prekeys')).catch(() => [])) {
|
|
134
|
+
const full = path.join(this.stateDir, 'prekeys', entry);
|
|
135
|
+
let bad;
|
|
136
|
+
try {
|
|
137
|
+
const j = await readJson(full);
|
|
138
|
+
bad = !j || typeof j.pub !== 'string' || typeof j.priv !== 'string';
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
bad = true;
|
|
142
|
+
}
|
|
143
|
+
if (bad) {
|
|
144
|
+
const dst = path.join(this.stateDir, 'quarantine', `prekey-${Date.now()}-${entry}`);
|
|
145
|
+
await fs.rename(full, dst).catch(() => { });
|
|
146
|
+
quarantinedPreKeys.push(full);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { quarantinedSessions, quarantinedPreKeys };
|
|
150
|
+
}
|
|
151
|
+
async getIdentityKeyPair() {
|
|
152
|
+
const id = await this.loadPersistedIdentity();
|
|
153
|
+
if (!id)
|
|
154
|
+
return undefined;
|
|
155
|
+
return deserializeKeyPair(id.identityKeyPair);
|
|
156
|
+
}
|
|
157
|
+
async getLocalRegistrationId() {
|
|
158
|
+
return (await this.loadPersistedIdentity())?.registrationId;
|
|
159
|
+
}
|
|
160
|
+
async isTrustedIdentity(identifier, identityKey, _direction) {
|
|
161
|
+
const cached = await this.loadPeerIdentity(identifier);
|
|
162
|
+
if (!cached)
|
|
163
|
+
return true;
|
|
164
|
+
return constantTimeEqualBytes(cached, identityKey);
|
|
165
|
+
}
|
|
166
|
+
async saveIdentity(encodedAddress, publicKey, _nonblockingApproval) {
|
|
167
|
+
const existing = await this.loadPeerIdentity(encodedAddress);
|
|
168
|
+
if (existing && constantTimeEqualBytes(existing, publicKey)) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
await writeJsonAtomic(this.identityCacheFile(encodedAddress), {
|
|
172
|
+
identityKey: fromArrayBuffer(publicKey),
|
|
173
|
+
});
|
|
174
|
+
return existing !== undefined;
|
|
175
|
+
}
|
|
176
|
+
async peekPeerIdentity(encodedAddress) {
|
|
177
|
+
return this.loadPeerIdentity(encodedAddress);
|
|
178
|
+
}
|
|
179
|
+
async loadSession(encodedAddress) {
|
|
180
|
+
const j = await readJson(this.sessionFile(encodedAddress));
|
|
181
|
+
return j?.record;
|
|
182
|
+
}
|
|
183
|
+
async storeSession(encodedAddress, record) {
|
|
184
|
+
await writeJsonAtomic(this.sessionFile(encodedAddress), { record });
|
|
185
|
+
}
|
|
186
|
+
async loadPreKey(keyId) {
|
|
187
|
+
const id = typeof keyId === 'string' ? Number(keyId) : keyId;
|
|
188
|
+
const j = await readJson(this.oneTimePreKeyFile(id));
|
|
189
|
+
if (!j)
|
|
190
|
+
return undefined;
|
|
191
|
+
return deserializeKeyPair(j);
|
|
192
|
+
}
|
|
193
|
+
async storePreKey(keyId, keyPair) {
|
|
194
|
+
const id = typeof keyId === 'string' ? Number(keyId) : keyId;
|
|
195
|
+
await writeJsonAtomic(this.oneTimePreKeyFile(id), {
|
|
196
|
+
keyId: id,
|
|
197
|
+
...serializeKeyPair(keyPair),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
async removePreKey(keyId) {
|
|
201
|
+
const id = typeof keyId === 'string' ? Number(keyId) : keyId;
|
|
202
|
+
await fs.unlink(this.oneTimePreKeyFile(id)).catch(err => {
|
|
203
|
+
if (err.code !== 'ENOENT')
|
|
204
|
+
throw err;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async loadSignedPreKey(keyId) {
|
|
208
|
+
const id = typeof keyId === 'string' ? Number(keyId) : keyId;
|
|
209
|
+
const j = await readJson(this.signedPreKeyFile(id));
|
|
210
|
+
if (!j)
|
|
211
|
+
return undefined;
|
|
212
|
+
return deserializeKeyPair(j);
|
|
213
|
+
}
|
|
214
|
+
async storeSignedPreKey(keyId, keyPair) {
|
|
215
|
+
const id = typeof keyId === 'string' ? Number(keyId) : keyId;
|
|
216
|
+
const existing = await readJson(this.signedPreKeyFile(id));
|
|
217
|
+
await writeJsonAtomic(this.signedPreKeyFile(id), {
|
|
218
|
+
keyId: id,
|
|
219
|
+
...serializeKeyPair(keyPair),
|
|
220
|
+
...(existing?.signature ? { signature: existing.signature } : {}),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async removeSignedPreKey(keyId) {
|
|
224
|
+
const id = typeof keyId === 'string' ? Number(keyId) : keyId;
|
|
225
|
+
await fs.unlink(this.signedPreKeyFile(id)).catch(err => {
|
|
226
|
+
if (err.code !== 'ENOENT')
|
|
227
|
+
throw err;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
async storeSignedPreKeyFull(rec) {
|
|
231
|
+
await writeJsonAtomic(this.signedPreKeyFile(rec.keyId), {
|
|
232
|
+
keyId: rec.keyId,
|
|
233
|
+
...serializeKeyPair(rec.keyPair),
|
|
234
|
+
signature: fromArrayBuffer(rec.signature),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
async getSignedPreKeyRecord(keyId) {
|
|
238
|
+
return readJson(this.signedPreKeyFile(keyId));
|
|
239
|
+
}
|
|
240
|
+
async listOneTimePreKeyIds() {
|
|
241
|
+
const dir = path.join(this.stateDir, 'prekeys');
|
|
242
|
+
const out = [];
|
|
243
|
+
for (const entry of await fs.readdir(dir).catch(() => [])) {
|
|
244
|
+
const m = /^onetime-(\d+)\.json$/.exec(entry);
|
|
245
|
+
if (m)
|
|
246
|
+
out.push(Number(m[1]));
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
async loadState() {
|
|
251
|
+
if (this.stateCache)
|
|
252
|
+
return this.stateCache;
|
|
253
|
+
const s = await readJson(this.stateFile());
|
|
254
|
+
if (s) {
|
|
255
|
+
this.stateCache = s;
|
|
256
|
+
return s;
|
|
257
|
+
}
|
|
258
|
+
const init = {
|
|
259
|
+
lastSignedPreKeyRotationMs: 0,
|
|
260
|
+
nextOneTimePreKeyId: 1,
|
|
261
|
+
nextSignedPreKeyId: 1,
|
|
262
|
+
};
|
|
263
|
+
await writeJsonAtomic(this.stateFile(), init);
|
|
264
|
+
this.stateCache = init;
|
|
265
|
+
return init;
|
|
266
|
+
}
|
|
267
|
+
async patchState(patch) {
|
|
268
|
+
const cur = await this.loadState();
|
|
269
|
+
const next = { ...cur, ...patch };
|
|
270
|
+
await writeJsonAtomic(this.stateFile(), next);
|
|
271
|
+
this.stateCache = next;
|
|
272
|
+
}
|
|
273
|
+
async loadPersistedIdentity() {
|
|
274
|
+
if (this.identityCache)
|
|
275
|
+
return this.identityCache;
|
|
276
|
+
const id = await readJson(this.identityFile());
|
|
277
|
+
if (id)
|
|
278
|
+
this.identityCache = id;
|
|
279
|
+
return id;
|
|
280
|
+
}
|
|
281
|
+
async loadPeerIdentity(encodedAddress) {
|
|
282
|
+
const j = await readJson(this.identityCacheFile(encodedAddress));
|
|
283
|
+
if (!j)
|
|
284
|
+
return undefined;
|
|
285
|
+
return toArrayBuffer(j.identityKey);
|
|
286
|
+
}
|
|
287
|
+
identityFile() { return path.join(this.stateDir, 'identity.json'); }
|
|
288
|
+
stateFile() { return path.join(this.stateDir, 'state.json'); }
|
|
289
|
+
sessionFile(addr) {
|
|
290
|
+
return path.join(this.stateDir, 'sessions', `${sanitizeAddr(addr)}.json`);
|
|
291
|
+
}
|
|
292
|
+
identityCacheFile(addr) {
|
|
293
|
+
return path.join(this.stateDir, 'identity-cache', `${sanitizeIdentityAddr(addr)}.json`);
|
|
294
|
+
}
|
|
295
|
+
signedPreKeyFile(keyId) {
|
|
296
|
+
return path.join(this.stateDir, 'prekeys', `signed-${keyId}.json`);
|
|
297
|
+
}
|
|
298
|
+
oneTimePreKeyFile(keyId) {
|
|
299
|
+
return path.join(this.stateDir, 'prekeys', `onetime-${keyId}.json`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function sanitizeAddr(addr) {
|
|
303
|
+
if (!/^\d+\.\d+$/.test(addr)) {
|
|
304
|
+
throw new Error(`refusing unsafe session address "${addr}" - expected <userId>.<deviceId>`);
|
|
305
|
+
}
|
|
306
|
+
return addr;
|
|
307
|
+
}
|
|
308
|
+
function sanitizeIdentityAddr(addr) {
|
|
309
|
+
if (!/^\d+(\.\d+)?$/.test(addr)) {
|
|
310
|
+
throw new Error(`refusing unsafe identity address "${addr}" - expected <userId> or <userId>.<deviceId>`);
|
|
311
|
+
}
|
|
312
|
+
return addr;
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../../src/crypto/stores.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attachment upload and download. uploadAttachment picks single-shot or chunked by plaintext size:
|
|
3
|
+
* <= 50 MB: POST /files/upload
|
|
4
|
+
* > 50 MB: POST /files/upload/init + chunk x N + complete
|
|
5
|
+
*
|
|
6
|
+
* Each chunk gets its own random IV and AAD chunkAad(i, total) so the server can't reorder or drop chunks
|
|
7
|
+
* Byte-exact mirror of frontend/src/api/files.ts
|
|
8
|
+
*
|
|
9
|
+
* Upload mints a fresh AES-256-GCM key, encrypts, SHA-256s the ciphertext (server dedup), POSTs
|
|
10
|
+
* The server returns { fileId, sha256, ... }. The caller embeds an EncryptedFileRef in the message plaintext
|
|
11
|
+
* Download is lazy via IncomingAttachment.download(): GET ciphertext, decrypt, length-check
|
|
12
|
+
*
|
|
13
|
+
* The plaintext never reaches the server. The 32-byte AES key is exported once into the ref
|
|
14
|
+
*/
|
|
15
|
+
import { webcrypto } from 'node:crypto';
|
|
16
|
+
import type { HttpClient } from '../transport/http.js';
|
|
17
|
+
import type { SdkLogger } from '../types.js';
|
|
18
|
+
type CryptoKey = webcrypto.CryptoKey;
|
|
19
|
+
export declare const SINGLE_UPLOAD_PLAINTEXT_LIMIT: number;
|
|
20
|
+
export declare const MAX_PLAINTEXT_BYTES: number;
|
|
21
|
+
/**
|
|
22
|
+
* `EncryptedFileRef` is the JSON handle the SDK embeds into the message plaintext,
|
|
23
|
+
* byte-for-byte compatible with the FE's `EncryptedFileRef` (frontend/src/api/files.ts)
|
|
24
|
+
*/
|
|
25
|
+
export interface EncryptedFileRef {
|
|
26
|
+
fileId: number;
|
|
27
|
+
sha256: string;
|
|
28
|
+
/** Base64 of the raw 32-byte AES key */
|
|
29
|
+
key: string;
|
|
30
|
+
/** Base64 of the 12-byte IV (single-shot only) */
|
|
31
|
+
iv: string;
|
|
32
|
+
/** Plaintext size in bytes */
|
|
33
|
+
size: number;
|
|
34
|
+
/** Plaintext mime */
|
|
35
|
+
mime: string;
|
|
36
|
+
/** Optional filename hint */
|
|
37
|
+
name?: string;
|
|
38
|
+
/** Set on chunked uploads */
|
|
39
|
+
chunked?: boolean;
|
|
40
|
+
totalChunks?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface UploadOptions {
|
|
43
|
+
/** Plaintext mime. Default application/octet-stream */
|
|
44
|
+
mime?: string;
|
|
45
|
+
/** Filename, server records via x-file-extension */
|
|
46
|
+
filename?: string;
|
|
47
|
+
/** true for voice and video_note. The server keeps bytes on local disk and skips the per-user quota */
|
|
48
|
+
noteMedia?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Thrown when the server rejects a file upload with a typed code,
|
|
52
|
+
* e.g. BOT_STAGING_FULL when the bot's unsent staging is full
|
|
53
|
+
*/
|
|
54
|
+
export declare class UploadRejectedError extends Error {
|
|
55
|
+
readonly code?: string | undefined;
|
|
56
|
+
readonly status?: number | undefined;
|
|
57
|
+
constructor(message: string, code?: string | undefined, status?: number | undefined);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Encrypt and upload, single-shot for <=50 MB and chunked for >50 MB up to 5 GB, throwing above 5 GB
|
|
61
|
+
* Note media (voice and video_note) rides either path and the server keeps it on the asset disk, off the quota
|
|
62
|
+
*/
|
|
63
|
+
export declare function uploadAttachment(http: HttpClient, plaintext: Uint8Array, opts?: UploadOptions, logger?: SdkLogger): Promise<EncryptedFileRef>;
|
|
64
|
+
/** Fetch and decrypt under the ref's key. Lazy getter behind IncomingAttachment.download()
|
|
65
|
+
* The server can return 404 (file row gone), 410 (sender hard-deleted), or 503 (P2P-only),
|
|
66
|
+
* all surface as a plain Error */
|
|
67
|
+
export declare function downloadAttachment(http: HttpClient, ref: EncryptedFileRef, logger?: SdkLogger): Promise<Buffer>;
|
|
68
|
+
/** Chunked decrypt. Walk the blob in WIRE_CHUNK_BUDGET strides, decrypt each chunk under chunkAad(i, total)
|
|
69
|
+
* so a reorder, drop, or swap fails the GCM tag. The last chunk may be shorter */
|
|
70
|
+
declare function downloadChunked(ref: EncryptedFileRef, ciphertextBytes: Buffer, key: CryptoKey, logger?: SdkLogger): Promise<Buffer>;
|
|
71
|
+
/** Narrows an unknown to EncryptedFileRef. A malformed ref is a misbehaving sender,
|
|
72
|
+
* the receiver drops it with a warn-log */
|
|
73
|
+
export declare function isEncryptedFileRef(x: unknown): x is EncryptedFileRef;
|
|
74
|
+
export declare const GALLERY_MIN_ITEMS = 2;
|
|
75
|
+
export declare const GALLERY_MAX_ITEMS = 10;
|
|
76
|
+
export type GalleryFileItem = {
|
|
77
|
+
type: 'file';
|
|
78
|
+
ref: EncryptedFileRef;
|
|
79
|
+
};
|
|
80
|
+
export type GalleryContactItem = {
|
|
81
|
+
type: 'contact';
|
|
82
|
+
userId: number;
|
|
83
|
+
username?: string;
|
|
84
|
+
displayName?: string;
|
|
85
|
+
avatarUrl?: string;
|
|
86
|
+
avatarEmoji?: string;
|
|
87
|
+
};
|
|
88
|
+
export type GalleryLocationItem = {
|
|
89
|
+
type: 'location';
|
|
90
|
+
lat: number;
|
|
91
|
+
lng: number;
|
|
92
|
+
};
|
|
93
|
+
export type GalleryItem = GalleryFileItem | GalleryContactItem | GalleryLocationItem;
|
|
94
|
+
export interface GalleryPayload {
|
|
95
|
+
type: 'gallery';
|
|
96
|
+
items: GalleryItem[];
|
|
97
|
+
caption?: string;
|
|
98
|
+
}
|
|
99
|
+
/** Build the JSON envelope for an outbound gallery. Pure, the caller passes already-uploaded refs */
|
|
100
|
+
export declare function buildGalleryPayload(items: GalleryItem[], caption?: string): string;
|
|
101
|
+
/** Narrow an unknown to GalleryItem, null if malformed. The caller decides whether to keep a half-valid gallery */
|
|
102
|
+
export declare function parseGalleryItem(raw: unknown): GalleryItem | null;
|
|
103
|
+
export declare const _internals: {
|
|
104
|
+
downloadChunked: typeof downloadChunked;
|
|
105
|
+
WIRE_CHUNK_BUDGET: number;
|
|
106
|
+
CHUNK_PLAINTEXT_SIZE: number;
|
|
107
|
+
MAX_CHUNK_COUNT: number;
|
|
108
|
+
};
|
|
109
|
+
export {};
|
|
110
|
+
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../src/flow/attachments.ts"],"names":[],"mappings":"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"}
|