fb-messenger-e2ee 0.1.1 → 0.1.3

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 (111) hide show
  1. package/DOCS.md +41 -3
  2. package/README.md +27 -2
  3. package/dist/index.cjs +9017 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +910 -0
  6. package/dist/index.d.ts +910 -0
  7. package/dist/index.js +9049 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +19 -4
  10. package/bun.lock +0 -1014
  11. package/examples/basic-usage.ts +0 -52
  12. package/jest.config.ts +0 -18
  13. package/src/config/env.ts +0 -15
  14. package/src/controllers/client.controller.ts +0 -805
  15. package/src/controllers/dgw-handler.ts +0 -171
  16. package/src/controllers/e2ee-handler.ts +0 -832
  17. package/src/controllers/event-mapper.ts +0 -327
  18. package/src/core/client.ts +0 -151
  19. package/src/e2ee/application/e2ee-client.ts +0 -376
  20. package/src/e2ee/application/fanout-planner.ts +0 -79
  21. package/src/e2ee/application/outbound-message-cache.ts +0 -58
  22. package/src/e2ee/application/prekey-maintenance.ts +0 -55
  23. package/src/e2ee/application/retry-manager.ts +0 -156
  24. package/src/e2ee/facebook/facebook-protocol-utils.ts +0 -230
  25. package/src/e2ee/facebook/icdc-payload.ts +0 -31
  26. package/src/e2ee/index.ts +0 -76
  27. package/src/e2ee/internal.ts +0 -27
  28. package/src/e2ee/media/media-crypto.ts +0 -147
  29. package/src/e2ee/media/media-upload.ts +0 -90
  30. package/src/e2ee/message/builders/client-payload.ts +0 -54
  31. package/src/e2ee/message/builders/consumer-application.ts +0 -362
  32. package/src/e2ee/message/builders/message-application.ts +0 -64
  33. package/src/e2ee/message/builders/message-transport.ts +0 -84
  34. package/src/e2ee/message/codecs/protobuf-codecs.ts +0 -101
  35. package/src/e2ee/message/constants.ts +0 -4
  36. package/src/e2ee/message/message-builder.ts +0 -15
  37. package/src/e2ee/signal/prekey-manager.ts +0 -140
  38. package/src/e2ee/signal/signal-manager.ts +0 -365
  39. package/src/e2ee/store/device-json.ts +0 -47
  40. package/src/e2ee/store/device-repository.ts +0 -12
  41. package/src/e2ee/store/device-store.ts +0 -538
  42. package/src/e2ee/transport/binary/decoder.ts +0 -180
  43. package/src/e2ee/transport/binary/encoder.ts +0 -143
  44. package/src/e2ee/transport/binary/stanzas.ts +0 -97
  45. package/src/e2ee/transport/binary/tokens.ts +0 -64
  46. package/src/e2ee/transport/binary/wa-binary.ts +0 -8
  47. package/src/e2ee/transport/dgw/dgw-socket.ts +0 -345
  48. package/src/e2ee/transport/noise/noise-handshake.ts +0 -417
  49. package/src/e2ee/transport/noise/noise-socket.ts +0 -230
  50. package/src/index.ts +0 -34
  51. package/src/models/client.ts +0 -26
  52. package/src/models/config.ts +0 -14
  53. package/src/models/domain.ts +0 -299
  54. package/src/models/e2ee.ts +0 -295
  55. package/src/models/media.ts +0 -41
  56. package/src/models/messaging.ts +0 -88
  57. package/src/models/thread.ts +0 -69
  58. package/src/repositories/session.repository.ts +0 -20
  59. package/src/services/auth.service.ts +0 -55
  60. package/src/services/e2ee.service.ts +0 -174
  61. package/src/services/facebook-gateway.service.ts +0 -245
  62. package/src/services/icdc.service.ts +0 -177
  63. package/src/services/media.service.ts +0 -304
  64. package/src/services/messaging.service.ts +0 -28
  65. package/src/services/thread.service.ts +0 -199
  66. package/src/types/advanced-types.ts +0 -61
  67. package/src/types/fca-unofficial.d.ts +0 -212
  68. package/src/types/protocol-types.ts +0 -43
  69. package/src/utils/fca-utils.ts +0 -30
  70. package/src/utils/logger.ts +0 -24
  71. package/src/utils/mime.ts +0 -51
  72. package/tests/.env.example +0 -87
  73. package/tests/data/1x1.png +0 -0
  74. package/tests/data/example.txt +0 -1
  75. package/tests/data/file_example.mp3 +0 -0
  76. package/tests/data/file_example.mp4 +0 -0
  77. package/tests/integration.test.ts +0 -498
  78. package/tests/script/echo-e2ee.ts +0 -174
  79. package/tests/script/send-e2ee-media.ts +0 -227
  80. package/tests/script/send-e2ee-reaction.ts +0 -108
  81. package/tests/script/send-e2ee-unsend.ts +0 -115
  82. package/tests/script/send-typing.ts +0 -107
  83. package/tests/script/test-group-send.ts +0 -105
  84. package/tests/setup.ts +0 -3
  85. package/tests/types.test.ts +0 -57
  86. package/tests/unit/controllers/dgw-handler.test.ts +0 -60
  87. package/tests/unit/controllers/e2ee-handler.test.ts +0 -293
  88. package/tests/unit/controllers/event-mapper.test.ts +0 -252
  89. package/tests/unit/e2ee/application-helpers.test.ts +0 -418
  90. package/tests/unit/e2ee/device-store.test.ts +0 -126
  91. package/tests/unit/e2ee/facebook-protocol-utils.test.ts +0 -134
  92. package/tests/unit/e2ee/media-crypto.test.ts +0 -55
  93. package/tests/unit/e2ee/media-upload.test.ts +0 -60
  94. package/tests/unit/e2ee/message-builder.test.ts +0 -42
  95. package/tests/unit/e2ee/message-builders.test.ts +0 -230
  96. package/tests/unit/e2ee/noise-handshake.test.ts +0 -260
  97. package/tests/unit/e2ee/prekey-manager.test.ts +0 -55
  98. package/tests/unit/e2ee/wa-binary.test.ts +0 -127
  99. package/tests/unit/services/e2ee.service.test.ts +0 -101
  100. package/tests/unit/services/facebook-gateway.service.test.ts +0 -138
  101. package/tests/unit/services/media.service.test.ts +0 -169
  102. package/tests/unit/utils/fca-utils.test.ts +0 -48
  103. package/tsconfig.json +0 -19
  104. /package/{src/e2ee/message/proto → proto}/ArmadilloApplication.proto +0 -0
  105. /package/{src/e2ee/message/proto → proto}/ArmadilloICDC.proto +0 -0
  106. /package/{src/e2ee/message/proto → proto}/ConsumerApplication.proto +0 -0
  107. /package/{src/e2ee/message/proto → proto}/MessageApplication.proto +0 -0
  108. /package/{src/e2ee/message/proto → proto}/MessageTransport.proto +0 -0
  109. /package/{src/e2ee/message/proto → proto}/WACommon.proto +0 -0
  110. /package/{src/e2ee/message/proto → proto}/WAMediaTransport.proto +0 -0
  111. /package/{src/e2ee/message/proto → proto}/proto-writer.ts +0 -0
@@ -1,227 +0,0 @@
1
- import { basename, dirname, isAbsolute, join } from "node:path";
2
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { FBClient } from "../../src/index.ts";
5
- import type { MessengerEvent } from "../../src/models/domain.ts";
6
- import { inferFileMediaKindFromMimeType, inferMimeTypeFromFileName, type FileMediaKind } from "../../src/utils/mime.ts";
7
-
8
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
9
- const ROOT_DIR = join(SCRIPT_DIR, "..", "..");
10
-
11
- const APPSTATE_PATH = join(ROOT_DIR, "tests/appstate.json");
12
- const SESSION_PATH = join(ROOT_DIR, "tests/session.json");
13
- const DEVICE_PATH = join(ROOT_DIR, "tests/device.json");
14
- const ENV_PATH = join(ROOT_DIR, "tests/.env");
15
-
16
- const DEFAULT_TARGET_JID = "100042415119261.0@msgr";
17
- const DEFAULT_DATA_DIR = join(ROOT_DIR, "tests/data");
18
- const DEFAULT_SEND_DELAY_MS = 2_000;
19
-
20
- type SendKind = FileMediaKind;
21
-
22
- interface TestMediaFile {
23
- path: string;
24
- name: string;
25
- /** Explicit user-provided MIME override. If absent, send APIs infer from fileName. */
26
- mimeType?: string;
27
- /** MIME inferred for logging and choosing the send method in this test script. */
28
- detectedMimeType: string;
29
- kind: SendKind;
30
- }
31
-
32
- function loadEnvFile(filePath: string): void {
33
- if (!existsSync(filePath)) return;
34
- const raw = readFileSync(filePath, "utf8");
35
- for (const line of raw.split(/\r?\n/)) {
36
- const trimmed = line.trim();
37
- if (!trimmed || trimmed.startsWith("#")) continue;
38
- const idx = trimmed.indexOf("=");
39
- if (idx <= 0) continue;
40
- const key = trimmed.slice(0, idx).trim();
41
- let value = trimmed.slice(idx + 1).trim();
42
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
43
- value = value.slice(1, -1);
44
- }
45
- process.env[key] = value;
46
- }
47
- }
48
-
49
- function parseMessengerUserId(value: string): string {
50
- const userPart = value.split("@")[0] ?? value;
51
- const dotIdx = userPart.indexOf(".");
52
- const colonIdx = userPart.indexOf(":");
53
- const cuts = [dotIdx, colonIdx].filter((idx) => idx >= 0).sort((a, b) => a - b);
54
- const end = cuts[0] ?? userPart.length;
55
- return userPart.slice(0, end) || value;
56
- }
57
-
58
- function sleep(ms: number): Promise<void> {
59
- return new Promise((resolve) => setTimeout(resolve, ms));
60
- }
61
-
62
- function parseDelayMs(value: string | undefined): number {
63
- if (!value) return DEFAULT_SEND_DELAY_MS;
64
- const parsed = Number.parseInt(value, 10);
65
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_SEND_DELAY_MS;
66
- }
67
-
68
- function resolvePath(pathOrRelative: string): string {
69
- return isAbsolute(pathOrRelative) ? pathOrRelative : join(ROOT_DIR, pathOrRelative);
70
- }
71
-
72
- function toTestMediaFile(filePath: string, mimeOverride?: string): TestMediaFile {
73
- const detectedMimeType = inferMimeTypeFromFileName(filePath);
74
- const effectiveMimeType = mimeOverride || detectedMimeType;
75
- return {
76
- path: filePath,
77
- name: basename(filePath),
78
- mimeType: mimeOverride,
79
- detectedMimeType,
80
- kind: inferFileMediaKindFromMimeType(effectiveMimeType),
81
- };
82
- }
83
-
84
- function listDataFiles(dataDir: string): TestMediaFile[] {
85
- if (!existsSync(dataDir)) {
86
- throw new Error(`Missing data directory at ${dataDir}`);
87
- }
88
-
89
- return readdirSync(dataDir)
90
- .map((name) => join(dataDir, name))
91
- .filter((filePath) => statSync(filePath).isFile())
92
- .sort((a, b) => basename(a).localeCompare(basename(b)))
93
- .map((filePath) => toTestMediaFile(filePath));
94
- }
95
-
96
- function getFilesToSend(): TestMediaFile[] {
97
- const pathList = process.env.TEST_E2EE_MEDIA_PATHS
98
- ?.split(",")
99
- .map((value) => value.trim())
100
- .filter((value) => value.length > 0);
101
-
102
- if (pathList && pathList.length > 0) {
103
- return pathList.map((value) => toTestMediaFile(resolvePath(value)));
104
- }
105
-
106
- // Backward-compatible single-file mode for the old image-only script.
107
- if (process.env.TEST_E2EE_IMAGE_PATH) {
108
- return [toTestMediaFile(resolvePath(process.env.TEST_E2EE_IMAGE_PATH), process.env.TEST_E2EE_IMAGE_MIME)];
109
- }
110
-
111
- const dataDir = resolvePath(process.env.TEST_E2EE_MEDIA_DIR ?? DEFAULT_DATA_DIR);
112
- return listDataFiles(dataDir);
113
- }
114
-
115
- async function sendFileByKind(
116
- client: FBClient,
117
- targetJid: string,
118
- file: TestMediaFile,
119
- caption?: string,
120
- ): Promise<Record<string, unknown>> {
121
- const data = readFileSync(file.path);
122
- const common = {
123
- threadId: targetJid,
124
- data,
125
- fileName: file.name,
126
- ...(file.mimeType ? { mimeType: file.mimeType } : {}),
127
- caption,
128
- };
129
-
130
- switch (file.kind) {
131
- case "image":
132
- return client.sendImage(common);
133
- case "video":
134
- return client.sendVideo(common);
135
- case "audio":
136
- return client.sendAudio({ ...common, ptt: false });
137
- case "file":
138
- return client.sendFile(common);
139
- }
140
- }
141
-
142
- async function main() {
143
- loadEnvFile(ENV_PATH);
144
-
145
- const targetJid = process.env.TEST_E2EE_MEDIA_JID ?? process.env.TEST_E2EE_IMAGE_JID ?? DEFAULT_TARGET_JID;
146
- const caption = (process.env.TEST_E2EE_MEDIA_CAPTION ?? process.env.TEST_E2EE_IMAGE_CAPTION) || undefined;
147
- const delayMs = parseDelayMs(process.env.TEST_E2EE_MEDIA_DELAY_MS);
148
- const files = getFilesToSend();
149
-
150
- if (!existsSync(APPSTATE_PATH)) {
151
- console.error("send-e2ee-media", `Missing appstate file at ${APPSTATE_PATH}`);
152
- process.exit(1);
153
- }
154
- for (const file of files) {
155
- if (!existsSync(file.path)) {
156
- console.error("send-e2ee-media", `Missing test data file at ${file.path}`);
157
- process.exit(1);
158
- }
159
- }
160
- if (files.length === 0) {
161
- console.error("send-e2ee-media", "No files found to send.");
162
- process.exit(1);
163
- }
164
- if (!process.env.FB_E2EE_MEDIA_UPLOAD_AUTH) {
165
- console.log(
166
- "send-e2ee-media",
167
- "FB_E2EE_MEDIA_UPLOAD_AUTH is not set; media upload auth will be requested from media_conn.",
168
- );
169
- }
170
-
171
- const client = new FBClient({
172
- appStatePath: APPSTATE_PATH,
173
- sessionStorePath: SESSION_PATH,
174
- });
175
-
176
- client.onEvent((event: MessengerEvent) => {
177
- if (event.type === "error") console.error("send-e2ee-media", "Client error:", event.data.message);
178
- if (event.type === "e2ee_connected") console.log("send-e2ee-media", "E2EE connected.");
179
- });
180
-
181
- try {
182
- console.log("send-e2ee-media", "Connecting to Messenger...");
183
- const { userId } = await client.connect();
184
- const selfUserId = parseMessengerUserId(userId);
185
- console.log("send-e2ee-media", `Connected as User ID: ${selfUserId}`);
186
-
187
- const userDevicePath = join(ROOT_DIR, `device-${selfUserId}.json`);
188
- const finalDevicePath = existsSync(userDevicePath) ? userDevicePath : DEVICE_PATH;
189
-
190
- console.log("send-e2ee-media", `Connecting E2EE stream using: ${finalDevicePath}`);
191
- await client.connectE2EE(finalDevicePath, selfUserId);
192
-
193
- console.log(
194
- "send-e2ee-media",
195
- `Sending ${files.length} file(s) to ${targetJid}; delay=${delayMs}ms: ${files.map((file) => file.name).join(", ")}`,
196
- );
197
-
198
- for (const [index, file] of files.entries()) {
199
- const size = statSync(file.path).size;
200
- console.log(
201
- "send-e2ee-media",
202
- `[${index + 1}/${files.length}] Sending ${file.name} (${file.kind}, ${file.mimeType ?? `auto:${file.detectedMimeType}`}, ${size} bytes)...`,
203
- );
204
-
205
- const result = await sendFileByKind(client, targetJid, file, caption);
206
- console.log("send-e2ee-media", `[${index + 1}/${files.length}] Send completed: ${JSON.stringify(result)}`);
207
-
208
- if (index < files.length - 1 && delayMs > 0) {
209
- console.log("send-e2ee-media", `Waiting ${delayMs}ms before next file...`);
210
- await sleep(delayMs);
211
- }
212
- }
213
-
214
- await sleep(1_000);
215
- await client.disconnect();
216
- process.exit(0);
217
- } catch (err) {
218
- console.error("send-e2ee-media", "Error:", err);
219
- await client.disconnect().catch(() => undefined);
220
- process.exit(1);
221
- }
222
- }
223
-
224
- main().catch((err) => {
225
- console.error("send-e2ee-media", "Fatal:", err);
226
- process.exit(1);
227
- });
@@ -1,108 +0,0 @@
1
- import { dirname, join } from "node:path";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { FBClient } from "../../src/index.ts";
5
- import type { MessengerEvent } from "../../src/models/domain.ts";
6
-
7
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
8
- const ROOT_DIR = join(SCRIPT_DIR, "..", "..");
9
-
10
- const APPSTATE_PATH = join(ROOT_DIR, "tests/appstate.json");
11
- const SESSION_PATH = join(ROOT_DIR, "tests/session.json");
12
- const DEVICE_PATH = join(ROOT_DIR, "tests/device.json");
13
- const ENV_PATH = join(ROOT_DIR, "tests/.env");
14
-
15
- // Defaults from the sample e2ee_message in the request.
16
- const DEFAULT_THREAD_ID = "1805602490133470@g.us";
17
- const DEFAULT_MESSAGE_ID = "7456658723671758234";
18
- const DEFAULT_TARGET_SENDER_JID = "100042415119261.145@msgr";
19
- const DEFAULT_REACTION = "👍";
20
-
21
- function loadEnvFile(filePath: string): void {
22
- if (!existsSync(filePath)) return;
23
- const raw = readFileSync(filePath, "utf8");
24
- for (const line of raw.split(/\r?\n/)) {
25
- const trimmed = line.trim();
26
- if (!trimmed || trimmed.startsWith("#")) continue;
27
- const idx = trimmed.indexOf("=");
28
- if (idx <= 0) continue;
29
- const key = trimmed.slice(0, idx).trim();
30
- let value = trimmed.slice(idx + 1).trim();
31
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
32
- value = value.slice(1, -1);
33
- }
34
- process.env[key] = value;
35
- }
36
- }
37
-
38
- function parseMessengerUserId(value: string): string {
39
- const userPart = value.split("@")[0] ?? value;
40
- const dotIdx = userPart.indexOf(".");
41
- const colonIdx = userPart.indexOf(":");
42
- const cuts = [dotIdx, colonIdx].filter((idx) => idx >= 0).sort((a, b) => a - b);
43
- const end = cuts[0] ?? userPart.length;
44
- return userPart.slice(0, end) || value;
45
- }
46
-
47
- async function main() {
48
- loadEnvFile(ENV_PATH);
49
-
50
- const threadId = process.env.TEST_E2EE_REACTION_THREAD_ID ?? DEFAULT_THREAD_ID;
51
- const messageId = process.env.TEST_E2EE_REACTION_MESSAGE_ID ?? DEFAULT_MESSAGE_ID;
52
- const senderJid = process.env.TEST_E2EE_REACTION_SENDER_JID ?? DEFAULT_TARGET_SENDER_JID;
53
- const reaction = process.env.TEST_E2EE_REACTION_EMOJI ?? DEFAULT_REACTION;
54
-
55
- if (!existsSync(APPSTATE_PATH)) {
56
- console.error("send-e2ee-reaction", `Missing appstate file at ${APPSTATE_PATH}`);
57
- process.exit(1);
58
- }
59
-
60
- const client = new FBClient({
61
- appStatePath: APPSTATE_PATH,
62
- sessionStorePath: SESSION_PATH,
63
- });
64
-
65
- client.onEvent((event: MessengerEvent) => {
66
- if (event.type === "error") console.error("send-e2ee-reaction", "Client error:", event.data.message);
67
- if (event.type === "e2ee_connected") console.log("send-e2ee-reaction", "E2EE connected.");
68
- });
69
-
70
- try {
71
- console.log("send-e2ee-reaction", "Connecting to Messenger...");
72
- const { userId } = await client.connect();
73
- const selfUserId = parseMessengerUserId(userId);
74
- console.log("send-e2ee-reaction", `Connected as User ID: ${selfUserId}`);
75
-
76
- const userDevicePath = join(ROOT_DIR, `device-${selfUserId}.json`);
77
- const finalDevicePath = existsSync(userDevicePath) ? userDevicePath : DEVICE_PATH;
78
-
79
- console.log("send-e2ee-reaction", `Connecting E2EE stream using: ${finalDevicePath}`);
80
- await client.connectE2EE(finalDevicePath, selfUserId);
81
-
82
- console.log(
83
- "send-e2ee-reaction",
84
- `Reacting to message=${messageId} in ${threadId}, targetSender=${senderJid}, emoji=${reaction}`,
85
- );
86
-
87
- await client.sendReaction({
88
- threadId,
89
- messageId,
90
- senderJid,
91
- reaction,
92
- });
93
-
94
- console.log("send-e2ee-reaction", "Reaction send completed.");
95
- await new Promise((resolve) => setTimeout(resolve, 1_000));
96
- await client.disconnect();
97
- process.exit(0);
98
- } catch (err) {
99
- console.error("send-e2ee-reaction", "Error:", err);
100
- await client.disconnect().catch(() => undefined);
101
- process.exit(1);
102
- }
103
- }
104
-
105
- main().catch((err) => {
106
- console.error("send-e2ee-reaction", "Fatal:", err);
107
- process.exit(1);
108
- });
@@ -1,115 +0,0 @@
1
- import { dirname, join } from "node:path";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { FBClient } from "../../src/index.ts";
5
- import type { MessengerEvent } from "../../src/models/domain.ts";
6
-
7
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
8
- const ROOT_DIR = join(SCRIPT_DIR, "..", "..");
9
-
10
- const APPSTATE_PATH = join(ROOT_DIR, "tests/appstate.json");
11
- const SESSION_PATH = join(ROOT_DIR, "tests/session.json");
12
- const DEVICE_PATH = join(ROOT_DIR, "tests/device.json");
13
- const ENV_PATH = join(ROOT_DIR, "tests/.env");
14
-
15
- const DEFAULT_THREAD_ID = "100042415119261.0@msgr";
16
- const DEFAULT_UNSEND_DELAY_MS = 2_000;
17
-
18
- function loadEnvFile(filePath: string): void {
19
- if (!existsSync(filePath)) return;
20
- const raw = readFileSync(filePath, "utf8");
21
- for (const line of raw.split(/\r?\n/)) {
22
- const trimmed = line.trim();
23
- if (!trimmed || trimmed.startsWith("#")) continue;
24
- const idx = trimmed.indexOf("=");
25
- if (idx <= 0) continue;
26
- const key = trimmed.slice(0, idx).trim();
27
- let value = trimmed.slice(idx + 1).trim();
28
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
29
- value = value.slice(1, -1);
30
- }
31
- process.env[key] = value;
32
- }
33
- }
34
-
35
- function parseMessengerUserId(value: string): string {
36
- const userPart = value.split("@")[0] ?? value;
37
- const dotIdx = userPart.indexOf(".");
38
- const colonIdx = userPart.indexOf(":");
39
- const cuts = [dotIdx, colonIdx].filter((idx) => idx >= 0).sort((a, b) => a - b);
40
- const end = cuts[0] ?? userPart.length;
41
- return userPart.slice(0, end) || value;
42
- }
43
-
44
- function sleep(ms: number): Promise<void> {
45
- return new Promise((resolve) => setTimeout(resolve, ms));
46
- }
47
-
48
- function pickMessageId(result: Record<string, unknown>): string {
49
- const raw = result.messageId ?? result.messageID ?? result.message_id;
50
- if (typeof raw === "string" && raw.length > 0) return raw;
51
- if (typeof raw === "number" || typeof raw === "bigint") return String(raw);
52
- throw new Error(`sendMessage did not return a message id: ${JSON.stringify(result)}`);
53
- }
54
-
55
- async function main() {
56
- loadEnvFile(ENV_PATH);
57
-
58
- const threadId = process.env.TEST_E2EE_UNSEND_THREAD_ID
59
- ?? process.env.TEST_E2EE_MEDIA_JID
60
- ?? DEFAULT_THREAD_ID;
61
- const delayMs = Number(process.env.TEST_E2EE_UNSEND_DELAY_MS ?? String(DEFAULT_UNSEND_DELAY_MS));
62
- const text = process.env.TEST_E2EE_UNSEND_TEXT
63
- ?? `unsend smoke ${new Date().toISOString()}`;
64
-
65
- if (!existsSync(APPSTATE_PATH)) {
66
- console.error("send-e2ee-unsend", `Missing appstate file at ${APPSTATE_PATH}`);
67
- process.exit(1);
68
- }
69
-
70
- const client = new FBClient({
71
- appStatePath: APPSTATE_PATH,
72
- sessionStorePath: SESSION_PATH,
73
- });
74
-
75
- client.onEvent((event: MessengerEvent) => {
76
- if (event.type === "error") console.error("send-e2ee-unsend", "Client error:", event.data.message);
77
- if (event.type === "e2ee_connected") console.log("send-e2ee-unsend", "E2EE connected.");
78
- if (event.type === "e2ee_message") console.log("send-e2ee-unsend", "e2ee_message:", event.data);
79
- });
80
-
81
- try {
82
- console.log("send-e2ee-unsend", "Connecting to Messenger...");
83
- const { userId } = await client.connect();
84
- const selfUserId = parseMessengerUserId(userId);
85
- console.log("send-e2ee-unsend", `Connected as User ID: ${selfUserId}`);
86
-
87
- const userDevicePath = join(ROOT_DIR, `device-${selfUserId}.json`);
88
- const finalDevicePath = existsSync(userDevicePath) ? userDevicePath : DEVICE_PATH;
89
-
90
- console.log("send-e2ee-unsend", `Connecting E2EE stream using: ${finalDevicePath}`);
91
- await client.connectE2EE(finalDevicePath, selfUserId);
92
-
93
- console.log("send-e2ee-unsend", `Sending test message to ${threadId}: ${JSON.stringify(text)}`);
94
- const sendResult = await client.sendMessage({ threadId, text });
95
- const messageId = pickMessageId(sendResult);
96
- console.log("send-e2ee-unsend", `Sent messageId=${messageId}. Waiting ${delayMs}ms before unsend...`);
97
-
98
- await sleep(delayMs);
99
- await client.unsendMessage(messageId, threadId);
100
- console.log("send-e2ee-unsend", `Unsend completed for messageId=${messageId}`);
101
-
102
- await sleep(1_000);
103
- await client.disconnect();
104
- process.exit(0);
105
- } catch (err) {
106
- console.error("send-e2ee-unsend", "Error:", err);
107
- await client.disconnect().catch(() => undefined);
108
- process.exit(1);
109
- }
110
- }
111
-
112
- main().catch((err) => {
113
- console.error("send-e2ee-unsend", "Fatal:", err);
114
- process.exit(1);
115
- });
@@ -1,107 +0,0 @@
1
- import { dirname, join } from "node:path";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { FBClient } from "../../src/index.ts";
5
- import type { MessengerEvent } from "../../src/models/domain.ts";
6
-
7
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
8
- const ROOT_DIR = join(SCRIPT_DIR, "..", "..");
9
-
10
- const APPSTATE_PATH = join(ROOT_DIR, "tests/appstate.json");
11
- const SESSION_PATH = join(ROOT_DIR, "tests/session.json");
12
- const DEVICE_PATH = join(ROOT_DIR, "tests/device.json");
13
- const ENV_PATH = join(ROOT_DIR, "tests/.env");
14
-
15
- const DEFAULT_THREAD_ID = "100042415119261.0@msgr";
16
- const DEFAULT_TYPING_DURATION_MS = 5_000;
17
-
18
- function loadEnvFile(filePath: string): void {
19
- if (!existsSync(filePath)) return;
20
- const raw = readFileSync(filePath, "utf8");
21
- for (const line of raw.split(/\r?\n/)) {
22
- const trimmed = line.trim();
23
- if (!trimmed || trimmed.startsWith("#")) continue;
24
- const idx = trimmed.indexOf("=");
25
- if (idx <= 0) continue;
26
- const key = trimmed.slice(0, idx).trim();
27
- let value = trimmed.slice(idx + 1).trim();
28
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
29
- value = value.slice(1, -1);
30
- }
31
- process.env[key] = value;
32
- }
33
- }
34
-
35
- function parseMessengerUserId(value: string): string {
36
- const userPart = value.split("@")[0] ?? value;
37
- const dotIdx = userPart.indexOf(".");
38
- const colonIdx = userPart.indexOf(":");
39
- const cuts = [dotIdx, colonIdx].filter((idx) => idx >= 0).sort((a, b) => a - b);
40
- const end = cuts[0] ?? userPart.length;
41
- return userPart.slice(0, end) || value;
42
- }
43
-
44
- function sleep(ms: number): Promise<void> {
45
- return new Promise((resolve) => setTimeout(resolve, ms));
46
- }
47
-
48
- async function main() {
49
- loadEnvFile(ENV_PATH);
50
-
51
- const threadId = process.env.TEST_TYPING_THREAD_ID
52
- ?? process.env.TEST_E2EE_UNSEND_THREAD_ID
53
- ?? process.env.TEST_E2EE_MEDIA_JID
54
- ?? DEFAULT_THREAD_ID;
55
- const durationMs = Number(process.env.TEST_TYPING_DURATION_MS ?? String(DEFAULT_TYPING_DURATION_MS));
56
-
57
- if (!existsSync(APPSTATE_PATH)) {
58
- console.error("send-typing", `Missing appstate file at ${APPSTATE_PATH}`);
59
- process.exit(1);
60
- }
61
-
62
- const client = new FBClient({
63
- appStatePath: APPSTATE_PATH,
64
- sessionStorePath: SESSION_PATH,
65
- });
66
-
67
- client.onEvent((event: MessengerEvent) => {
68
- if (event.type === "error") console.error("send-typing", "Client error:", event.data.message);
69
- if (event.type === "ready") console.log("send-typing", "Client ready.");
70
- });
71
-
72
- try {
73
- console.log("send-typing", "Connecting to Messenger...");
74
- const { userId } = await client.connect();
75
- const selfUserId = parseMessengerUserId(userId);
76
- console.log("send-typing", `Connected as User ID: ${selfUserId}`);
77
-
78
- const userDevicePath = join(ROOT_DIR, `device-${selfUserId}.json`);
79
- const finalDevicePath = existsSync(userDevicePath) ? userDevicePath : DEVICE_PATH;
80
-
81
- console.log("send-typing", `Connecting E2EE stream using: ${finalDevicePath}`);
82
- await client.connectE2EE(finalDevicePath, selfUserId);
83
-
84
- console.log("send-typing", `sendTyping true to ${threadId}`);
85
- await client.sendTyping({ threadId, isTyping: true });
86
-
87
- console.log("send-typing", `Waiting ${durationMs}ms...`);
88
- await sleep(durationMs);
89
-
90
- console.log("send-typing", `sendTyping false to ${threadId}`);
91
- await client.sendTyping({ threadId, isTyping: false });
92
-
93
- console.log("send-typing", "Typing test completed.");
94
- await sleep(500);
95
- await client.disconnect();
96
- process.exit(0);
97
- } catch (err) {
98
- console.error("send-typing", "Error:", err);
99
- await client.disconnect().catch(() => undefined);
100
- process.exit(1);
101
- }
102
- }
103
-
104
- main().catch((err) => {
105
- console.error("send-typing", "Fatal:", err);
106
- process.exit(1);
107
- });
@@ -1,105 +0,0 @@
1
- import { dirname, join } from "path";
2
- import { existsSync, readFileSync } from "fs";
3
- import { fileURLToPath } from "url";
4
- import { FBClient } from "../../src/index.ts";
5
-
6
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
7
- const ROOT_DIR = join(SCRIPT_DIR, "..", "..");
8
-
9
- const APPSTATE_PATH = join(ROOT_DIR, "tests/appstate.json");
10
- const SESSION_PATH = join(ROOT_DIR, "tests/session.json");
11
- const DEVICE_PATH = join(ROOT_DIR, "tests/device.json");
12
- const ENV_PATH = join(ROOT_DIR, "tests/.env");
13
-
14
- function loadEnvFile(filePath: string): void {
15
- if (!existsSync(filePath)) return;
16
- const raw = readFileSync(filePath, "utf8");
17
- for (const line of raw.split(/\r?\n/)) {
18
- const trimmed = line.trim();
19
- if (!trimmed || trimmed.startsWith("#")) continue;
20
- const idx = trimmed.indexOf("=");
21
- if (idx <= 0) continue;
22
- const key = trimmed.slice(0, idx).trim();
23
- let value = trimmed.slice(idx + 1).trim();
24
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
25
- value = value.slice(1, -1);
26
- }
27
- process.env[key] = value;
28
- }
29
- }
30
-
31
- async function main() {
32
- loadEnvFile(ENV_PATH);
33
-
34
- if (!existsSync(APPSTATE_PATH)) {
35
- console.error("test-group-send", `Missing appstate file at ${APPSTATE_PATH}`);
36
- process.exit(1);
37
- }
38
-
39
- const client = new FBClient({
40
- appStatePath: APPSTATE_PATH,
41
- sessionStorePath: SESSION_PATH,
42
- });
43
-
44
- try {
45
- console.log("test-group-send", `Connecting to Messenger...`);
46
- const { userId } = await client.connect();
47
- console.log("test-group-send", `Connected as User ID: ${userId}`);
48
-
49
- const userDevicePath = join(ROOT_DIR, `device-${userId}.json`);
50
- const finalDevicePath = existsSync(userDevicePath) ? userDevicePath : DEVICE_PATH;
51
-
52
- await client.connectE2EE(finalDevicePath, userId);
53
- console.log("test-group-send", `E2EE Stream active. Waiting for messages...`);
54
-
55
- const targetGroupJid = process.env.TEST_GROUP_JID ?? "1805602490133470@g.us";
56
- const text = process.env.TEST_MESSAGE_TEXT ?? "capture sample from e2ee";
57
- const echoPromise = new Promise<void>((resolve) => {
58
- client.onEvent("e2ee_message", (event) => {
59
- const threadId = (event as any).threadId ?? (event as any).threadID;
60
- const messageText = (event as any).text ?? (event as any).body;
61
- const senderId = (event as any).senderId ?? (event as any).senderID;
62
-
63
- if (threadId !== targetGroupJid) return;
64
-
65
- console.log(
66
- "test-group-send",
67
- `Received group message in ${threadId}: "${messageText}" from ${senderId}`,
68
- );
69
-
70
- if (messageText === text) {
71
- console.log("test-group-send", `✅ RECEIVED echo for our message: "${messageText}"`);
72
- resolve();
73
- }
74
- });
75
- });
76
-
77
- console.log("test-group-send", `Sending message to ${targetGroupJid}: "${text}"`);
78
-
79
- const sendResult = await client.sendMessage({
80
- threadId: targetGroupJid,
81
- text: text,
82
- });
83
-
84
- console.log("test-group-send", `Message send command completed: ${JSON.stringify(sendResult)}`);
85
- console.log("test-group-send", `Waiting for the echoed group message to arrive...`);
86
-
87
- const timeoutPromise = new Promise<void>((_, reject) => {
88
- setTimeout(() => reject(new Error("Message echo not received within 15s")), 15000);
89
- });
90
-
91
- await Promise.race([echoPromise, timeoutPromise]);
92
-
93
- console.log("test-group-send", `SUCCESS: Message verified in group.`);
94
- await new Promise(r => setTimeout(r, 1000));
95
- await client.disconnect();
96
- process.exit(0);
97
-
98
- } catch (err) {
99
- console.error("test-group-send", `Error:`, err);
100
- await client.disconnect().catch(() => { });
101
- process.exit(1);
102
- }
103
- }
104
-
105
- main().catch(console.error);
package/tests/setup.ts DELETED
@@ -1,3 +0,0 @@
1
- // Global test setup
2
- process.env.NODE_ENV = 'test';
3
- delete process.env.DEBUG;