1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const fs_1 = tslib_1.__importDefault(require("fs"));
|
5 | const path_1 = tslib_1.__importDefault(require("path"));
|
6 | const FacebookDeviceId_1 = tslib_1.__importDefault(require("./FacebookDeviceId"));
|
7 | const HttpApi_1 = tslib_1.__importDefault(require("./http/HttpApi"));
|
8 | const MqttApi_1 = tslib_1.__importDefault(require("./mqtt/MqttApi"));
|
9 | const Thread_1 = require("./types/Thread");
|
10 | const User_1 = require("./types/User");
|
11 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
12 | const Message_1 = require("./types/Message");
|
13 | const parseDeltaEvent_1 = tslib_1.__importDefault(require("./types/events/parseDeltaEvent"));
|
14 | const events_1 = tslib_1.__importDefault(require("events"));
|
15 | const Errors_1 = require("./types/Errors");
|
16 | const Payloads = tslib_1.__importStar(require("./mqtt/payloads"));
|
17 | const debugLog = debug_1.default('fblib');
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | class Client extends events_1.default {
|
23 | constructor(options = { selfListen: false, session: null }) {
|
24 | super();
|
25 | this.seqId = '';
|
26 | this.loggedIn = false;
|
27 | this.options = options;
|
28 | this.mqttApi = new MqttApi_1.default();
|
29 | this.httpApi = new HttpApi_1.default();
|
30 | let session = options.session;
|
31 | if (!session) {
|
32 | session = { tokens: null, deviceId: null };
|
33 | }
|
34 | if (options.deviceId) {
|
35 | session.deviceId = options.deviceId;
|
36 | }
|
37 | if (!session.deviceId) {
|
38 | const deviceId = FacebookDeviceId_1.default();
|
39 | session.deviceId = deviceId;
|
40 | this.httpApi.deviceId = deviceId.deviceId;
|
41 | }
|
42 | if (session.tokens) {
|
43 | this.httpApi.token = session.tokens.access_token;
|
44 | }
|
45 | this.session = session;
|
46 | }
|
47 | login(email, password) {
|
48 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
49 |
|
50 | if (this.loggedIn)
|
51 | throw new Error('Already logged in!');
|
52 | if (!email || typeof email !== 'string' || !email.trim() ||
|
53 | !password || typeof password !== 'string' || !password.trim())
|
54 | throw new Error('Wrong username/password!');
|
55 | yield this.doLogin(email, password);
|
56 | this.loggedIn = true;
|
57 | });
|
58 | }
|
59 | doLogin(login, password) {
|
60 | return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
61 | if (!this.session.tokens) {
|
62 | let tokens;
|
63 | try {
|
64 | tokens = yield this.httpApi.auth(login, password);
|
65 | }
|
66 | catch (err) {
|
67 | return reject(err);
|
68 | }
|
69 | this.httpApi.token = tokens.access_token;
|
70 | this.session.tokens = tokens;
|
71 | }
|
72 | this.mqttApi.on('publish', (publish) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
73 | debugLog(publish.topic);
|
74 | if (publish.topic === '/send_message_response') {
|
75 | const response = JSON.parse(publish.data.toString('utf8'));
|
76 | debugLog(response);
|
77 | this.mqttApi.emit('sentMessage:' + response.msgid, response);
|
78 | }
|
79 | if (publish.topic === '/t_ms')
|
80 | this.handleMS(publish.data.toString('utf8'));
|
81 | }));
|
82 | this.mqttApi.on('connected', () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
83 | let viewer;
|
84 | try {
|
85 | ({ viewer } = yield this.httpApi.querySeqId());
|
86 | }
|
87 | catch (err) {
|
88 | return reject(err);
|
89 | }
|
90 | const seqId = viewer.message_threads.sync_sequence_id;
|
91 | this.seqId = seqId;
|
92 | resolve();
|
93 | if (!this.session.tokens.syncToken) {
|
94 | yield this.createQueue(seqId);
|
95 | return;
|
96 | }
|
97 | yield this.createQueue(seqId);
|
98 | }));
|
99 | try {
|
100 | yield this.mqttApi.connect(this.session.tokens, this.session.deviceId);
|
101 | }
|
102 | catch (err) {
|
103 | return reject(err);
|
104 | }
|
105 | }));
|
106 | }
|
107 | getSession() {
|
108 | return this.session;
|
109 | }
|
110 | sendMessage(threadId, message, options) {
|
111 | return this.mqttApi.sendMessage(threadId, message, options);
|
112 | }
|
113 | |
114 |
|
115 |
|
116 |
|
117 | sendPresenceState(recipientUserId, present) {
|
118 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
119 | const payload = new Payloads.PresenceState(recipientUserId, present);
|
120 | return this.mqttApi.sendPublish(payload.getTopic(), yield Payloads.encodePayload(payload));
|
121 | });
|
122 | }
|
123 | |
124 |
|
125 |
|
126 |
|
127 | sendTypingState(threadOrRecipientUserId, present) {
|
128 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
129 | const payload = new Payloads.TypingState(this.session.tokens.uid, present, threadOrRecipientUserId);
|
130 | return this.mqttApi.sendPublish(payload.getTopic(), yield Payloads.encodePayload(payload));
|
131 | });
|
132 | }
|
133 | |
134 |
|
135 |
|
136 | sendReadReceipt(message) {
|
137 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
138 | const payload = new Payloads.ReadReceipt(message);
|
139 | return this.mqttApi.sendPublish(payload.getTopic(), yield Payloads.encodePayload(payload));
|
140 | });
|
141 | }
|
142 | getThreadList(count) {
|
143 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
144 | const threads = yield this.httpApi.threadListQuery(count);
|
145 | return threads.viewer.message_threads.nodes.map(Thread_1.parseThread);
|
146 | });
|
147 | }
|
148 | sendAttachmentFile(threadId, attachmentPath, extension) {
|
149 | if (!fs_1.default.existsSync(attachmentPath))
|
150 | throw new Errors_1.AttachmentNotFoundError(attachmentPath);
|
151 | const stream = fs_1.default.createReadStream(attachmentPath);
|
152 | if (!extension)
|
153 | extension = path_1.default.parse(attachmentPath).ext;
|
154 | const length = fs_1.default.statSync(attachmentPath).size.toString();
|
155 | return this.httpApi.sendImage(stream, extension, this.session.tokens.uid, threadId, length);
|
156 | }
|
157 | sendAttachmentStream(threadId, extension, attachment) {
|
158 | return this.httpApi.sendImage(attachment, extension, this.session.tokens.uid, threadId);
|
159 | }
|
160 | getAttachmentURL(messageId, attachmentId) {
|
161 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
162 | const attachment = yield this.httpApi.getAttachment(messageId, attachmentId);
|
163 | if (!attachment.redirect_uri)
|
164 | throw new Errors_1.AttachmentURLMissingError(attachment);
|
165 | return attachment.redirect_uri;
|
166 | });
|
167 | }
|
168 | getAttachmentInfo(messageId, attachmentId) {
|
169 | return this.httpApi.getAttachment(messageId, attachmentId);
|
170 | }
|
171 | getStickerURL(stickerId) {
|
172 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
173 | const sticker = yield this.httpApi.getSticker(stickerId);
|
174 | return sticker[stickerId.toString()].thread_image.uri;
|
175 | });
|
176 | }
|
177 | getThreadInfo(threadId) {
|
178 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
179 | const res = yield this.httpApi.threadQuery(threadId);
|
180 | const thread = res[threadId];
|
181 | if (!thread)
|
182 | return null;
|
183 | return Thread_1.parseThread(thread);
|
184 | });
|
185 | }
|
186 | getUserInfo(userId) {
|
187 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
188 | const res = yield this.httpApi.userQuery(userId);
|
189 | const user = res[userId];
|
190 | if (!user)
|
191 | return null;
|
192 | return User_1.parseUser(user);
|
193 | });
|
194 | }
|
195 | getMessages(threadId, count) {
|
196 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
197 | const res = yield this.httpApi.threadMessagesQuery(threadId, count);
|
198 | const thread = res[threadId];
|
199 | if (!thread)
|
200 | return null;
|
201 | return thread.messages.nodes.map(message => Message_1.parseThreadMessage(threadId, message));
|
202 | });
|
203 | }
|
204 | createQueue(seqId) {
|
205 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | const obj = ({
|
211 | initial_titan_sequence_id: seqId,
|
212 | delta_batch_size: 125,
|
213 | device_params: {
|
214 | image_sizes: {
|
215 | 0: '4096x4096',
|
216 | 4: '312x312',
|
217 | 1: '768x768',
|
218 | 2: '420x420',
|
219 | 3: '312x312'
|
220 | },
|
221 | animated_image_format: 'WEBP,GIF',
|
222 | animated_image_sizes: {
|
223 | 0: '4096x4096',
|
224 | 4: '312x312',
|
225 | 1: '768x768',
|
226 | 2: '420x420',
|
227 | 3: '312x312'
|
228 | }
|
229 | },
|
230 | entity_fbid: this.session.tokens.uid,
|
231 | sync_api_version: 3,
|
232 | encoding: 'JSON',
|
233 | queue_params: {
|
234 |
|
235 |
|
236 | client_delta_sync_bitmask: 'Amvr2dBlf7PNgA',
|
237 | graphql_query_hashes: {
|
238 | xma_query_id: '306810703252313'
|
239 | },
|
240 | graphql_query_params: {
|
241 | 306810703252313: {
|
242 | xma_id: '<ID>',
|
243 | small_preview_width: 624,
|
244 | small_preview_height: 312,
|
245 | large_preview_width: 1536,
|
246 | large_preview_height: 768,
|
247 | full_screen_width: 4096,
|
248 | full_screen_height: 4096,
|
249 | blur: 0.0,
|
250 | nt_context: {
|
251 | styles_id: 'fe1fd5357bb40c81777dc915dfbd6aa4',
|
252 | pixel_ratio: 3.0
|
253 | }
|
254 | }
|
255 | }
|
256 | }
|
257 | });
|
258 | yield this.mqttApi.sendPublish('/messenger_sync_create_queue', JSON.stringify(obj));
|
259 | });
|
260 | }
|
261 | connectQueue(seqId) {
|
262 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
263 |
|
264 |
|
265 |
|
266 | const obj = {
|
267 | delta_batch_size: 125,
|
268 | max_deltas_able_to_process: 1250,
|
269 | sync_api_version: 10,
|
270 | encoding: 'JSON',
|
271 | last_seq_id: seqId,
|
272 | sync_token: this.session.tokens.syncToken
|
273 | };
|
274 | yield this.mqttApi.sendPublish('/messenger_sync_get_diffs', JSON.stringify(obj));
|
275 | });
|
276 | }
|
277 | handleMS(ms) {
|
278 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
279 | let data;
|
280 | try {
|
281 | data = JSON.parse(ms.replace('\u0000', ''));
|
282 | }
|
283 | catch (err) {
|
284 | console.error('Error while parsing the following message:');
|
285 | console.error(ms);
|
286 | return;
|
287 | }
|
288 |
|
289 | if (data.syncToken) {
|
290 | this.session.tokens.syncToken = data.syncToken;
|
291 | yield this.connectQueue(this.seqId);
|
292 | return;
|
293 | }
|
294 | if (!data.deltas || !Array.isArray(data.deltas))
|
295 | return;
|
296 | data.deltas.forEach(delta => {
|
297 | debugLog(delta);
|
298 | this.handleMessage(delta);
|
299 | });
|
300 | });
|
301 | }
|
302 | handleMessage(event) {
|
303 | if (event.deltaNewMessage) {
|
304 | const message = Message_1.parseDeltaMessage(event.deltaNewMessage);
|
305 | if (!message || message.authorId === this.session.tokens.uid && !this.options.selfListen)
|
306 | return;
|
307 | this.emit('message', message);
|
308 | }
|
309 | const deltaEvent = parseDeltaEvent_1.default(event);
|
310 | if (!deltaEvent)
|
311 | return;
|
312 | this.emit('event', deltaEvent);
|
313 |
|
314 | this.emit(deltaEvent.type, deltaEvent.event);
|
315 | }
|
316 | }
|
317 | exports.default = Client;
|
318 |
|
\ | No newline at end of file |