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.
Files changed (96) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +602 -0
  3. package/README.ru.md +602 -0
  4. package/dist/bot.d.ts +232 -0
  5. package/dist/bot.d.ts.map +1 -0
  6. package/dist/bot.js +558 -0
  7. package/dist/bot.js.map +1 -0
  8. package/dist/crypto/channel-cipher.d.ts +32 -0
  9. package/dist/crypto/channel-cipher.d.ts.map +1 -0
  10. package/dist/crypto/channel-cipher.js +77 -0
  11. package/dist/crypto/channel-cipher.js.map +1 -0
  12. package/dist/crypto/channel-key-store.d.ts +37 -0
  13. package/dist/crypto/channel-key-store.d.ts.map +1 -0
  14. package/dist/crypto/channel-key-store.js +149 -0
  15. package/dist/crypto/channel-key-store.js.map +1 -0
  16. package/dist/crypto/cross-signing.d.ts +57 -0
  17. package/dist/crypto/cross-signing.d.ts.map +1 -0
  18. package/dist/crypto/cross-signing.js +111 -0
  19. package/dist/crypto/cross-signing.js.map +1 -0
  20. package/dist/crypto/file-cipher.d.ts +36 -0
  21. package/dist/crypto/file-cipher.d.ts.map +1 -0
  22. package/dist/crypto/file-cipher.js +61 -0
  23. package/dist/crypto/file-cipher.js.map +1 -0
  24. package/dist/crypto/group-secret-cipher.d.ts +49 -0
  25. package/dist/crypto/group-secret-cipher.d.ts.map +1 -0
  26. package/dist/crypto/group-secret-cipher.js +69 -0
  27. package/dist/crypto/group-secret-cipher.js.map +1 -0
  28. package/dist/crypto/group-secret-store.d.ts +35 -0
  29. package/dist/crypto/group-secret-store.d.ts.map +1 -0
  30. package/dist/crypto/group-secret-store.js +149 -0
  31. package/dist/crypto/group-secret-store.js.map +1 -0
  32. package/dist/crypto/signal.d.ts +81 -0
  33. package/dist/crypto/signal.d.ts.map +1 -0
  34. package/dist/crypto/signal.js +125 -0
  35. package/dist/crypto/signal.js.map +1 -0
  36. package/dist/crypto/stores.d.ts +130 -0
  37. package/dist/crypto/stores.d.ts.map +1 -0
  38. package/dist/crypto/stores.js +314 -0
  39. package/dist/crypto/stores.js.map +1 -0
  40. package/dist/flow/attachments.d.ts +110 -0
  41. package/dist/flow/attachments.d.ts.map +1 -0
  42. package/dist/flow/attachments.js +409 -0
  43. package/dist/flow/attachments.js.map +1 -0
  44. package/dist/flow/conv-cache.d.ts +36 -0
  45. package/dist/flow/conv-cache.d.ts.map +1 -0
  46. package/dist/flow/conv-cache.js +84 -0
  47. package/dist/flow/conv-cache.js.map +1 -0
  48. package/dist/flow/direct.d.ts +109 -0
  49. package/dist/flow/direct.d.ts.map +1 -0
  50. package/dist/flow/direct.js +346 -0
  51. package/dist/flow/direct.js.map +1 -0
  52. package/dist/flow/groups.d.ts +146 -0
  53. package/dist/flow/groups.d.ts.map +1 -0
  54. package/dist/flow/groups.js +768 -0
  55. package/dist/flow/groups.js.map +1 -0
  56. package/dist/flow/prekeys.d.ts +45 -0
  57. package/dist/flow/prekeys.d.ts.map +1 -0
  58. package/dist/flow/prekeys.js +111 -0
  59. package/dist/flow/prekeys.js.map +1 -0
  60. package/dist/flow/receive.d.ts +125 -0
  61. package/dist/flow/receive.d.ts.map +1 -0
  62. package/dist/flow/receive.js +773 -0
  63. package/dist/flow/receive.js.map +1 -0
  64. package/dist/index.d.ts +15 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +6 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/morokbot-file.d.ts +14 -0
  69. package/dist/morokbot-file.d.ts.map +1 -0
  70. package/dist/morokbot-file.js +88 -0
  71. package/dist/morokbot-file.js.map +1 -0
  72. package/dist/ratelimit.d.ts +40 -0
  73. package/dist/ratelimit.d.ts.map +1 -0
  74. package/dist/ratelimit.js +76 -0
  75. package/dist/ratelimit.js.map +1 -0
  76. package/dist/sessions.d.ts +34 -0
  77. package/dist/sessions.d.ts.map +1 -0
  78. package/dist/sessions.js +69 -0
  79. package/dist/sessions.js.map +1 -0
  80. package/dist/state-lock.d.ts +17 -0
  81. package/dist/state-lock.d.ts.map +1 -0
  82. package/dist/state-lock.js +66 -0
  83. package/dist/state-lock.js.map +1 -0
  84. package/dist/transport/http.d.ts +48 -0
  85. package/dist/transport/http.d.ts.map +1 -0
  86. package/dist/transport/http.js +112 -0
  87. package/dist/transport/http.js.map +1 -0
  88. package/dist/transport/ws.d.ts +65 -0
  89. package/dist/transport/ws.d.ts.map +1 -0
  90. package/dist/transport/ws.js +219 -0
  91. package/dist/transport/ws.js.map +1 -0
  92. package/dist/types.d.ts +254 -0
  93. package/dist/types.d.ts.map +1 -0
  94. package/dist/types.js +2 -0
  95. package/dist/types.js.map +1 -0
  96. package/package.json +59 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * DM send. Receive lives in flow/receive.ts
3
+ *
4
+ * Send path:
5
+ * 1. resolve peer (number or username to userId)
6
+ * 2. POST /conversations to get-or-create a DIRECT conversation
7
+ * 3. GET /prekeys/:userId/devices for the peer's device list
8
+ * 4. for each device, install a Signal session if missing, encrypt, WS frame with fanoutId
9
+ * 5. wait for the own-echo matched by fanoutId, resolve with messageId
10
+ *
11
+ * Success bar is at least one device acked, a single offline or stale device must not fail the send
12
+ */
13
+ import type { HttpClient } from '../transport/http.js';
14
+ import type { WsClient } from '../transport/ws.js';
15
+ import { SignalEngine } from '../crypto/signal.js';
16
+ import { type EncryptedFileRef } from './attachments.js';
17
+ import type { SdkLogger, AttachmentInput } from '../types.js';
18
+ export interface SendTextOptions {
19
+ /** Reply target */
20
+ replyToId?: number;
21
+ /** clientMsgId of the parent so threading survives fan-out */
22
+ replyToClientMsgId?: string;
23
+ /** Channel-comment thread root */
24
+ threadRootId?: number;
25
+ /** Own-echo timeout. Default 10s, the echo usually lands sub-100ms since the WS is already connected */
26
+ sendTimeoutMs?: number;
27
+ }
28
+ export interface SendResult {
29
+ /** Server message id of the own-echo row (the bot's own conversation_members copy)
30
+ * Peers have their own copies with different ids, that is the fan-out shape */
31
+ messageId: number;
32
+ clientMsgId: string;
33
+ conversationId: number;
34
+ }
35
+ /** Upload attachment bytes under a fresh AES key and build the JSON envelope for the Signal plaintext
36
+ * Voice and video_note upload with noteMedia: true so the bytes don't hit the bot's 10 GB quota */
37
+ export declare function uploadAndBuildPayload(http: HttpClient, attachment: AttachmentInput, caption: string | undefined, logger?: SdkLogger): Promise<{
38
+ payload: string;
39
+ ref: EncryptedFileRef;
40
+ kind: 'file' | 'voice' | 'video_note';
41
+ }>;
42
+ /**
43
+ * Upload N attachments (file kind only) and build the gallery payload
44
+ * - each item uploaded under its own fresh AES key
45
+ * - 2..10 items, voice and video_note rejected
46
+ * - refs[0] is the head (server's messages.file_id), refs[1..] go into additionalFileIds on the WS frame
47
+ */
48
+ export declare function uploadGalleryAndBuildPayload(http: HttpClient, attachments: AttachmentInput[], caption: string | undefined, logger?: SdkLogger): Promise<{
49
+ payload: string;
50
+ refs: EncryptedFileRef[];
51
+ headFileId: number;
52
+ additionalFileIds: number[];
53
+ }>;
54
+ /**
55
+ * Thrown when the server rejects a send with an `error` frame. `code` carries the machine-readable reason
56
+ * ('recipient_storage_full' means the recipient's storage is full and the file was not delivered,
57
+ * plus 'bot_not_started', 'send_blocked') so a bot can branch on it without string-matching the human `message`
58
+ * Undefined `code` means the server sent only a message
59
+ */
60
+ export declare class SendRejectedError extends Error {
61
+ readonly code?: string | undefined;
62
+ constructor(message: string, code?: string | undefined);
63
+ }
64
+ /**
65
+ * Thrown when the socket drops while a send is in flight, before the server confirmed it
66
+ * The message may or may not have been delivered, a retry can double-deliver, so reconcile or accept at-least-once
67
+ */
68
+ export declare class SendUncertainError extends Error {
69
+ readonly clientMsgId?: string | undefined;
70
+ readonly conversationId?: number | undefined;
71
+ constructor(message: string, clientMsgId?: string | undefined, conversationId?: number | undefined);
72
+ }
73
+ export declare class DirectFlow {
74
+ private readonly http;
75
+ private readonly ws;
76
+ private readonly signal;
77
+ private readonly logger?;
78
+ private pendingSends;
79
+ constructor(http: HttpClient, ws: WsClient, signal: SignalEngine, logger?: SdkLogger | undefined);
80
+ /** DM send. Resolves with the messageId from the first device's own-echo
81
+ * Plaintext is raw UTF-8 for text, or the JSON envelope (file/voice/video_note/gallery) for attachments */
82
+ sendMessage(peerUserId: number, body: {
83
+ text?: string;
84
+ attachment?: AttachmentInput;
85
+ attachments?: AttachmentInput[];
86
+ }, opts?: SendTextOptions): Promise<SendResult>;
87
+ private sendToDevice;
88
+ private onFrame;
89
+ /**
90
+ * Encrypt a reaction payload to every device of `peerUserId` and return the per-device distributions
91
+ * for a `reaction_add` frame. Open a Signal session if needed, then encrypt
92
+ * A device the bot can't reach is skipped, the others still get the reaction
93
+ * The bot is single-device, so no sibling or self-AES distributions are produced
94
+ */
95
+ encryptReaction(peerUserId: number, plaintext: Uint8Array): Promise<Array<{
96
+ recipientId: number;
97
+ recipientDeviceId: number;
98
+ ciphertext: string;
99
+ messageType: 1 | 3;
100
+ }>>;
101
+ private ensureConversation;
102
+ private fetchPeerDevices;
103
+ private fetchPreKeyBundle;
104
+ /** Reject pending sends and clear timers. Called by MorokBot on stop() */
105
+ shutdown(): void;
106
+ /** Fail in-flight sends after a transient disconnect with an uncertain result */
107
+ private failPendingUncertain;
108
+ }
109
+ //# sourceMappingURL=direct.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct.d.ts","sourceRoot":"","sources":["../../src/flow/direct.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,346 @@
1
+ import { randomBytes, randomUUID } from 'node:crypto';
2
+ import { uploadAttachment, buildGalleryPayload, GALLERY_MIN_ITEMS, GALLERY_MAX_ITEMS, } from './attachments.js';
3
+ const VOICE_DURATION_MIN = 0.1;
4
+ const VOICE_DURATION_MAX = 600;
5
+ const VOICE_WAVEFORM_MAX_BARS = 64;
6
+ const VIDEO_NOTE_DURATION_MIN = 0.5;
7
+ const VIDEO_NOTE_DURATION_MAX = 300;
8
+ const VIDEO_NOTE_SHAPE_RE = /^[a-zA-Z0-9]{1,32}$/;
9
+ const VIDEO_NOTE_DEFAULT_SHAPE = 'circle';
10
+ function buildFilePayload(ref, caption) {
11
+ const body = {
12
+ type: 'file', ref,
13
+ };
14
+ if (caption && caption.length > 0)
15
+ body.caption = caption;
16
+ return JSON.stringify(body);
17
+ }
18
+ function buildVoicePayload(ref, duration, waveform) {
19
+ const dur = Math.max(VOICE_DURATION_MIN, Math.min(VOICE_DURATION_MAX, Number(duration) || 0));
20
+ const wf = Array.isArray(waveform)
21
+ ? waveform.slice(0, VOICE_WAVEFORM_MAX_BARS).map(n => {
22
+ const v = Math.round(Number(n) || 0);
23
+ return v < 0 ? 0 : v > 100 ? 100 : v;
24
+ })
25
+ : [];
26
+ return JSON.stringify({ type: 'voice', ref, duration: dur, waveform: wf });
27
+ }
28
+ function buildVideoNotePayload(ref, duration, shape) {
29
+ const dur = Math.max(VIDEO_NOTE_DURATION_MIN, Math.min(VIDEO_NOTE_DURATION_MAX, Number(duration) || 0));
30
+ const sh = typeof shape === 'string' && VIDEO_NOTE_SHAPE_RE.test(shape) ? shape : VIDEO_NOTE_DEFAULT_SHAPE;
31
+ return JSON.stringify({ type: 'video_note', ref, duration: dur, shape: sh });
32
+ }
33
+ const DEFAULT_VOICE_MIME = 'audio/ogg';
34
+ const DEFAULT_VIDEO_NOTE_MIME = 'video/webm';
35
+ function assertNever(x) {
36
+ throw new Error(`unsupported attachment kind: ${x.kind}`);
37
+ }
38
+ export async function uploadAndBuildPayload(http, attachment, caption, logger) {
39
+ const data = attachment.data;
40
+ switch (attachment.kind) {
41
+ case 'file': {
42
+ const ref = await uploadAttachment(http, data, {
43
+ mime: attachment.mime ?? 'application/octet-stream',
44
+ filename: attachment.name,
45
+ }, logger);
46
+ return { payload: buildFilePayload(ref, caption), ref, kind: 'file' };
47
+ }
48
+ case 'voice': {
49
+ const ref = await uploadAttachment(http, data, {
50
+ mime: attachment.mime ?? DEFAULT_VOICE_MIME,
51
+ noteMedia: true,
52
+ }, logger);
53
+ return {
54
+ payload: buildVoicePayload(ref, attachment.duration, attachment.waveform),
55
+ ref,
56
+ kind: 'voice',
57
+ };
58
+ }
59
+ case 'video_note': {
60
+ const ref = await uploadAttachment(http, data, {
61
+ mime: attachment.mime ?? DEFAULT_VIDEO_NOTE_MIME,
62
+ noteMedia: true,
63
+ }, logger);
64
+ return {
65
+ payload: buildVideoNotePayload(ref, attachment.duration, attachment.shape),
66
+ ref,
67
+ kind: 'video_note',
68
+ };
69
+ }
70
+ default:
71
+ return assertNever(attachment);
72
+ }
73
+ }
74
+ export async function uploadGalleryAndBuildPayload(http, attachments, caption, logger) {
75
+ if (!Array.isArray(attachments) || attachments.length < GALLERY_MIN_ITEMS) {
76
+ throw new Error(`gallery requires >= ${GALLERY_MIN_ITEMS} attachments, got ${attachments?.length ?? 0}`);
77
+ }
78
+ if (attachments.length > GALLERY_MAX_ITEMS) {
79
+ throw new Error(`gallery capped at ${GALLERY_MAX_ITEMS} attachments, got ${attachments.length}`);
80
+ }
81
+ for (const a of attachments) {
82
+ if (!a || typeof a !== 'object')
83
+ throw new Error('gallery: every item must be an AttachmentInput object');
84
+ if (a.kind !== 'file') {
85
+ throw new Error(`gallery items must be kind='file' (got '${a.kind}'); voice / video_note are single-only`);
86
+ }
87
+ }
88
+ const refs = [];
89
+ for (let i = 0; i < attachments.length; i++) {
90
+ const a = attachments[i];
91
+ const data = a.data;
92
+ try {
93
+ const ref = await uploadAttachment(http, data, {
94
+ mime: a.mime ?? 'application/octet-stream',
95
+ filename: a.name,
96
+ }, logger);
97
+ refs.push(ref);
98
+ }
99
+ catch (err) {
100
+ throw new Error(`gallery upload failed at item ${i + 1}/${attachments.length}: ${err.message}`);
101
+ }
102
+ }
103
+ const items = refs.map(ref => ({ type: 'file', ref }));
104
+ const payload = buildGalleryPayload(items, caption);
105
+ return {
106
+ payload,
107
+ refs,
108
+ headFileId: refs[0].fileId,
109
+ additionalFileIds: refs.slice(1).map(r => r.fileId),
110
+ };
111
+ }
112
+ function mintFanoutId() {
113
+ return randomBytes(12).toString('base64url');
114
+ }
115
+ function mintClientMsgId() {
116
+ return randomUUID().replace(/-/g, '');
117
+ }
118
+ export class SendRejectedError extends Error {
119
+ code;
120
+ constructor(message, code) {
121
+ super(message);
122
+ this.code = code;
123
+ this.name = 'SendRejectedError';
124
+ }
125
+ }
126
+ export class SendUncertainError extends Error {
127
+ clientMsgId;
128
+ conversationId;
129
+ constructor(message, clientMsgId, conversationId) {
130
+ super(message);
131
+ this.clientMsgId = clientMsgId;
132
+ this.conversationId = conversationId;
133
+ this.name = 'SendUncertainError';
134
+ }
135
+ }
136
+ export class DirectFlow {
137
+ http;
138
+ ws;
139
+ signal;
140
+ logger;
141
+ pendingSends = new Map();
142
+ constructor(http, ws, signal, logger) {
143
+ this.http = http;
144
+ this.ws = ws;
145
+ this.signal = signal;
146
+ this.logger = logger;
147
+ this.ws.on('frame', (frame) => { this.onFrame(frame); });
148
+ this.ws.on('close', (info) => { if (info.willReconnect)
149
+ this.failPendingUncertain(); });
150
+ }
151
+ async sendMessage(peerUserId, body, opts = {}) {
152
+ if (!Number.isInteger(peerUserId) || peerUserId < 1) {
153
+ throw new Error(`sendMessage: peerUserId must be a positive integer, got ${peerUserId}`);
154
+ }
155
+ const hasText = typeof body.text === 'string' && body.text.length > 0;
156
+ const hasAttachment = body.attachment !== undefined;
157
+ const hasGallery = Array.isArray(body.attachments) && body.attachments.length > 0;
158
+ if (hasAttachment && hasGallery) {
159
+ throw new Error('sendMessage: `attachment` and `attachments` are mutually exclusive');
160
+ }
161
+ if (!hasText && !hasAttachment && !hasGallery) {
162
+ throw new Error('sendMessage: must supply at least one of `text`, `attachment`, or `attachments`');
163
+ }
164
+ let plaintext;
165
+ let fileId;
166
+ let kind = 'text';
167
+ let additionalFileIds;
168
+ if (hasGallery) {
169
+ const built = await uploadGalleryAndBuildPayload(this.http, body.attachments, hasText ? body.text : undefined, this.logger);
170
+ plaintext = new TextEncoder().encode(built.payload);
171
+ fileId = built.headFileId;
172
+ kind = 'gallery';
173
+ additionalFileIds = built.additionalFileIds;
174
+ }
175
+ else if (hasAttachment) {
176
+ const built = await uploadAndBuildPayload(this.http, body.attachment, hasText ? body.text : undefined, this.logger);
177
+ plaintext = new TextEncoder().encode(built.payload);
178
+ fileId = built.ref.fileId;
179
+ kind = built.kind;
180
+ if (kind !== 'file' && hasText) {
181
+ this.logger?.warn({ kind, droppedTextLen: body.text.length }, '[direct] caption ignored: only kind="file" supports caption');
182
+ }
183
+ }
184
+ else {
185
+ plaintext = new TextEncoder().encode(body.text);
186
+ }
187
+ const conv = await this.ensureConversation(peerUserId);
188
+ const devices = await this.fetchPeerDevices(peerUserId);
189
+ if (devices.length === 0) {
190
+ throw new Error(`sendMessage: peer ${peerUserId} has no addressable devices`);
191
+ }
192
+ const clientMsgId = mintClientMsgId();
193
+ const timeoutMs = opts.sendTimeoutMs ?? 10_000;
194
+ const perDevice = devices.map((deviceId) => this.sendToDevice({
195
+ peerUserId, peerDeviceId: deviceId,
196
+ conversationId: conv.id,
197
+ plaintext, clientMsgId, timeoutMs,
198
+ replyToId: opts.replyToId,
199
+ replyToClientMsgId: opts.replyToClientMsgId,
200
+ threadRootId: opts.threadRootId,
201
+ fileId,
202
+ kind,
203
+ additionalFileIds,
204
+ }).catch(err => ({ kind: 'err', err: err })));
205
+ const results = await Promise.all(perDevice);
206
+ const ok = results.find((r) => typeof r === 'number');
207
+ if (ok !== undefined) {
208
+ return { messageId: ok, clientMsgId, conversationId: conv.id };
209
+ }
210
+ const errResults = results
211
+ .filter((r) => typeof r === 'object' && 'kind' in r);
212
+ const errs = errResults.map(r => r.err.message);
213
+ const aggMsg = `sendMessage: every device send failed - ${errs.join(' ; ')}`;
214
+ if (errResults.length > 0 && errResults.every(r => r.err instanceof SendUncertainError)) {
215
+ throw new SendUncertainError(aggMsg, clientMsgId, conv.id);
216
+ }
217
+ const distinctCodes = [...new Set(errResults
218
+ .map(r => (r.err instanceof SendRejectedError ? r.err.code : undefined))
219
+ .filter((c) => c !== undefined))];
220
+ const commonCode = distinctCodes.length === 1 ? distinctCodes[0] : undefined;
221
+ throw commonCode ? new SendRejectedError(aggMsg, commonCode) : new Error(aggMsg);
222
+ }
223
+ sendToDevice(args) {
224
+ const { peerUserId, peerDeviceId, conversationId, plaintext, clientMsgId } = args;
225
+ return this.signal.withPeerLock(peerUserId, peerDeviceId, async () => {
226
+ if (!(await this.signal.hasOpenSession(peerUserId, peerDeviceId))) {
227
+ const bundle = await this.fetchPreKeyBundle(peerUserId, peerDeviceId);
228
+ await this.signal.processPreKeyBundle(bundle);
229
+ }
230
+ const { type, body } = await this.signal.encrypt(peerUserId, peerDeviceId, plaintext);
231
+ const fanoutId = mintFanoutId();
232
+ return new Promise((resolve, reject) => {
233
+ const timer = setTimeout(() => {
234
+ if (this.pendingSends.delete(fanoutId)) {
235
+ reject(new Error(`send timeout after ${args.timeoutMs}ms (peer=${peerUserId}.${peerDeviceId} conv=${conversationId})`));
236
+ }
237
+ }, args.timeoutMs);
238
+ this.pendingSends.set(fanoutId, { resolve, reject, timer });
239
+ const frame = {
240
+ type: 'message',
241
+ conversationId,
242
+ recipientId: peerUserId,
243
+ recipientDeviceId: peerDeviceId,
244
+ ciphertext: body,
245
+ messageType: type,
246
+ clientMsgId,
247
+ fanoutId,
248
+ kind: args.kind ?? 'text',
249
+ };
250
+ if (args.replyToId !== undefined)
251
+ frame.replyToId = args.replyToId;
252
+ if (args.replyToClientMsgId !== undefined)
253
+ frame.replyToClientMsgId = args.replyToClientMsgId;
254
+ if (args.threadRootId !== undefined)
255
+ frame.threadRootId = args.threadRootId;
256
+ if (args.fileId !== undefined)
257
+ frame.fileId = args.fileId;
258
+ if (args.additionalFileIds !== undefined && args.additionalFileIds.length > 0) {
259
+ frame.additionalFileIds = args.additionalFileIds;
260
+ }
261
+ this.ws.send(frame);
262
+ });
263
+ });
264
+ }
265
+ onFrame(frame) {
266
+ const fanoutId = frame.fanoutId;
267
+ if (typeof fanoutId !== 'string')
268
+ return;
269
+ const pending = this.pendingSends.get(fanoutId);
270
+ if (!pending)
271
+ return;
272
+ clearTimeout(pending.timer);
273
+ this.pendingSends.delete(fanoutId);
274
+ if (frame.type === 'error') {
275
+ const code = typeof frame.code === 'string' ? frame.code : undefined;
276
+ const msg = typeof frame.message === 'string'
277
+ ? frame.message
278
+ : code ?? 'send rejected by server';
279
+ const codeStr = code ? ` (code=${code})` : '';
280
+ pending.reject(new SendRejectedError(`send rejected: ${msg}${codeStr}`, code));
281
+ return;
282
+ }
283
+ if (frame.type === 'message') {
284
+ const id = typeof frame.id === 'number' ? frame.id : null;
285
+ if (id === null) {
286
+ pending.reject(new Error('own-echo missing message id'));
287
+ return;
288
+ }
289
+ pending.resolve(id);
290
+ return;
291
+ }
292
+ pending.reject(new Error(`unexpected frame type "${frame.type}" for fanoutId ${fanoutId}`));
293
+ }
294
+ async encryptReaction(peerUserId, plaintext) {
295
+ const devices = await this.fetchPeerDevices(peerUserId);
296
+ const dists = [];
297
+ for (const deviceId of devices) {
298
+ try {
299
+ const env = await this.signal.withPeerLock(peerUserId, deviceId, async () => {
300
+ if (!(await this.signal.hasOpenSession(peerUserId, deviceId))) {
301
+ const bundle = await this.fetchPreKeyBundle(peerUserId, deviceId);
302
+ await this.signal.processPreKeyBundle(bundle);
303
+ }
304
+ return this.signal.encrypt(peerUserId, deviceId, plaintext);
305
+ });
306
+ dists.push({
307
+ recipientId: peerUserId,
308
+ recipientDeviceId: deviceId,
309
+ ciphertext: env.body,
310
+ messageType: env.type,
311
+ });
312
+ }
313
+ catch (err) {
314
+ this.logger?.warn({ peerUserId, deviceId, err: err.message }, '[direct] reaction encrypt failed for device, skipping');
315
+ }
316
+ }
317
+ return dists;
318
+ }
319
+ async ensureConversation(targetUserId) {
320
+ const res = await this.http.post('/conversations', { targetUserId });
321
+ return { id: res.data.conversation.id };
322
+ }
323
+ async fetchPeerDevices(peerUserId) {
324
+ const res = await this.http.get(`/prekeys/${peerUserId}/devices`);
325
+ return Array.isArray(res.data?.deviceIds) ? res.data.deviceIds : [];
326
+ }
327
+ async fetchPreKeyBundle(peerUserId, peerDeviceId) {
328
+ const res = await this.http.get(`/prekeys/${peerUserId}/${peerDeviceId}`);
329
+ return res.data;
330
+ }
331
+ shutdown() {
332
+ for (const [, p] of this.pendingSends) {
333
+ clearTimeout(p.timer);
334
+ p.reject(new Error('SDK shutdown - send aborted'));
335
+ }
336
+ this.pendingSends.clear();
337
+ }
338
+ failPendingUncertain() {
339
+ for (const [, p] of this.pendingSends) {
340
+ clearTimeout(p.timer);
341
+ p.reject(new SendUncertainError('send interrupted by a disconnect before the server confirmed'));
342
+ }
343
+ this.pendingSends.clear();
344
+ }
345
+ }
346
+ //# sourceMappingURL=direct.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct.js","sourceRoot":"","sources":["../../src/flow/direct.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Group-chat / channel flow: fetch channel-keys, decrypt incoming group_messages, send group_messages,
3
+ * rotate channel-key, rotate group_secret, backfill epochs to new joiners
4
+ *
5
+ * Receive side: parse [MAGIC || epoch || iv || ct+tag], pick the right per-epoch secret from the local store,
6
+ * AES-GCM decrypt. On a missing epoch, lazy-fetch once and retry. Mirrors frontend/src/signal/channel.ts
7
+ *
8
+ * Locks: withPeerLock(sharerId, sharerDevice) is shared with the DM ratchet so concurrent DM traffic
9
+ * with the same sharer can't corrupt session state. A per-conv lock inside ChannelKeyStore and GroupSecretStore
10
+ * serialises file writes
11
+ */
12
+ import type { HttpClient } from '../transport/http.js';
13
+ import type { WsClient } from '../transport/ws.js';
14
+ import type { SignalEngine } from '../crypto/signal.js';
15
+ import type { SdkLogger } from '../types.js';
16
+ import type { ChannelKeyStore } from '../crypto/channel-key-store.js';
17
+ import type { GroupSecretStore } from '../crypto/group-secret-store.js';
18
+ import type { ConvCache } from './conv-cache.js';
19
+ export interface GroupSendOptions {
20
+ sendTimeoutMs?: number;
21
+ replyToId?: number;
22
+ replyToClientMsgId?: string;
23
+ threadRootId?: number;
24
+ /** Optional file id from a prior `uploadAttachment`. For a gallery this is the head ref's fileId */
25
+ fileId?: number;
26
+ /** Wire-side render hint matching the server's ALLOWED_MESSAGE_KINDS, including 'gallery' */
27
+ kind?: 'text' | 'file' | 'voice' | 'video_note' | 'gallery';
28
+ /** Gallery-only: file ids 2..N (head is `fileId`). Server caps at 9 extras (10 total including the head) */
29
+ additionalFileIds?: number[];
30
+ /** Disappearing-message TTL. Server-validated */
31
+ expiresInSeconds?: number;
32
+ }
33
+ export interface GroupSendResult {
34
+ messageId: number;
35
+ clientMsgId: string;
36
+ fanoutId: string;
37
+ conversationId: number;
38
+ epoch: number;
39
+ }
40
+ export declare class GroupsFlow {
41
+ private readonly botUserId;
42
+ private readonly http;
43
+ private readonly ws;
44
+ private readonly signal;
45
+ private readonly store;
46
+ private readonly gsStore;
47
+ private readonly convCache;
48
+ private readonly logger?;
49
+ private readonly inflightInstalls;
50
+ private readonly pendingSends;
51
+ private readonly inflightGroupSecret;
52
+ private readonly inflightBackfills;
53
+ constructor(botUserId: number, http: HttpClient, ws: WsClient, signal: SignalEngine, store: ChannelKeyStore, gsStore: GroupSecretStore, convCache: ConvCache, logger?: SdkLogger | undefined);
54
+ /** Fetch + install all channel-key epochs above sinceEpoch (default -1 = everything), returns the count
55
+ * Concurrent calls with the same (conv, sinceEpoch) share one HTTP round-trip */
56
+ installFromServer(conversationId: number, opts?: {
57
+ sinceEpoch?: number;
58
+ }): Promise<number>;
59
+ private doInstall;
60
+ /** Decrypt a group-message wire under the right epoch's secret. On a missing epoch, lazy-fetch once and retry,
61
+ * if still missing it throws. Plaintext is UTF-8 text or the JSON envelope for attachments (caller sniffs) */
62
+ decryptGroupMessage(conversationId: number, wireBase64: string): Promise<Uint8Array>;
63
+ /** Drop local channel-key + group-secret state. Called on conversation_kicked */
64
+ forgetConversation(conversationId: number): Promise<void>;
65
+ /** Send a group/channel message under the current epoch. Resolves with messageId on own-echo,
66
+ * rejects on an error frame or after sendTimeoutMs (default 10s)
67
+ *
68
+ * Wire: plaintext -> AES-GCM under the epoch secret -> [MAGIC | epoch_BE | iv | ct+tag] -> base64 -> WS frame
69
+ *
70
+ * With no local key for this conv, we install once and bail with a clear error */
71
+ sendMessage(conversationId: number, plaintext: Uint8Array, opts?: GroupSendOptions): Promise<GroupSendResult>;
72
+ /**
73
+ * Encrypt `plaintext` under the conversation's current channel-key epoch and return the base64 wire
74
+ * and the epoch used. Lazy-installs the channel key if the bot just joined and has none yet
75
+ * Used by the reaction path, a group reaction is a channel-key envelope (messageType 8) whose plaintext
76
+ * is the JSON `{ emoji }`, like a group message body
77
+ */
78
+ encryptForChannel(conversationId: number, plaintext: Uint8Array): Promise<{
79
+ ciphertext: string;
80
+ epoch: number;
81
+ }>;
82
+ /**
83
+ * Mint a fresh channel-key, wrap it to every member device, POST to /channel-key/rotate,
84
+ * install the new epoch locally
85
+ *
86
+ * Server allows any member to rotate, the bot's call is identical to a human admin's
87
+ * Per-device wrap failures (no SPK, bundle fetch fails) are skipped,
88
+ * those devices pick up the new epoch later via /channel-key
89
+ *
90
+ * If the bot has the current group_secret it also attaches a sealed-to-group-secret bundle,
91
+ * so new joiners can unwrap the new epoch through the bundle path. predictedEpoch is localMax+1,
92
+ * if the server's actual epoch differs the receiver's inner check throws and the bundle is ignored
93
+ */
94
+ rotateChannelKey(conversationId: number): Promise<{
95
+ epoch: number;
96
+ }>;
97
+ /**
98
+ * Atomic dual rotation: new group_secret v(N+1) plus a new channel-key epoch sealed under v(N+1),
99
+ * in one server transaction. Per-device Signal wraps to every member plus a sealed bundle,
100
+ * so new joiners can unseal even if every wrapping device goes offline
101
+ *
102
+ * Forward secrecy: after kicking a leaker, a rotate ensures the old group_secret can't unseal any future bundle
103
+ *
104
+ * Server constraints:
105
+ * - conv type must be GROUP
106
+ * - conv must be private or the caller must be owner (otherwise 409 NOT_PRIVATE)
107
+ * - stale expectedCurrentVersion -> 409 SECRET_VERSION_STALE
108
+ *
109
+ * 409 SECRET_VERSION_STALE auto-retry: refresh and retry once, a second stale bubbles to the caller
110
+ * The retry is bounded, so concurrent rotators can't loop forever
111
+ */
112
+ rotateGroupSecret(conversationId: number): Promise<{
113
+ epoch: number;
114
+ version: number;
115
+ }>;
116
+ private rotateGroupSecretOnce;
117
+ /** Share locally-known epochs with another member's devices,
118
+ * useful when the bot is the only online member at the time of a join
119
+ *
120
+ * Server runs a possess-epoch check and a joined_secret_version cut-off so the bot can't leak pre-join history
121
+ *
122
+ * Chunked at 500 distributions per POST (matches FE). One device * one epoch = one distribution */
123
+ backfillChannelKeys(conversationId: number, target: {
124
+ userId: number;
125
+ deviceIds?: number[];
126
+ }, opts?: {
127
+ epochs?: number[];
128
+ }): Promise<{
129
+ accepted: number;
130
+ }>;
131
+ /** Fetch and store the bot's per-device wrap of the current group_secret. After this, installFromServer
132
+ * can unwrap sealed-to-group-secret bundles
133
+ *
134
+ * Concurrent callers share one in-flight promise. No-op if the conv has no group_secret yet,
135
+ * or no wrap for the bot's device */
136
+ installGroupSecret(conversationId: number): Promise<void>;
137
+ private doInstallGroupSecret;
138
+ /** Fail in-flight sends after a transient disconnect with an uncertain result */
139
+ private failPendingUncertain;
140
+ /** Reject pending sends and drop backfill refs */
141
+ shutdown(): void;
142
+ /** Register a fire-and-forget backfill. Called from ReceiveFlow's auto-backfill path */
143
+ trackBackfill(p: Promise<unknown>): void;
144
+ private onFrame;
145
+ }
146
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/flow/groups.ts"],"names":[],"mappings":"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"}