UNPKG

10.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const fs_1 = tslib_1.__importDefault(require("fs"));
5const path_1 = tslib_1.__importDefault(require("path"));
6const FacebookDeviceId_1 = tslib_1.__importDefault(require("./FacebookDeviceId"));
7const HttpApi_1 = tslib_1.__importDefault(require("./http/HttpApi"));
8const MqttApi_1 = tslib_1.__importDefault(require("./mqtt/MqttApi"));
9const Thread_1 = require("./types/Thread");
10const User_1 = require("./types/User");
11const debug_1 = tslib_1.__importDefault(require("debug"));
12const Message_1 = require("./types/Message");
13const parseDeltaMessage_1 = tslib_1.__importDefault(require("./types/message/parseDeltaMessage"));
14const parseDeltaEvent_1 = tslib_1.__importDefault(require("./types/events/parseDeltaEvent"));
15const events_1 = tslib_1.__importDefault(require("events"));
16const Errors_1 = require("./types/Errors");
17const debugLog = debug_1.default('fblib');
18// 🥖
19/**
20 * Main client class
21 */
22class Client extends events_1.default {
23 constructor(options = { selfListen: false, session: null }) {
24 super();
25 this.seqId = '';
26 this.loggedIn = false;
27 this.getThreadList = (count) => tslib_1.__awaiter(this, void 0, void 0, function* () {
28 const threads = yield this.httpApi.threadListQuery(count);
29 return threads.viewer.message_threads.nodes.map(Thread_1.parseThread);
30 });
31 this.mqttApi = new MqttApi_1.default();
32 this.httpApi = new HttpApi_1.default();
33 let session = options.session;
34 if (!session) {
35 session = { tokens: null, deviceId: null };
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 // trim to check for spaces (which are truthy)
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) {
111 return this.mqttApi.sendMessage(threadId, message);
112 }
113 sendAttachmentFile(threadId, attachmentPath, extension) {
114 if (!fs_1.default.existsSync(attachmentPath))
115 throw new Errors_1.AttachmentNotFoundError(attachmentPath);
116 const stream = fs_1.default.createReadStream(attachmentPath);
117 if (!extension)
118 extension = path_1.default.parse(attachmentPath).ext;
119 const length = fs_1.default.statSync(attachmentPath).size.toString();
120 return this.httpApi.sendImage(stream, extension, this.session.tokens.uid, threadId, length);
121 }
122 sendAttachmentStream(threadId, extension, attachment) {
123 return this.httpApi.sendImage(attachment, extension, this.session.tokens.uid, threadId);
124 }
125 getAttachmentURL(messageId, attachmentId) {
126 return tslib_1.__awaiter(this, void 0, void 0, function* () {
127 const attachment = yield this.httpApi.getAttachment(messageId, attachmentId);
128 if (!attachment.redirect_uri)
129 throw new Errors_1.AttachmentURLMissingError(attachment);
130 return attachment.redirect_uri;
131 });
132 }
133 getAttachmentInfo(messageId, attachmentId) {
134 return this.httpApi.getAttachment(messageId, attachmentId);
135 }
136 getStickerURL(stickerId) {
137 return tslib_1.__awaiter(this, void 0, void 0, function* () {
138 const sticker = yield this.httpApi.getSticker(stickerId);
139 return sticker[stickerId.toString()].thread_image.uri;
140 });
141 }
142 getThreadInfo(threadId) {
143 return tslib_1.__awaiter(this, void 0, void 0, function* () {
144 const res = yield this.httpApi.threadQuery(threadId);
145 const thread = res[threadId];
146 if (!thread)
147 return null;
148 return Thread_1.parseThread(thread);
149 });
150 }
151 getUserInfo(userId) {
152 return tslib_1.__awaiter(this, void 0, void 0, function* () {
153 const res = yield this.httpApi.userQuery(userId);
154 const user = res[userId];
155 if (!user)
156 return null;
157 return User_1.parseUser(user);
158 });
159 }
160 getMessages(threadId, count) {
161 return tslib_1.__awaiter(this, void 0, void 0, function* () {
162 const res = yield this.httpApi.threadMessagesQuery(threadId, count);
163 const thread = res[threadId];
164 if (!thread)
165 return null;
166 return thread.messages.nodes.map(message => Message_1.parseThreadMessage(threadId, message));
167 });
168 }
169 createQueue(seqId) {
170 return tslib_1.__awaiter(this, void 0, void 0, function* () {
171 const obj = {
172 delta_batch_size: 125,
173 max_deltas_able_to_process: 1250,
174 sync_api_version: 3,
175 encoding: 'JSON',
176 initial_titan_sequence_id: seqId,
177 device_id: this.session.deviceId.deviceId,
178 entity_fbid: this.session.tokens.uid,
179 queue_params: {
180 buzz_on_deltas_enabled: 'false',
181 graphql_query_hashes: {
182 xma_query_id: '10153919431161729'
183 },
184 graphql_query_params: {
185 '10153919431161729': {
186 xma_id: '<ID>'
187 }
188 }
189 }
190 };
191 yield this.mqttApi.sendPublish('/messenger_sync_create_queue', JSON.stringify(obj));
192 });
193 }
194 connectQueue(seqId) {
195 return tslib_1.__awaiter(this, void 0, void 0, function* () {
196 const obj = {
197 delta_batch_size: 125,
198 max_deltas_able_to_process: 1250,
199 sync_api_version: 3,
200 encoding: 'JSON',
201 last_seq_id: seqId,
202 sync_token: this.session.tokens.syncToken
203 };
204 yield this.mqttApi.sendPublish('/messenger_sync_get_diffs', JSON.stringify(obj));
205 });
206 }
207 handleMS(ms) {
208 return tslib_1.__awaiter(this, void 0, void 0, function* () {
209 let data;
210 try {
211 data = JSON.parse(ms.replace('\u0000', ''));
212 }
213 catch (err) {
214 console.error('Error while parsing the following message:');
215 console.error(ms);
216 return;
217 }
218 // Handled on queue creation
219 if (data.syncToken) {
220 this.session.tokens.syncToken = data.syncToken;
221 yield this.connectQueue(this.seqId);
222 return;
223 }
224 if (!data.deltas || !Array.isArray(data.deltas))
225 return;
226 data.deltas.forEach(delta => {
227 debugLog(delta);
228 this.handleMessage(delta);
229 });
230 });
231 }
232 handleMessage(event) {
233 if (event.deltaNewMessage) {
234 const message = parseDeltaMessage_1.default(event.deltaNewMessage);
235 if (!message || message.authorId === this.session.tokens.uid && !this.options.selfListen)
236 return;
237 this.emit('message', message);
238 }
239 const deltaEvent = parseDeltaEvent_1.default(event);
240 if (!deltaEvent)
241 return;
242 this.emit('event', deltaEvent);
243 // @ts-ignore TypeScript somehow doesn't recognize that EventType is compatible with the properties defined in ClientEvents
244 this.emit(deltaEvent.type, deltaEvent.event);
245 }
246}
247exports.default = Client;
248//# sourceMappingURL=Client.js.map
\No newline at end of file